def __init__(self, model, **kwargs): self.client = JSONAPIClient() self.model = model self._fields = kwargs.get("fields", {}) self._include = kwargs.get("include", []) self._filters = kwargs.get("filters", {}) self._sort = kwargs.get("sort", []) self._cache = None
def test_jsonapi_client_get_with_include_empty(mock_requests): expected_params = { "fields[related_records]": "name,other_related", } url = f"http://example.com/related-records/?{urlencode(expected_params)}" mock_requests.get(url, json={}) client = JSONAPIClient() client.get("related_records", include=[]) assert mock_requests.called assert mock_requests.last_request.url == url
def test_jsonapi_client_get_record_default(mock_requests): expected_params = { "fields[related_records]": "name,other_related", "include": "other_related", } url = f"http://example.com/related-records/42/?{urlencode(expected_params)}" mock_requests.get(url, json={}) client = JSONAPIClient() client.get("related_records", resource_id=42) assert mock_requests.called assert mock_requests.last_request.url == url
def test_jsonapi_client_get_with_fields_override_resource_default( mock_requests): expected_params = { "fields[related_records]": "name", "include": "other_related", } url = f"http://example.com/related-records/?{urlencode(expected_params)}" mock_requests.get(url, json="{}") client = JSONAPIClient() client.get("related_records", fields={"related_records": ["name"]}) assert mock_requests.called assert mock_requests.last_request.url == url
def test_jsonapi_client_get_with_page_number(mock_requests): expected_params = { "fields[related_records]": "name,other_related", "include": "other_related", "page[number]": 12, } url = f"http://example.com/related-records/?{urlencode(expected_params)}" mock_requests.get(url, json="{}") client = JSONAPIClient() client.get("related_records", page_number=12) assert mock_requests.called assert mock_requests.last_request.url == url
def test_jsonapi_client_get_with_sort(mock_requests): expected_params = { "fields[related_records]": "name,other_related", "include": "other_related", "sort": "-name,other_related", } url = f"http://example.com/related-records/?{urlencode(expected_params)}" mock_requests.get(url, json="{}") client = JSONAPIClient() client.get("related_records", sort=["-name", "other_related"]) assert mock_requests.called assert mock_requests.last_request.url == url
def test_jsonapi_client_get_handles_http_errors(mock_requests): expected_params = { "include": "other_related", "fields[related_records]": "name,other_related", } mock_requests.register_uri( "GET", f"http://example.com/related-records/?{urlencode(expected_params)}", status_code=400, json={"error": "some error"}, ) client = JSONAPIClient() with pytest.raises(JSONAPIClientError): client.get("related_records") assert mock_requests.called
def test_jsonapi_client_get_with_fields_for_related(mock_requests): expected_params = { "fields[other_related]": "something", "fields[related_records]": "name,other_related", "include": "other_related", } url = f"http://example.com/related-records/?{urlencode(expected_params)}" mock_requests.get(url, json={}) client = JSONAPIClient() client.get("related_records", fields={ "other_related": ["something"], "not_used": [] }) assert mock_requests.called assert mock_requests.last_request.url == url
def test_jsonapi_client_session_headers(mock_version): del settings.DJANGO_JSON_API_ADDITIONAL_HEADERS client = JSONAPIClient() assert client.session.headers["Accept"] == "application/vnd.api+json" assert client.session.headers["Content-Type"] == "application/vnd.api+json" assert client.session.headers["User-Agent"] == "JSONAPIClient/0.1.2" setattr( settings, "DJANGO_JSON_API_ADDITIONAL_HEADERS", { "User-Agent": f"SW_{settings.SERVICE}/JsonAPI", "X-SW-service": settings.SERVICE, }, ) client = JSONAPIClient() assert client.session.headers["Accept"] == "application/vnd.api+json" assert client.session.headers["Content-Type"] == "application/vnd.api+json" assert client.session.headers["User-Agent"] == "SW_test/JsonAPI" assert client.session.headers["X-SW-service"] == "test"
def _fetch_iterate(self) -> Iterator: client = JSONAPIClient() client.session.headers["X-No-Count"] = "true" page_size = getattr(self.model._meta, "page_size", 50) page_number = 1 while True: page = client.get( self.resource_type, filters=self._filters, include=self._include or None, fields=self._fields, sort=self._sort, page_size=page_size, page_number=page_number, ) included = page.get("included") or [] data = page.get("data") page_number += 1 self.model.from_resources(included) yield from self.model.from_resources(data) next_url = page.get("links", {}).get("next") if next_url is None: break
class JSONAPIManager: def __init__(self, model, **kwargs): self.client = JSONAPIClient() self.model = model self._fields = kwargs.get("fields", {}) self._include = kwargs.get("include", []) self._filters = kwargs.get("filters", {}) self._sort = kwargs.get("sort", []) self._cache = None def modify(self, **kwargs) -> "JSONAPIManager": _fields = { **self._fields, **kwargs.get("fields", {}), } _filters = { **self._filters, **kwargs.get("filters", {}), } _include = deepcopy(self._include) _include.extend(list(kwargs.get("include", []))) _sort = deepcopy(self._sort) _sort.extend(list(kwargs.get("sort", []))) return JSONAPIManager( model=self.model, fields=_fields, filters=_filters, include=_include, sort=_sort, ) def sort(self, *args) -> "JSONAPIManager": return self.modify(sort=args) def filter(self, **kwargs) -> "JSONAPIManager": return self.modify(filters=kwargs) def include(self, *args) -> "JSONAPIManager": return self.modify(include=args) def fields(self, **kwargs) -> "JSONAPIManager": return self.modify(fields=kwargs) @property def resource_type(self) -> str: return self.model._meta.resource_type def _fetch_get(self, resource_id: Union[str, int] = None) -> dict: return self.client.get( self.resource_type, resource_id=resource_id, filters=self._filters, include=self._include or None, fields=self._fields, sort=self._sort, ) def _fetch_iterate(self) -> Iterator: client = JSONAPIClient() client.session.headers["X-No-Count"] = "true" page_size = getattr(self.model._meta, "page_size", 50) page_number = 1 while True: page = client.get( self.resource_type, filters=self._filters, include=self._include or None, fields=self._fields, sort=self._sort, page_size=page_size, page_number=page_number, ) included = page.get("included") or [] data = page.get("data") page_number += 1 self.model.from_resources(included) yield from self.model.from_resources(data) next_url = page.get("links", {}).get("next") if next_url is None: break def _fetch_all(self) -> List["JSONAPIModel"]: # noqa if self._cache is None: self._cache = list(self._fetch_iterate()) return self._cache def count(self) -> int: data = self.client.get( self.resource_type, include=[], fields={self.resource_type: []}, filters=self._filters, page_size=1, ) return data.get("meta", {}).get("record_count") def iterator(self) -> Iterator["JSONAPIModel"]: # noqa return self._fetch_iterate() def all(self) -> List["JSONAPIModel"]: # noqa return self._fetch_all() def get(self, pk, ignore_cache=False) -> "JSONAPIModel": # noqa record = self.model.from_cache(pk) if record is None or ignore_cache: document = self._fetch_get(resource_id=pk) data = document["data"] record = self.model.from_resource(data) self.model.from_resources(document.get("included") or []) return record def __getitem__(self, k) -> "JSONAPIModel": # noqa self._fetch_all() return self._cache[k] def __iter__(self) -> Iterator["JSONAPIModel"]: # noqa self._fetch_all() return iter(self._cache) def __bool__(self: "JSONAPIManager") -> bool: self._fetch_all() return bool(self._cache)
def test_jsonapi_get_unresolved_resource(): client = JSONAPIClient() with pytest.raises(JSONAPIClientError, match=r'Cannot resolve resource "unresolvable"'): client.get("unresolvable")