From acbf731d369b0adfd2da9268008ca458848a20b1 Mon Sep 17 00:00:00 2001 From: Oskar Hollmann Date: Thu, 2 Nov 2023 14:16:16 +0100 Subject: [PATCH] refactor: Use an enum instead of strings to identify API resources This is not only cleaner but will be useful when implementing custom de-serializers. --- rossum_api/api_client.py | 113 +++++++++++++++----- rossum_api/elis_api_client.py | 89 ++++++++------- rossum_api/elis_api_client_sync.py | 8 +- tests/elis_api_client/test_annotations.py | 29 ++--- tests/elis_api_client/test_client_sync.py | 2 +- tests/elis_api_client/test_connectors.py | 13 +-- tests/elis_api_client/test_hooks.py | 13 +-- tests/elis_api_client/test_inboxes.py | 5 +- tests/elis_api_client/test_organizations.py | 13 +-- tests/elis_api_client/test_queues.py | 33 +++--- tests/elis_api_client/test_schemas.py | 17 +-- tests/elis_api_client/test_user_roles.py | 5 +- tests/elis_api_client/test_users.py | 13 +-- tests/elis_api_client/test_workspaces.py | 17 +-- tests/test_api_client.py | 44 ++++---- 15 files changed, 246 insertions(+), 168 deletions(-) diff --git a/rossum_api/api_client.py b/rossum_api/api_client.py index 532b770..dce4f9a 100644 --- a/rossum_api/api_client.py +++ b/rossum_api/api_client.py @@ -7,6 +7,7 @@ import logging import random import typing +from enum import Enum import httpx @@ -20,6 +21,25 @@ logger = logging.getLogger(__name__) +class Resource(Enum): + """Convenient representation of resources provided by Elis API. + + Value is always the corresponding URL part. + """ + + Annotation = "annotations" + Auth = "auth" + Connector = "connectors" + Group = "groups" + Hook = "hooks" + Inbox = "inboxes" + Organization = "organizations" + Queue = "queues" + Schema = "schemas" + User = "users" + Workspace = "workspaces" + + class APIClientError(Exception): def __init__(self, status_code, error): self.status_code = status_code @@ -126,13 +146,13 @@ def __init__( def _headers(self): return {"Authorization": f"token {self.token}"} - async def fetch_one(self, resource: str, id_: Union[int, str]) -> Dict[str, Any]: + async def fetch_one(self, resource: Resource, id_: Union[int, str]) -> Dict[str, Any]: """Retrieve a single object in a specific resource.""" - return await self.request_json("GET", f"{resource}/{id_}") + return await self.request_json("GET", f"{resource.value}/{id_}") async def fetch_all( self, - resource: str, + resource: Resource, ordering: Sequence[str] = (), sideloads: Sequence[str] = (), content_schema_ids: Sequence[str] = (), @@ -165,6 +185,53 @@ async def fetch_all( filters mapping from resource field to value used to filter records """ + async for result in self.fetch_all_by_url( + resource.value, + ordering, + sideloads, + content_schema_ids, + method, + max_pages, + json, + **filters, + ): + yield result + + async def fetch_all_by_url( + self, + url: str, + ordering: Sequence[str] = (), + sideloads: Sequence[str] = (), + content_schema_ids: Sequence[str] = (), + method: str = "GET", + max_pages: Optional[int] = None, + json: Optional[dict] = None, + **filters: Any, + ) -> AsyncIterator[Dict[str, Any]]: + """Retrieve a list of objects from a specified URL. + + Arguments + --------- + url + url relative to the Elis API domain, e.g. "/annotations/search" + ordering + comma-delimited fields of the resource, prepend the field with - for descending + sideloads + A sequence of resources to be fetched along with the requested resource, + e.g. ["content", "automation_blockers"] when fetching `annotations` resource. + content_schema_ids + sideloads only particular `content` fields when fetching `annotations` resource, + has no effect when fetching other resources + method + export endpoints have different semantics when POST is used, allow customization of + method so that export() can re-use fetch_all() implementation + max_pages + maximum number of pages to fetch + json + json payload sent with the request. Used for POST requests. + filters + mapping from resource field to value used to filter records + """ query_params = { "page_size": 100, "ordering": ",".join(ordering), @@ -173,7 +240,7 @@ async def fetch_all( **filters, } results, total_pages = await self._fetch_page( - f"{resource}", method, query_params, sideloads, json=json + url, method, query_params, sideloads, json=json ) # Fire async tasks to fetch the rest of the pages and start yielding results from page 1 last_page = min(total_pages, max_pages or total_pages) @@ -183,11 +250,7 @@ async def fetch_all( async def _fetch_page(page_number): async with in_flight_guard: return await self._fetch_page( - f"{resource}", - method, - {**query_params, "page": page_number}, - sideloads, - json=json, + url, method, {**query_params, "page": page_number}, sideloads, json=json ) page_requests = [asyncio.create_task(_fetch_page(i)) for i in range(2, last_page + 1)] @@ -202,13 +265,13 @@ async def _fetch_page(page_number): async def _fetch_page( self, - resource: str, + url: str, method: str, query_params: Dict[str, Any], sideload_groups: Sequence[str], json: Optional[dict] = None, ) -> Tuple[List[Dict[str, Any]], int]: - data = await self.request_json(method, resource, params=query_params, json=json) + data = await self.request_json(method, url, params=query_params, json=json) self._embed_sideloads(data, sideload_groups) return data["results"], data["pagination"]["total_pages"] @@ -248,28 +311,28 @@ def annotation_id(datapoint): sideload_id, [] ) # `content` can have 0 datapoints, use [] default value in this case - async def create(self, resource: str, data: Dict[str, Any]) -> Dict[str, Any]: + async def create(self, resource: Resource, data: Dict[str, Any]) -> Dict[str, Any]: """Create a new object.""" - return await self.request_json("POST", resource, json=data) + return await self.request_json("POST", resource.value, json=data) - async def replace(self, resource: str, id_: int, data: Dict[str, Any]) -> Dict[str, Any]: + async def replace(self, resource: Resource, id_: int, data: Dict[str, Any]) -> Dict[str, Any]: "Modify an entire existing object." - return await self.request_json("PUT", f"{resource}/{id_}", json=data) + return await self.request_json("PUT", f"{resource.value}/{id_}", json=data) - async def update(self, resource: str, id_: int, data: Dict[str, Any]) -> Dict[str, Any]: + async def update(self, resource: Resource, id_: int, data: Dict[str, Any]) -> Dict[str, Any]: "Modify particular fields of an existing object." - return await self.request_json("PATCH", f"{resource}/{id_}", json=data) + return await self.request_json("PATCH", f"{resource.value}/{id_}", json=data) - async def delete(self, resource: str, id_: int) -> None: + async def delete(self, resource: Resource, id_: int) -> None: """Delete a particular object. Use with caution: For some objects, it triggers a cascade delete of related objects. """ - await self._request("DELETE", f"{resource}/{id_}") + await self._request("DELETE", f"{resource.value}/{id_}") async def upload( self, - resource: str, + resource: Resource, id_: int, fp: AsyncBufferedReader, filename: str, @@ -288,7 +351,7 @@ async def upload( may be used to initialize values of the object created from the uploaded file, semantics is different for each resource """ - url = f"{resource}/{id_}/upload" + url = f"{resource.value}/{id_}/upload" files = {"content": (filename, await fp.read(), "application/octet-stream")} # Filename of values and metadata must be "", otherwise Elis API returns HTTP 400 with body @@ -301,7 +364,7 @@ async def upload( async def export( self, - resource: str, + resource: Resource, id_: int, export_format: str, columns: Sequence[str] = (), @@ -313,13 +376,13 @@ async def export( query_params = {**query_params, **filters} if columns: query_params["columns"] = ",".join(columns) - url = f"{resource}/{id_}/export" + url = f"{resource.value}/{id_}/export" # to_status parameter is valid only in POST requests, we can use GET in all other cases method = "POST" if "to_status" in filters else "GET" if export_format == "json": # JSON export is paginated just like a regular fetch_all, it abuses **filters kwargs of - # fetch_all to pass export-specific query params - async for result in self.fetch_all(url, method=method, **query_params): # type: ignore + # fetch_all_by_url to pass export-specific query params + async for result in self.fetch_all_by_url(url, method=method, **query_params): # type: ignore yield result else: # In CSV/XML/XLSX case, all annotations are returned, i.e. the response can be large, diff --git a/rossum_api/elis_api_client.py b/rossum_api/elis_api_client.py index d2d8b76..96d6f3a 100644 --- a/rossum_api/elis_api_client.py +++ b/rossum_api/elis_api_client.py @@ -7,7 +7,7 @@ import aiofiles import dacite -from rossum_api.api_client import APIClient +from rossum_api.api_client import APIClient, Resource from rossum_api.models.annotation import Annotation from rossum_api.models.connector import Connector from rossum_api.models.hook import Hook @@ -53,7 +53,7 @@ async def retrieve_queue( queue_id: int, ) -> Queue: """https://elis.rossum.ai/api/docs/#retrieve-a-queue-2.""" - queue = await self._http_client.fetch_one("queues", queue_id) + queue = await self._http_client.fetch_one(Resource.Queue, queue_id) return dacite.from_dict(Queue, queue) @@ -63,18 +63,18 @@ async def list_all_queues( **filters: Any, ) -> AsyncIterable[Queue]: """https://elis.rossum.ai/api/docs/#list-all-queues.""" - async for q in self._http_client.fetch_all("queues", ordering, **filters): + async for q in self._http_client.fetch_all(Resource.Queue, ordering, **filters): yield dacite.from_dict(Queue, q) async def create_new_queue(self, data: Dict[str, Any]) -> Queue: """https://elis.rossum.ai/api/docs/#create-new-queue.""" - queue = await self._http_client.create("queues", data) + queue = await self._http_client.create(Resource.Queue, data) return dacite.from_dict(Queue, queue) async def delete_queue(self, queue_id: int) -> None: """https://elis.rossum.ai/api/docs/#delete-a-queue.""" - return await self._http_client.delete("queues", queue_id) + return await self._http_client.delete(Resource.Queue, queue_id) async def import_document( self, @@ -109,7 +109,7 @@ async def import_document( async def _upload(self, file, queue_id, filename, values, metadata) -> int: async with aiofiles.open(file, "rb") as fp: results = await self._http_client.upload( - "queues", queue_id, fp, filename, values, metadata + Resource.Queue, queue_id, fp, filename, values, metadata ) (result,) = results["results"] # We're uploading 1 file in 1 request, we can unpack return int(result["annotation"].split("/")[-1]) @@ -122,7 +122,7 @@ async def export_annotations_to_json( JSON export is paginated and returns the result in a way similar to other list_all methods. """ - async for chunk in self._http_client.export("queues", queue_id, "json"): + async for chunk in self._http_client.export(Resource.Queue, queue_id, "json"): # JSON export can be translated directly to Annotation object yield dacite.from_dict(Annotation, typing.cast(typing.Dict, chunk)) @@ -133,7 +133,7 @@ async def export_annotations_to_file( XLSX/CSV/XML exports can be huge, therefore byte streaming is used to keep memory consumption low. """ - async for chunk in self._http_client.export("queues", queue_id, str(export_format)): + async for chunk in self._http_client.export(Resource.Queue, queue_id, str(export_format)): yield typing.cast(bytes, chunk) # ##### ORGANIZATIONS ##### @@ -143,18 +143,20 @@ async def list_all_organizations( **filters: Any, ): """https://elis.rossum.ai/api/docs/#list-all-organizations.""" - async for o in self._http_client.fetch_all("organizations", ordering, **filters): + async for o in self._http_client.fetch_all(Resource.Organization, ordering, **filters): yield dacite.from_dict(Organization, o) async def retrieve_organization(self, org_id: int) -> Organization: """https://elis.rossum.ai/api/docs/#retrieve-an-organization.""" - organization: Dict[Any, Any] = await self._http_client.fetch_one("organizations", org_id) + organization: Dict[Any, Any] = await self._http_client.fetch_one( + Resource.Organization, org_id + ) return dacite.from_dict(Organization, organization) async def retrieve_own_organization(self) -> Organization: - """Retrive organization of currently logged in user.""" - user: Dict[Any, Any] = await self._http_client.fetch_one("auth", "user") + """Retrieve organization of currently logged in user.""" + user: Dict[Any, Any] = await self._http_client.fetch_one(Resource.Auth, "user") organization_id = user["organization"].split("/")[-1] return await self.retrieve_organization(organization_id) @@ -165,24 +167,24 @@ async def list_all_schemas( **filters: Any, ) -> AsyncIterable[Schema]: """https://elis.rossum.ai/api/docs/#list-all-schemas.""" - async for s in self._http_client.fetch_all("schemas", ordering, **filters): + async for s in self._http_client.fetch_all(Resource.Schema, ordering, **filters): yield dacite.from_dict(Schema, s) async def retrieve_schema(self, schema_id: int) -> Schema: """https://elis.rossum.ai/api/docs/#retrieve-a-schema.""" - schema: Dict[Any, Any] = await self._http_client.fetch_one("schemas", schema_id) + schema: Dict[Any, Any] = await self._http_client.fetch_one(Resource.Schema, schema_id) return dacite.from_dict(Schema, schema) async def create_new_schema(self, data: Dict[str, Any]) -> Schema: """https://elis.rossum.ai/api/docs/#create-a-new-schema.""" - queue = await self._http_client.create("schemas", data) + queue = await self._http_client.create(Resource.Schema, data) return dacite.from_dict(Schema, queue) async def delete_schema(self, schema_id) -> None: """https://elis.rossum.ai/api/docs/#delete-a-schema.""" - return await self._http_client.delete("schemas", schema_id) + return await self._http_client.delete(Resource.Schema, schema_id) # ##### USERS ##### async def list_all_users( @@ -191,18 +193,18 @@ async def list_all_users( **filters: Any, ) -> AsyncIterable[User]: """https://elis.rossum.ai/api/docs/#list-all-users.""" - async for u in self._http_client.fetch_all("users", ordering, **filters): + async for u in self._http_client.fetch_all(Resource.User, ordering, **filters): yield dacite.from_dict(User, u) async def retrieve_user(self, user_id: int) -> User: """https://elis.rossum.ai/api/docs/#retrieve-a-user-2.""" - user = await self._http_client.fetch_one("users", user_id) + user = await self._http_client.fetch_one(Resource.User, user_id) return dacite.from_dict(User, user) async def create_new_user(self, data: Dict[str, Any]) -> User: """https://elis.rossum.ai/api/docs/#create-new-user.""" - user = await self._http_client.create("users", data) + user = await self._http_client.create(Resource.User, data) return dacite.from_dict(User, user) @@ -228,7 +230,7 @@ async def list_all_annotations( 'When content sideloading is requested, "content_schema_ids" must be provided' ) async for a in self._http_client.fetch_all( - "annotations", ordering, sideloads, content_schema_ids, **filters + Resource.Annotation, ordering, sideloads, content_schema_ids, **filters ): yield dacite.from_dict(Annotation, a) @@ -249,8 +251,13 @@ async def search_for_annotations( if query_string: json_payload["query_string"] = query_string - async for a in self._http_client.fetch_all( - "annotations/search", ordering, sideloads, json=json_payload, method="POST", **kwargs + async for a in self._http_client.fetch_all_by_url( + f"{Resource.Annotation.value}/search", + ordering, + sideloads, + json=json_payload, + method="POST", + **kwargs, ): yield dacite.from_dict(Annotation, a) @@ -258,7 +265,7 @@ async def retrieve_annotation( self, annotation_id: int, sideloads: Sequence[str] = () ) -> Annotation: """https://elis.rossum.ai/api/docs/#retrieve-an-annotation.""" - annotation_json = await self._http_client.fetch_one("annotations", annotation_id) + annotation_json = await self._http_client.fetch_one(Resource.Annotation, annotation_id) if sideloads: await self._sideload(annotation_json, sideloads) return dacite.from_dict(Annotation, annotation_json) @@ -274,13 +281,13 @@ async def poll_annotation( Sideloading is done only once after the predicate becomes true to avoid spaming the server. """ - annotation_json = await self._http_client.fetch_one("annotations", annotation_id) + annotation_json = await self._http_client.fetch_one(Resource.Annotation, annotation_id) # Parse early, we want predicate to work with Annotation instances for convenience annotation = dacite.from_dict(Annotation, annotation_json) while not predicate(annotation): await asyncio.sleep(sleep_s) - annotation_json = await self._http_client.fetch_one("annotations", annotation_id) + annotation_json = await self._http_client.fetch_one(Resource.Annotation, annotation_id) annotation = dacite.from_dict(Annotation, annotation_json) if sideloads: @@ -289,13 +296,13 @@ async def poll_annotation( async def update_annotation(self, annotation_id: int, data: Dict[str, Any]) -> Annotation: """https://elis.rossum.ai/api/docs/#update-an-annotation.""" - annotation = await self._http_client.replace("annotations", annotation_id, data) + annotation = await self._http_client.replace(Resource.Annotation, annotation_id, data) return dacite.from_dict(Annotation, annotation) async def update_part_annotation(self, annotation_id: int, data: Dict[str, Any]) -> Annotation: """https://elis.rossum.ai/api/docs/#update-part-of-an-annotation.""" - annotation = await self._http_client.update("annotations", annotation_id, data) + annotation = await self._http_client.update(Resource.Annotation, annotation_id, data) return dacite.from_dict(Annotation, annotation) @@ -306,29 +313,29 @@ async def list_all_workspaces( **filters: Any, ) -> AsyncIterable[Workspace]: """https://elis.rossum.ai/api/docs/#list-all-workspaces.""" - async for w in self._http_client.fetch_all("workspaces", ordering, **filters): + async for w in self._http_client.fetch_all(Resource.Workspace, ordering, **filters): yield dacite.from_dict(Workspace, w) async def retrieve_workspace(self, workspace_id) -> Workspace: """https://elis.rossum.ai/api/docs/#retrieve-a-workspace.""" - workspace = await self._http_client.fetch_one("workspaces", workspace_id) + workspace = await self._http_client.fetch_one(Resource.Workspace, workspace_id) return dacite.from_dict(Workspace, workspace) async def create_new_workspace(self, data: Dict[str, Any]) -> Workspace: """https://elis.rossum.ai/api/docs/#create-a-new-workspace.""" - workspace = await self._http_client.create("workspaces", data) + workspace = await self._http_client.create(Resource.Workspace, data) return dacite.from_dict(Workspace, workspace) async def delete_workspace(self, workspace_id) -> None: """https://elis.rossum.ai/api/docs/#delete-a-workspace.""" - return await self._http_client.delete("workspaces", workspace_id) + return await self._http_client.delete(Resource.Workspace, workspace_id) # ##### INBOX ##### async def create_new_inbox(self, data: Dict[str, Any]) -> Inbox: """https://elis.rossum.ai/api/docs/#create-a-new-inbox.""" - inbox = await self._http_client.create("inboxes", data) + inbox = await self._http_client.create(Resource.Inbox, data) return dacite.from_dict(Inbox, inbox) @@ -339,18 +346,18 @@ async def list_all_connectors( **filters: Any, ) -> AsyncIterable[Connector]: """https://elis.rossum.ai/api/docs/#list-all-connectors.""" - async for c in self._http_client.fetch_all("connectors", ordering, **filters): + async for c in self._http_client.fetch_all(Resource.Connector, ordering, **filters): yield dacite.from_dict(Connector, c) async def retrieve_connector(self, connector_id) -> Connector: """https://elis.rossum.ai/api/docs/#retrieve-a-connector.""" - connector = await self._http_client.fetch_one("connectors", connector_id) + connector = await self._http_client.fetch_one(Resource.Connector, connector_id) return dacite.from_dict(Connector, connector) async def create_new_connector(self, data: Dict[str, Any]) -> Connector: """https://elis.rossum.ai/api/docs/#create-a-new-connector.""" - connector = await self._http_client.create("connectors", data) + connector = await self._http_client.create(Resource.Connector, data) return dacite.from_dict(Connector, connector) @@ -361,18 +368,18 @@ async def list_all_hooks( **filters: Any, ) -> AsyncIterable[Hook]: """https://elis.rossum.ai/api/docs/#list-all-hooks.""" - async for h in self._http_client.fetch_all("hooks", ordering, **filters): + async for h in self._http_client.fetch_all(Resource.Hook, ordering, **filters): yield dacite.from_dict(Hook, h) async def retrieve_hook(self, hook_id) -> Hook: """https://elis.rossum.ai/api/docs/#retrieve-a-hook.""" - hook = await self._http_client.fetch_one("hooks", hook_id) + hook = await self._http_client.fetch_one(Resource.Hook, hook_id) return dacite.from_dict(Hook, hook) async def create_new_hook(self, data: Dict[str, Any]) -> Hook: """https://elis.rossum.ai/api/docs/#create-a-new-hook.""" - hook = await self._http_client.create("hooks", data) + hook = await self._http_client.create(Resource.Hook, data) return dacite.from_dict(Hook, hook) @@ -383,15 +390,15 @@ async def list_all_user_roles( **filters: Any, ) -> AsyncIterable[UserRole]: """https://elis.rossum.ai/api/docs/#list-all-user-roles.""" - async for u in self._http_client.fetch_all("groups", ordering, **filters): + async for u in self._http_client.fetch_all(Resource.Group, ordering, **filters): yield dacite.from_dict(UserRole, u) # ##### GENERIC METHODS ##### - async def request_paginated(self, resource: str, *args, **kwargs) -> AsyncIterable[dict]: + async def request_paginated(self, url: str, *args, **kwargs) -> AsyncIterable[dict]: """Use to perform requests to seldomly used or experimental endpoints with paginated response that do not have direct support in the client and return iterable. """ - async for element in self._http_client.fetch_all(resource, *args, **kwargs): + async for element in self._http_client.fetch_all_by_url(url, *args, **kwargs): yield element async def request_json(self, method: str, *args, **kwargs) -> Dict[str, Any]: diff --git a/rossum_api/elis_api_client_sync.py b/rossum_api/elis_api_client_sync.py index 5ccdc3b..0c22010 100644 --- a/rossum_api/elis_api_client_sync.py +++ b/rossum_api/elis_api_client_sync.py @@ -166,7 +166,7 @@ def retrieve_organization( ) def retrieve_own_organization(self) -> Organization: - """Retrive organization of currently logged in user.""" + """Retrieve organization of currently logged in user.""" return self.event_loop.run_until_complete(self.elis_api_client.retrieve_own_organization()) # ##### SCHEMAS ##### @@ -350,13 +350,11 @@ def list_all_user_roles( """https://elis.rossum.ai/api/docs/#list-all-user-roles.""" return self._iter_over_async(self.elis_api_client.list_all_user_roles(ordering, **filters)) - def request_paginated(self, resource: str, *args, **kwargs) -> Iterable[dict]: + def request_paginated(self, url: str, *args, **kwargs) -> Iterable[dict]: """Use to perform requests to seldomly used or experimental endpoints with paginated response that do not have direct support in the client and return iterable. """ - return self._iter_over_async( - self.elis_api_client.request_paginated(resource, *args, **kwargs) - ) + return self._iter_over_async(self.elis_api_client.request_paginated(url, *args, **kwargs)) def request_json(self, method: str, *args, **kwargs) -> Dict[str, Any]: """Use to perform requests to seldomly used or experimental endpoints that do not have diff --git a/tests/elis_api_client/test_annotations.py b/tests/elis_api_client/test_annotations.py index 64c2fbe..3c5e2f9 100644 --- a/tests/elis_api_client/test_annotations.py +++ b/tests/elis_api_client/test_annotations.py @@ -3,6 +3,7 @@ import pytest from mock.mock import MagicMock, patch +from rossum_api.api_client import Resource from rossum_api.models.annotation import Annotation from rossum_api.models.automation_blocker import AutomationBlocker, AutomationBlockerContent from rossum_api.models.document import Document @@ -128,7 +129,7 @@ async def test_list_all_annotations(self, elis_client, dummy_annotation, mock_ge async for a in annotations: assert a == Annotation(**dummy_annotation) - http_client.fetch_all.assert_called_with("annotations", (), (), ()) + http_client.fetch_all.assert_called_with(Resource.Annotation, (), (), ()) async def test_list_all_annotations_with_sideloads( self, elis_client, dummy_annotation_with_sideloads, mock_generator @@ -158,7 +159,7 @@ async def test_list_all_annotations_with_sideloads( assert a == annotation http_client.fetch_all.assert_called_with( - "annotations", + Resource.Annotation, (), ["documents", "automation_blockers", "content", "modifiers"], ["325164"], @@ -187,7 +188,7 @@ async def test_search_for_annotations(self, elis_client, dummy_annotation, mock_ async for a in annotations: assert a == Annotation(**dummy_annotation) - http_client.fetch_all.assert_called_with( + http_client.fetch_all_by_url.assert_called_with( "annotations/search", (), (), @@ -204,7 +205,7 @@ async def test_retrieve_annotation(self, elis_client, dummy_annotation): assert annotation == Annotation(**dummy_annotation) - http_client.fetch_one.assert_called_with("annotations", aid) + http_client.fetch_one.assert_called_with(Resource.Annotation, aid) async def test_retrieve_annotation_with_sideloads(self, elis_client, dummy_annotation): client, http_client = elis_client @@ -216,7 +217,7 @@ async def test_retrieve_annotation_with_sideloads(self, elis_client, dummy_annot assert annotation == Annotation(**{**dummy_annotation, "content": []}) - http_client.fetch_one.assert_called_with("annotations", aid) + http_client.fetch_one.assert_called_with(Resource.Annotation, aid) async def test_poll_annotation(self, elis_client, dummy_annotation): def is_imported(annotation): @@ -252,7 +253,7 @@ async def test_update_annotation(self, elis_client, dummy_annotation): assert annotation == Annotation(**dummy_annotation) - http_client.replace.assert_called_with("annotations", aid, data) + http_client.replace.assert_called_with(Resource.Annotation, aid, data) async def test_update_part_annotation(self, elis_client, dummy_annotation): client, http_client = elis_client @@ -266,7 +267,7 @@ async def test_update_part_annotation(self, elis_client, dummy_annotation): assert annotation == Annotation(**dummy_annotation) - http_client.update.assert_called_with("annotations", aid, data) + http_client.update.assert_called_with(Resource.Annotation, aid, data) class TestAnnotationsSync: @@ -279,7 +280,7 @@ def test_list_all_annotations(self, elis_client_sync, dummy_annotation, mock_gen for a in annotations: assert a == Annotation(**dummy_annotation) - http_client.fetch_all.assert_called_with("annotations", (), (), ()) + http_client.fetch_all.assert_called_with(Resource.Annotation, (), (), ()) def test_list_all_annotations_with_sideloads( self, elis_client_sync, dummy_annotation_with_sideloads, mock_generator @@ -309,7 +310,7 @@ def test_list_all_annotations_with_sideloads( assert a == annotation http_client.fetch_all.assert_called_with( - "annotations", + Resource.Annotation, (), ["documents", "automation_blockers", "content", "modifiers"], ["325164"], @@ -338,7 +339,7 @@ def test_search_for_annotations(self, elis_client_sync, dummy_annotation, mock_g for a in annotations: assert a == Annotation(**dummy_annotation) - http_client.fetch_all.assert_called_with( + http_client.fetch_all_by_url.assert_called_with( "annotations/search", (), (), @@ -355,7 +356,7 @@ def test_retrieve_annotation(self, elis_client_sync, dummy_annotation): assert annotation == Annotation(**dummy_annotation) - http_client.fetch_one.assert_called_with("annotations", aid) + http_client.fetch_one.assert_called_with(Resource.Annotation, aid) def test_retrieve_annotation_with_sideloads(self, elis_client_sync, dummy_annotation): client, http_client = elis_client_sync @@ -367,7 +368,7 @@ def test_retrieve_annotation_with_sideloads(self, elis_client_sync, dummy_annota assert annotation == Annotation(**{**dummy_annotation, "content": []}) - http_client.fetch_one.assert_called_with("annotations", aid) + http_client.fetch_one.assert_called_with(Resource.Annotation, aid) def test_poll_annotation(self, elis_client_sync, dummy_annotation): def is_imported(annotation): @@ -403,7 +404,7 @@ def test_update_annotation(self, elis_client_sync, dummy_annotation): assert annotation == Annotation(**dummy_annotation) - http_client.replace.assert_called_with("annotations", aid, data) + http_client.replace.assert_called_with(Resource.Annotation, aid, data) def test_update_part_annotation(self, elis_client_sync, dummy_annotation): client, http_client = elis_client_sync @@ -417,4 +418,4 @@ def test_update_part_annotation(self, elis_client_sync, dummy_annotation): assert annotation == Annotation(**dummy_annotation) - http_client.update.assert_called_with("annotations", aid, data) + http_client.update.assert_called_with(Resource.Annotation, aid, data) diff --git a/tests/elis_api_client/test_client_sync.py b/tests/elis_api_client/test_client_sync.py index cad8a6d..dc09f93 100644 --- a/tests/elis_api_client/test_client_sync.py +++ b/tests/elis_api_client/test_client_sync.py @@ -39,7 +39,7 @@ def test_existing_event_loop_running(self): def test_request_paginated(self, elis_client_sync, mock_generator): client, http_client = elis_client_sync - http_client.fetch_all.return_value = mock_generator({"some": "json"}) + http_client.fetch_all_by_url.return_value = mock_generator({"some": "json"}) kwargs = {"whatever": "kwarg"} data = client.request_paginated("hook_templates", **kwargs) assert list(data) == [{"some": "json"}] diff --git a/tests/elis_api_client/test_connectors.py b/tests/elis_api_client/test_connectors.py index 1c70c26..189f01a 100644 --- a/tests/elis_api_client/test_connectors.py +++ b/tests/elis_api_client/test_connectors.py @@ -2,6 +2,7 @@ import pytest +from rossum_api.api_client import Resource from rossum_api.models.connector import Connector @@ -32,7 +33,7 @@ async def test_list_all_connectors(self, elis_client, dummy_connector, mock_gene async for c in connectors: assert c == Connector(**dummy_connector) - http_client.fetch_all.assert_called_with("connectors", ()) + http_client.fetch_all.assert_called_with(Resource.Connector, ()) @pytest.mark.asyncio async def test_retrieve_connector(self, elis_client, dummy_connector): @@ -44,7 +45,7 @@ async def test_retrieve_connector(self, elis_client, dummy_connector): assert connector == Connector(**dummy_connector) - http_client.fetch_one.assert_called_with("connectors", cid) + http_client.fetch_one.assert_called_with(Resource.Connector, cid) async def test_create_new_connector(self, elis_client, dummy_connector): client, http_client = elis_client @@ -60,7 +61,7 @@ async def test_create_new_connector(self, elis_client, dummy_connector): assert connector == Connector(**dummy_connector) - http_client.create.assert_called_with("connectors", data) + http_client.create.assert_called_with(Resource.Connector, data) class TestUsersSync: @@ -73,7 +74,7 @@ def test_list_all_connectors(self, elis_client_sync, dummy_connector, mock_gener for c in connectors: assert c == Connector(**dummy_connector) - http_client.fetch_all.assert_called_with("connectors", ()) + http_client.fetch_all.assert_called_with(Resource.Connector, ()) def test_retrieve_connector(self, elis_client_sync, dummy_connector): client, http_client = elis_client_sync @@ -84,7 +85,7 @@ def test_retrieve_connector(self, elis_client_sync, dummy_connector): assert connector == Connector(**dummy_connector) - http_client.fetch_one.assert_called_with("connectors", cid) + http_client.fetch_one.assert_called_with(Resource.Connector, cid) def test_create_new_connector(self, elis_client_sync, dummy_connector): client, http_client = elis_client_sync @@ -100,4 +101,4 @@ def test_create_new_connector(self, elis_client_sync, dummy_connector): assert connector == Connector(**dummy_connector) - http_client.create.assert_called_with("connectors", data) + http_client.create.assert_called_with(Resource.Connector, data) diff --git a/tests/elis_api_client/test_hooks.py b/tests/elis_api_client/test_hooks.py index f909fde..7261e5d 100644 --- a/tests/elis_api_client/test_hooks.py +++ b/tests/elis_api_client/test_hooks.py @@ -2,6 +2,7 @@ import pytest +from rossum_api.api_client import Resource from rossum_api.models.hook import Hook @@ -49,7 +50,7 @@ async def test_list_all_hooks(self, elis_client, dummy_hook, mock_generator): async for h in hooks: assert h == Hook(**dummy_hook) - http_client.fetch_all.assert_called_with("hooks", ()) + http_client.fetch_all.assert_called_with(Resource.Hook, ()) async def test_retrieve_user(self, elis_client, dummy_hook): client, http_client = elis_client @@ -60,7 +61,7 @@ async def test_retrieve_user(self, elis_client, dummy_hook): assert hook == Hook(**dummy_hook) - http_client.fetch_one.assert_called_with("hooks", uid) + http_client.fetch_one.assert_called_with(Resource.Hook, uid) async def test_create_new_user(self, elis_client, dummy_hook): client, http_client = elis_client @@ -76,7 +77,7 @@ async def test_create_new_user(self, elis_client, dummy_hook): assert hook == Hook(**dummy_hook) - http_client.create.assert_called_with("hooks", data) + http_client.create.assert_called_with(Resource.Hook, data) class TestHooksAsync: @@ -89,7 +90,7 @@ def test_list_all_hooks(self, elis_client_sync, dummy_hook, mock_generator): for h in hooks: assert h == Hook(**dummy_hook) - http_client.fetch_all.assert_called_with("hooks", ()) + http_client.fetch_all.assert_called_with(Resource.Hook, ()) def test_retrieve_user(self, elis_client_sync, dummy_hook): client, http_client = elis_client_sync @@ -100,7 +101,7 @@ def test_retrieve_user(self, elis_client_sync, dummy_hook): assert hook == Hook(**dummy_hook) - http_client.fetch_one.assert_called_with("hooks", uid) + http_client.fetch_one.assert_called_with(Resource.Hook, uid) def test_create_new_user(self, elis_client_sync, dummy_hook): client, http_client = elis_client_sync @@ -116,4 +117,4 @@ def test_create_new_user(self, elis_client_sync, dummy_hook): assert hook == Hook(**dummy_hook) - http_client.create.assert_called_with("hooks", data) + http_client.create.assert_called_with(Resource.Hook, data) diff --git a/tests/elis_api_client/test_inboxes.py b/tests/elis_api_client/test_inboxes.py index dba79f3..30271a9 100644 --- a/tests/elis_api_client/test_inboxes.py +++ b/tests/elis_api_client/test_inboxes.py @@ -2,6 +2,7 @@ import pytest +from rossum_api.api_client import Resource from rossum_api.models.inbox import Inbox @@ -52,7 +53,7 @@ async def test_create_new_inbox(self, elis_client, dummy_inbox): assert inbox == Inbox(**dummy_inbox) - http_client.create.assert_called_with("inboxes", data) + http_client.create.assert_called_with(Resource.Inbox, data) class TestInboxesSync: @@ -72,4 +73,4 @@ def test_create_new_inbox(self, elis_client_sync, dummy_inbox): assert inbox == Inbox(**dummy_inbox) - http_client.create.assert_called_with("inboxes", data) + http_client.create.assert_called_with(Resource.Inbox, data) diff --git a/tests/elis_api_client/test_organizations.py b/tests/elis_api_client/test_organizations.py index 34b406f..d227f29 100644 --- a/tests/elis_api_client/test_organizations.py +++ b/tests/elis_api_client/test_organizations.py @@ -3,6 +3,7 @@ import pytest from mock import call +from rossum_api.api_client import Resource from rossum_api.models.organization import Organization @@ -35,7 +36,7 @@ async def test_list_all_organizations(self, elis_client, dummy_organization, moc async for o in organizations: assert o == Organization(**dummy_organization) - http_client.fetch_all.assert_called_with("organizations", ()) + http_client.fetch_all.assert_called_with(Resource.Organization, ()) async def test_retrieve_organization(self, elis_client, dummy_organization): client, http_client = elis_client @@ -46,7 +47,7 @@ async def test_retrieve_organization(self, elis_client, dummy_organization): assert organization == Organization(**dummy_organization) - http_client.fetch_one.assert_called_with("organizations", oid) + http_client.fetch_one.assert_called_with(Resource.Organization, oid) async def test_retrieve_own_organization(self, elis_client, dummy_user, dummy_organization): client, http_client = elis_client @@ -57,7 +58,7 @@ async def test_retrieve_own_organization(self, elis_client, dummy_user, dummy_or assert organization == Organization(**dummy_organization) http_client.fetch_one.assert_has_calls( - [call("auth", "user"), call("organizations", "406")] + [call(Resource.Auth, "user"), call(Resource.Organization, "406")] ) @@ -71,7 +72,7 @@ def test_list_all_organizations(self, elis_client_sync, dummy_organization, mock for o in organizations: assert o == Organization(**dummy_organization) - http_client.fetch_all.assert_called_with("organizations", ()) + http_client.fetch_all.assert_called_with(Resource.Organization, ()) def test_retrieve_organization(self, elis_client_sync, dummy_organization): client, http_client = elis_client_sync @@ -82,7 +83,7 @@ def test_retrieve_organization(self, elis_client_sync, dummy_organization): assert organization == Organization(**dummy_organization) - http_client.fetch_one.assert_called_with("organizations", oid) + http_client.fetch_one.assert_called_with(Resource.Organization, oid) def test_retrieve_own_organization(self, elis_client_sync, dummy_user, dummy_organization): client, http_client = elis_client_sync @@ -93,5 +94,5 @@ def test_retrieve_own_organization(self, elis_client_sync, dummy_user, dummy_org assert organization == Organization(**dummy_organization) http_client.fetch_one.assert_has_calls( - [call("auth", "user"), call("organizations", "406")] + [call(Resource.Auth, "user"), call(Resource.Organization, "406")] ) diff --git a/tests/elis_api_client/test_queues.py b/tests/elis_api_client/test_queues.py index d1da3e3..8175800 100644 --- a/tests/elis_api_client/test_queues.py +++ b/tests/elis_api_client/test_queues.py @@ -3,6 +3,7 @@ import pytest from mock import MagicMock, call, patch +from rossum_api.api_client import Resource from rossum_api.models.annotation import Annotation from rossum_api.models.queue import Queue @@ -104,7 +105,7 @@ async def test_list_all_queues(self, elis_client, dummy_queue, mock_generator): async for q in queues: assert q == Queue(**dummy_queue) - http_client.fetch_all.assert_called_with("queues", ()) + http_client.fetch_all.assert_called_with(Resource.Queue, ()) async def test_retrieve_queue(self, elis_client, dummy_queue): client, http_client = elis_client @@ -115,7 +116,7 @@ async def test_retrieve_queue(self, elis_client, dummy_queue): assert queue == Queue(**dummy_queue) - http_client.fetch_one.assert_called_with("queues", qid) + http_client.fetch_one.assert_called_with(Resource.Queue, qid) async def test_create_new_queue(self, elis_client, dummy_queue): client, http_client = elis_client @@ -130,7 +131,7 @@ async def test_create_new_queue(self, elis_client, dummy_queue): assert queue == Queue(**dummy_queue) - http_client.create.assert_called_with("queues", data) + http_client.create.assert_called_with(Resource.Queue, data) async def test_delete_queue(self, elis_client, dummy_queue): client, http_client = elis_client @@ -139,7 +140,7 @@ async def test_delete_queue(self, elis_client, dummy_queue): qid = dummy_queue["id"] await client.delete_queue(qid) - http_client.delete.assert_called_with("queues", qid) + http_client.delete.assert_called_with(Resource.Queue, qid) async def test_import_document(self, elis_client): client, http_client = elis_client @@ -188,8 +189,8 @@ async def upload(resource, id_, fp, filename, *args, **kwargs): assert annotation_ids == [111, 222] calls = [ - call("queues", 123, open_mock_first, "document.pdf", {"a": 1}, {"b": 2}), - call("queues", 123, open_mock_second, "document 🎁.pdf", {"a": 1}, {"b": 2}), + call(Resource.Queue, 123, open_mock_first, "document.pdf", {"a": 1}, {"b": 2}), + call(Resource.Queue, 123, open_mock_second, "document 🎁.pdf", {"a": 1}, {"b": 2}), ] http_client.upload.assert_has_calls(calls, any_order=True) @@ -203,7 +204,7 @@ async def test_export_annotations_to_json(self, elis_client, dummy_annotation, m async for a in client.export_annotations_to_json(queue_id=qid): assert a == Annotation(**dummy_annotation) - http_client.export.assert_called_with("queues", qid, export_format) + http_client.export.assert_called_with(Resource.Queue, qid, export_format) async def test_export_annotations_to_file(self, elis_client, mock_file_read): client, http_client = elis_client @@ -218,7 +219,7 @@ async def test_export_annotations_to_file(self, elis_client, mock_file_read): ): result += a - http_client.export.assert_called_with("queues", qid, export_format) + http_client.export.assert_called_with(Resource.Queue, qid, export_format) with open("tests/data/annotation_export.xml", "rb") as fp: for i, line in enumerate(fp.read()): @@ -235,7 +236,7 @@ def test_list_all_queues(self, elis_client_sync, dummy_queue, mock_generator): for q in queues: assert q == Queue(**dummy_queue) - http_client.fetch_all.assert_called_with("queues", ()) + http_client.fetch_all.assert_called_with(Resource.Queue, ()) def test_retrieve_queue(self, elis_client_sync, dummy_queue): client, http_client = elis_client_sync @@ -246,7 +247,7 @@ def test_retrieve_queue(self, elis_client_sync, dummy_queue): assert queue == Queue(**dummy_queue) - http_client.fetch_one.assert_called_with("queues", qid) + http_client.fetch_one.assert_called_with(Resource.Queue, qid) def test_create_new_queue(self, elis_client_sync, dummy_queue): client, http_client = elis_client_sync @@ -261,7 +262,7 @@ def test_create_new_queue(self, elis_client_sync, dummy_queue): assert queue == Queue(**dummy_queue) - http_client.create.assert_called_with("queues", data) + http_client.create.assert_called_with(Resource.Queue, data) def test_import_document(self, elis_client_sync): client, http_client = elis_client_sync @@ -310,8 +311,8 @@ async def upload(resource, id_, fp, filename, *args, **kwargs): assert annotation_ids == [111, 222] calls = [ - call("queues", 123, open_mock_first, "document.pdf", {"a": 1}, {"b": 2}), - call("queues", 123, open_mock_second, "document 🎁.pdf", {"a": 1}, {"b": 2}), + call(Resource.Queue, 123, open_mock_first, "document.pdf", {"a": 1}, {"b": 2}), + call(Resource.Queue, 123, open_mock_second, "document 🎁.pdf", {"a": 1}, {"b": 2}), ] http_client.upload.assert_has_calls(calls, any_order=True) @@ -323,7 +324,7 @@ def test_delete_schema(self, elis_client_sync, dummy_queue): qid = dummy_queue["id"] client.delete_queue(qid) - http_client.delete.assert_called_with("queues", qid) + http_client.delete.assert_called_with(Resource.Queue, qid) def test_export_annotations_to_json(self, elis_client_sync, dummy_annotation, mock_generator): client, http_client = elis_client_sync @@ -335,7 +336,7 @@ def test_export_annotations_to_json(self, elis_client_sync, dummy_annotation, mo for a in client.export_annotations_to_json(queue_id=qid): assert a == Annotation(**dummy_annotation) - http_client.export.assert_called_with("queues", qid, export_format) + http_client.export.assert_called_with(Resource.Queue, qid, export_format) def test_export_annotations_to_file(self, elis_client_sync, mock_file_read): client, http_client = elis_client_sync @@ -348,7 +349,7 @@ def test_export_annotations_to_file(self, elis_client_sync, mock_file_read): for a in client.export_annotations_to_file(queue_id=qid, export_format=export_format): result += a - http_client.export.assert_called_with("queues", qid, export_format) + http_client.export.assert_called_with(Resource.Queue, qid, export_format) with open("tests/data/annotation_export.xml", "rb") as fp: for i, line in enumerate(fp.read()): diff --git a/tests/elis_api_client/test_schemas.py b/tests/elis_api_client/test_schemas.py index 93cdbfe..2f276f0 100644 --- a/tests/elis_api_client/test_schemas.py +++ b/tests/elis_api_client/test_schemas.py @@ -2,6 +2,7 @@ import pytest +from rossum_api.api_client import Resource from rossum_api.models.schema import Schema @@ -45,7 +46,7 @@ async def test_list_all_schemas(self, elis_client, dummy_schema, mock_generator) async for s in schemas: assert s == Schema(**dummy_schema) - http_client.fetch_all.assert_called_with("schemas", ()) + http_client.fetch_all.assert_called_with(Resource.Schema, ()) async def test_retrieve_schema(self, elis_client, dummy_schema): client, http_client = elis_client @@ -56,7 +57,7 @@ async def test_retrieve_schema(self, elis_client, dummy_schema): assert schema == Schema(**dummy_schema) - http_client.fetch_one.assert_called_with("schemas", sid) + http_client.fetch_one.assert_called_with(Resource.Schema, sid) async def test_create_new_schema(self, elis_client, dummy_schema): client, http_client = elis_client @@ -67,7 +68,7 @@ async def test_create_new_schema(self, elis_client, dummy_schema): assert schema == Schema(**dummy_schema) - http_client.create.assert_called_with("schemas", data) + http_client.create.assert_called_with(Resource.Schema, data) async def test_delete_schema(self, elis_client, dummy_schema): client, http_client = elis_client @@ -76,7 +77,7 @@ async def test_delete_schema(self, elis_client, dummy_schema): sid = dummy_schema["id"] await client.delete_schema(sid) - http_client.delete.assert_called_with("schemas", sid) + http_client.delete.assert_called_with(Resource.Schema, sid) class TestSchemasSync: @@ -89,7 +90,7 @@ def test_list_all_schemas(self, elis_client_sync, dummy_schema, mock_generator): for s in schemas: assert s == Schema(**dummy_schema) - http_client.fetch_all.assert_called_with("schemas", ()) + http_client.fetch_all.assert_called_with(Resource.Schema, ()) def test_retrieve_schema(self, elis_client_sync, dummy_schema): client, http_client = elis_client_sync @@ -100,7 +101,7 @@ def test_retrieve_schema(self, elis_client_sync, dummy_schema): assert schema == Schema(**dummy_schema) - http_client.fetch_one.assert_called_with("schemas", sid) + http_client.fetch_one.assert_called_with(Resource.Schema, sid) def test_create_new_schema(self, elis_client_sync, dummy_schema): client, http_client = elis_client_sync @@ -111,7 +112,7 @@ def test_create_new_schema(self, elis_client_sync, dummy_schema): assert schema == Schema(**dummy_schema) - http_client.create.assert_called_with("schemas", data) + http_client.create.assert_called_with(Resource.Schema, data) def test_delete_schema(self, elis_client_sync, dummy_schema): client, http_client = elis_client_sync @@ -120,4 +121,4 @@ def test_delete_schema(self, elis_client_sync, dummy_schema): sid = dummy_schema["id"] client.delete_schema(sid) - http_client.delete.assert_called_with("schemas", sid) + http_client.delete.assert_called_with(Resource.Schema, sid) diff --git a/tests/elis_api_client/test_user_roles.py b/tests/elis_api_client/test_user_roles.py index 03a5b8f..1055715 100644 --- a/tests/elis_api_client/test_user_roles.py +++ b/tests/elis_api_client/test_user_roles.py @@ -2,6 +2,7 @@ import pytest +from rossum_api.api_client import Resource from rossum_api.models.user_role import UserRole @@ -21,7 +22,7 @@ async def test_list_all_user_roles(self, elis_client, dummy_user_role, mock_gene async for u in user_roles: assert u == UserRole(**dummy_user_role) - http_client.fetch_all.assert_called_with("groups", ()) + http_client.fetch_all.assert_called_with(Resource.Group, ()) class TestWorkspacesAsync: @@ -34,4 +35,4 @@ def test_list_all_user_roles(self, elis_client_sync, dummy_user_role, mock_gener for u in user_roles: assert u == UserRole(**dummy_user_role) - http_client.fetch_all.assert_called_with("groups", ()) + http_client.fetch_all.assert_called_with(Resource.Group, ()) diff --git a/tests/elis_api_client/test_users.py b/tests/elis_api_client/test_users.py index e2c0957..14c9ac6 100644 --- a/tests/elis_api_client/test_users.py +++ b/tests/elis_api_client/test_users.py @@ -2,6 +2,7 @@ import pytest +from rossum_api.api_client import Resource from rossum_api.models.user import User @@ -16,7 +17,7 @@ async def test_list_all_users(self, elis_client, dummy_user, mock_generator): async for u in users: assert u == User(**dummy_user) - http_client.fetch_all.assert_called_with("users", ()) + http_client.fetch_all.assert_called_with(Resource.User, ()) async def test_retrieve_user(self, elis_client, dummy_user): client, http_client = elis_client @@ -27,7 +28,7 @@ async def test_retrieve_user(self, elis_client, dummy_user): assert user == User(**dummy_user) - http_client.fetch_one.assert_called_with("users", uid) + http_client.fetch_one.assert_called_with(Resource.User, uid) async def test_create_new_user(self, elis_client, dummy_user): client, http_client = elis_client @@ -44,7 +45,7 @@ async def test_create_new_user(self, elis_client, dummy_user): assert user == User(**dummy_user) - http_client.create.assert_called_with("users", data) + http_client.create.assert_called_with(Resource.User, data) class TestUsersSync: @@ -57,7 +58,7 @@ def test_list_all_users(self, elis_client_sync, dummy_user, mock_generator): for u in users: assert u == User(**dummy_user) - http_client.fetch_all.assert_called_with("users", ()) + http_client.fetch_all.assert_called_with(Resource.User, ()) def test_retrieve_user(self, elis_client_sync, dummy_user): client, http_client = elis_client_sync @@ -68,7 +69,7 @@ def test_retrieve_user(self, elis_client_sync, dummy_user): assert user == User(**dummy_user) - http_client.fetch_one.assert_called_with("users", uid) + http_client.fetch_one.assert_called_with(Resource.User, uid) def test_create_new_user(self, elis_client_sync, dummy_user): client, http_client = elis_client_sync @@ -85,4 +86,4 @@ def test_create_new_user(self, elis_client_sync, dummy_user): assert user == User(**dummy_user) - http_client.create.assert_called_with("users", data) + http_client.create.assert_called_with(Resource.User, data) diff --git a/tests/elis_api_client/test_workspaces.py b/tests/elis_api_client/test_workspaces.py index 0434019..563ea03 100644 --- a/tests/elis_api_client/test_workspaces.py +++ b/tests/elis_api_client/test_workspaces.py @@ -2,6 +2,7 @@ import pytest +from rossum_api.api_client import Resource from rossum_api.models.workspace import Workspace @@ -32,7 +33,7 @@ async def test_list_all_workspaces(self, elis_client, dummy_workspace, mock_gene async for w in workspaces: assert w == Workspace(**dummy_workspace) - http_client.fetch_all.assert_called_with("workspaces", ()) + http_client.fetch_all.assert_called_with(Resource.Workspace, ()) async def test_retrieve_workspace(self, elis_client, dummy_workspace): client, http_client = elis_client @@ -43,7 +44,7 @@ async def test_retrieve_workspace(self, elis_client, dummy_workspace): assert workspace == Workspace(**dummy_workspace) - http_client.fetch_one.assert_called_with("workspaces", oid) + http_client.fetch_one.assert_called_with(Resource.Workspace, oid) async def test_create_new_workspace(self, elis_client, dummy_workspace): client, http_client = elis_client @@ -58,7 +59,7 @@ async def test_create_new_workspace(self, elis_client, dummy_workspace): assert workspace == Workspace(**dummy_workspace) - http_client.create.assert_called_with("workspaces", data) + http_client.create.assert_called_with(Resource.Workspace, data) async def test_delete_workspace(self, elis_client, dummy_workspace): client, http_client = elis_client @@ -67,7 +68,7 @@ async def test_delete_workspace(self, elis_client, dummy_workspace): oid = dummy_workspace["id"] await client.delete_workspace(oid) - http_client.delete.assert_called_with("workspaces", oid) + http_client.delete.assert_called_with(Resource.Workspace, oid) class TestWorkspacesSync: @@ -80,7 +81,7 @@ def test_list_all_workspaces(self, elis_client_sync, dummy_workspace, mock_gener for w in workspaces: assert w == Workspace(**dummy_workspace) - http_client.fetch_all.assert_called_with("workspaces", ()) + http_client.fetch_all.assert_called_with(Resource.Workspace, ()) def test_retrieve_workspace(self, elis_client_sync, dummy_workspace): client, http_client = elis_client_sync @@ -91,7 +92,7 @@ def test_retrieve_workspace(self, elis_client_sync, dummy_workspace): assert workspace == Workspace(**dummy_workspace) - http_client.fetch_one.assert_called_with("workspaces", oid) + http_client.fetch_one.assert_called_with(Resource.Workspace, oid) def test_create_new_workspace(self, elis_client_sync, dummy_workspace): client, http_client = elis_client_sync @@ -105,7 +106,7 @@ def test_create_new_workspace(self, elis_client_sync, dummy_workspace): assert workspace == Workspace(**dummy_workspace) - http_client.create.assert_called_with("workspaces", data) + http_client.create.assert_called_with(Resource.Workspace, data) def test_delete_workspace(self, elis_client_sync, dummy_workspace): client, http_client = elis_client_sync @@ -114,4 +115,4 @@ def test_delete_workspace(self, elis_client_sync, dummy_workspace): oid = dummy_workspace["id"] client.delete_workspace(oid) - http_client.delete.assert_called_with("workspaces", oid) + http_client.delete.assert_called_with(Resource.Workspace, oid) diff --git a/tests/test_api_client.py b/tests/test_api_client.py index fad7f5c..e13134a 100644 --- a/tests/test_api_client.py +++ b/tests/test_api_client.py @@ -18,7 +18,7 @@ import pytest import pytest_httpx -from rossum_api.api_client import APIClient, APIClientError +from rossum_api.api_client import APIClient, APIClientError, Resource WORKSPACES = [ { @@ -181,7 +181,7 @@ async def test_init_token(httpx_mock): url="https://elis.rossum.ai/api/v1/users/1", json={}, ) - await client.fetch_one("users", 1) + await client.fetch_one(Resource.User, 1) assert len(httpx_mock.get_requests()) == 1 assert httpx_mock.get_requests()[0].headers["Authorization"] == f"token {FAKE_TOKEN}" @@ -203,7 +203,7 @@ async def test_not_possible_to_reauth(httpx_mock): json={"password": ["This field may not be blank."]}, ) with pytest.raises(APIClientError) as err: - await client.fetch_one("users", 1) + await client.fetch_one(Resource.User, 1) assert err.value.status_code == 400 assert err.value.error == '{"password": ["This field may not be blank."]}' @@ -219,7 +219,7 @@ def custom_response(request: httpx.Request, n_calls: int): return httpx.Response(status_code=200, json=WORKSPACES[0]) httpx_mock.add_callback(custom_response) - workspace = await client.fetch_one("workspaces", id_=7694) + workspace = await client.fetch_one(Resource.Workspace, id_=7694) assert workspace == WORKSPACES[0] @@ -235,7 +235,7 @@ def custom_response(request: httpx.Request, n_calls: int): httpx_mock.add_callback(custom_response) with pytest.raises(httpx.ReadTimeout): - await client.fetch_one("workspaces", id_=7694) + await client.fetch_one(Resource.Workspace, id_=7694) @pytest.mark.asyncio @@ -245,7 +245,7 @@ async def test_fetch_one(client, httpx_mock): url="https://elis.rossum.ai/api/v1/workspaces/7694", json=WORKSPACES[0], ) - workspace = await client.fetch_one("workspaces", id_=7694) + workspace = await client.fetch_one(Resource.Workspace, id_=7694) assert workspace == WORKSPACES[0] @@ -277,7 +277,7 @@ async def test_fetch_all(client, httpx_mock): "results": WORKSPACES[2:], }, ) - workspaces = [w async for w in client.fetch_all("workspaces")] + workspaces = [w async for w in client.fetch_all(Resource.Workspace)] assert workspaces == WORKSPACES @@ -292,7 +292,7 @@ async def test_fetch_all_with_max_pages_limit(client, httpx_mock): "results": WORKSPACES[:1], }, ) - workspaces = [w async for w in client.fetch_all("workspaces", max_pages=1)] + workspaces = [w async for w in client.fetch_all(Resource.Workspace, max_pages=1)] assert workspaces == WORKSPACES[:1] @@ -306,7 +306,7 @@ async def test_fetch_all_ordering(client, httpx_mock): "results": WORKSPACES, }, ) - workspaces = [w async for w in client.fetch_all("workspaces", ordering=["-id", "name"])] + workspaces = [w async for w in client.fetch_all(Resource.Workspace, ordering=["-id", "name"])] assert workspaces == WORKSPACES @@ -320,7 +320,7 @@ async def test_fetch_all_filters(client, httpx_mock): "results": WORKSPACES, }, ) - workspaces = [w async for w in client.fetch_all("workspaces", name="Test", autopilot=1)] + workspaces = [w async for w in client.fetch_all(Resource.Workspace, name="Test", autopilot=1)] assert workspaces == WORKSPACES @@ -339,7 +339,7 @@ async def test_fetch_all_sideload(client, httpx_mock): workspaces = [ w async for w in client.fetch_all( - "workspaces", + Resource.Workspace, sideloads=["content", "automation_blockers"], content_schema_ids=["invoice_id", "date_issue"], ) @@ -409,13 +409,13 @@ async def _request(*args, **kwargs): yield concurrency with track_concurrency(client) as concurrency: - workspaces = [w async for w in client.fetch_all("workspaces")] + workspaces = [w async for w in client.fetch_all(Resource.Workspace)] assert workspaces == [WORKSPACES[0]] * (page_count - 1) + WORKSPACES[-1:] assert concurrency["max"] == 4, "default max_in_flight_requests is 4" with track_concurrency(client) as concurrency: client.max_in_flight_requests = 3 - workspaces = [w async for w in client.fetch_all("workspaces")] + workspaces = [w async for w in client.fetch_all(Resource.Workspace)] assert workspaces == [WORKSPACES[0]] * (page_count - 1) + WORKSPACES[-1:] assert concurrency["max"] == 3, "based on overridden max_in_flight_requests to 3" @@ -432,7 +432,7 @@ async def test_create(client, httpx_mock): match_content=json.dumps(data).encode("utf-8"), json=WORKSPACES[0], ) - workspace = await client.create("workspaces", data=data) + workspace = await client.create(Resource.Workspace, data=data) assert workspace == WORKSPACES[0] @@ -448,7 +448,7 @@ async def test_replace(client, httpx_mock): match_content=json.dumps(data).encode("utf-8"), json=WORKSPACES[0], ) - workspace = await client.replace("workspaces", id_=123, data=data) + workspace = await client.replace(Resource.Workspace, id_=123, data=data) assert workspace == WORKSPACES[0] @@ -461,7 +461,7 @@ async def test_update(client, httpx_mock): match_content=json.dumps(data).encode("utf-8"), json=WORKSPACES[0], ) - workspace = await client.update("workspaces", id_=123, data=data) + workspace = await client.update(Resource.Workspace, id_=123, data=data) assert workspace == WORKSPACES[0] @@ -471,7 +471,7 @@ async def test_delete(client, httpx_mock): method="DELETE", url="https://elis.rossum.ai/api/v1/workspaces/123", ) - result = await client.delete("workspaces", id_=123) + result = await client.delete(Resource.Workspace, id_=123) assert result is None @@ -492,7 +492,7 @@ async def test_upload(client, httpx_mock): await fp.flush() async with aiofiles.open(fp.name, "rb") as fp: response = await client.upload( - "queues", + Resource.Queue, id_=123, fp=fp, filename="filename.pdf", @@ -541,7 +541,7 @@ async def test_export_json(client, httpx_mock, filters, expected_method, first_u annotations = [ w async for w in client.export( - "queues", id_=123, export_format="json", columns=cols, id="456,789", **filters + Resource.Queue, id_=123, export_format="json", columns=cols, id="456,789", **filters ) ] assert annotations == ANNOTATIONS # Annotations are yielded in correct order @@ -573,7 +573,7 @@ async def test_export_csv(client, httpx_mock, filters, expected_method, expected export_chunks = [ w async for w in client.export( - "queues", id_=123, export_format="csv", columns=cols, id="456,789", **filters + Resource.Queue, id_=123, export_format="csv", columns=cols, id="456,789", **filters ) ] assert b"".join(export_chunks) == CSV_EXPORT # Streamed chunks are yielded in correct order @@ -598,7 +598,7 @@ def set_token(): client.token = "new-token" with mock.patch.object(client, "_authenticate", side_effect=set_token): - workspace = await client.fetch_one("workspaces", id_=7694) + workspace = await client.fetch_one(Resource.Workspace, id_=7694) assert workspace == WORKSPACES[0] @@ -639,7 +639,7 @@ def set_token(): client.token = "new-token" with mock.patch.object(client, "_authenticate", side_effect=set_token): - workspace = await client.fetch_one("workspaces", id_=7694) + workspace = await client.fetch_one(Resource.Workspace, id_=7694) assert workspace == WORKSPACES[0]