From 40e484e7ab0082356472189fa879cfcfcb7c8046 Mon Sep 17 00:00:00 2001 From: Simona Nemeckova Date: Wed, 11 Dec 2024 15:48:37 +0100 Subject: [PATCH] refactor: Rename clients --- README.md | 72 ++++++++++--------- rossum_api/__init__.py | 10 +-- rossum_api/clients/__init__.py | 0 .../external_async_client.py} | 8 +-- .../external_sync_client.py} | 8 +-- .../internal_async_client.py} | 6 +- .../{ => clients}/internal_sync_client.py | 4 +- rossum_api/domain_logic/urls.py | 2 +- test.py | 10 +-- tests/conftest.py | 10 +-- tests/e2e.py | 6 +- tests/test_api_client.py | 16 +++-- 12 files changed, 79 insertions(+), 73 deletions(-) create mode 100644 rossum_api/clients/__init__.py rename rossum_api/{elis_api_client.py => clients/external_async_client.py} (99%) rename rossum_api/{elis_api_client_sync.py => clients/external_sync_client.py} (99%) rename rossum_api/{api_client.py => clients/internal_async_client.py} (99%) rename rossum_api/{ => clients}/internal_sync_client.py (99%) diff --git a/README.md b/README.md index 54380ee..1e6da59 100644 --- a/README.md +++ b/README.md @@ -48,28 +48,30 @@ Async version: ```python import asyncio -from rossum_api import ElisAPIClient +from rossum_api import AsyncRossumAPIClient WORKSPACE = { - "name": "Rossum Client NG Test", - "organization": "https://elis.rossum.ai/api/v1/organizations/116390", + "name": "Rossum Client NG Test", + "organization": "https://elis.rossum.ai/api/v1/organizations/116390", } + async def main_with_async_client(): - client = ElisAPIClient( - os.environ["ELIS_USERNAME"], - os.environ["ELIS_PASSWORD"], - base_url="https://elis.rossum.ai/api/v1", - ) - ws = await client.create_new_workspace(data=WORKSPACE) - workspace_id = ws.id - ws = await client.retrieve_workspace(workspace_id) - print("GET result:", ws) - print("LIST results:") - async for w in client.list_all_workspaces(ordering=["-id"], name=WORKSPACE["name"]): - print(w) - await client.delete_workspace(workspace_id) - print(f"Workspace {workspace_id} deleted.") + client = AsyncRossumAPIClient( + os.environ["ELIS_USERNAME"], + os.environ["ELIS_PASSWORD"], + base_url="https://elis.rossum.ai/api/v1", + ) + ws = await client.create_new_workspace(data=WORKSPACE) + workspace_id = ws.id + ws = await client.retrieve_workspace(workspace_id) + print("GET result:", ws) + print("LIST results:") + async for w in client.list_all_workspaces(ordering=["-id"], name=WORKSPACE["name"]): + print(w) + await client.delete_workspace(workspace_id) + print(f"Workspace {workspace_id} deleted.") + asyncio.run(main_with_async_client()) ``` @@ -77,28 +79,30 @@ asyncio.run(main_with_async_client()) Sync version: ```python -from rossum_api import ElisAPIClientSync +from rossum_api import SyncRossumAPIClient WORKSPACE = { - "name": "Rossum Client NG Test", - "organization": "https://elis.rossum.ai/api/v1/organizations/116390", + "name": "Rossum Client NG Test", + "organization": "https://elis.rossum.ai/api/v1/organizations/116390", } + def main_with_sync_client(): - client = ElisAPIClientSync( - os.environ["ELIS_USERNAME"], - os.environ["ELIS_PASSWORD"], - base_url="https://elis.rossum.ai/api/v1", - ) - ws = client.create_new_workspace(data=WORKSPACE) - workspace_id = ws.id - ws = client.retrieve_workspace(workspace_id) - print("GET result:", ws) - print("LIST results:") - for w in client.list_all_workspaces(ordering=["-id"], name=WORKSPACE["name"]): - print(w) - client.delete_workspace(workspace_id) - print(f"Workspace {workspace_id} deleted.") + client = SyncRossumAPIClient( + os.environ["ELIS_USERNAME"], + os.environ["ELIS_PASSWORD"], + base_url="https://elis.rossum.ai/api/v1", + ) + ws = client.create_new_workspace(data=WORKSPACE) + workspace_id = ws.id + ws = client.retrieve_workspace(workspace_id) + print("GET result:", ws) + print("LIST results:") + for w in client.list_all_workspaces(ordering=["-id"], name=WORKSPACE["name"]): + print(w) + client.delete_workspace(workspace_id) + print(f"Workspace {workspace_id} deleted.") + main_with_sync_client() ``` diff --git a/rossum_api/__init__.py b/rossum_api/__init__.py index 03e5d62..7a74819 100644 --- a/rossum_api/__init__.py +++ b/rossum_api/__init__.py @@ -1,14 +1,14 @@ from __future__ import annotations -from rossum_api.api_client import APIClientError -from rossum_api.elis_api_client import ElisAPIClient, ExportFileFormats -from rossum_api.elis_api_client_sync import ElisAPIClientSync +from rossum_api.clients.external_async_client import AsyncRossumAPIClient, ExportFileFormats +from rossum_api.clients.external_sync_client import SyncRossumAPIClient +from rossum_api.clients.internal_async_client import APIClientError __version__ = "0.20.0" __all__ = ( "APIClientError", - "ElisAPIClient", - "ElisAPIClientSync", + "AsyncRossumAPIClient", + "SyncRossumAPIClient", "ExportFileFormats", ) diff --git a/rossum_api/clients/__init__.py b/rossum_api/clients/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rossum_api/elis_api_client.py b/rossum_api/clients/external_async_client.py similarity index 99% rename from rossum_api/elis_api_client.py rename to rossum_api/clients/external_async_client.py index d8460f5..7917cf8 100644 --- a/rossum_api/elis_api_client.py +++ b/rossum_api/clients/external_async_client.py @@ -7,7 +7,7 @@ import aiofiles -from rossum_api.api_client import APIClient +from rossum_api.clients.internal_async_client import InternalAsyncClient from rossum_api.domain_logic.annotations import ( is_annotation_imported, validate_list_annotations_params, @@ -53,14 +53,14 @@ class Sideload: pass -class ElisAPIClient: +class AsyncRossumAPIClient: def __init__( self, username: Optional[str] = None, password: Optional[str] = None, token: Optional[str] = None, base_url: str = DEFAULT_BASE_URL, - http_client: Optional[APIClient] = None, + http_client: Optional[InternalAsyncClient] = None, deserializer: Optional[Deserializer] = None, ): """ @@ -72,7 +72,7 @@ def __init__( deserializer pass a custom deserialization callable if different model classes should be returned """ - self._http_client = http_client or APIClient(base_url, username, password, token) + self._http_client = http_client or InternalAsyncClient(base_url, username, password, token) self._deserializer = deserializer or deserialize_default # ##### QUEUE ##### diff --git a/rossum_api/elis_api_client_sync.py b/rossum_api/clients/external_sync_client.py similarity index 99% rename from rossum_api/elis_api_client_sync.py rename to rossum_api/clients/external_sync_client.py index 2f42104..af51218 100644 --- a/rossum_api/elis_api_client_sync.py +++ b/rossum_api/clients/external_sync_client.py @@ -6,7 +6,8 @@ from typing import Any, Callable, Iterator, Optional, Sequence, Tuple, Union, cast from rossum_api import ExportFileFormats -from rossum_api.api_client import Resource +from rossum_api.clients.internal_async_client import Resource +from rossum_api.clients.internal_sync_client import InternalSyncClient from rossum_api.domain_logic.annotations import ( get_http_method_for_annotation_export, is_annotation_imported, @@ -17,7 +18,6 @@ from rossum_api.domain_logic.upload import build_upload_files from rossum_api.domain_logic.urls import build_upload_url, parse_resource_id_from_url from rossum_api.dtos import Token, UserCredentials -from rossum_api.internal_sync_client import InternalSyncRossumAPIClient from rossum_api.models import ( Annotation, Connector, @@ -40,7 +40,7 @@ from rossum_api.models.task import TaskStatus -class ElisAPIClientSync: +class SyncRossumAPIClient: def __init__( self, base_url: str, @@ -48,7 +48,7 @@ def __init__( deserializer: Optional[Deserializer] = None, ): self._deserializer = deserializer or deserialize_default - self.internal_client = InternalSyncRossumAPIClient(base_url, credentials) + self.internal_client = InternalSyncClient(base_url, credentials) # ##### QUEUES ##### diff --git a/rossum_api/api_client.py b/rossum_api/clients/internal_async_client.py similarity index 99% rename from rossum_api/api_client.py rename to rossum_api/clients/internal_async_client.py index 8e8adb8..ac1ff48 100644 --- a/rossum_api/api_client.py +++ b/rossum_api/clients/internal_async_client.py @@ -51,7 +51,7 @@ def authenticate_if_needed(method): """ @functools.wraps(method) - async def authenticate_if_needed(self: APIClient, *args, **kwargs): + async def authenticate_if_needed(self: InternalAsyncClient, *args, **kwargs): # Authenticate if there is no token, no need to fire the request only to get 401 and retry if self.token is None: await self._authenticate() @@ -81,7 +81,7 @@ def authenticate_generator_if_needed(method): """ @functools.wraps(method) - async def authenticate_if_needed(self: APIClient, *args, **kwargs): + async def authenticate_if_needed(self: InternalAsyncClient, *args, **kwargs): # Authenticate if there is no token, no need to fire the request only to get 401 and retry if self.token is None: await self._authenticate() @@ -99,7 +99,7 @@ async def authenticate_if_needed(self: APIClient, *args, **kwargs): return authenticate_if_needed -class APIClient: +class InternalAsyncClient: """Perform CRUD operations over resources provided by Elis API. Requests will be retried up to `n_retries` times with exponential backoff. diff --git a/rossum_api/internal_sync_client.py b/rossum_api/clients/internal_sync_client.py similarity index 99% rename from rossum_api/internal_sync_client.py rename to rossum_api/clients/internal_sync_client.py index 9616472..fde36f3 100644 --- a/rossum_api/internal_sync_client.py +++ b/rossum_api/clients/internal_sync_client.py @@ -6,7 +6,7 @@ import tenacity from rossum_api import APIClientError -from rossum_api.api_client import Resource +from rossum_api.clients.internal_async_client import Resource from rossum_api.domain_logic.pagination import build_pagination_params from rossum_api.domain_logic.retry import AlwaysRetry, should_retry from rossum_api.domain_logic.sideloads import build_sideload_params, embed_sideloads @@ -16,7 +16,7 @@ from rossum_api.utils import enforce_domain -class InternalSyncRossumAPIClient: +class InternalSyncClient: def __init__( self, base_url: str, diff --git a/rossum_api/domain_logic/urls.py b/rossum_api/domain_logic/urls.py index f574e13..343fe6e 100644 --- a/rossum_api/domain_logic/urls.py +++ b/rossum_api/domain_logic/urls.py @@ -3,7 +3,7 @@ import re from typing import TYPE_CHECKING -from rossum_api.api_client import Resource +from rossum_api.clients.internal_async_client import Resource if TYPE_CHECKING: from rossum_api.models import Resource diff --git a/test.py b/test.py index d2ba97b..741c940 100644 --- a/test.py +++ b/test.py @@ -12,8 +12,8 @@ import aiofiles -from rossum_api import ElisAPIClient, ElisAPIClientSync -from rossum_api.api_client import APIClient +from rossum_api import AsyncRossumAPIClient, SyncRossumAPIClient +from rossum_api.clients.internal_async_client import InternalAsyncClient logging.basicConfig() logging.getLogger().setLevel(logging.DEBUG) @@ -51,7 +51,7 @@ async def main(): - client = APIClient( + client = InternalAsyncClient( os.environ["ELIS_USERNAME"], os.environ["ELIS_PASSWORD"], base_url="https://elis.develop.r8.lol/api/v1", @@ -112,7 +112,7 @@ async def main(): async def main_with_async_client(): - client = ElisAPIClient( + client = AsyncRossumAPIClient( os.environ["ELIS_USERNAME"], os.environ["ELIS_PASSWORD"], base_url="https://elis.develop.r8.lol/api/v1", @@ -146,7 +146,7 @@ async def main_with_async_client(): def main_with_sync_client(): - client = ElisAPIClientSync( + client = SyncRossumAPIClient( os.environ["ELIS_USERNAME"], os.environ["ELIS_PASSWORD"], base_url="https://elis.develop.r8.lol/api/v1", diff --git a/tests/conftest.py b/tests/conftest.py index 01e996a..dcde063 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,24 +6,24 @@ import pytest import pytest_asyncio -from rossum_api import ElisAPIClient, ElisAPIClientSync -from rossum_api.api_client import APIClient +from rossum_api import AsyncRossumAPIClient, SyncRossumAPIClient +from rossum_api.clients.internal_async_client import InternalAsyncClient @pytest.fixture def http_client(): - return MagicMock(APIClient) + return MagicMock(InternalAsyncClient) @pytest_asyncio.fixture def elis_client(http_client): - client = ElisAPIClient(username="", password="", base_url=None, http_client=http_client) + client = AsyncRossumAPIClient(username="", password="", base_url=None, http_client=http_client) return (client, http_client) @pytest.fixture def elis_client_sync(http_client): - client = ElisAPIClientSync(username="", password="", base_url=None, http_client=http_client) + client = SyncRossumAPIClient(username="", password="", base_url=None, http_client=http_client) return (client, http_client) diff --git a/tests/e2e.py b/tests/e2e.py index a449f0a..b62f3c5 100644 --- a/tests/e2e.py +++ b/tests/e2e.py @@ -18,7 +18,7 @@ import pytest from aiofiles import os as aios -from rossum_api import ElisAPIClient +from rossum_api import AsyncRossumAPIClient from rossum_api.domain_logic.resources import Resource if TYPE_CHECKING: @@ -44,7 +44,7 @@ async def test_import_document(self): queue: Optional[Queue] = None schema: Optional[Schema] = None - client = ElisAPIClient( + client = AsyncRossumAPIClient( token=os.environ["ROSSUM_TOKEN"], base_url=os.environ["ROSSUM_BASE_URL"], ) @@ -89,7 +89,7 @@ async def test_create_upload(self): queue: Optional[Queue] = None schema: Optional[Schema] = None - client = ElisAPIClient( + client = AsyncRossumAPIClient( token=os.environ["ROSSUM_TOKEN"], base_url=os.environ["ROSSUM_BASE_URL"], ) diff --git a/tests/test_api_client.py b/tests/test_api_client.py index 5bcf364..b935198 100644 --- a/tests/test_api_client.py +++ b/tests/test_api_client.py @@ -19,7 +19,7 @@ import pytest import pytest_httpx -from rossum_api.api_client import APIClient, APIClientError +from rossum_api.clients.internal_async_client import APIClientError, InternalAsyncClient from rossum_api.domain_logic.resources import Resource WORKSPACES = [ @@ -150,7 +150,9 @@ def count_calls_(*args, **kwargs): @pytest.fixture def client(): # Set retrying parameters to zero to keep tests fast - client = APIClient("username", "password", retry_backoff_factor=0, retry_max_jitter=0) + client = InternalAsyncClient( + "username", "password", retry_backoff_factor=0, retry_max_jitter=0 + ) client.token = FAKE_TOKEN return client @@ -207,7 +209,7 @@ def custom_response(request: httpx.Request, n_calls: int): @pytest.mark.asyncio async def test_init_token(httpx_mock): """Verifies that we can create client using token and call API without 'auth' call.""" - client = APIClient(token=FAKE_TOKEN) + client = InternalAsyncClient(token=FAKE_TOKEN) httpx_mock.add_response( method="GET", @@ -222,7 +224,7 @@ async def test_init_token(httpx_mock): @pytest.mark.asyncio async def test_reauth_no_credentials(httpx_mock): """Invalid token used but no credentials available for re-authentication. Raise 401.""" - client = APIClient(token=FAKE_TOKEN) + client = InternalAsyncClient(token=FAKE_TOKEN) httpx_mock.add_response( method="GET", @@ -239,7 +241,7 @@ async def test_reauth_no_credentials(httpx_mock): @pytest.mark.asyncio async def test_reauth_success(login_mock, httpx_mock): """Invalid token used, credentials available => reauthenticate in the background. Fetch user without raising any errors.""" - client = APIClient("username", "password", token=FAKE_TOKEN) + client = InternalAsyncClient("username", "password", token=FAKE_TOKEN) httpx_mock.add_response( method="GET", @@ -678,7 +680,7 @@ def set_token(): @pytest.mark.asyncio async def test_authenticate_if_needed_no_token(httpx_mock): - client = APIClient("username", "password") + client = InternalAsyncClient("username", "password") httpx_mock.add_response( method="GET", url="https://elis.rossum.ai/api/v1/workspaces/7694", @@ -697,7 +699,7 @@ def set_token(): @pytest.mark.asyncio async def test_authenticate_generator_if_needed_no_token(client, httpx_mock): - client = APIClient("username", "password") + client = InternalAsyncClient("username", "password") httpx_mock.add_response( method="GET", url="https://elis.rossum.ai/api/v1/queues/123/export?format=csv",