Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions campus_python/json_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,29 +169,50 @@ def post(self: Self, path: str, json: JsonDict | None = None) -> JsonResponse:
raise errors.ServerError(error_description=str(exc)) from None
return CampusResponse(resp)

def put(self: Self, path: str, json: JsonDict | None = None) -> JsonResponse:
def put(
self: Self,
path: str,
json: JsonDict | None = None,
query: JsonDict | None = None
) -> JsonResponse:
"""Sends a PUT request."""
url = self._build_url(path)
try:
resp = self._session.put(url, json=json, timeout=self._timeout)
resp = self._session.put(
url, json=json, params=query, timeout=self._timeout
)
except requests.RequestException as exc:
raise errors.ServerError(error_description=str(exc)) from None
return CampusResponse(resp)

def delete(self: Self, path: str, json: JsonDict | None = None) -> JsonResponse:
def delete(
self: Self,
path: str,
json: JsonDict | None = None,
query: JsonDict | None = None
) -> JsonResponse:
"""Sends a DELETE request."""
url = self._build_url(path)
try:
resp = self._session.delete(url, json=json, timeout=self._timeout)
resp = self._session.delete(
url, json=json, params=query, timeout=self._timeout
)
except requests.RequestException as exc:
raise errors.ServerError(error_description=str(exc)) from None
return CampusResponse(resp)

def patch(self: Self, path: str, json: Any = None) -> JsonResponse:
def patch(
self: Self,
path: str,
json: Any = None,
query: JsonDict | None = None
) -> JsonResponse:
"""Sends a PATCH request."""
url = self._build_url(path)
try:
resp = self._session.patch(url, json=json, timeout=self._timeout)
resp = self._session.patch(
url, json=json, params=query, timeout=self._timeout
)
except requests.RequestException as exc:
raise errors.ServerError(error_description=str(exc)) from None
return CampusResponse(resp)
21 changes: 18 additions & 3 deletions campus_python/json_client/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,28 @@ def post(self: Self, path: str, json: JsonDict | None = None) -> JsonResponse:
"""Sends a POST request."""

@abstractmethod
def put(self: Self, path: str, json: JsonDict | None = None) -> JsonResponse:
def put(
self: Self,
path: str,
json: JsonDict | None = None,
query: JsonDict | None = None
) -> JsonResponse:
"""Sends a PUT request."""

@abstractmethod
def delete(self: Self, path: str, json: JsonDict | None = None) -> JsonResponse:
def delete(
self: Self,
path: str,
json: JsonDict | None = None,
query: JsonDict | None = None
) -> JsonResponse:
"""Sends a DELETE request."""

@abstractmethod
def patch(self: Self, path: str, json: Any = None) -> JsonResponse:
def patch(
self: Self,
path: str,
json: Any = None,
query: JsonDict | None = None
) -> JsonResponse:
"""Sends a PATCH request."""
56 changes: 56 additions & 0 deletions tests/unit/test_json_client_query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""Tests for the query= parameter on non-GET JsonClient verbs.

get() has always taken query=; put/delete/patch gained it so callers
don't hand-build URLs when a non-GET endpoint reads the query string
(e.g. campus.auth's DELETE /auth/v1/connections/.../?user_id=...).
"""

import os
import unittest
from unittest import mock

os.environ.setdefault("CLIENT_ID", "test-client-id")
os.environ.setdefault("CLIENT_SECRET", "test-client-secret")

from campus_python.json_client import CampusRequest


class TestQueryKwargOnNonGetVerbs(unittest.TestCase):
"""query= rides the session params on every verb, not just get()."""

def setUp(self):
self.client = CampusRequest(
base_url="https://auth.example.test", mode="device"
)

def _assert_params_passed(self, verb: str, session_method: mock.Mock):
self.client._timeout = 5
with mock.patch.object(
self.client._session, session_method,
return_value=mock.Mock()) as method:
getattr(self.client, verb)(
"/some/path", json={"k": "v"}, query={"user_id": "u"})
_, kwargs = method.call_args
self.assertEqual(kwargs["json"], {"k": "v"})
self.assertEqual(kwargs["params"], {"user_id": "u"})

def test_put_passes_query_as_params(self):
self._assert_params_passed("put", "put")

def test_delete_passes_query_as_params(self):
self._assert_params_passed("delete", "delete")

def test_patch_passes_query_as_params(self):
self._assert_params_passed("patch", "patch")

def test_query_defaults_to_none(self):
"""Omitting query= sends params=None, preserving old behavior."""
with mock.patch.object(
self.client._session, "delete",
return_value=mock.Mock()) as delete:
self.client.delete("/some/path")
self.assertIsNone(delete.call_args.kwargs["params"])


if __name__ == "__main__":
unittest.main()
Loading