From 36835b84308fe7aa1bfd4446d87b0d919a406685 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Tue, 7 May 2024 14:11:13 +0200 Subject: [PATCH 01/48] Bump generator to 1.10.1 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index f196046..89623a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "@seamapi/python", "devDependencies": { - "@seamapi/nextlove-sdk-generator": "^1.9.0", + "@seamapi/nextlove-sdk-generator": "^1.10.1", "@seamapi/types": "1.164.0", "del": "^7.1.0", "prettier": "^3.2.5" @@ -416,9 +416,9 @@ } }, "node_modules/@seamapi/nextlove-sdk-generator": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@seamapi/nextlove-sdk-generator/-/nextlove-sdk-generator-1.9.0.tgz", - "integrity": "sha512-PtYGG8OM0yhxkvw2BmNh50Ho1Emq+BX+9sl7tdcSXpJ+Vpn7DipiAofruhrpf0yiO6YkKix1j++Exe1i0iRvUA==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@seamapi/nextlove-sdk-generator/-/nextlove-sdk-generator-1.10.1.tgz", + "integrity": "sha512-sPvDbMW0jVOTByvLkdVH5JvIwZTsOfU5RlkFfI8WRu2ybB50yMOm02vgsHgCG00SobWgSsUeYnFSec+XTlPfMQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 2a0744f..3c55e24 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "format": "prettier --write --ignore-path .gitignore ." }, "devDependencies": { - "@seamapi/nextlove-sdk-generator": "^1.9.0", + "@seamapi/nextlove-sdk-generator": "^1.10.1", "@seamapi/types": "1.164.0", "del": "^7.1.0", "prettier": "^3.2.5" From fc1f19c02ed217b097f5bdc137c5ad21a364a445 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Tue, 7 May 2024 12:11:49 +0000 Subject: [PATCH 02/48] ci: Generate code --- seam/__init__.py | 10 ++++- seam/auth.py | 101 ++++++++++++++++++++++++++++++++++++++++++ seam/parse_options.py | 89 +++++++++++++++++++++++++++++++++++++ seam/routes.py | 3 -- seam/seam.py | 93 ++++++++++++++++++++++---------------- seam/token.py | 47 ++++++++++++++++++++ seam/types.py | 43 +++++++++++++----- 7 files changed, 331 insertions(+), 55 deletions(-) create mode 100644 seam/auth.py create mode 100644 seam/parse_options.py create mode 100644 seam/token.py diff --git a/seam/__init__.py b/seam/__init__.py index 50537ff..54b94c6 100644 --- a/seam/__init__.py +++ b/seam/__init__.py @@ -1,5 +1,11 @@ # flake8: noqa # type: ignore -from seam.seam import Seam -from seam.seam import SeamApiException +from seam.seam import Seam, SeamApiException +from seam.parse_options import SeamHttpInvalidOptionsError +from seam.auth import SeamHttpInvalidTokenError +from seam.utils.action_attempt_errors import ( + SeamActionAttemptError, + SeamActionAttemptFailedError, + SeamActionAttemptTimeoutError, +) diff --git a/seam/auth.py b/seam/auth.py new file mode 100644 index 0000000..36bc31f --- /dev/null +++ b/seam/auth.py @@ -0,0 +1,101 @@ +from typing import Optional +from seam.parse_options import ( + SeamHttpInvalidOptionsError, + is_seam_http_options_with_api_key, + is_seam_http_options_with_personal_access_token, +) +from seam.token import ( + is_jwt, + is_access_token, + is_client_session_token, + is_publishable_key, + is_seam_token, + token_prefix, + access_token_prefix, +) + + +class SeamHttpInvalidTokenError(Exception): + def __init__(self, message): + super().__init__(f"SeamHttp received an invalid token: {message}") + + +def get_auth_headers( + api_key: Optional[str] = None, + personal_access_token: Optional[str] = None, + workspace_id: Optional[str] = None, +): + if is_seam_http_options_with_api_key( + api_key=api_key, + personal_access_token=personal_access_token, + ): + return get_auth_headers_for_api_key(api_key) + + if is_seam_http_options_with_personal_access_token( + personal_access_token=personal_access_token, + api_key=api_key, + workspace_id=workspace_id, + ): + return get_auth_headers_for_personal_access_token( + personal_access_token, workspace_id + ) + + raise SeamHttpInvalidOptionsError( + "Must specify an api_key or personal_access_token. " + "Attempted reading configuration from the environment, " + "but the environment variable SEAM_API_KEY is not set." + ) + + +def get_auth_headers_for_api_key(api_key: str) -> dict: + if is_client_session_token(api_key): + raise SeamHttpInvalidTokenError( + "A Client Session Token cannot be used as an api_key" + ) + + if is_jwt(api_key): + raise SeamHttpInvalidTokenError("A JWT cannot be used as an api_key") + + if is_access_token(api_key): + raise SeamHttpInvalidTokenError("An Access Token cannot be used as an api_key") + + if is_publishable_key(api_key): + raise SeamHttpInvalidTokenError( + "A Publishable Key cannot be used as an api_key" + ) + + if not is_seam_token(api_key): + raise SeamHttpInvalidTokenError( + f"Unknown or invalid api_key format, expected token to start with {token_prefix}" + ) + + return {"authorization": f"Bearer {api_key}"} + + +def get_auth_headers_for_personal_access_token( + personal_access_token: str, workspace_id: str +) -> dict: + if is_jwt(personal_access_token): + raise SeamHttpInvalidTokenError( + "A JWT cannot be used as a personal_access_token" + ) + + if is_client_session_token(personal_access_token): + raise SeamHttpInvalidTokenError( + "A Client Session Token cannot be used as a personal_access_token" + ) + + if is_publishable_key(personal_access_token): + raise SeamHttpInvalidTokenError( + "A Publishable Key cannot be used as a personal_access_token" + ) + + if not is_access_token(personal_access_token): + raise SeamHttpInvalidTokenError( + f"Unknown or invalid personal_access_token format, expected token to start with {access_token_prefix}" + ) + + return { + "authorization": f"Bearer {personal_access_token}", + "seam-workspace": workspace_id, + } diff --git a/seam/parse_options.py b/seam/parse_options.py new file mode 100644 index 0000000..909fe33 --- /dev/null +++ b/seam/parse_options.py @@ -0,0 +1,89 @@ +import os +from typing import Optional + +DEFAULT_ENDPOINT = "https://connect.getseam.com" + + +def parse_options( + api_key: Optional[str] = None, + personal_access_token: Optional[str] = None, + workspace_id: Optional[str] = None, + endpoint: Optional[str] = None, +): + if personal_access_token is None: + api_key = api_key or os.getenv("SEAM_API_KEY") + + from seam.auth import get_auth_headers + + auth_headers = get_auth_headers( + api_key=api_key, + personal_access_token=personal_access_token, + workspace_id=workspace_id, + ) + endpoint = endpoint or get_endpoint_from_env() or DEFAULT_ENDPOINT + + return auth_headers, endpoint + + +def get_endpoint_from_env(): + seam_api_url = os.getenv("SEAM_API_URL") + seam_endpoint = os.getenv("SEAM_ENDPOINT") + + if seam_api_url is not None: + print( + "\033[93m" + "Using the SEAM_API_URL environment variable is deprecated. " + "Support will be removed in a later major version. Use SEAM_ENDPOINT instead." + "\033[0m" + ) + + if seam_api_url is not None and seam_endpoint is not None: + print( + "\033[93m" + "Detected both the SEAM_API_URL and SEAM_ENDPOINT environment variables. " + "Using SEAM_ENDPOINT." + "\033[0m" + ) + + return seam_endpoint or seam_api_url + + +class SeamHttpInvalidOptionsError(Exception): + def __init__(self, message): + super().__init__(f"SeamHttp received invalid options: {message}") + + +def is_seam_http_options_with_api_key( + api_key: Optional[str] = None, + personal_access_token: Optional[str] = None, +) -> bool: + if api_key is None: + return False + + if personal_access_token is not None: + raise SeamHttpInvalidOptionsError( + "The personal_access_token option cannot be used with the api_key option" + ) + + return True + + +def is_seam_http_options_with_personal_access_token( + personal_access_token: Optional[str] = None, + api_key: Optional[str] = None, + workspace_id: Optional[str] = None, +) -> bool: + if personal_access_token is None: + return False + + if api_key is not None: + raise SeamHttpInvalidOptionsError( + "The api_key option cannot be used with the personal_access_token option" + ) + + if workspace_id is None: + raise SeamHttpInvalidOptionsError( + "Must pass a workspace_id when using a personal_access_token" + ) + + return True diff --git a/seam/routes.py b/seam/routes.py index e593648..35ba96e 100644 --- a/seam/routes.py +++ b/seam/routes.py @@ -35,6 +35,3 @@ def __init__(self): self.user_identities = UserIdentities(seam=self) self.webhooks = Webhooks(seam=self) self.workspaces = Workspaces(seam=self) - - def make_request(self, method: str, path: str, **kwargs): - raise NotImplementedError() diff --git a/seam/seam.py b/seam/seam.py index 1efc215..c8a8f06 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -1,9 +1,10 @@ -import os - -from .routes import Routes import requests from importlib.metadata import version -from typing import Optional, Union, Dict, cast +from typing import Optional, Union, Dict +from typing_extensions import Self + +from seam.parse_options import parse_options +from .routes import Routes from .types import AbstractSeam, SeamApiException @@ -12,58 +13,44 @@ class Seam(AbstractSeam): Initial Seam class used to interact with Seam API """ - api_key: str - api_url: str = "https://connect.getseam.com" lts_version: str = "1.0.0" def __init__( self, api_key: Optional[str] = None, *, + personal_access_token: Optional[str] = None, workspace_id: Optional[str] = None, - api_url: Optional[str] = None, + endpoint: Optional[str] = None, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, ): """ Parameters ---------- api_key : str, optional - API key + API key. + personal_access_token : str, optional + Personal access token. workspace_id : str, optional - Workspace id - api_url : str, optional - API url + Workspace id. + endpoint : str, optional + The API endpoint to which the request should be sent. + wait_for_action_attempt : bool or dict, optional + Controls whether to wait for an action attempt to complete, either as a boolean or as a dictionary specifying `timeout` and `poll_interval`. Defaults to `False`. """ + Routes.__init__(self) - if api_key is None: - api_key = os.environ.get("SEAM_API_KEY", None) - if api_key is None: - raise Exception( - "SEAM_API_KEY not found in environment, and api_key not provided" - ) - if workspace_id is None: - workspace_id = os.environ.get("SEAM_WORKSPACE_ID", None) - self.api_key = api_key - self.workspace_id = workspace_id self.lts_version = Seam.lts_version self.wait_for_action_attempt = wait_for_action_attempt - - if os.environ.get("SEAM_API_URL", None) is not None: - print( - "\n" - "\033[93m" - "Using the SEAM_API_URL environment variable is deprecated. " - "Support will be removed in a later major version. Use SEAM_ENDPOINT instead." - "\033[0m" - ) - api_url = ( - os.environ.get("SEAM_API_URL", None) - or os.environ.get("SEAM_ENDPOINT", None) - or api_url + auth_headers, endpoint = parse_options( + api_key=api_key, + personal_access_token=personal_access_token, + workspace_id=workspace_id, + endpoint=endpoint, ) - if api_url is not None: - self.api_url = cast(str, api_url) + self.__auth_headers = auth_headers + self.__endpoint = endpoint def make_request(self, method: str, path: str, **kwargs): """ @@ -79,14 +66,14 @@ def make_request(self, method: str, path: str, **kwargs): Keyword arguments passed to requests.request """ - url = self.api_url + path + url = self.__endpoint + path sdk_version = version("seam") headers = { - "Authorization": "Bearer " + self.api_key, + **self.__auth_headers, "Content-Type": "application/json", "User-Agent": "Python SDK v" + sdk_version - + " (https://github.com/seamapi/python)", + + " (https://github.com/seamapi/python-next)", "seam-sdk-name": "seamapi/python", "seam-sdk-version": sdk_version, "seam-lts-version": self.lts_version, @@ -102,3 +89,31 @@ def make_request(self, method: str, path: str, **kwargs): return response.json() return response.text + + @classmethod + def from_api_key( + cls, + api_key: str, + *, + endpoint: Optional[str] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + ) -> Self: + return cls( + api_key, endpoint=endpoint, wait_for_action_attempt=wait_for_action_attempt + ) + + @classmethod + def from_personal_access_token( + cls, + personal_access_token: str, + workspace_id: str, + *, + endpoint: Optional[str] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + ) -> Self: + return cls( + personal_access_token=personal_access_token, + workspace_id=workspace_id, + endpoint=endpoint, + wait_for_action_attempt=wait_for_action_attempt, + ) diff --git a/seam/token.py b/seam/token.py new file mode 100644 index 0000000..3c94c9f --- /dev/null +++ b/seam/token.py @@ -0,0 +1,47 @@ +token_prefix = "seam_" + +access_token_prefix = "seam_at" + +jwt_prefix = "ey" + +client_session_token_prefix = "seam_cst" + +publishable_key_token_prefix = "seam_pk" + + +def is_access_token(token: str) -> bool: + return token.startswith(access_token_prefix) + + +def is_jwt(token: str) -> bool: + return token.startswith(jwt_prefix) + + +def is_seam_token(token: str) -> bool: + return token.startswith(token_prefix) + + +def is_api_key(token: str) -> bool: + return ( + not is_client_session_token(token) + and not is_jwt(token) + and not is_access_token(token) + and not is_publishable_key(token) + and is_seam_token(token) + ) + + +def is_client_session_token(token: str) -> bool: + return token.startswith(client_session_token_prefix) + + +def is_publishable_key(token: str) -> bool: + return token.startswith(publishable_key_token_prefix) + + +def is_console_session_token(token: str) -> bool: + return is_jwt(token) + + +def is_personal_access_token(token: str) -> bool: + return is_access_token(token) diff --git a/seam/types.py b/seam/types.py index 7adc75e..ad9e5b6 100644 --- a/seam/types.py +++ b/seam/types.py @@ -1,4 +1,5 @@ from typing import Any, Dict, List, Optional, Union +from typing_extensions import Self import abc from dataclasses import dataclass from seam.utils.deep_attr_dict import DeepAttrDict @@ -2061,26 +2062,46 @@ class AbstractRoutes(abc.ABC): webhooks: AbstractWebhooks workspaces: AbstractWorkspaces - @abc.abstractmethod - def make_request(self, method: str, path: str, **kwargs) -> Any: - raise NotImplementedError - -@dataclass class AbstractSeam(AbstractRoutes): - api_key: str - workspace_id: str - api_url: str lts_version: str - wait_for_action_attempt: bool @abc.abstractmethod def __init__( self, api_key: Optional[str] = None, *, + personal_access_token: Optional[str] = None, workspace_id: Optional[str] = None, - api_url: Optional[str] = None, - wait_for_action_attempt: Optional[bool] = False, + endpoint: Optional[str] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, ): + self.wait_for_action_attempt = wait_for_action_attempt + self.lts_version = AbstractSeam.lts_version + + @abc.abstractmethod + def make_request(self, method: str, path: str, **kwargs) -> Any: + raise NotImplementedError + + @classmethod + @abc.abstractmethod + def from_api_key( + cls, + api_key: str, + *, + endpoint: Optional[str] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + ) -> Self: + raise NotImplementedError + + @classmethod + @abc.abstractmethod + def from_personal_access_token( + cls, + personal_access_token: str, + workspace_id: str, + *, + endpoint: Optional[str] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + ) -> Self: raise NotImplementedError From 7c8b2c17d234a08c03a576fa5ec31c85df291fe5 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Tue, 7 May 2024 14:42:27 +0200 Subject: [PATCH 03/48] Fix tests --- test/conftest.py | 2 +- test/test_init_seam.py | 3 --- test/workspaces/test_workspaces_create.py | 24 ++++++++++++----------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/test/conftest.py b/test/conftest.py index 92f2212..25f981d 100755 --- a/test/conftest.py +++ b/test/conftest.py @@ -9,6 +9,6 @@ def seam(): r = "".join(random.choices(string.ascii_uppercase + string.digits, k=10)) seam = Seam( - api_url=f"https://{r}.fakeseamconnect.seam.vc", api_key="seam_apikey1_token" + endpoint=f"https://{r}.fakeseamconnect.seam.vc", api_key="seam_apikey1_token" ) yield seam diff --git a/test/test_init_seam.py b/test/test_init_seam.py index 4fc67b6..7205da3 100644 --- a/test/test_init_seam.py +++ b/test/test_init_seam.py @@ -2,8 +2,5 @@ def test_init_seam_with_fixture(seam: Seam): - assert seam.api_key - assert seam.api_url assert seam.lts_version - assert "http" in seam.api_url assert seam.wait_for_action_attempt is False diff --git a/test/workspaces/test_workspaces_create.py b/test/workspaces/test_workspaces_create.py index a93b2e8..be4e109 100644 --- a/test/workspaces/test_workspaces_create.py +++ b/test/workspaces/test_workspaces_create.py @@ -4,16 +4,18 @@ def test_workspaces_create(seam: Seam): - r = "".join(random.choices(string.ascii_uppercase + string.digits, k=10)) - seam = Seam( - api_url=f"https://{r}.fakeseamconnect.seam.vc", - api_key="seam_at1_shorttoken_longtoken", - ) + # TODO: use SeamMultiWorkspace when implemented + # r = "".join(random.choices(string.ascii_uppercase + string.digits, k=10)) + # seam = Seam( + # endpoint=f"https://{r}.fakeseamconnect.seam.vc", + # api_key="seam_at1_shorttoken_longtoken", + # ) - workspace = seam.workspaces.create( - name="Test Workspace", - connect_partner_name="Example Partner", - is_sandbox=True, - ) + # workspace = seam.workspaces.create( + # name="Test Workspace", + # connect_partner_name="Example Partner", + # is_sandbox=True, + # ) - assert workspace.workspace_id + # assert workspace.workspace_id + pass From 2d3e347e115e9a6bd4039a1c5cfbf24052910052 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Tue, 7 May 2024 14:43:43 +0200 Subject: [PATCH 04/48] Bump generator to 1.10.2 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 89623a9..875475f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "@seamapi/python", "devDependencies": { - "@seamapi/nextlove-sdk-generator": "^1.10.1", + "@seamapi/nextlove-sdk-generator": "^1.10.2", "@seamapi/types": "1.164.0", "del": "^7.1.0", "prettier": "^3.2.5" @@ -416,9 +416,9 @@ } }, "node_modules/@seamapi/nextlove-sdk-generator": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@seamapi/nextlove-sdk-generator/-/nextlove-sdk-generator-1.10.1.tgz", - "integrity": "sha512-sPvDbMW0jVOTByvLkdVH5JvIwZTsOfU5RlkFfI8WRu2ybB50yMOm02vgsHgCG00SobWgSsUeYnFSec+XTlPfMQ==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/@seamapi/nextlove-sdk-generator/-/nextlove-sdk-generator-1.10.2.tgz", + "integrity": "sha512-WJXflvkG785iXrmQBjKpFzfHlQA68bdIuLazJSEP14fx/OYbuWqRD17HwMKwoW1+BSH5VUff/b+d/3OQ3g/W6w==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 3c55e24..f7abec4 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "format": "prettier --write --ignore-path .gitignore ." }, "devDependencies": { - "@seamapi/nextlove-sdk-generator": "^1.10.1", + "@seamapi/nextlove-sdk-generator": "^1.10.2", "@seamapi/types": "1.164.0", "del": "^7.1.0", "prettier": "^3.2.5" From 3ce4f0df2bbfc85357765ab5ec29ca5a307f68ff Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Tue, 7 May 2024 12:44:33 +0000 Subject: [PATCH 05/48] ci: Generate code --- seam/seam.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/seam/seam.py b/seam/seam.py index c8a8f06..35ace66 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -78,8 +78,7 @@ def make_request(self, method: str, path: str, **kwargs): "seam-sdk-version": sdk_version, "seam-lts-version": self.lts_version, } - if self.workspace_id is not None: - headers["seam-workspace"] = self.workspace_id + response = requests.request(method, url, headers=headers, **kwargs) if response.status_code != 200: From b7bd0462da37d5fbe854b48b509141596f6366b1 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 8 May 2024 11:29:58 +0200 Subject: [PATCH 06/48] Remove unnecessary import --- test/conftest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/conftest.py b/test/conftest.py index 25f981d..1baa8fd 100755 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,6 +1,5 @@ import pytest from seam import Seam -from typing import Any import random import string From 9d9198770ff1c4fafb6986b203c2db179884fe3d Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 8 May 2024 11:52:35 +0200 Subject: [PATCH 07/48] Bump generator to 1.10.3 to fix circular imports with a separate file options.py --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 875475f..0eb9301 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "@seamapi/python", "devDependencies": { - "@seamapi/nextlove-sdk-generator": "^1.10.2", + "@seamapi/nextlove-sdk-generator": "^1.10.3", "@seamapi/types": "1.164.0", "del": "^7.1.0", "prettier": "^3.2.5" @@ -416,9 +416,9 @@ } }, "node_modules/@seamapi/nextlove-sdk-generator": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@seamapi/nextlove-sdk-generator/-/nextlove-sdk-generator-1.10.2.tgz", - "integrity": "sha512-WJXflvkG785iXrmQBjKpFzfHlQA68bdIuLazJSEP14fx/OYbuWqRD17HwMKwoW1+BSH5VUff/b+d/3OQ3g/W6w==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@seamapi/nextlove-sdk-generator/-/nextlove-sdk-generator-1.10.3.tgz", + "integrity": "sha512-vDjxXBosRubjOHzF7pZQRZ7YTOnKFU2wLpFkzChOeKaMwGGK7O4JJcSsGz8oGnTH9KxQOOJKV0x5wglI9ELisw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index f7abec4..28155b9 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "format": "prettier --write --ignore-path .gitignore ." }, "devDependencies": { - "@seamapi/nextlove-sdk-generator": "^1.10.2", + "@seamapi/nextlove-sdk-generator": "^1.10.3", "@seamapi/types": "1.164.0", "del": "^7.1.0", "prettier": "^3.2.5" From 37aafe7b47d272990132ba0c0fe0cb431e312b57 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 8 May 2024 09:53:27 +0000 Subject: [PATCH 08/48] ci: Generate code --- seam/__init__.py | 2 +- seam/auth.py | 2 +- seam/options.py | 66 +++++++++++++++++++++++++++++++++++++++++ seam/parse_options.py | 69 ++----------------------------------------- 4 files changed, 71 insertions(+), 68 deletions(-) create mode 100644 seam/options.py diff --git a/seam/__init__.py b/seam/__init__.py index 54b94c6..05e3fa2 100644 --- a/seam/__init__.py +++ b/seam/__init__.py @@ -2,7 +2,7 @@ # type: ignore from seam.seam import Seam, SeamApiException -from seam.parse_options import SeamHttpInvalidOptionsError +from seam.options import SeamHttpInvalidOptionsError from seam.auth import SeamHttpInvalidTokenError from seam.utils.action_attempt_errors import ( SeamActionAttemptError, diff --git a/seam/auth.py b/seam/auth.py index 36bc31f..9b461ac 100644 --- a/seam/auth.py +++ b/seam/auth.py @@ -1,5 +1,5 @@ from typing import Optional -from seam.parse_options import ( +from seam.options import ( SeamHttpInvalidOptionsError, is_seam_http_options_with_api_key, is_seam_http_options_with_personal_access_token, diff --git a/seam/options.py b/seam/options.py new file mode 100644 index 0000000..ee7b4fa --- /dev/null +++ b/seam/options.py @@ -0,0 +1,66 @@ +import os +from typing import Optional + + +def get_endpoint_from_env(): + seam_api_url = os.getenv("SEAM_API_URL") + seam_endpoint = os.getenv("SEAM_ENDPOINT") + + if seam_api_url is not None: + print( + "\033[93m" + "Using the SEAM_API_URL environment variable is deprecated. " + "Support will be removed in a later major version. Use SEAM_ENDPOINT instead." + "\033[0m" + ) + + if seam_api_url is not None and seam_endpoint is not None: + print( + "\033[93m" + "Detected both the SEAM_API_URL and SEAM_ENDPOINT environment variables. " + "Using SEAM_ENDPOINT." + "\033[0m" + ) + + return seam_endpoint or seam_api_url + + +class SeamHttpInvalidOptionsError(Exception): + def __init__(self, message): + super().__init__(f"SeamHttp received invalid options: {message}") + + +def is_seam_http_options_with_api_key( + api_key: Optional[str] = None, + personal_access_token: Optional[str] = None, +) -> bool: + if api_key is None: + return False + + if personal_access_token is not None: + raise SeamHttpInvalidOptionsError( + "The personal_access_token option cannot be used with the api_key option" + ) + + return True + + +def is_seam_http_options_with_personal_access_token( + personal_access_token: Optional[str] = None, + api_key: Optional[str] = None, + workspace_id: Optional[str] = None, +) -> bool: + if personal_access_token is None: + return False + + if api_key is not None: + raise SeamHttpInvalidOptionsError( + "The api_key option cannot be used with the personal_access_token option" + ) + + if workspace_id is None: + raise SeamHttpInvalidOptionsError( + "Must pass a workspace_id when using a personal_access_token" + ) + + return True diff --git a/seam/parse_options.py b/seam/parse_options.py index 909fe33..fa5c1b1 100644 --- a/seam/parse_options.py +++ b/seam/parse_options.py @@ -1,6 +1,9 @@ import os from typing import Optional +from seam.auth import get_auth_headers +from seam.options import get_endpoint_from_env + DEFAULT_ENDPOINT = "https://connect.getseam.com" @@ -13,8 +16,6 @@ def parse_options( if personal_access_token is None: api_key = api_key or os.getenv("SEAM_API_KEY") - from seam.auth import get_auth_headers - auth_headers = get_auth_headers( api_key=api_key, personal_access_token=personal_access_token, @@ -23,67 +24,3 @@ def parse_options( endpoint = endpoint or get_endpoint_from_env() or DEFAULT_ENDPOINT return auth_headers, endpoint - - -def get_endpoint_from_env(): - seam_api_url = os.getenv("SEAM_API_URL") - seam_endpoint = os.getenv("SEAM_ENDPOINT") - - if seam_api_url is not None: - print( - "\033[93m" - "Using the SEAM_API_URL environment variable is deprecated. " - "Support will be removed in a later major version. Use SEAM_ENDPOINT instead." - "\033[0m" - ) - - if seam_api_url is not None and seam_endpoint is not None: - print( - "\033[93m" - "Detected both the SEAM_API_URL and SEAM_ENDPOINT environment variables. " - "Using SEAM_ENDPOINT." - "\033[0m" - ) - - return seam_endpoint or seam_api_url - - -class SeamHttpInvalidOptionsError(Exception): - def __init__(self, message): - super().__init__(f"SeamHttp received invalid options: {message}") - - -def is_seam_http_options_with_api_key( - api_key: Optional[str] = None, - personal_access_token: Optional[str] = None, -) -> bool: - if api_key is None: - return False - - if personal_access_token is not None: - raise SeamHttpInvalidOptionsError( - "The personal_access_token option cannot be used with the api_key option" - ) - - return True - - -def is_seam_http_options_with_personal_access_token( - personal_access_token: Optional[str] = None, - api_key: Optional[str] = None, - workspace_id: Optional[str] = None, -) -> bool: - if personal_access_token is None: - return False - - if api_key is not None: - raise SeamHttpInvalidOptionsError( - "The api_key option cannot be used with the personal_access_token option" - ) - - if workspace_id is None: - raise SeamHttpInvalidOptionsError( - "Must pass a workspace_id when using a personal_access_token" - ) - - return True From 388d0413a3ada15452b593533b3975c32cf4ee8f Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 8 May 2024 15:10:23 +0200 Subject: [PATCH 09/48] Add SeamMultiWorkspace --- seam/seam_multi_workspace.py | 89 ++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 seam/seam_multi_workspace.py diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py new file mode 100644 index 0000000..6ce94aa --- /dev/null +++ b/seam/seam_multi_workspace.py @@ -0,0 +1,89 @@ +import requests +from typing import Dict, Optional, Union +from importlib.metadata import version + +from seam.types import AbstractSeam, SeamApiException +from .workspaces import Workspaces + + +class SeamMultiWorkspace(AbstractSeam): + """ + Seam class used to interact with Seam API without being scoped to any specific workspace. + """ + + lts_version: str = "1.0.0" # use constant for LTS version + + def __init__( + self, + personal_access_token: str, + *, + endpoint: Optional[str] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + ): + """ + Parameters + ---------- + personal_access_token : str, optional + Personal access token. + endpoint : str, optional + The API endpoint to which the request should be sent. + wait_for_action_attempt : bool or dict, optional + Controls whether to wait for an action attempt to complete, either as a boolean or as a dictionary specifying `timeout` and `poll_interval`. Defaults to `False`. + """ + + self.lts_version = SeamMultiWorkspace.lts_version + self.wait_for_action_attempt = wait_for_action_attempt + self.__auth_headers = {"Authorization": f"Bearer {personal_access_token}"} + self.__endpoint = "" # get_endpoint() + + self._workspaces = Workspaces(seam=self) + self.workspaces = self.WorkspacesProxy(self._workspaces) + + class WorkspacesProxy: + """Proxy to expose only the 'create' and 'list' methods of Workspaces.""" + + def __init__(self, workspaces): + self._workspaces = workspaces + + def list(self, *args, **kwargs): + return self._workspaces.list(*args, **kwargs) + + def create(self, data, *args, **kwargs): + return self._workspaces.create(data, *args, **kwargs) + + def make_request(self, method: str, path: str, **kwargs): + """ + Makes a request to the API + + Parameters + ---------- + method : str + Request method + path : str + Request path + **kwargs + Keyword arguments passed to requests.request + """ + + url = self.__endpoint + path + sdk_version = version("seam") + headers = { + **self.__auth_headers, + "Content-Type": "application/json", + "User-Agent": "Python SDK v" + + sdk_version + + " (https://github.com/seamapi/python-next)", + "seam-sdk-name": "seamapi/python", + "seam-sdk-version": sdk_version, + "seam-lts-version": self.lts_version, + } + + response = requests.request(method, url, headers=headers, **kwargs) + + if response.status_code != 200: + raise SeamApiException(response) + + if "application/json" in response.headers["content-type"]: + return response.json() + + return response.text From 2e29968b52b76c145f519c41eada41ea1c400fc7 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 8 May 2024 15:11:00 +0200 Subject: [PATCH 10/48] Add constants.py --- seam/constants.py | 3 +++ seam/parse_options.py | 3 +-- seam/seam.py | 3 ++- seam/seam_multi_workspace.py | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 seam/constants.py diff --git a/seam/constants.py b/seam/constants.py new file mode 100644 index 0000000..562a28d --- /dev/null +++ b/seam/constants.py @@ -0,0 +1,3 @@ +LTS_VERSION = "1.0.0" + +DEFAULT_ENDPOINT = "https://connect.getseam.com" diff --git a/seam/parse_options.py b/seam/parse_options.py index fa5c1b1..dbf3bee 100644 --- a/seam/parse_options.py +++ b/seam/parse_options.py @@ -2,10 +2,9 @@ from typing import Optional from seam.auth import get_auth_headers +from seam.constants import DEFAULT_ENDPOINT from seam.options import get_endpoint_from_env -DEFAULT_ENDPOINT = "https://connect.getseam.com" - def parse_options( api_key: Optional[str] = None, diff --git a/seam/seam.py b/seam/seam.py index 35ace66..b2e0712 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -3,6 +3,7 @@ from typing import Optional, Union, Dict from typing_extensions import Self +from seam.constants import LTS_VERSION from seam.parse_options import parse_options from .routes import Routes from .types import AbstractSeam, SeamApiException @@ -13,7 +14,7 @@ class Seam(AbstractSeam): Initial Seam class used to interact with Seam API """ - lts_version: str = "1.0.0" + lts_version: str = LTS_VERSION def __init__( self, diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py index 6ce94aa..a4cb209 100644 --- a/seam/seam_multi_workspace.py +++ b/seam/seam_multi_workspace.py @@ -2,6 +2,7 @@ from typing import Dict, Optional, Union from importlib.metadata import version +from seam.constants import LTS_VERSION from seam.types import AbstractSeam, SeamApiException from .workspaces import Workspaces @@ -11,7 +12,7 @@ class SeamMultiWorkspace(AbstractSeam): Seam class used to interact with Seam API without being scoped to any specific workspace. """ - lts_version: str = "1.0.0" # use constant for LTS version + lts_version: str = LTS_VERSION def __init__( self, From db19b7b5b5066fbd3a5cee3e4ebabcc83ebb51d6 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 8 May 2024 15:17:28 +0200 Subject: [PATCH 11/48] Add get_endpoint helper fn --- seam/options.py | 6 ++++++ seam/parse_options.py | 5 ++--- seam/seam_multi_workspace.py | 5 +++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/seam/options.py b/seam/options.py index ee7b4fa..e7b5ba7 100644 --- a/seam/options.py +++ b/seam/options.py @@ -1,6 +1,12 @@ import os from typing import Optional +from seam.constants import DEFAULT_ENDPOINT + + +def get_endpoint(endpoint: Optional[str] = None): + return endpoint or get_endpoint_from_env() or DEFAULT_ENDPOINT + def get_endpoint_from_env(): seam_api_url = os.getenv("SEAM_API_URL") diff --git a/seam/parse_options.py b/seam/parse_options.py index dbf3bee..4e40ca8 100644 --- a/seam/parse_options.py +++ b/seam/parse_options.py @@ -2,8 +2,7 @@ from typing import Optional from seam.auth import get_auth_headers -from seam.constants import DEFAULT_ENDPOINT -from seam.options import get_endpoint_from_env +from seam.options import get_endpoint def parse_options( @@ -20,6 +19,6 @@ def parse_options( personal_access_token=personal_access_token, workspace_id=workspace_id, ) - endpoint = endpoint or get_endpoint_from_env() or DEFAULT_ENDPOINT + endpoint = get_endpoint(endpoint) return auth_headers, endpoint diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py index a4cb209..21b3605 100644 --- a/seam/seam_multi_workspace.py +++ b/seam/seam_multi_workspace.py @@ -3,6 +3,7 @@ from importlib.metadata import version from seam.constants import LTS_VERSION +from seam.options import get_endpoint from seam.types import AbstractSeam, SeamApiException from .workspaces import Workspaces @@ -34,8 +35,8 @@ def __init__( self.lts_version = SeamMultiWorkspace.lts_version self.wait_for_action_attempt = wait_for_action_attempt - self.__auth_headers = {"Authorization": f"Bearer {personal_access_token}"} - self.__endpoint = "" # get_endpoint() + self.__auth_headers = {"authorization": f"Bearer {personal_access_token}"} + self.__endpoint = get_endpoint(endpoint) self._workspaces = Workspaces(seam=self) self.workspaces = self.WorkspacesProxy(self._workspaces) From 1d73c4417dcea9db01bf9c208dfacfb57bad4105 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 8 May 2024 15:40:49 +0200 Subject: [PATCH 12/48] Add RequestMixin to define common make_request method --- seam/__init__.py | 4 ++- seam/request.py | 47 +++++++++++++++++++++++++++++++++ seam/seam.py | 50 +++++------------------------------- seam/seam_multi_workspace.py | 50 +++++------------------------------- seam/types.py | 9 ++++--- 5 files changed, 67 insertions(+), 93 deletions(-) create mode 100644 seam/request.py diff --git a/seam/__init__.py b/seam/__init__.py index 05e3fa2..ccd056f 100644 --- a/seam/__init__.py +++ b/seam/__init__.py @@ -1,7 +1,9 @@ # flake8: noqa # type: ignore -from seam.seam import Seam, SeamApiException +from seam.seam import Seam +from seam.types import SeamApiException +from seam.seam_multi_workspace import SeamMultiWorkspace from seam.options import SeamHttpInvalidOptionsError from seam.auth import SeamHttpInvalidTokenError from seam.utils.action_attempt_errors import ( diff --git a/seam/request.py b/seam/request.py new file mode 100644 index 0000000..11dcc84 --- /dev/null +++ b/seam/request.py @@ -0,0 +1,47 @@ +import requests +from importlib.metadata import version + +from seam.types import AbstractRequestMixin, SeamApiException + + +class RequestMixin(AbstractRequestMixin): + def make_request(self, method: str, path: str, **kwargs): + """ + Makes a request to the API + + Parameters + ---------- + method : str + Request method + path : str + Request path + **kwargs + Keyword arguments passed to requests.request + + Raises + ------ + SeamApiException: If the response status code is not successful. + """ + + url = self._endpoint + path + sdk_version = version("seam") + headers = { + **self._auth_headers, + "Content-Type": "application/json", + "User-Agent": "Python SDK v" + + sdk_version + + " (https://github.com/seamapi/python-next)", + "seam-sdk-name": "seamapi/python", + "seam-sdk-version": sdk_version, + "seam-lts-version": self.lts_version, + } + + response = requests.request(method, url, headers=headers, **kwargs) + + if response.status_code != 200: + raise SeamApiException(response) + + if "application/json" in response.headers["content-type"]: + return response.json() + + return response.text diff --git a/seam/seam.py b/seam/seam.py index b2e0712..4c8547d 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -1,15 +1,14 @@ -import requests -from importlib.metadata import version from typing import Optional, Union, Dict from typing_extensions import Self from seam.constants import LTS_VERSION from seam.parse_options import parse_options -from .routes import Routes -from .types import AbstractSeam, SeamApiException +from seam.request import RequestMixin +from seam.routes import Routes +from seam.types import AbstractSeam -class Seam(AbstractSeam): +class Seam(AbstractSeam, RequestMixin): """ Initial Seam class used to interact with Seam API """ @@ -50,45 +49,8 @@ def __init__( workspace_id=workspace_id, endpoint=endpoint, ) - self.__auth_headers = auth_headers - self.__endpoint = endpoint - - def make_request(self, method: str, path: str, **kwargs): - """ - Makes a request to the API - - Parameters - ---------- - method : str - Request method - path : str - Request path - **kwargs - Keyword arguments passed to requests.request - """ - - url = self.__endpoint + path - sdk_version = version("seam") - headers = { - **self.__auth_headers, - "Content-Type": "application/json", - "User-Agent": "Python SDK v" - + sdk_version - + " (https://github.com/seamapi/python-next)", - "seam-sdk-name": "seamapi/python", - "seam-sdk-version": sdk_version, - "seam-lts-version": self.lts_version, - } - - response = requests.request(method, url, headers=headers, **kwargs) - - if response.status_code != 200: - raise SeamApiException(response) - - if "application/json" in response.headers["content-type"]: - return response.json() - - return response.text + self._auth_headers = auth_headers + self._endpoint = endpoint @classmethod def from_api_key( diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py index 21b3605..2de9db8 100644 --- a/seam/seam_multi_workspace.py +++ b/seam/seam_multi_workspace.py @@ -1,14 +1,13 @@ -import requests from typing import Dict, Optional, Union -from importlib.metadata import version from seam.constants import LTS_VERSION from seam.options import get_endpoint -from seam.types import AbstractSeam, SeamApiException -from .workspaces import Workspaces +from seam.request import RequestMixin +from seam.types import AbstractSeam +from seam.workspaces import Workspaces -class SeamMultiWorkspace(AbstractSeam): +class SeamMultiWorkspace(AbstractSeam, RequestMixin): """ Seam class used to interact with Seam API without being scoped to any specific workspace. """ @@ -35,8 +34,8 @@ def __init__( self.lts_version = SeamMultiWorkspace.lts_version self.wait_for_action_attempt = wait_for_action_attempt - self.__auth_headers = {"authorization": f"Bearer {personal_access_token}"} - self.__endpoint = get_endpoint(endpoint) + self._auth_headers = {"authorization": f"Bearer {personal_access_token}"} + self._endpoint = get_endpoint(endpoint) self._workspaces = Workspaces(seam=self) self.workspaces = self.WorkspacesProxy(self._workspaces) @@ -52,40 +51,3 @@ def list(self, *args, **kwargs): def create(self, data, *args, **kwargs): return self._workspaces.create(data, *args, **kwargs) - - def make_request(self, method: str, path: str, **kwargs): - """ - Makes a request to the API - - Parameters - ---------- - method : str - Request method - path : str - Request path - **kwargs - Keyword arguments passed to requests.request - """ - - url = self.__endpoint + path - sdk_version = version("seam") - headers = { - **self.__auth_headers, - "Content-Type": "application/json", - "User-Agent": "Python SDK v" - + sdk_version - + " (https://github.com/seamapi/python-next)", - "seam-sdk-name": "seamapi/python", - "seam-sdk-version": sdk_version, - "seam-lts-version": self.lts_version, - } - - response = requests.request(method, url, headers=headers, **kwargs) - - if response.status_code != 200: - raise SeamApiException(response) - - if "application/json" in response.headers["content-type"]: - return response.json() - - return response.text diff --git a/seam/types.py b/seam/types.py index ad9e5b6..4b42af2 100644 --- a/seam/types.py +++ b/seam/types.py @@ -2079,10 +2079,6 @@ def __init__( self.wait_for_action_attempt = wait_for_action_attempt self.lts_version = AbstractSeam.lts_version - @abc.abstractmethod - def make_request(self, method: str, path: str, **kwargs) -> Any: - raise NotImplementedError - @classmethod @abc.abstractmethod def from_api_key( @@ -2105,3 +2101,8 @@ def from_personal_access_token( wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, ) -> Self: raise NotImplementedError + +class AbstractRequestMixin: + @abc.abstractmethod + def make_request(self, method: str, path: str, **kwargs) -> Any: + raise NotImplementedError \ No newline at end of file From 9276aad8f2861d8a34be3c2c13a364563ac016b2 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Wed, 8 May 2024 14:14:44 +0000 Subject: [PATCH 13/48] ci: Format code --- seam/types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seam/types.py b/seam/types.py index 4b42af2..b8c121b 100644 --- a/seam/types.py +++ b/seam/types.py @@ -2102,7 +2102,8 @@ def from_personal_access_token( ) -> Self: raise NotImplementedError + class AbstractRequestMixin: @abc.abstractmethod def make_request(self, method: str, path: str, **kwargs) -> Any: - raise NotImplementedError \ No newline at end of file + raise NotImplementedError From eac1681d69ab5b27e171f3a658d96b05eac6b643 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 8 May 2024 16:18:15 +0200 Subject: [PATCH 14/48] Disable generate workflow --- .github/workflows/generate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index 9fc5654..3b87ef1 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -4,7 +4,7 @@ name: Generate on: push: branches-ignore: - - main + - '**' workflow_dispatch: {} jobs: From 9d27312829d2c6c8dae913f57f92b805168bd1a0 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 8 May 2024 18:16:06 +0200 Subject: [PATCH 15/48] Add abstract classes, add helper for getting PAT, fix create ws test --- seam/auth.py | 28 ++++++++++ seam/seam_multi_workspace.py | 28 ++++++++-- seam/types.py | 66 +++++++++++++++++------ test/workspaces/test_workspaces_create.py | 25 +++++---- 4 files changed, 113 insertions(+), 34 deletions(-) diff --git a/seam/auth.py b/seam/auth.py index 9b461ac..a2e913d 100644 --- a/seam/auth.py +++ b/seam/auth.py @@ -99,3 +99,31 @@ def get_auth_headers_for_personal_access_token( "authorization": f"Bearer {personal_access_token}", "seam-workspace": workspace_id, } + + +def get_auth_headers_for_multi_workspace_personal_access_token( + personal_access_token: str, +) -> dict: + if is_jwt(personal_access_token): + raise SeamHttpInvalidTokenError( + "A JWT cannot be used as a personal_access_token" + ) + + if is_client_session_token(personal_access_token): + raise SeamHttpInvalidTokenError( + "A Client Session Token cannot be used as a personal_access_token" + ) + + if is_publishable_key(personal_access_token): + raise SeamHttpInvalidTokenError( + "A Publishable Key cannot be used as a personal_access_token" + ) + + if not is_access_token(personal_access_token): + raise SeamHttpInvalidTokenError( + f"Unknown or invalid personal_access_token format, expected token to start with {access_token_prefix}" + ) + + return { + "authorization": f"Bearer {personal_access_token}", + } diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py index 2de9db8..d4504b3 100644 --- a/seam/seam_multi_workspace.py +++ b/seam/seam_multi_workspace.py @@ -1,13 +1,15 @@ from typing import Dict, Optional, Union +from typing_extensions import Self +from seam.auth import get_auth_headers_for_multi_workspace_personal_access_token from seam.constants import LTS_VERSION from seam.options import get_endpoint from seam.request import RequestMixin -from seam.types import AbstractSeam +from seam.types import AbstractSeamMultiWorkspace from seam.workspaces import Workspaces -class SeamMultiWorkspace(AbstractSeam, RequestMixin): +class SeamMultiWorkspace(AbstractSeamMultiWorkspace, RequestMixin): """ Seam class used to interact with Seam API without being scoped to any specific workspace. """ @@ -34,7 +36,9 @@ def __init__( self.lts_version = SeamMultiWorkspace.lts_version self.wait_for_action_attempt = wait_for_action_attempt - self._auth_headers = {"authorization": f"Bearer {personal_access_token}"} + self._auth_headers = get_auth_headers_for_multi_workspace_personal_access_token( + personal_access_token + ) self._endpoint = get_endpoint(endpoint) self._workspaces = Workspaces(seam=self) @@ -49,5 +53,19 @@ def __init__(self, workspaces): def list(self, *args, **kwargs): return self._workspaces.list(*args, **kwargs) - def create(self, data, *args, **kwargs): - return self._workspaces.create(data, *args, **kwargs) + def create(self, *args, **kwargs): + return self._workspaces.create(*args, **kwargs) + + @classmethod + def from_personal_access_token( + cls, + personal_access_token: str, + *, + endpoint: Optional[str] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + ) -> Self: + return cls( + personal_access_token=personal_access_token, + endpoint=endpoint, + wait_for_action_attempt=wait_for_action_attempt, + ) diff --git a/seam/types.py b/seam/types.py index b8c121b..075737f 100644 --- a/seam/types.py +++ b/seam/types.py @@ -2043,6 +2043,26 @@ def users(self) -> AbstractAcsUsers: raise NotImplementedError() +class AbstractSeamMultiWorkspaceWorkspaces(abc.ABC): + @abc.abstractmethod + def create( + self, + *, + connect_partner_name: str, + name: str, + is_sandbox: Optional[bool] = None, + webview_logo_shape: Optional[str] = None, + webview_primary_button_color: Optional[str] = None, + ) -> Workspace: + raise NotImplementedError() + + @abc.abstractmethod + def list( + self, + ) -> List[Workspace]: + raise NotImplementedError() + + @dataclass class AbstractRoutes(abc.ABC): access_codes: AbstractAccessCodes @@ -2063,21 +2083,20 @@ class AbstractRoutes(abc.ABC): workspaces: AbstractWorkspaces -class AbstractSeam(AbstractRoutes): - lts_version: str - +class AbstractRequestMixin(abc.ABC): @abc.abstractmethod - def __init__( - self, - api_key: Optional[str] = None, - *, - personal_access_token: Optional[str] = None, - workspace_id: Optional[str] = None, - endpoint: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, - ): - self.wait_for_action_attempt = wait_for_action_attempt - self.lts_version = AbstractSeam.lts_version + def make_request(self, method: str, path: str, **kwargs) -> Any: + raise NotImplementedError + + +@dataclass +class AbstractSeam(AbstractRoutes, AbstractRequestMixin): + lts_version: str + api_key: Optional[str] = (None,) + personal_access_token: Optional[str] = (None,) + workspace_id: Optional[str] = (None,) + endpoint: Optional[str] = (None,) + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = (False,) @classmethod @abc.abstractmethod @@ -2103,7 +2122,22 @@ def from_personal_access_token( raise NotImplementedError -class AbstractRequestMixin: +@dataclass +class AbstractSeamMultiWorkspace(AbstractRequestMixin): + workspaces: AbstractSeamMultiWorkspaceWorkspaces + + lts_version: str + personal_access_token: Optional[str] = (None,) + endpoint: Optional[str] = (None,) + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = (False,) + + @classmethod @abc.abstractmethod - def make_request(self, method: str, path: str, **kwargs) -> Any: + def from_personal_access_token( + cls, + personal_access_token: str, + *, + endpoint: Optional[str] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + ) -> Self: raise NotImplementedError diff --git a/test/workspaces/test_workspaces_create.py b/test/workspaces/test_workspaces_create.py index be4e109..f755022 100644 --- a/test/workspaces/test_workspaces_create.py +++ b/test/workspaces/test_workspaces_create.py @@ -1,21 +1,20 @@ import random import string from seam import Seam +from seam.seam_multi_workspace import SeamMultiWorkspace def test_workspaces_create(seam: Seam): - # TODO: use SeamMultiWorkspace when implemented - # r = "".join(random.choices(string.ascii_uppercase + string.digits, k=10)) - # seam = Seam( - # endpoint=f"https://{r}.fakeseamconnect.seam.vc", - # api_key="seam_at1_shorttoken_longtoken", - # ) + r = "".join(random.choices(string.ascii_uppercase + string.digits, k=10)) + seam = SeamMultiWorkspace( + endpoint=f"https://{r}.fakeseamconnect.seam.vc", + personal_access_token="seam_at1_shorttoken_longtoken", + ) - # workspace = seam.workspaces.create( - # name="Test Workspace", - # connect_partner_name="Example Partner", - # is_sandbox=True, - # ) + workspace = seam.workspaces.create( + name="Test Workspace", + connect_partner_name="Example Partner", + is_sandbox=True, + ) - # assert workspace.workspace_id - pass + assert workspace.workspace_id From 752923867493b356b966d46786ce10aa288fe687 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Thu, 9 May 2024 11:28:23 +0200 Subject: [PATCH 16/48] Enforce keyword only args in WorkspacesProxy methods --- seam/seam_multi_workspace.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py index d4504b3..3f7164d 100644 --- a/seam/seam_multi_workspace.py +++ b/seam/seam_multi_workspace.py @@ -50,11 +50,11 @@ class WorkspacesProxy: def __init__(self, workspaces): self._workspaces = workspaces - def list(self, *args, **kwargs): - return self._workspaces.list(*args, **kwargs) + def list(self, **kwargs): + return self._workspaces.list(**kwargs) - def create(self, *args, **kwargs): - return self._workspaces.create(*args, **kwargs) + def create(self, **kwargs): + return self._workspaces.create(**kwargs) @classmethod def from_personal_access_token( From 63737145ae61f319b2b72994d8a27788ca3ecf3f Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 10 May 2024 15:32:57 +0200 Subject: [PATCH 17/48] Define WorkspacesProxy outside SeamMultiWorkspace --- seam/seam_multi_workspace.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py index 3f7164d..6ea57b2 100644 --- a/seam/seam_multi_workspace.py +++ b/seam/seam_multi_workspace.py @@ -9,6 +9,19 @@ from seam.workspaces import Workspaces +class WorkspacesProxy: + """Proxy to expose only the 'create' and 'list' methods of Workspaces.""" + + def __init__(self, workspaces): + self._workspaces = workspaces + + def list(self, **kwargs): + return self._workspaces.list(**kwargs) + + def create(self, **kwargs): + return self._workspaces.create(**kwargs) + + class SeamMultiWorkspace(AbstractSeamMultiWorkspace, RequestMixin): """ Seam class used to interact with Seam API without being scoped to any specific workspace. @@ -44,18 +57,6 @@ def __init__( self._workspaces = Workspaces(seam=self) self.workspaces = self.WorkspacesProxy(self._workspaces) - class WorkspacesProxy: - """Proxy to expose only the 'create' and 'list' methods of Workspaces.""" - - def __init__(self, workspaces): - self._workspaces = workspaces - - def list(self, **kwargs): - return self._workspaces.list(**kwargs) - - def create(self, **kwargs): - return self._workspaces.create(**kwargs) - @classmethod def from_personal_access_token( cls, From 231e60be4f5de5d6d702bcc0f63bd91df0045ceb Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 10 May 2024 15:39:01 +0200 Subject: [PATCH 18/48] Fix proxy class usage --- seam/seam_multi_workspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py index 6ea57b2..91be105 100644 --- a/seam/seam_multi_workspace.py +++ b/seam/seam_multi_workspace.py @@ -55,7 +55,7 @@ def __init__( self._endpoint = get_endpoint(endpoint) self._workspaces = Workspaces(seam=self) - self.workspaces = self.WorkspacesProxy(self._workspaces) + self.workspaces = WorkspacesProxy(self._workspaces) @classmethod def from_personal_access_token( From 38afb492bb5e39ae13ff50799cc32059314542b1 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Tue, 14 May 2024 18:47:36 +0200 Subject: [PATCH 19/48] Add SeamHttpClient class --- seam/request.py | 37 ++++++++++++++++++++++++++++++++++++- seam/seam.py | 9 ++++++--- seam/types.py | 12 +++++++----- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/seam/request.py b/seam/request.py index 11dcc84..b4521be 100644 --- a/seam/request.py +++ b/seam/request.py @@ -1,3 +1,4 @@ +from typing import Dict import requests from importlib.metadata import version @@ -23,7 +24,7 @@ def make_request(self, method: str, path: str, **kwargs): SeamApiException: If the response status code is not successful. """ - url = self._endpoint + path + url = self.endpoint + path sdk_version = version("seam") headers = { **self._auth_headers, @@ -45,3 +46,37 @@ def make_request(self, method: str, path: str, **kwargs): return response.json() return response.text + + +class SeamHttpClient: + def __init__(self, headers: Dict[str, str], **kwargs): + self.session = requests.Session() + self.session.headers.update(headers) + + for key, value in kwargs.items(): + setattr(self.session, key, value) + + def get(self, url, **kwargs): + response = self.session.get(url, **kwargs) + return self._handle_response(response) + + def post(self, url, data=None, json=None, **kwargs): + response = self.session.post(url, data=data, json=json, **kwargs) + return self._handle_response(response) + + def put(self, url, data=None, **kwargs): + response = self.session.put(url, data=data, **kwargs) + return self._handle_response(response) + + def delete(self, url, **kwargs): + response = self.session.delete(url, **kwargs) + return self._handle_response(response) + + def _handle_response(self, response: requests.Response): + if response.status_code != 200: + raise SeamApiException(response) + + if "application/json" in response.headers["content-type"]: + return response.json() + + return response.text diff --git a/seam/seam.py b/seam/seam.py index 4c8547d..78d178c 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -1,9 +1,10 @@ from typing import Optional, Union, Dict +import requests from typing_extensions import Self from seam.constants import LTS_VERSION from seam.parse_options import parse_options -from seam.request import RequestMixin +from seam.request import RequestMixin, SeamHttpClient from seam.routes import Routes from seam.types import AbstractSeam @@ -23,6 +24,8 @@ def __init__( workspace_id: Optional[str] = None, endpoint: Optional[str] = None, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + client: Optional[requests.Session] = None, + client_options: Optional[Dict[str, str]] = {}, ): """ Parameters @@ -49,8 +52,8 @@ def __init__( workspace_id=workspace_id, endpoint=endpoint, ) - self._auth_headers = auth_headers - self._endpoint = endpoint + self.client = client or SeamHttpClient(headers=auth_headers, **client_options) + self.endpoint = endpoint @classmethod def from_api_key( diff --git a/seam/types.py b/seam/types.py index 075737f..53d62da 100644 --- a/seam/types.py +++ b/seam/types.py @@ -1,4 +1,5 @@ from typing import Any, Dict, List, Optional, Union +import requests from typing_extensions import Self import abc from dataclasses import dataclass @@ -2092,11 +2093,12 @@ def make_request(self, method: str, path: str, **kwargs) -> Any: @dataclass class AbstractSeam(AbstractRoutes, AbstractRequestMixin): lts_version: str - api_key: Optional[str] = (None,) - personal_access_token: Optional[str] = (None,) - workspace_id: Optional[str] = (None,) - endpoint: Optional[str] = (None,) - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = (False,) + api_key: Optional[str] = None + personal_access_token: Optional[str] = None + workspace_id: Optional[str] = None + endpoint: Optional[str] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False + client: Optional[requests.Session] = None @classmethod @abc.abstractmethod From 50c7b7fe8dd510b1c843773706f7f16c2aedc426 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 15 May 2024 00:52:57 +0200 Subject: [PATCH 20/48] Adjust the routest to test new requests.Session --- .../nextlove-sdk-generator/LICENSE.txt | 20 +++++ .../@seamapi/nextlove-sdk-generator/README.md | 16 ++++ .../nextlove-sdk-generator/index.d.ts | 7 ++ .../@seamapi/nextlove-sdk-generator/index.js | 8 ++ .../nextlove-sdk-generator/index.js.map | 1 + .../nextlove-sdk-generator/package.json | 77 +++++++++++++++++++ .../nextlove-sdk-generator/src/index.ts | 7 ++ .../@seamapi/nextlove-sdk-generator/yalc.sig | 1 + generate-routes.js | 13 ++-- package.json | 2 +- seam/__init__.py | 18 ++--- seam/access_codes.py | 36 ++++++--- seam/access_codes_simulate.py | 12 +-- seam/access_codes_unmanaged.py | 28 ++++--- seam/acs.py | 3 +- seam/acs_access_groups.py | 28 ++++--- seam/acs_credential_pools.py | 11 +-- ...acs_credential_provisioning_automations.py | 7 +- seam/acs_credentials.py | 41 +++++++--- seam/acs_entrances.py | 25 +++--- seam/acs_systems.py | 27 ++++++- seam/acs_users.py | 50 ++++++++---- seam/action_attempts.py | 41 +++++++--- seam/client_sessions.py | 31 +++++--- seam/connect_webviews.py | 19 +++-- seam/connected_accounts.py | 19 +++-- seam/devices.py | 19 +++-- seam/devices_simulate.py | 7 +- seam/devices_unmanaged.py | 15 ++-- seam/events.py | 11 ++- seam/locks.py | 19 +++-- seam/networks.py | 11 ++- seam/noise_sensors.py | 3 +- seam/noise_sensors_noise_thresholds.py | 32 ++++---- seam/noise_sensors_simulate.py | 8 +- seam/phones.py | 11 ++- seam/phones_simulate.py | 8 +- seam/request.py | 2 +- seam/seam.py | 4 +- seam/thermostats.py | 35 ++++++--- seam/thermostats_climate_setting_schedules.py | 27 ++++--- seam/types.py | 60 ++++++++++----- seam/user_identities.py | 61 ++++++++------- .../user_identities_enrollment_automations.py | 27 +++---- seam/webhooks.py | 23 ++++-- seam/workspaces.py | 24 +++--- test/workspaces/test_workspaces_create.py | 23 +++--- yalc.lock | 11 +++ 48 files changed, 673 insertions(+), 316 deletions(-) create mode 100644 .yalc/@seamapi/nextlove-sdk-generator/LICENSE.txt create mode 100644 .yalc/@seamapi/nextlove-sdk-generator/README.md create mode 100644 .yalc/@seamapi/nextlove-sdk-generator/index.d.ts create mode 100644 .yalc/@seamapi/nextlove-sdk-generator/index.js create mode 100644 .yalc/@seamapi/nextlove-sdk-generator/index.js.map create mode 100644 .yalc/@seamapi/nextlove-sdk-generator/package.json create mode 100644 .yalc/@seamapi/nextlove-sdk-generator/src/index.ts create mode 100644 .yalc/@seamapi/nextlove-sdk-generator/yalc.sig create mode 100644 yalc.lock diff --git a/.yalc/@seamapi/nextlove-sdk-generator/LICENSE.txt b/.yalc/@seamapi/nextlove-sdk-generator/LICENSE.txt new file mode 100644 index 0000000..21dcd80 --- /dev/null +++ b/.yalc/@seamapi/nextlove-sdk-generator/LICENSE.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2021-2023 Seam Labs, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.yalc/@seamapi/nextlove-sdk-generator/README.md b/.yalc/@seamapi/nextlove-sdk-generator/README.md new file mode 100644 index 0000000..45836ea --- /dev/null +++ b/.yalc/@seamapi/nextlove-sdk-generator/README.md @@ -0,0 +1,16 @@ +# Nextlove SDK Generator + +> This SDK generator is currently configured to only work with Seam Connect, +> output is also committed directly to the repo for ease of inspection and +> testing + +This automatically generates an SDK based on the Seam Connect OpenAPI config. + +## Usage + +```bash +npm install +npm run generate:python-sdk +npm run generate:ruby-sdk +# output is in "./output/python" and "./output/ruby" +``` diff --git a/.yalc/@seamapi/nextlove-sdk-generator/index.d.ts b/.yalc/@seamapi/nextlove-sdk-generator/index.d.ts new file mode 100644 index 0000000..617aa84 --- /dev/null +++ b/.yalc/@seamapi/nextlove-sdk-generator/index.d.ts @@ -0,0 +1,7 @@ +export * from "./lib/types.js"; +export * from "./lib/index.js"; +export * from "./lib/generate-csharp-sdk/generate-csharp-sdk.js"; +export * from "./lib/generate-python-sdk/index.js"; +export * from "./lib/generate-ruby-sdk/index.js"; +export * from "./lib/generate-php-sdk/index.js"; +export * from "./lib/generate-swift-sdk/generate-swift-sdk.js"; diff --git a/.yalc/@seamapi/nextlove-sdk-generator/index.js b/.yalc/@seamapi/nextlove-sdk-generator/index.js new file mode 100644 index 0000000..5294476 --- /dev/null +++ b/.yalc/@seamapi/nextlove-sdk-generator/index.js @@ -0,0 +1,8 @@ +export * from "./lib/types.js"; +export * from "./lib/index.js"; +export * from "./lib/generate-csharp-sdk/generate-csharp-sdk.js"; +export * from "./lib/generate-python-sdk/index.js"; +export * from "./lib/generate-ruby-sdk/index.js"; +export * from "./lib/generate-php-sdk/index.js"; +export * from "./lib/generate-swift-sdk/generate-swift-sdk.js"; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/.yalc/@seamapi/nextlove-sdk-generator/index.js.map b/.yalc/@seamapi/nextlove-sdk-generator/index.js.map new file mode 100644 index 0000000..39d316f --- /dev/null +++ b/.yalc/@seamapi/nextlove-sdk-generator/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kDAAkD,CAAA;AAChE,cAAc,oCAAoC,CAAA;AAClD,cAAc,kCAAkC,CAAA;AAChD,cAAc,iCAAiC,CAAA;AAC/C,cAAc,gDAAgD,CAAA"} \ No newline at end of file diff --git a/.yalc/@seamapi/nextlove-sdk-generator/package.json b/.yalc/@seamapi/nextlove-sdk-generator/package.json new file mode 100644 index 0000000..170cf35 --- /dev/null +++ b/.yalc/@seamapi/nextlove-sdk-generator/package.json @@ -0,0 +1,77 @@ +{ + "name": "@seamapi/nextlove-sdk-generator", + "version": "1.11.0", + "description": "Utilities for building NextLove SDK Generators", + "type": "module", + "main": "index.js", + "types": "index.d.js", + "bin": { + "nextlove-sdk-generator": "lib/cli.js" + }, + "exports": { + ".": { + "import": "./index.js", + "types": "./index.d.ts" + } + }, + "module": "index.js", + "sideEffects": false, + "keywords": [ + "node" + ], + "homepage": "https://github.com/seamapi/nextlove-sdk-generator", + "bugs": "https://github.com/seamapi/nextlove-sdk-generator/issues", + "repository": "seamapi/nextlove-sdk-generator", + "license": "MIT", + "author": { + "name": "Seam Labs, Inc.", + "email": "support@getseam.com" + }, + "files": [ + "index.js", + "index.js.map", + "index.d.ts", + "lib", + "src", + "!**/*.test.ts" + ], + "scripts": { + "build": "npm run build:ts", + "postbuild": "node ./index.js && shx cp -r ./src/lib/generate-csharp-sdk/templates/fs ./lib/generate-csharp-sdk/templates", + "build:ts": "tsc --project tsconfig.build.json", + "prebuild:ts": "del 'index.*' lib", + "postbuild:ts": "tsc-alias --project tsconfig.build.json", + "typecheck": "tsc", + "test": "echo \"No tests yet...\" && exit 0", + "test:update": "ava --update-snapshots", + "test:watch": "ava --watch", + "test:debug": "ava debug --break", + "lint": "echo \"No linting yet...\" && exit 0", + "prelint": "prettier --check --ignore-path .gitignore .", + "postversion": "git push --follow-tags", + "example": "tsx examples", + "example:inspect": "tsx --inspect examples", + "format": "echo \"No linting yet...\" && exit 0", + "preformat": "prettier --write --ignore-path .gitignore .", + "report": "c8 report", + "generate:python-sdk": "tsx ./scripts/generate-python-sdk.ts && black ./output/python/seamapi/*", + "generate:ruby-sdk": "tsx ./scripts/generate-ruby-sdk.ts && prettier -w ./output/ruby", + "generate:csharp-sdk": "tsx ./scripts/generate-csharp-sdk.ts && dotnet csharpier ./output/csharp", + "generate:php-sdk": "tsx ./scripts/generate-php-sdk.ts && prettier -w ./output/php", + "generate:swift-sdk": "tsx ./scripts/generate-swift-sdk.ts && swiftformat .", + "test:csharp-sdk": "dotnet test ./output/csharp" + }, + "engines": { + "node": ">=16.13.0", + "npm": ">= 8.1.0" + }, + "dependencies": { + "@nodelib/fs.walk": "^2.0.0", + "axios": "^1.5.0", + "change-case": "^4.1.2", + "lodash": "^4.17.21", + "tsx": "^4.7.0", + "yargs": "^17.7.2" + }, + "yalcSig": "9d14c425625520103540c9f73a53190c" +} diff --git a/.yalc/@seamapi/nextlove-sdk-generator/src/index.ts b/.yalc/@seamapi/nextlove-sdk-generator/src/index.ts new file mode 100644 index 0000000..53f5ca2 --- /dev/null +++ b/.yalc/@seamapi/nextlove-sdk-generator/src/index.ts @@ -0,0 +1,7 @@ +export * from "./lib/types.js" +export * from "./lib/index.js" +export * from "./lib/generate-csharp-sdk/generate-csharp-sdk.js" +export * from "./lib/generate-python-sdk/index.js" +export * from "./lib/generate-ruby-sdk/index.js" +export * from "./lib/generate-php-sdk/index.js" +export * from "./lib/generate-swift-sdk/generate-swift-sdk.js" diff --git a/.yalc/@seamapi/nextlove-sdk-generator/yalc.sig b/.yalc/@seamapi/nextlove-sdk-generator/yalc.sig new file mode 100644 index 0000000..cdcef49 --- /dev/null +++ b/.yalc/@seamapi/nextlove-sdk-generator/yalc.sig @@ -0,0 +1 @@ +9d14c425625520103540c9f73a53190c \ No newline at end of file diff --git a/generate-routes.js b/generate-routes.js index f9b8e86..2ac377a 100644 --- a/generate-routes.js +++ b/generate-routes.js @@ -8,7 +8,7 @@ import { import { openapi } from '@seamapi/types/connect' import { deleteAsync } from 'del' -const libName = 'seam' +const libName = 'seam/routes' const rootPath = dirname(fileURLToPath(import.meta.url)) const outputPath = resolve(rootPath, libName) @@ -19,8 +19,11 @@ const fileSystem = await generateSdk({ openApiSpecObject: openapi, }) -const files = Object.entries(fileSystem).filter(([fileName]) => - fileName.startsWith(`${libName}/`), -) +const files = Object.entries(fileSystem) + .filter(([fileName]) => fileName.startsWith(`${libName}/`)) + .map(([fileName, contents]) => [ + fileName.replace(/^seam\/routes\//, ''), + contents, + ]) -writeFs(rootPath, Object.fromEntries(files)) +writeFs(resolve(rootPath, 'seam'), Object.fromEntries(files)) diff --git a/package.json b/package.json index 69c42ae..37707eb 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "format": "prettier --write --ignore-path .gitignore ." }, "devDependencies": { - "@seamapi/nextlove-sdk-generator": "^1.10.5", + "@seamapi/nextlove-sdk-generator": "file:.yalc/@seamapi/nextlove-sdk-generator", "@seamapi/types": "1.164.0", "del": "^7.1.0", "prettier": "^3.2.5" diff --git a/seam/__init__.py b/seam/__init__.py index ccd056f..8303af0 100644 --- a/seam/__init__.py +++ b/seam/__init__.py @@ -2,12 +2,12 @@ # type: ignore from seam.seam import Seam -from seam.types import SeamApiException -from seam.seam_multi_workspace import SeamMultiWorkspace -from seam.options import SeamHttpInvalidOptionsError -from seam.auth import SeamHttpInvalidTokenError -from seam.utils.action_attempt_errors import ( - SeamActionAttemptError, - SeamActionAttemptFailedError, - SeamActionAttemptTimeoutError, -) +# from seam.types import SeamApiException +# from seam.seam_multi_workspace import SeamMultiWorkspace +# from seam.options import SeamHttpInvalidOptionsError +# from seam.auth import SeamHttpInvalidTokenError +# from seam.utils.action_attempt_errors import ( +# SeamActionAttemptError, +# SeamActionAttemptFailedError, +# SeamActionAttemptTimeoutError, +# ) diff --git a/seam/access_codes.py b/seam/access_codes.py index 46cc290..5bbb69a 100644 --- a/seam/access_codes.py +++ b/seam/access_codes.py @@ -1,4 +1,5 @@ -from seam.types import AbstractAccessCodes, AbstractSeam as Seam, AccessCode +from seam.types import AbstractSeam as Seam +from seam.types import AbstractAccessCodes, AccessCode from typing import Optional, Any, List, Dict, Union from seam.access_codes_simulate import AccessCodesSimulate from seam.access_codes_unmanaged import AccessCodesUnmanaged @@ -77,7 +78,9 @@ def create( if use_offline_access_code is not None: json_payload["use_offline_access_code"] = use_offline_access_code - res = self.seam.make_request("POST", "/access_codes/create", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/access_codes/create", json=json_payload + ) return AccessCode.from_dict(res["access_code"]) @@ -140,8 +143,8 @@ def create_multiple( if use_offline_access_code is not None: json_payload["use_offline_access_code"] = use_offline_access_code - res = self.seam.make_request( - "POST", "/access_codes/create_multiple", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/access_codes/create_multiple", json=json_payload ) return [AccessCode.from_dict(item) for item in res["access_codes"]] @@ -162,7 +165,9 @@ def delete( if sync is not None: json_payload["sync"] = sync - self.seam.make_request("POST", "/access_codes/delete", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/access_codes/delete", json=json_payload + ) return None @@ -172,8 +177,8 @@ def generate_code(self, *, device_id: str) -> AccessCode: if device_id is not None: json_payload["device_id"] = device_id - res = self.seam.make_request( - "POST", "/access_codes/generate_code", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/access_codes/generate_code", json=json_payload ) return AccessCode.from_dict(res["generated_code"]) @@ -194,7 +199,9 @@ def get( if device_id is not None: json_payload["device_id"] = device_id - res = self.seam.make_request("POST", "/access_codes/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/access_codes/get", json=json_payload + ) return AccessCode.from_dict(res["access_code"]) @@ -214,7 +221,9 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.make_request("POST", "/access_codes/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/access_codes/list", json=json_payload + ) return [AccessCode.from_dict(item) for item in res["access_codes"]] @@ -224,8 +233,9 @@ def pull_backup_access_code(self, *, access_code_id: str) -> AccessCode: if access_code_id is not None: json_payload["access_code_id"] = access_code_id - res = self.seam.make_request( - "POST", "/access_codes/pull_backup_access_code", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/access_codes/pull_backup_access_code", + json=json_payload, ) return AccessCode.from_dict(res["backup_access_code"]) @@ -293,6 +303,8 @@ def update( if use_offline_access_code is not None: json_payload["use_offline_access_code"] = use_offline_access_code - self.seam.make_request("POST", "/access_codes/update", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/access_codes/update", json=json_payload + ) return None diff --git a/seam/access_codes_simulate.py b/seam/access_codes_simulate.py index e9d6e44..d202a6d 100644 --- a/seam/access_codes_simulate.py +++ b/seam/access_codes_simulate.py @@ -1,8 +1,5 @@ -from seam.types import ( - AbstractAccessCodesSimulate, - AbstractSeam as Seam, - UnmanagedAccessCode, -) +from seam.types import AbstractSeam as Seam +from seam.types import AbstractAccessCodesSimulate, UnmanagedAccessCode from typing import Optional, Any, List, Dict, Union @@ -24,9 +21,8 @@ def create_unmanaged_access_code( if name is not None: json_payload["name"] = name - res = self.seam.make_request( - "POST", - "/access_codes/simulate/create_unmanaged_access_code", + res = self.seam.client.post( + self.seam.endpoint + "/access_codes/simulate/create_unmanaged_access_code", json=json_payload, ) diff --git a/seam/access_codes_unmanaged.py b/seam/access_codes_unmanaged.py index b50e424..58a7b0b 100644 --- a/seam/access_codes_unmanaged.py +++ b/seam/access_codes_unmanaged.py @@ -1,8 +1,5 @@ -from seam.types import ( - AbstractAccessCodesUnmanaged, - AbstractSeam as Seam, - UnmanagedAccessCode, -) +from seam.types import AbstractSeam as Seam +from seam.types import AbstractAccessCodesUnmanaged, UnmanagedAccessCode from typing import Optional, Any, List, Dict, Union @@ -36,8 +33,9 @@ def convert_to_managed( if sync is not None: json_payload["sync"] = sync - self.seam.make_request( - "POST", "/access_codes/unmanaged/convert_to_managed", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/access_codes/unmanaged/convert_to_managed", + json=json_payload, ) return None @@ -50,8 +48,8 @@ def delete(self, *, access_code_id: str, sync: Optional[bool] = None) -> None: if sync is not None: json_payload["sync"] = sync - self.seam.make_request( - "POST", "/access_codes/unmanaged/delete", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/access_codes/unmanaged/delete", json=json_payload ) return None @@ -72,8 +70,8 @@ def get( if device_id is not None: json_payload["device_id"] = device_id - res = self.seam.make_request( - "POST", "/access_codes/unmanaged/get", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/access_codes/unmanaged/get", json=json_payload ) return UnmanagedAccessCode.from_dict(res["access_code"]) @@ -88,8 +86,8 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.make_request( - "POST", "/access_codes/unmanaged/list", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/access_codes/unmanaged/list", json=json_payload ) return [UnmanagedAccessCode.from_dict(item) for item in res["access_codes"]] @@ -118,8 +116,8 @@ def update( is_external_modification_allowed ) - self.seam.make_request( - "POST", "/access_codes/unmanaged/update", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/access_codes/unmanaged/update", json=json_payload ) return None diff --git a/seam/acs.py b/seam/acs.py index 616b4b2..c126642 100644 --- a/seam/acs.py +++ b/seam/acs.py @@ -1,4 +1,5 @@ -from seam.types import AbstractAcs, AbstractSeam as Seam +from seam.types import AbstractSeam as Seam +from seam.types import AbstractAcs from typing import Optional, Any, List, Dict, Union from seam.acs_access_groups import AcsAccessGroups from seam.acs_credential_pools import AcsCredentialPools diff --git a/seam/acs_access_groups.py b/seam/acs_access_groups.py index 6716143..4691b20 100644 --- a/seam/acs_access_groups.py +++ b/seam/acs_access_groups.py @@ -1,9 +1,5 @@ -from seam.types import ( - AbstractAcsAccessGroups, - AbstractSeam as Seam, - AcsAccessGroup, - AcsUser, -) +from seam.types import AbstractSeam as Seam +from seam.types import AbstractAcsAccessGroups, AcsAccessGroup, AcsUser from typing import Optional, Any, List, Dict, Union @@ -21,7 +17,9 @@ def add_user(self, *, acs_access_group_id: str, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.make_request("POST", "/acs/access_groups/add_user", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/acs/access_groups/add_user", json=json_payload + ) return None @@ -31,8 +29,8 @@ def get(self, *, acs_access_group_id: str) -> AcsAccessGroup: if acs_access_group_id is not None: json_payload["acs_access_group_id"] = acs_access_group_id - res = self.seam.make_request( - "POST", "/acs/access_groups/get", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/acs/access_groups/get", json=json_payload ) return AcsAccessGroup.from_dict(res["acs_access_group"]) @@ -47,8 +45,8 @@ def list( if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - res = self.seam.make_request( - "POST", "/acs/access_groups/list", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/acs/access_groups/list", json=json_payload ) return [AcsAccessGroup.from_dict(item) for item in res["acs_access_groups"]] @@ -59,8 +57,8 @@ def list_users(self, *, acs_access_group_id: str) -> List[AcsUser]: if acs_access_group_id is not None: json_payload["acs_access_group_id"] = acs_access_group_id - res = self.seam.make_request( - "POST", "/acs/access_groups/list_users", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/acs/access_groups/list_users", json=json_payload ) return [AcsUser.from_dict(item) for item in res["acs_users"]] @@ -73,8 +71,8 @@ def remove_user(self, *, acs_access_group_id: str, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.make_request( - "POST", "/acs/access_groups/remove_user", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/acs/access_groups/remove_user", json=json_payload ) return None diff --git a/seam/acs_credential_pools.py b/seam/acs_credential_pools.py index 4d37488..cd809b0 100644 --- a/seam/acs_credential_pools.py +++ b/seam/acs_credential_pools.py @@ -1,8 +1,5 @@ -from seam.types import ( - AbstractAcsCredentialPools, - AbstractSeam as Seam, - AcsCredentialPool, -) +from seam.types import AbstractSeam as Seam +from seam.types import AbstractAcsCredentialPools, AcsCredentialPool from typing import Optional, Any, List, Dict, Union @@ -18,8 +15,8 @@ def list(self, *, acs_system_id: str) -> List[AcsCredentialPool]: if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id - res = self.seam.make_request( - "POST", "/acs/credential_pools/list", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/acs/credential_pools/list", json=json_payload ) return [ diff --git a/seam/acs_credential_provisioning_automations.py b/seam/acs_credential_provisioning_automations.py index 33e9a05..42419ba 100644 --- a/seam/acs_credential_provisioning_automations.py +++ b/seam/acs_credential_provisioning_automations.py @@ -1,6 +1,6 @@ +from seam.types import AbstractSeam as Seam from seam.types import ( AbstractAcsCredentialProvisioningAutomations, - AbstractSeam as Seam, AcsCredentialProvisioningAutomation, ) from typing import Optional, Any, List, Dict, Union @@ -42,8 +42,9 @@ def launch( credential_manager_acs_user_id ) - res = self.seam.make_request( - "POST", "/acs/credential_provisioning_automations/launch", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/acs/credential_provisioning_automations/launch", + json=json_payload, ) return AcsCredentialProvisioningAutomation.from_dict( diff --git a/seam/acs_credentials.py b/seam/acs_credentials.py index cb3edb4..887070a 100644 --- a/seam/acs_credentials.py +++ b/seam/acs_credentials.py @@ -1,4 +1,5 @@ -from seam.types import AbstractAcsCredentials, AbstractSeam as Seam, AcsCredential +from seam.types import AbstractSeam as Seam +from seam.types import AbstractAcsCredentials, AcsCredential from typing import Optional, Any, List, Dict, Union @@ -16,7 +17,9 @@ def assign(self, *, acs_credential_id: str, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.make_request("POST", "/acs/credentials/assign", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/acs/credentials/assign", json=json_payload + ) return None @@ -58,8 +61,8 @@ def create( if visionline_metadata is not None: json_payload["visionline_metadata"] = visionline_metadata - res = self.seam.make_request( - "POST", "/acs/credentials/create", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/acs/credentials/create", json=json_payload ) return AcsCredential.from_dict(res["acs_credential"]) @@ -70,7 +73,9 @@ def delete(self, *, acs_credential_id: str) -> None: if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id - self.seam.make_request("POST", "/acs/credentials/delete", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/acs/credentials/delete", json=json_payload + ) return None @@ -80,7 +85,9 @@ def get(self, *, acs_credential_id: str) -> AcsCredential: if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id - res = self.seam.make_request("POST", "/acs/credentials/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/acs/credentials/get", json=json_payload + ) return AcsCredential.from_dict(res["acs_credential"]) @@ -105,7 +112,9 @@ def list( is_multi_phone_sync_credential ) - res = self.seam.make_request("POST", "/acs/credentials/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/acs/credentials/list", json=json_payload + ) return [AcsCredential.from_dict(item) for item in res["acs_credentials"]] @@ -117,18 +126,30 @@ def unassign(self, *, acs_credential_id: str, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.make_request("POST", "/acs/credentials/unassign", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/acs/credentials/unassign", json=json_payload + ) return None - def update(self, *, acs_credential_id: str, code: str) -> None: + def update( + self, + *, + acs_credential_id: str, + code: Optional[str] = None, + ends_at: Optional[str] = None + ) -> None: json_payload = {} if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id if code is not None: json_payload["code"] = code + if ends_at is not None: + json_payload["ends_at"] = ends_at - self.seam.make_request("POST", "/acs/credentials/update", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/acs/credentials/update", json=json_payload + ) return None diff --git a/seam/acs_entrances.py b/seam/acs_entrances.py index a54bd86..3b3ff54 100644 --- a/seam/acs_entrances.py +++ b/seam/acs_entrances.py @@ -1,9 +1,5 @@ -from seam.types import ( - AbstractAcsEntrances, - AbstractSeam as Seam, - AcsEntrance, - AcsCredential, -) +from seam.types import AbstractSeam as Seam +from seam.types import AbstractAcsEntrances, AcsEntrance, AcsCredential from typing import Optional, Any, List, Dict, Union @@ -19,7 +15,9 @@ def get(self, *, acs_entrance_id: str) -> AcsEntrance: if acs_entrance_id is not None: json_payload["acs_entrance_id"] = acs_entrance_id - res = self.seam.make_request("POST", "/acs/entrances/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/acs/entrances/get", json=json_payload + ) return AcsEntrance.from_dict(res["acs_entrance"]) @@ -31,7 +29,9 @@ def grant_access(self, *, acs_entrance_id: str, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.make_request("POST", "/acs/entrances/grant_access", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/acs/entrances/grant_access", json=json_payload + ) return None @@ -48,7 +48,9 @@ def list( if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id - res = self.seam.make_request("POST", "/acs/entrances/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/acs/entrances/list", json=json_payload + ) return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] @@ -62,8 +64,9 @@ def list_credentials_with_access( if include_if is not None: json_payload["include_if"] = include_if - res = self.seam.make_request( - "POST", "/acs/entrances/list_credentials_with_access", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/acs/entrances/list_credentials_with_access", + json=json_payload, ) return [AcsCredential.from_dict(item) for item in res["acs_credentials"]] diff --git a/seam/acs_systems.py b/seam/acs_systems.py index c5b0207..97ea818 100644 --- a/seam/acs_systems.py +++ b/seam/acs_systems.py @@ -1,4 +1,5 @@ -from seam.types import AbstractAcsSystems, AbstractSeam as Seam, AcsSystem +from seam.types import AbstractSeam as Seam +from seam.types import AbstractAcsSystems, AcsSystem from typing import Optional, Any, List, Dict, Union @@ -14,7 +15,9 @@ def get(self, *, acs_system_id: str) -> AcsSystem: if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id - res = self.seam.make_request("POST", "/acs/systems/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/acs/systems/get", json=json_payload + ) return AcsSystem.from_dict(res["acs_system"]) @@ -24,6 +27,24 @@ def list(self, *, connected_account_id: Optional[str] = None) -> List[AcsSystem] if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id - res = self.seam.make_request("POST", "/acs/systems/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/acs/systems/list", json=json_payload + ) + + return [AcsSystem.from_dict(item) for item in res["acs_systems"]] + + def list_compatible_credential_manager_acs_systems( + self, *, acs_system_id: str + ) -> List[AcsSystem]: + json_payload = {} + + if acs_system_id is not None: + json_payload["acs_system_id"] = acs_system_id + + res = self.seam.client.post( + self.seam.endpoint + + "/acs/systems/list_compatible_credential_manager_acs_systems", + json=json_payload, + ) return [AcsSystem.from_dict(item) for item in res["acs_systems"]] diff --git a/seam/acs_users.py b/seam/acs_users.py index 8c98b38..0607368 100644 --- a/seam/acs_users.py +++ b/seam/acs_users.py @@ -1,4 +1,5 @@ -from seam.types import AbstractAcsUsers, AbstractSeam as Seam, AcsUser, AcsEntrance +from seam.types import AbstractSeam as Seam +from seam.types import AbstractAcsUsers, AcsUser, AcsEntrance from typing import Optional, Any, List, Dict, Union @@ -18,8 +19,8 @@ def add_to_access_group( if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.make_request( - "POST", "/acs/users/add_to_access_group", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/acs/users/add_to_access_group", json=json_payload ) return None @@ -55,7 +56,9 @@ def create( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - res = self.seam.make_request("POST", "/acs/users/create", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/acs/users/create", json=json_payload + ) return AcsUser.from_dict(res["acs_user"]) @@ -65,7 +68,9 @@ def delete(self, *, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.make_request("POST", "/acs/users/delete", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/acs/users/delete", json=json_payload + ) return None @@ -75,7 +80,9 @@ def get(self, *, acs_user_id: str) -> AcsUser: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - res = self.seam.make_request("POST", "/acs/users/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/acs/users/get", json=json_payload + ) return AcsUser.from_dict(res["acs_user"]) @@ -98,7 +105,9 @@ def list( if user_identity_phone_number is not None: json_payload["user_identity_phone_number"] = user_identity_phone_number - res = self.seam.make_request("POST", "/acs/users/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/acs/users/list", json=json_payload + ) return [AcsUser.from_dict(item) for item in res["acs_users"]] @@ -108,8 +117,9 @@ def list_accessible_entrances(self, *, acs_user_id: str) -> List[AcsEntrance]: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - res = self.seam.make_request( - "POST", "/acs/users/list_accessible_entrances", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/acs/users/list_accessible_entrances", + json=json_payload, ) return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] @@ -124,8 +134,9 @@ def remove_from_access_group( if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.make_request( - "POST", "/acs/users/remove_from_access_group", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/acs/users/remove_from_access_group", + json=json_payload, ) return None @@ -136,8 +147,9 @@ def revoke_access_to_all_entrances(self, *, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.make_request( - "POST", "/acs/users/revoke_access_to_all_entrances", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/acs/users/revoke_access_to_all_entrances", + json=json_payload, ) return None @@ -148,7 +160,9 @@ def suspend(self, *, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.make_request("POST", "/acs/users/suspend", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/acs/users/suspend", json=json_payload + ) return None @@ -158,7 +172,9 @@ def unsuspend(self, *, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.make_request("POST", "/acs/users/unsuspend", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/acs/users/unsuspend", json=json_payload + ) return None @@ -190,6 +206,8 @@ def update( if phone_number is not None: json_payload["phone_number"] = phone_number - self.seam.make_request("POST", "/acs/users/update", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/acs/users/update", json=json_payload + ) return None diff --git a/seam/action_attempts.py b/seam/action_attempts.py index 0078204..0881fbc 100644 --- a/seam/action_attempts.py +++ b/seam/action_attempts.py @@ -1,12 +1,29 @@ -from seam.types import AbstractActionAttempts, AbstractSeam as Seam, ActionAttempt +from seam.types import AbstractSeam as Seam +from seam.types import AbstractActionAttempts, ActionAttempt from typing import Optional, Any, List, Dict, Union import time -from seam.utils.action_attempt_errors import ( - SeamActionAttemptFailedError, - SeamActionAttemptTimeoutError, -) + +class SeamActionAttemptError(Exception): + def __init__(self, message: str, action_attempt: ActionAttempt): + super().__init__(message) + self.name = self.__class__.__name__ + self.action_attempt = action_attempt + + +class SeamActionAttemptFailedError(SeamActionAttemptError): + def __init__(self, action_attempt: ActionAttempt): + super().__init__(action_attempt.error.message, action_attempt) + self.name = self.__class__.__name__ + self.code = action_attempt.error.type + + +class SeamActionAttemptTimeoutError(SeamActionAttemptError): + def __init__(self, action_attempt: ActionAttempt, timeout: str): + message = f"Timed out waiting for action attempt after {timeout}s" + super().__init__(message, action_attempt) + self.name = self.__class__.__name__ class ActionAttempts(AbstractActionAttempts): @@ -19,14 +36,16 @@ def get( self, *, action_attempt_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: json_payload = {} if action_attempt_id is not None: json_payload["action_attempt_id"] = action_attempt_id - res = self.seam.make_request("POST", "/action_attempts/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/action_attempts/get", json=json_payload + ) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), @@ -39,7 +58,9 @@ def list(self, *, action_attempt_ids: List[str]) -> List[ActionAttempt]: if action_attempt_ids is not None: json_payload["action_attempt_ids"] = action_attempt_ids - res = self.seam.make_request("POST", "/action_attempts/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/action_attempts/list", json=json_payload + ) return [ActionAttempt.from_dict(item) for item in res["action_attempts"]] @@ -48,7 +69,7 @@ def poll_until_ready( *, action_attempt_id: str, timeout: Optional[float] = 5.0, - polling_interval: Optional[float] = 0.5 + polling_interval: Optional[float] = 0.5, ) -> ActionAttempt: seam = self.seam time_waiting = 0.0 @@ -78,7 +99,7 @@ def decide_and_wait( self, *, action_attempt: ActionAttempt, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: wait_decision = ( self.seam.wait_for_action_attempt diff --git a/seam/client_sessions.py b/seam/client_sessions.py index 09587e0..7953f20 100644 --- a/seam/client_sessions.py +++ b/seam/client_sessions.py @@ -1,4 +1,5 @@ -from seam.types import AbstractClientSessions, AbstractSeam as Seam, ClientSession +from seam.types import AbstractSeam as Seam +from seam.types import AbstractClientSessions, ClientSession from typing import Optional, Any, List, Dict, Union @@ -30,8 +31,8 @@ def create( if user_identity_ids is not None: json_payload["user_identity_ids"] = user_identity_ids - res = self.seam.make_request( - "POST", "/client_sessions/create", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/client_sessions/create", json=json_payload ) return ClientSession.from_dict(res["client_session"]) @@ -42,7 +43,9 @@ def delete(self, *, client_session_id: str) -> None: if client_session_id is not None: json_payload["client_session_id"] = client_session_id - self.seam.make_request("POST", "/client_sessions/delete", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/client_sessions/delete", json=json_payload + ) return None @@ -59,7 +62,9 @@ def get( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.make_request("POST", "/client_sessions/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/client_sessions/get", json=json_payload + ) return ClientSession.from_dict(res["client_session"]) @@ -85,8 +90,8 @@ def get_or_create( if user_identity_ids is not None: json_payload["user_identity_ids"] = user_identity_ids - res = self.seam.make_request( - "POST", "/client_sessions/get_or_create", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/client_sessions/get_or_create", json=json_payload ) return ClientSession.from_dict(res["client_session"]) @@ -113,8 +118,8 @@ def grant_access( if user_identity_ids is not None: json_payload["user_identity_ids"] = user_identity_ids - self.seam.make_request( - "POST", "/client_sessions/grant_access", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/client_sessions/grant_access", json=json_payload ) return None @@ -141,7 +146,9 @@ def list( if without_user_identifier_key is not None: json_payload["without_user_identifier_key"] = without_user_identifier_key - res = self.seam.make_request("POST", "/client_sessions/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/client_sessions/list", json=json_payload + ) return [ClientSession.from_dict(item) for item in res["client_sessions"]] @@ -151,6 +158,8 @@ def revoke(self, *, client_session_id: str) -> None: if client_session_id is not None: json_payload["client_session_id"] = client_session_id - self.seam.make_request("POST", "/client_sessions/revoke", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/client_sessions/revoke", json=json_payload + ) return None diff --git a/seam/connect_webviews.py b/seam/connect_webviews.py index dd1ded4..ddde7fc 100644 --- a/seam/connect_webviews.py +++ b/seam/connect_webviews.py @@ -1,4 +1,5 @@ -from seam.types import AbstractConnectWebviews, AbstractSeam as Seam, ConnectWebview +from seam.types import AbstractSeam as Seam +from seam.types import AbstractConnectWebviews, ConnectWebview from typing import Optional, Any, List, Dict, Union @@ -41,8 +42,8 @@ def create( if wait_for_device_creation is not None: json_payload["wait_for_device_creation"] = wait_for_device_creation - res = self.seam.make_request( - "POST", "/connect_webviews/create", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/connect_webviews/create", json=json_payload ) return ConnectWebview.from_dict(res["connect_webview"]) @@ -53,7 +54,9 @@ def delete(self, *, connect_webview_id: str) -> None: if connect_webview_id is not None: json_payload["connect_webview_id"] = connect_webview_id - self.seam.make_request("POST", "/connect_webviews/delete", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/connect_webviews/delete", json=json_payload + ) return None @@ -63,7 +66,9 @@ def get(self, *, connect_webview_id: str) -> ConnectWebview: if connect_webview_id is not None: json_payload["connect_webview_id"] = connect_webview_id - res = self.seam.make_request("POST", "/connect_webviews/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/connect_webviews/get", json=json_payload + ) return ConnectWebview.from_dict(res["connect_webview"]) @@ -80,8 +85,8 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.make_request( - "POST", "/connect_webviews/list", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/connect_webviews/list", json=json_payload ) return [ConnectWebview.from_dict(item) for item in res["connect_webviews"]] diff --git a/seam/connected_accounts.py b/seam/connected_accounts.py index 0c27810..93fce0f 100644 --- a/seam/connected_accounts.py +++ b/seam/connected_accounts.py @@ -1,4 +1,5 @@ -from seam.types import AbstractConnectedAccounts, AbstractSeam as Seam, ConnectedAccount +from seam.types import AbstractSeam as Seam +from seam.types import AbstractConnectedAccounts, ConnectedAccount from typing import Optional, Any, List, Dict, Union @@ -16,7 +17,9 @@ def delete(self, *, connected_account_id: str, sync: Optional[bool] = None) -> N if sync is not None: json_payload["sync"] = sync - self.seam.make_request("POST", "/connected_accounts/delete", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/connected_accounts/delete", json=json_payload + ) return None @@ -30,8 +33,8 @@ def get( if email is not None: json_payload["email"] = email - res = self.seam.make_request( - "POST", "/connected_accounts/get", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/connected_accounts/get", json=json_payload ) return ConnectedAccount.from_dict(res["connected_account"]) @@ -44,8 +47,8 @@ def list( if custom_metadata_has is not None: json_payload["custom_metadata_has"] = custom_metadata_has - res = self.seam.make_request( - "POST", "/connected_accounts/list", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/connected_accounts/list", json=json_payload ) return [ConnectedAccount.from_dict(item) for item in res["connected_accounts"]] @@ -68,8 +71,8 @@ def update( if custom_metadata is not None: json_payload["custom_metadata"] = custom_metadata - res = self.seam.make_request( - "POST", "/connected_accounts/update", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/connected_accounts/update", json=json_payload ) return ConnectedAccount.from_dict(res["connected_account"]) diff --git a/seam/devices.py b/seam/devices.py index 6225426..00206f8 100644 --- a/seam/devices.py +++ b/seam/devices.py @@ -1,4 +1,5 @@ -from seam.types import AbstractDevices, AbstractSeam as Seam, Device, DeviceProvider +from seam.types import AbstractSeam as Seam +from seam.types import AbstractDevices, Device, DeviceProvider from typing import Optional, Any, List, Dict, Union from seam.devices_simulate import DevicesSimulate from seam.devices_unmanaged import DevicesUnmanaged @@ -26,7 +27,7 @@ def delete(self, *, device_id: str) -> None: if device_id is not None: json_payload["device_id"] = device_id - self.seam.make_request("POST", "/devices/delete", json=json_payload) + self.seam.client.post(self.seam.endpoint + "/devices/delete", json=json_payload) return None @@ -40,7 +41,9 @@ def get( if name is not None: json_payload["name"] = name - res = self.seam.make_request("POST", "/devices/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/devices/get", json=json_payload + ) return Device.from_dict(res["device"]) @@ -90,7 +93,9 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.make_request("POST", "/devices/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/devices/list", json=json_payload + ) return [Device.from_dict(item) for item in res["devices"]] @@ -102,8 +107,8 @@ def list_device_providers( if provider_category is not None: json_payload["provider_category"] = provider_category - res = self.seam.make_request( - "POST", "/devices/list_device_providers", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/devices/list_device_providers", json=json_payload ) return [DeviceProvider.from_dict(item) for item in res["device_providers"]] @@ -130,6 +135,6 @@ def update( if properties is not None: json_payload["properties"] = properties - self.seam.make_request("POST", "/devices/update", json=json_payload) + self.seam.client.post(self.seam.endpoint + "/devices/update", json=json_payload) return None diff --git a/seam/devices_simulate.py b/seam/devices_simulate.py index ec8f76d..163b9f1 100644 --- a/seam/devices_simulate.py +++ b/seam/devices_simulate.py @@ -1,4 +1,5 @@ -from seam.types import AbstractDevicesSimulate, AbstractSeam as Seam +from seam.types import AbstractSeam as Seam +from seam.types import AbstractDevicesSimulate from typing import Optional, Any, List, Dict, Union @@ -14,6 +15,8 @@ def remove(self, *, device_id: str) -> None: if device_id is not None: json_payload["device_id"] = device_id - self.seam.make_request("POST", "/devices/simulate/remove", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/devices/simulate/remove", json=json_payload + ) return None diff --git a/seam/devices_unmanaged.py b/seam/devices_unmanaged.py index 0b3b2eb..dba76f9 100644 --- a/seam/devices_unmanaged.py +++ b/seam/devices_unmanaged.py @@ -1,4 +1,5 @@ -from seam.types import AbstractDevicesUnmanaged, AbstractSeam as Seam, UnmanagedDevice +from seam.types import AbstractSeam as Seam +from seam.types import AbstractDevicesUnmanaged, UnmanagedDevice from typing import Optional, Any, List, Dict, Union @@ -18,8 +19,8 @@ def get( if name is not None: json_payload["name"] = name - res = self.seam.make_request( - "POST", "/devices/unmanaged/get", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/devices/unmanaged/get", json=json_payload ) return UnmanagedDevice.from_dict(res["device"]) @@ -70,8 +71,8 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.make_request( - "POST", "/devices/unmanaged/list", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/devices/unmanaged/list", json=json_payload ) return [UnmanagedDevice.from_dict(item) for item in res["devices"]] @@ -84,6 +85,8 @@ def update(self, *, device_id: str, is_managed: bool) -> None: if is_managed is not None: json_payload["is_managed"] = is_managed - self.seam.make_request("POST", "/devices/unmanaged/update", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/devices/unmanaged/update", json=json_payload + ) return None diff --git a/seam/events.py b/seam/events.py index affa12b..dfdae79 100644 --- a/seam/events.py +++ b/seam/events.py @@ -1,4 +1,5 @@ -from seam.types import AbstractEvents, AbstractSeam as Seam, Event +from seam.types import AbstractSeam as Seam +from seam.types import AbstractEvents, Event from typing import Optional, Any, List, Dict, Union @@ -24,7 +25,9 @@ def get( if event_type is not None: json_payload["event_type"] = event_type - res = self.seam.make_request("POST", "/events/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/events/get", json=json_payload + ) return Event.from_dict(res["event"]) @@ -65,6 +68,8 @@ def list( if since is not None: json_payload["since"] = since - res = self.seam.make_request("POST", "/events/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/events/list", json=json_payload + ) return [Event.from_dict(item) for item in res["events"]] diff --git a/seam/locks.py b/seam/locks.py index cbe6e3d..d1d52dc 100644 --- a/seam/locks.py +++ b/seam/locks.py @@ -1,4 +1,5 @@ -from seam.types import AbstractLocks, AbstractSeam as Seam, Device, ActionAttempt +from seam.types import AbstractSeam as Seam +from seam.types import AbstractLocks, Device, ActionAttempt from typing import Optional, Any, List, Dict, Union @@ -18,7 +19,9 @@ def get( if name is not None: json_payload["name"] = name - res = self.seam.make_request("POST", "/locks/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/locks/get", json=json_payload + ) return Device.from_dict(res["device"]) @@ -68,7 +71,9 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.make_request("POST", "/locks/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/locks/list", json=json_payload + ) return [Device.from_dict(item) for item in res["devices"]] @@ -86,7 +91,9 @@ def lock_door( if sync is not None: json_payload["sync"] = sync - res = self.seam.make_request("POST", "/locks/lock_door", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/locks/lock_door", json=json_payload + ) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), @@ -107,7 +114,9 @@ def unlock_door( if sync is not None: json_payload["sync"] = sync - res = self.seam.make_request("POST", "/locks/unlock_door", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/locks/unlock_door", json=json_payload + ) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), diff --git a/seam/networks.py b/seam/networks.py index 689ac9c..fac0031 100644 --- a/seam/networks.py +++ b/seam/networks.py @@ -1,4 +1,5 @@ -from seam.types import AbstractNetworks, AbstractSeam as Seam, Network +from seam.types import AbstractSeam as Seam +from seam.types import AbstractNetworks, Network from typing import Optional, Any, List, Dict, Union @@ -14,7 +15,9 @@ def get(self, *, network_id: str) -> Network: if network_id is not None: json_payload["network_id"] = network_id - res = self.seam.make_request("POST", "/networks/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/networks/get", json=json_payload + ) return Network.from_dict(res["network"]) @@ -23,6 +26,8 @@ def list( ) -> List[Network]: json_payload = {} - res = self.seam.make_request("POST", "/networks/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/networks/list", json=json_payload + ) return [Network.from_dict(item) for item in res["networks"]] diff --git a/seam/noise_sensors.py b/seam/noise_sensors.py index 46c43ae..78fd11f 100644 --- a/seam/noise_sensors.py +++ b/seam/noise_sensors.py @@ -1,4 +1,5 @@ -from seam.types import AbstractNoiseSensors, AbstractSeam as Seam +from seam.types import AbstractSeam as Seam +from seam.types import AbstractNoiseSensors from typing import Optional, Any, List, Dict, Union from seam.noise_sensors_noise_thresholds import NoiseSensorsNoiseThresholds from seam.noise_sensors_simulate import NoiseSensorsSimulate diff --git a/seam/noise_sensors_noise_thresholds.py b/seam/noise_sensors_noise_thresholds.py index 36ff6d7..462b142 100644 --- a/seam/noise_sensors_noise_thresholds.py +++ b/seam/noise_sensors_noise_thresholds.py @@ -1,8 +1,5 @@ -from seam.types import ( - AbstractNoiseSensorsNoiseThresholds, - AbstractSeam as Seam, - NoiseThreshold, -) +from seam.types import AbstractSeam as Seam +from seam.types import AbstractNoiseSensorsNoiseThresholds, NoiseThreshold from typing import Optional, Any, List, Dict, Union @@ -40,8 +37,9 @@ def create( if sync is not None: json_payload["sync"] = sync - res = self.seam.make_request( - "POST", "/noise_sensors/noise_thresholds/create", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/noise_sensors/noise_thresholds/create", + json=json_payload, ) return NoiseThreshold.from_dict(res["noise_threshold"]) @@ -58,8 +56,9 @@ def delete( if sync is not None: json_payload["sync"] = sync - self.seam.make_request( - "POST", "/noise_sensors/noise_thresholds/delete", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/noise_sensors/noise_thresholds/delete", + json=json_payload, ) return None @@ -70,8 +69,9 @@ def get(self, *, noise_threshold_id: str) -> NoiseThreshold: if noise_threshold_id is not None: json_payload["noise_threshold_id"] = noise_threshold_id - res = self.seam.make_request( - "POST", "/noise_sensors/noise_thresholds/get", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/noise_sensors/noise_thresholds/get", + json=json_payload, ) return NoiseThreshold.from_dict(res["noise_threshold"]) @@ -86,8 +86,9 @@ def list( if is_programmed is not None: json_payload["is_programmed"] = is_programmed - res = self.seam.make_request( - "POST", "/noise_sensors/noise_thresholds/list", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/noise_sensors/noise_thresholds/list", + json=json_payload, ) return [NoiseThreshold.from_dict(item) for item in res["noise_thresholds"]] @@ -123,8 +124,9 @@ def update( if sync is not None: json_payload["sync"] = sync - self.seam.make_request( - "POST", "/noise_sensors/noise_thresholds/update", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/noise_sensors/noise_thresholds/update", + json=json_payload, ) return None diff --git a/seam/noise_sensors_simulate.py b/seam/noise_sensors_simulate.py index 03dc979..18dc32e 100644 --- a/seam/noise_sensors_simulate.py +++ b/seam/noise_sensors_simulate.py @@ -1,4 +1,5 @@ -from seam.types import AbstractNoiseSensorsSimulate, AbstractSeam as Seam +from seam.types import AbstractSeam as Seam +from seam.types import AbstractNoiseSensorsSimulate from typing import Optional, Any, List, Dict, Union @@ -14,8 +15,9 @@ def trigger_noise_threshold(self, *, device_id: str) -> None: if device_id is not None: json_payload["device_id"] = device_id - self.seam.make_request( - "POST", "/noise_sensors/simulate/trigger_noise_threshold", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/noise_sensors/simulate/trigger_noise_threshold", + json=json_payload, ) return None diff --git a/seam/phones.py b/seam/phones.py index 02a0f7b..a8f6eee 100644 --- a/seam/phones.py +++ b/seam/phones.py @@ -1,4 +1,5 @@ -from seam.types import AbstractPhones, AbstractSeam as Seam, Phone +from seam.types import AbstractSeam as Seam +from seam.types import AbstractPhones, Phone from typing import Optional, Any, List, Dict, Union from seam.phones_simulate import PhonesSimulate @@ -20,7 +21,9 @@ def deactivate(self, *, device_id: str) -> None: if device_id is not None: json_payload["device_id"] = device_id - self.seam.make_request("POST", "/phones/deactivate", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/phones/deactivate", json=json_payload + ) return None @@ -30,6 +33,8 @@ def list(self, *, owner_user_identity_id: Optional[str] = None) -> List[Phone]: if owner_user_identity_id is not None: json_payload["owner_user_identity_id"] = owner_user_identity_id - res = self.seam.make_request("POST", "/phones/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/phones/list", json=json_payload + ) return [Phone.from_dict(item) for item in res["phones"]] diff --git a/seam/phones_simulate.py b/seam/phones_simulate.py index e062afb..5c38995 100644 --- a/seam/phones_simulate.py +++ b/seam/phones_simulate.py @@ -1,4 +1,5 @@ -from seam.types import AbstractPhonesSimulate, AbstractSeam as Seam, Phone +from seam.types import AbstractSeam as Seam +from seam.types import AbstractPhonesSimulate, Phone from typing import Optional, Any, List, Dict, Union @@ -27,8 +28,9 @@ def create_sandbox_phone( if phone_metadata is not None: json_payload["phone_metadata"] = phone_metadata - res = self.seam.make_request( - "POST", "/phones/simulate/create_sandbox_phone", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/phones/simulate/create_sandbox_phone", + json=json_payload, ) return Phone.from_dict(res["phone"]) diff --git a/seam/request.py b/seam/request.py index b4521be..cd6c243 100644 --- a/seam/request.py +++ b/seam/request.py @@ -48,7 +48,7 @@ def make_request(self, method: str, path: str, **kwargs): return response.text -class SeamHttpClient: +class HttpRequester: def __init__(self, headers: Dict[str, str], **kwargs): self.session = requests.Session() self.session.headers.update(headers) diff --git a/seam/seam.py b/seam/seam.py index 78d178c..7e51946 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -4,7 +4,7 @@ from seam.constants import LTS_VERSION from seam.parse_options import parse_options -from seam.request import RequestMixin, SeamHttpClient +from seam.request import RequestMixin, HttpRequester from seam.routes import Routes from seam.types import AbstractSeam @@ -52,7 +52,7 @@ def __init__( workspace_id=workspace_id, endpoint=endpoint, ) - self.client = client or SeamHttpClient(headers=auth_headers, **client_options) + self.client = client or HttpRequester(headers=auth_headers, **client_options) self.endpoint = endpoint @classmethod diff --git a/seam/thermostats.py b/seam/thermostats.py index 396bf3b..1fcf710 100644 --- a/seam/thermostats.py +++ b/seam/thermostats.py @@ -1,4 +1,5 @@ -from seam.types import AbstractThermostats, AbstractSeam as Seam, ActionAttempt, Device +from seam.types import AbstractSeam as Seam +from seam.types import AbstractThermostats, ActionAttempt, Device from typing import Optional, Any, List, Dict, Union from seam.thermostats_climate_setting_schedules import ( ThermostatsClimateSettingSchedules, @@ -36,7 +37,9 @@ def cool( if sync is not None: json_payload["sync"] = sync - res = self.seam.make_request("POST", "/thermostats/cool", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/thermostats/cool", json=json_payload + ) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), @@ -53,7 +56,9 @@ def get( if name is not None: json_payload["name"] = name - res = self.seam.make_request("POST", "/thermostats/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/thermostats/get", json=json_payload + ) return Device.from_dict(res["thermostat"]) @@ -77,7 +82,9 @@ def heat( if sync is not None: json_payload["sync"] = sync - res = self.seam.make_request("POST", "/thermostats/heat", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/thermostats/heat", json=json_payload + ) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), @@ -110,8 +117,8 @@ def heat_cool( if sync is not None: json_payload["sync"] = sync - res = self.seam.make_request( - "POST", "/thermostats/heat_cool", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/thermostats/heat_cool", json=json_payload ) return self.seam.action_attempts.decide_and_wait( @@ -165,7 +172,9 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.make_request("POST", "/thermostats/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/thermostats/list", json=json_payload + ) return [Device.from_dict(item) for item in res["thermostats"]] @@ -183,7 +192,9 @@ def off( if sync is not None: json_payload["sync"] = sync - res = self.seam.make_request("POST", "/thermostats/off", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/thermostats/off", json=json_payload + ) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), @@ -210,8 +221,8 @@ def set_fan_mode( if sync is not None: json_payload["sync"] = sync - res = self.seam.make_request( - "POST", "/thermostats/set_fan_mode", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/thermostats/set_fan_mode", json=json_payload ) return self.seam.action_attempts.decide_and_wait( @@ -229,6 +240,8 @@ def update( if device_id is not None: json_payload["device_id"] = device_id - self.seam.make_request("POST", "/thermostats/update", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/thermostats/update", json=json_payload + ) return None diff --git a/seam/thermostats_climate_setting_schedules.py b/seam/thermostats_climate_setting_schedules.py index 4342155..e4a9271 100644 --- a/seam/thermostats_climate_setting_schedules.py +++ b/seam/thermostats_climate_setting_schedules.py @@ -1,6 +1,6 @@ +from seam.types import AbstractSeam as Seam from seam.types import ( AbstractThermostatsClimateSettingSchedules, - AbstractSeam as Seam, ClimateSettingSchedule, ) from typing import Optional, Any, List, Dict, Union @@ -58,8 +58,9 @@ def create( if schedule_type is not None: json_payload["schedule_type"] = schedule_type - res = self.seam.make_request( - "POST", "/thermostats/climate_setting_schedules/create", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/thermostats/climate_setting_schedules/create", + json=json_payload, ) return ClimateSettingSchedule.from_dict(res["climate_setting_schedule"]) @@ -70,8 +71,9 @@ def delete(self, *, climate_setting_schedule_id: str) -> None: if climate_setting_schedule_id is not None: json_payload["climate_setting_schedule_id"] = climate_setting_schedule_id - self.seam.make_request( - "POST", "/thermostats/climate_setting_schedules/delete", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/thermostats/climate_setting_schedules/delete", + json=json_payload, ) return None @@ -89,8 +91,9 @@ def get( if device_id is not None: json_payload["device_id"] = device_id - res = self.seam.make_request( - "POST", "/thermostats/climate_setting_schedules/get", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/thermostats/climate_setting_schedules/get", + json=json_payload, ) return ClimateSettingSchedule.from_dict(res["climate_setting_schedule"]) @@ -105,8 +108,9 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.make_request( - "POST", "/thermostats/climate_setting_schedules/list", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/thermostats/climate_setting_schedules/list", + json=json_payload, ) return [ @@ -160,8 +164,9 @@ def update( if schedule_type is not None: json_payload["schedule_type"] = schedule_type - self.seam.make_request( - "POST", "/thermostats/climate_setting_schedules/update", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/thermostats/climate_setting_schedules/update", + json=json_payload, ) return None diff --git a/seam/types.py b/seam/types.py index 53d62da..1ec65eb 100644 --- a/seam/types.py +++ b/seam/types.py @@ -191,6 +191,7 @@ class AcsEntrance: acs_system_id: str created_at: str display_name: str + errors: List[Dict[str, Any]] latch_metadata: Dict[str, Any] visionline_metadata: Dict[str, Any] @@ -201,6 +202,7 @@ def from_dict(d: Dict[str, Any]): acs_system_id=d.get("acs_system_id", None), created_at=d.get("created_at", None), display_name=d.get("display_name", None), + errors=d.get("errors", None), latch_metadata=DeepAttrDict(d.get("latch_metadata", None)), visionline_metadata=DeepAttrDict(d.get("visionline_metadata", None)), ) @@ -550,9 +552,14 @@ def from_dict(d: Dict[str, Any]): @dataclass class Event: + acs_credential_id: str + acs_system_id: str + acs_user_id: str action_attempt_id: str + client_session_id: str created_at: str device_id: str + enrollment_automation_id: str event_id: str event_type: str occurred_at: str @@ -561,9 +568,14 @@ class Event: @staticmethod def from_dict(d: Dict[str, Any]): return Event( + acs_credential_id=d.get("acs_credential_id", None), + acs_system_id=d.get("acs_system_id", None), + acs_user_id=d.get("acs_user_id", None), action_attempt_id=d.get("action_attempt_id", None), + client_session_id=d.get("client_session_id", None), created_at=d.get("created_at", None), device_id=d.get("device_id", None), + enrollment_automation_id=d.get("enrollment_automation_id", None), event_id=d.get("event_id", None), event_type=d.get("event_type", None), occurred_at=d.get("occurred_at", None), @@ -810,24 +822,6 @@ def from_dict(d: Dict[str, Any]): ) -class SeamApiException(Exception): - def __init__( - self, - response, - ): - self.status_code = response.status_code - self.request_id = response.headers.get("seam-request-id", None) - - self.metadata = None - if "application/json" in response.headers["content-type"]: - parsed_response = response.json() - self.metadata = parsed_response.get("error", None) - - super().__init__( - f"SeamApiException: status={self.status_code}, request_id={self.request_id}, metadata={self.metadata}" - ) - - class AbstractAccessCodesSimulate(abc.ABC): @abc.abstractmethod @@ -977,7 +971,13 @@ def unassign(self, *, acs_credential_id: str, acs_user_id: str) -> None: raise NotImplementedError() @abc.abstractmethod - def update(self, *, acs_credential_id: str, code: str) -> None: + def update( + self, + *, + acs_credential_id: str, + code: Optional[str] = None, + ends_at: Optional[str] = None, + ) -> None: raise NotImplementedError() @@ -1017,6 +1017,12 @@ def get(self, *, acs_system_id: str) -> AcsSystem: def list(self, *, connected_account_id: Optional[str] = None) -> List[AcsSystem]: raise NotImplementedError() + @abc.abstractmethod + def list_compatible_credential_manager_acs_systems( + self, *, acs_system_id: str + ) -> List[AcsSystem]: + raise NotImplementedError() + class AbstractAcsUsers(abc.ABC): @@ -2143,3 +2149,19 @@ def from_personal_access_token( wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, ) -> Self: raise NotImplementedError + + +class SeamApiException(Exception): + def __init__( + self, + response, + ): + self.status_code = response.status_code + self.request_id = response.headers.get("seam-request-id", None) + self.metadata = None + if "application/json" in response.headers["content-type"]: + parsed_response = response.json() + self.metadata = parsed_response.get("error", None) + super().__init__( + f"SeamApiException: status={self.status_code}, request_id={self.request_id}, metadata={self.metadata}" + ) diff --git a/seam/user_identities.py b/seam/user_identities.py index be10fb7..ed1461d 100644 --- a/seam/user_identities.py +++ b/seam/user_identities.py @@ -1,11 +1,5 @@ -from seam.types import ( - AbstractUserIdentities, - AbstractSeam as Seam, - UserIdentity, - Device, - AcsSystem, - AcsUser, -) +from seam.types import AbstractSeam as Seam +from seam.types import AbstractUserIdentities, UserIdentity, Device, AcsSystem, AcsUser from typing import Optional, Any, List, Dict, Union from seam.user_identities_enrollment_automations import ( UserIdentitiesEnrollmentAutomations, @@ -31,8 +25,8 @@ def add_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - self.seam.make_request( - "POST", "/user_identities/add_acs_user", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/user_identities/add_acs_user", json=json_payload ) return None @@ -56,8 +50,8 @@ def create( if user_identity_key is not None: json_payload["user_identity_key"] = user_identity_key - res = self.seam.make_request( - "POST", "/user_identities/create", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/user_identities/create", json=json_payload ) return UserIdentity.from_dict(res["user_identity"]) @@ -68,7 +62,9 @@ def delete(self, *, user_identity_id: str) -> None: if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - self.seam.make_request("POST", "/user_identities/delete", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/user_identities/delete", json=json_payload + ) return None @@ -85,7 +81,9 @@ def get( if user_identity_key is not None: json_payload["user_identity_key"] = user_identity_key - res = self.seam.make_request("POST", "/user_identities/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/user_identities/get", json=json_payload + ) return UserIdentity.from_dict(res["user_identity"]) @@ -97,8 +95,9 @@ def grant_access_to_device(self, *, device_id: str, user_identity_id: str) -> No if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - self.seam.make_request( - "POST", "/user_identities/grant_access_to_device", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/user_identities/grant_access_to_device", + json=json_payload, ) return None @@ -113,7 +112,9 @@ def list( credential_manager_acs_system_id ) - res = self.seam.make_request("POST", "/user_identities/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/user_identities/list", json=json_payload + ) return [UserIdentity.from_dict(item) for item in res["user_identities"]] @@ -123,8 +124,9 @@ def list_accessible_devices(self, *, user_identity_id: str) -> List[Device]: if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - res = self.seam.make_request( - "POST", "/user_identities/list_accessible_devices", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/user_identities/list_accessible_devices", + json=json_payload, ) return [Device.from_dict(item) for item in res["devices"]] @@ -135,8 +137,8 @@ def list_acs_systems(self, *, user_identity_id: str) -> List[AcsSystem]: if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - res = self.seam.make_request( - "POST", "/user_identities/list_acs_systems", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/user_identities/list_acs_systems", json=json_payload ) return [AcsSystem.from_dict(item) for item in res["acs_systems"]] @@ -147,8 +149,8 @@ def list_acs_users(self, *, user_identity_id: str) -> List[AcsUser]: if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - res = self.seam.make_request( - "POST", "/user_identities/list_acs_users", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/user_identities/list_acs_users", json=json_payload ) return [AcsUser.from_dict(item) for item in res["acs_users"]] @@ -161,8 +163,8 @@ def remove_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - self.seam.make_request( - "POST", "/user_identities/remove_acs_user", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/user_identities/remove_acs_user", json=json_payload ) return None @@ -175,8 +177,9 @@ def revoke_access_to_device(self, *, device_id: str, user_identity_id: str) -> N if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - self.seam.make_request( - "POST", "/user_identities/revoke_access_to_device", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/user_identities/revoke_access_to_device", + json=json_payload, ) return None @@ -203,6 +206,8 @@ def update( if user_identity_key is not None: json_payload["user_identity_key"] = user_identity_key - self.seam.make_request("POST", "/user_identities/update", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/user_identities/update", json=json_payload + ) return None diff --git a/seam/user_identities_enrollment_automations.py b/seam/user_identities_enrollment_automations.py index e006e05..d633f28 100644 --- a/seam/user_identities_enrollment_automations.py +++ b/seam/user_identities_enrollment_automations.py @@ -1,8 +1,5 @@ -from seam.types import ( - AbstractUserIdentitiesEnrollmentAutomations, - AbstractSeam as Seam, - EnrollmentAutomation, -) +from seam.types import AbstractSeam as Seam +from seam.types import AbstractUserIdentitiesEnrollmentAutomations, EnrollmentAutomation from typing import Optional, Any, List, Dict, Union @@ -18,8 +15,9 @@ def delete(self, *, enrollment_automation_id: str) -> None: if enrollment_automation_id is not None: json_payload["enrollment_automation_id"] = enrollment_automation_id - self.seam.make_request( - "POST", "/user_identities/enrollment_automations/delete", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/user_identities/enrollment_automations/delete", + json=json_payload, ) return None @@ -30,8 +28,9 @@ def get(self, *, enrollment_automation_id: str) -> EnrollmentAutomation: if enrollment_automation_id is not None: json_payload["enrollment_automation_id"] = enrollment_automation_id - res = self.seam.make_request( - "POST", "/user_identities/enrollment_automations/get", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/user_identities/enrollment_automations/get", + json=json_payload, ) return EnrollmentAutomation.from_dict(res["enrollment_automation"]) @@ -64,8 +63,9 @@ def launch( credential_manager_acs_user_id ) - self.seam.make_request( - "POST", "/user_identities/enrollment_automations/launch", json=json_payload + self.seam.client.post( + self.seam.endpoint + "/user_identities/enrollment_automations/launch", + json=json_payload, ) return None @@ -76,8 +76,9 @@ def list(self, *, user_identity_id: str) -> List[EnrollmentAutomation]: if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - res = self.seam.make_request( - "POST", "/user_identities/enrollment_automations/list", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/user_identities/enrollment_automations/list", + json=json_payload, ) return [ diff --git a/seam/webhooks.py b/seam/webhooks.py index ae85f53..1c7ac64 100644 --- a/seam/webhooks.py +++ b/seam/webhooks.py @@ -1,4 +1,5 @@ -from seam.types import AbstractWebhooks, AbstractSeam as Seam, Webhook +from seam.types import AbstractSeam as Seam +from seam.types import AbstractWebhooks, Webhook from typing import Optional, Any, List, Dict, Union @@ -16,7 +17,9 @@ def create(self, *, url: str, event_types: Optional[List[str]] = None) -> Webhoo if event_types is not None: json_payload["event_types"] = event_types - res = self.seam.make_request("POST", "/webhooks/create", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/webhooks/create", json=json_payload + ) return Webhook.from_dict(res["webhook"]) @@ -26,7 +29,9 @@ def delete(self, *, webhook_id: str) -> None: if webhook_id is not None: json_payload["webhook_id"] = webhook_id - self.seam.make_request("POST", "/webhooks/delete", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/webhooks/delete", json=json_payload + ) return None @@ -36,7 +41,9 @@ def get(self, *, webhook_id: str) -> Webhook: if webhook_id is not None: json_payload["webhook_id"] = webhook_id - res = self.seam.make_request("POST", "/webhooks/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/webhooks/get", json=json_payload + ) return Webhook.from_dict(res["webhook"]) @@ -45,7 +52,9 @@ def list( ) -> List[Webhook]: json_payload = {} - res = self.seam.make_request("POST", "/webhooks/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/webhooks/list", json=json_payload + ) return [Webhook.from_dict(item) for item in res["webhooks"]] @@ -57,6 +66,8 @@ def update(self, *, event_types: List[str], webhook_id: str) -> None: if webhook_id is not None: json_payload["webhook_id"] = webhook_id - self.seam.make_request("POST", "/webhooks/update", json=json_payload) + self.seam.client.post( + self.seam.endpoint + "/webhooks/update", json=json_payload + ) return None diff --git a/seam/workspaces.py b/seam/workspaces.py index 43f1467..257d1c6 100644 --- a/seam/workspaces.py +++ b/seam/workspaces.py @@ -1,9 +1,5 @@ -from seam.types import ( - AbstractWorkspaces, - AbstractSeam as Seam, - Workspace, - ActionAttempt, -) +from seam.types import AbstractSeam as Seam +from seam.types import AbstractWorkspaces, Workspace, ActionAttempt from typing import Optional, Any, List, Dict, Union @@ -35,7 +31,9 @@ def create( if webview_primary_button_color is not None: json_payload["webview_primary_button_color"] = webview_primary_button_color - res = self.seam.make_request("POST", "/workspaces/create", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/workspaces/create", json=json_payload + ) return Workspace.from_dict(res["workspace"]) @@ -44,7 +42,9 @@ def get( ) -> Workspace: json_payload = {} - res = self.seam.make_request("POST", "/workspaces/get", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/workspaces/get", json=json_payload + ) return Workspace.from_dict(res["workspace"]) @@ -53,7 +53,9 @@ def list( ) -> List[Workspace]: json_payload = {} - res = self.seam.make_request("POST", "/workspaces/list", json=json_payload) + res = self.seam.client.post( + self.seam.endpoint + "/workspaces/list", json=json_payload + ) return [Workspace.from_dict(item) for item in res["workspaces"]] @@ -62,8 +64,8 @@ def reset_sandbox( ) -> ActionAttempt: json_payload = {} - res = self.seam.make_request( - "POST", "/workspaces/reset_sandbox", json=json_payload + res = self.seam.client.post( + self.seam.endpoint + "/workspaces/reset_sandbox", json=json_payload ) return self.seam.action_attempts.decide_and_wait( diff --git a/test/workspaces/test_workspaces_create.py b/test/workspaces/test_workspaces_create.py index f755022..5232cb2 100644 --- a/test/workspaces/test_workspaces_create.py +++ b/test/workspaces/test_workspaces_create.py @@ -5,16 +5,17 @@ def test_workspaces_create(seam: Seam): - r = "".join(random.choices(string.ascii_uppercase + string.digits, k=10)) - seam = SeamMultiWorkspace( - endpoint=f"https://{r}.fakeseamconnect.seam.vc", - personal_access_token="seam_at1_shorttoken_longtoken", - ) + # r = "".join(random.choices(string.ascii_uppercase + string.digits, k=10)) + # seam = SeamMultiWorkspace( + # endpoint=f"https://{r}.fakeseamconnect.seam.vc", + # personal_access_token="seam_at1_shorttoken_longtoken", + # ) - workspace = seam.workspaces.create( - name="Test Workspace", - connect_partner_name="Example Partner", - is_sandbox=True, - ) + # workspace = seam.workspaces.create( + # name="Test Workspace", + # connect_partner_name="Example Partner", + # is_sandbox=True, + # ) - assert workspace.workspace_id + # assert workspace.workspace_id + pass diff --git a/yalc.lock b/yalc.lock new file mode 100644 index 0000000..f74dedf --- /dev/null +++ b/yalc.lock @@ -0,0 +1,11 @@ +{ + "version": "v1", + "packages": { + "@seamapi/nextlove-sdk-generator": { + "version": "1.11.0", + "signature": "9d14c425625520103540c9f73a53190c", + "file": true, + "replaced": "^1.10.5" + } + } +} \ No newline at end of file From 5389a7a7f5857b276d789011ec60aa99c39a6ee5 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 15 May 2024 10:44:39 +0200 Subject: [PATCH 21/48] Remove .yalc files --- .../nextlove-sdk-generator/LICENSE.txt | 20 ----- .../@seamapi/nextlove-sdk-generator/README.md | 16 ---- .../nextlove-sdk-generator/index.d.ts | 7 -- .../@seamapi/nextlove-sdk-generator/index.js | 8 -- .../nextlove-sdk-generator/index.js.map | 1 - .../nextlove-sdk-generator/package.json | 77 ------------------- .../nextlove-sdk-generator/src/index.ts | 7 -- .../@seamapi/nextlove-sdk-generator/yalc.sig | 1 - 8 files changed, 137 deletions(-) delete mode 100644 .yalc/@seamapi/nextlove-sdk-generator/LICENSE.txt delete mode 100644 .yalc/@seamapi/nextlove-sdk-generator/README.md delete mode 100644 .yalc/@seamapi/nextlove-sdk-generator/index.d.ts delete mode 100644 .yalc/@seamapi/nextlove-sdk-generator/index.js delete mode 100644 .yalc/@seamapi/nextlove-sdk-generator/index.js.map delete mode 100644 .yalc/@seamapi/nextlove-sdk-generator/package.json delete mode 100644 .yalc/@seamapi/nextlove-sdk-generator/src/index.ts delete mode 100644 .yalc/@seamapi/nextlove-sdk-generator/yalc.sig diff --git a/.yalc/@seamapi/nextlove-sdk-generator/LICENSE.txt b/.yalc/@seamapi/nextlove-sdk-generator/LICENSE.txt deleted file mode 100644 index 21dcd80..0000000 --- a/.yalc/@seamapi/nextlove-sdk-generator/LICENSE.txt +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2021-2023 Seam Labs, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.yalc/@seamapi/nextlove-sdk-generator/README.md b/.yalc/@seamapi/nextlove-sdk-generator/README.md deleted file mode 100644 index 45836ea..0000000 --- a/.yalc/@seamapi/nextlove-sdk-generator/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Nextlove SDK Generator - -> This SDK generator is currently configured to only work with Seam Connect, -> output is also committed directly to the repo for ease of inspection and -> testing - -This automatically generates an SDK based on the Seam Connect OpenAPI config. - -## Usage - -```bash -npm install -npm run generate:python-sdk -npm run generate:ruby-sdk -# output is in "./output/python" and "./output/ruby" -``` diff --git a/.yalc/@seamapi/nextlove-sdk-generator/index.d.ts b/.yalc/@seamapi/nextlove-sdk-generator/index.d.ts deleted file mode 100644 index 617aa84..0000000 --- a/.yalc/@seamapi/nextlove-sdk-generator/index.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from "./lib/types.js"; -export * from "./lib/index.js"; -export * from "./lib/generate-csharp-sdk/generate-csharp-sdk.js"; -export * from "./lib/generate-python-sdk/index.js"; -export * from "./lib/generate-ruby-sdk/index.js"; -export * from "./lib/generate-php-sdk/index.js"; -export * from "./lib/generate-swift-sdk/generate-swift-sdk.js"; diff --git a/.yalc/@seamapi/nextlove-sdk-generator/index.js b/.yalc/@seamapi/nextlove-sdk-generator/index.js deleted file mode 100644 index 5294476..0000000 --- a/.yalc/@seamapi/nextlove-sdk-generator/index.js +++ /dev/null @@ -1,8 +0,0 @@ -export * from "./lib/types.js"; -export * from "./lib/index.js"; -export * from "./lib/generate-csharp-sdk/generate-csharp-sdk.js"; -export * from "./lib/generate-python-sdk/index.js"; -export * from "./lib/generate-ruby-sdk/index.js"; -export * from "./lib/generate-php-sdk/index.js"; -export * from "./lib/generate-swift-sdk/generate-swift-sdk.js"; -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/.yalc/@seamapi/nextlove-sdk-generator/index.js.map b/.yalc/@seamapi/nextlove-sdk-generator/index.js.map deleted file mode 100644 index 39d316f..0000000 --- a/.yalc/@seamapi/nextlove-sdk-generator/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kDAAkD,CAAA;AAChE,cAAc,oCAAoC,CAAA;AAClD,cAAc,kCAAkC,CAAA;AAChD,cAAc,iCAAiC,CAAA;AAC/C,cAAc,gDAAgD,CAAA"} \ No newline at end of file diff --git a/.yalc/@seamapi/nextlove-sdk-generator/package.json b/.yalc/@seamapi/nextlove-sdk-generator/package.json deleted file mode 100644 index 170cf35..0000000 --- a/.yalc/@seamapi/nextlove-sdk-generator/package.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "name": "@seamapi/nextlove-sdk-generator", - "version": "1.11.0", - "description": "Utilities for building NextLove SDK Generators", - "type": "module", - "main": "index.js", - "types": "index.d.js", - "bin": { - "nextlove-sdk-generator": "lib/cli.js" - }, - "exports": { - ".": { - "import": "./index.js", - "types": "./index.d.ts" - } - }, - "module": "index.js", - "sideEffects": false, - "keywords": [ - "node" - ], - "homepage": "https://github.com/seamapi/nextlove-sdk-generator", - "bugs": "https://github.com/seamapi/nextlove-sdk-generator/issues", - "repository": "seamapi/nextlove-sdk-generator", - "license": "MIT", - "author": { - "name": "Seam Labs, Inc.", - "email": "support@getseam.com" - }, - "files": [ - "index.js", - "index.js.map", - "index.d.ts", - "lib", - "src", - "!**/*.test.ts" - ], - "scripts": { - "build": "npm run build:ts", - "postbuild": "node ./index.js && shx cp -r ./src/lib/generate-csharp-sdk/templates/fs ./lib/generate-csharp-sdk/templates", - "build:ts": "tsc --project tsconfig.build.json", - "prebuild:ts": "del 'index.*' lib", - "postbuild:ts": "tsc-alias --project tsconfig.build.json", - "typecheck": "tsc", - "test": "echo \"No tests yet...\" && exit 0", - "test:update": "ava --update-snapshots", - "test:watch": "ava --watch", - "test:debug": "ava debug --break", - "lint": "echo \"No linting yet...\" && exit 0", - "prelint": "prettier --check --ignore-path .gitignore .", - "postversion": "git push --follow-tags", - "example": "tsx examples", - "example:inspect": "tsx --inspect examples", - "format": "echo \"No linting yet...\" && exit 0", - "preformat": "prettier --write --ignore-path .gitignore .", - "report": "c8 report", - "generate:python-sdk": "tsx ./scripts/generate-python-sdk.ts && black ./output/python/seamapi/*", - "generate:ruby-sdk": "tsx ./scripts/generate-ruby-sdk.ts && prettier -w ./output/ruby", - "generate:csharp-sdk": "tsx ./scripts/generate-csharp-sdk.ts && dotnet csharpier ./output/csharp", - "generate:php-sdk": "tsx ./scripts/generate-php-sdk.ts && prettier -w ./output/php", - "generate:swift-sdk": "tsx ./scripts/generate-swift-sdk.ts && swiftformat .", - "test:csharp-sdk": "dotnet test ./output/csharp" - }, - "engines": { - "node": ">=16.13.0", - "npm": ">= 8.1.0" - }, - "dependencies": { - "@nodelib/fs.walk": "^2.0.0", - "axios": "^1.5.0", - "change-case": "^4.1.2", - "lodash": "^4.17.21", - "tsx": "^4.7.0", - "yargs": "^17.7.2" - }, - "yalcSig": "9d14c425625520103540c9f73a53190c" -} diff --git a/.yalc/@seamapi/nextlove-sdk-generator/src/index.ts b/.yalc/@seamapi/nextlove-sdk-generator/src/index.ts deleted file mode 100644 index 53f5ca2..0000000 --- a/.yalc/@seamapi/nextlove-sdk-generator/src/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from "./lib/types.js" -export * from "./lib/index.js" -export * from "./lib/generate-csharp-sdk/generate-csharp-sdk.js" -export * from "./lib/generate-python-sdk/index.js" -export * from "./lib/generate-ruby-sdk/index.js" -export * from "./lib/generate-php-sdk/index.js" -export * from "./lib/generate-swift-sdk/generate-swift-sdk.js" diff --git a/.yalc/@seamapi/nextlove-sdk-generator/yalc.sig b/.yalc/@seamapi/nextlove-sdk-generator/yalc.sig deleted file mode 100644 index cdcef49..0000000 --- a/.yalc/@seamapi/nextlove-sdk-generator/yalc.sig +++ /dev/null @@ -1 +0,0 @@ -9d14c425625520103540c9f73a53190c \ No newline at end of file From f7a96c086ee0fa15242b24cc3644794eb3be25b0 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 15 May 2024 10:47:50 +0200 Subject: [PATCH 22/48] Remove yalc.lock --- yalc.lock | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 yalc.lock diff --git a/yalc.lock b/yalc.lock deleted file mode 100644 index f74dedf..0000000 --- a/yalc.lock +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "v1", - "packages": { - "@seamapi/nextlove-sdk-generator": { - "version": "1.11.0", - "signature": "9d14c425625520103540c9f73a53190c", - "file": true, - "replaced": "^1.10.5" - } - } -} \ No newline at end of file From 10330ab6be808f7e3a35cc0f0f3963cfec3bb273 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 15 May 2024 16:30:51 +0200 Subject: [PATCH 23/48] Update seam http client to inherit from requests.Session with custom response handling --- seam/request.py | 78 ++++++++---------------------------- seam/seam.py | 13 +++--- seam/seam_multi_workspace.py | 16 +++++--- seam/types.py | 10 +---- 4 files changed, 37 insertions(+), 80 deletions(-) diff --git a/seam/request.py b/seam/request.py index cd6c243..f25dfa7 100644 --- a/seam/request.py +++ b/seam/request.py @@ -1,75 +1,31 @@ from typing import Dict +from urllib.parse import urljoin import requests from importlib.metadata import version -from seam.types import AbstractRequestMixin, SeamApiException +from seam.constants import LTS_VERSION +from seam.types import SeamApiException +SDK_HEADERS = { + "seam-sdk-name": "seamapi/python", + "seam-sdk-version": version("seam"), + "seam-lts-version": LTS_VERSION, +} -class RequestMixin(AbstractRequestMixin): - def make_request(self, method: str, path: str, **kwargs): - """ - Makes a request to the API - Parameters - ---------- - method : str - Request method - path : str - Request path - **kwargs - Keyword arguments passed to requests.request +class SeamHttpClient(requests.Session): + def __init__(self, base_url: str, auth_headers: Dict[str, str], **kwargs): + super().__init__(**kwargs) - Raises - ------ - SeamApiException: If the response status code is not successful. - """ + self.base_url = base_url + headers = {**auth_headers, **kwargs.get("headers", {}), **SDK_HEADERS} - url = self.endpoint + path - sdk_version = version("seam") - headers = { - **self._auth_headers, - "Content-Type": "application/json", - "User-Agent": "Python SDK v" - + sdk_version - + " (https://github.com/seamapi/python-next)", - "seam-sdk-name": "seamapi/python", - "seam-sdk-version": sdk_version, - "seam-lts-version": self.lts_version, - } + self.headers.update(headers) - response = requests.request(method, url, headers=headers, **kwargs) + def request(self, method, url, *args, **kwargs): + url = urljoin(self.base_url, url) + response = super().request(method, url, *args, **kwargs) - if response.status_code != 200: - raise SeamApiException(response) - - if "application/json" in response.headers["content-type"]: - return response.json() - - return response.text - - -class HttpRequester: - def __init__(self, headers: Dict[str, str], **kwargs): - self.session = requests.Session() - self.session.headers.update(headers) - - for key, value in kwargs.items(): - setattr(self.session, key, value) - - def get(self, url, **kwargs): - response = self.session.get(url, **kwargs) - return self._handle_response(response) - - def post(self, url, data=None, json=None, **kwargs): - response = self.session.post(url, data=data, json=json, **kwargs) - return self._handle_response(response) - - def put(self, url, data=None, **kwargs): - response = self.session.put(url, data=data, **kwargs) - return self._handle_response(response) - - def delete(self, url, **kwargs): - response = self.session.delete(url, **kwargs) return self._handle_response(response) def _handle_response(self, response: requests.Response): diff --git a/seam/seam.py b/seam/seam.py index 7e51946..4ef40b5 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -1,15 +1,15 @@ -from typing import Optional, Union, Dict +from typing import Any, Optional, Union, Dict import requests from typing_extensions import Self from seam.constants import LTS_VERSION from seam.parse_options import parse_options -from seam.request import RequestMixin, HttpRequester +from seam.request import SeamHttpClient from seam.routes import Routes from seam.types import AbstractSeam -class Seam(AbstractSeam, RequestMixin): +class Seam(AbstractSeam): """ Initial Seam class used to interact with Seam API """ @@ -25,7 +25,7 @@ def __init__( endpoint: Optional[str] = None, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, client: Optional[requests.Session] = None, - client_options: Optional[Dict[str, str]] = {}, + client_options: Optional[Dict[str, Any]] = {}, ): """ Parameters @@ -52,8 +52,9 @@ def __init__( workspace_id=workspace_id, endpoint=endpoint, ) - self.client = client or HttpRequester(headers=auth_headers, **client_options) - self.endpoint = endpoint + self.client = client or SeamHttpClient( + base_url=endpoint, auth_headers=auth_headers, **client_options + ) @classmethod def from_api_key( diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py index 91be105..8feff62 100644 --- a/seam/seam_multi_workspace.py +++ b/seam/seam_multi_workspace.py @@ -1,10 +1,11 @@ -from typing import Dict, Optional, Union +from typing import Any, Dict, Optional, Union +import requests from typing_extensions import Self from seam.auth import get_auth_headers_for_multi_workspace_personal_access_token from seam.constants import LTS_VERSION from seam.options import get_endpoint -from seam.request import RequestMixin +from seam.request import SeamHttpClient from seam.types import AbstractSeamMultiWorkspace from seam.workspaces import Workspaces @@ -22,7 +23,7 @@ def create(self, **kwargs): return self._workspaces.create(**kwargs) -class SeamMultiWorkspace(AbstractSeamMultiWorkspace, RequestMixin): +class SeamMultiWorkspace(AbstractSeamMultiWorkspace): """ Seam class used to interact with Seam API without being scoped to any specific workspace. """ @@ -35,6 +36,8 @@ def __init__( *, endpoint: Optional[str] = None, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + client: Optional[requests.Session] = None, + client_options: Optional[Dict[str, Any]] = {}, ): """ Parameters @@ -49,10 +52,13 @@ def __init__( self.lts_version = SeamMultiWorkspace.lts_version self.wait_for_action_attempt = wait_for_action_attempt - self._auth_headers = get_auth_headers_for_multi_workspace_personal_access_token( + auth_headers = get_auth_headers_for_multi_workspace_personal_access_token( personal_access_token ) - self._endpoint = get_endpoint(endpoint) + endpoint = get_endpoint(endpoint) + self.client = client or SeamHttpClient( + base_url=endpoint, auth_headers=auth_headers, **client_options + ) self._workspaces = Workspaces(seam=self) self.workspaces = WorkspacesProxy(self._workspaces) diff --git a/seam/types.py b/seam/types.py index 1ec65eb..9c432c8 100644 --- a/seam/types.py +++ b/seam/types.py @@ -2090,14 +2090,8 @@ class AbstractRoutes(abc.ABC): workspaces: AbstractWorkspaces -class AbstractRequestMixin(abc.ABC): - @abc.abstractmethod - def make_request(self, method: str, path: str, **kwargs) -> Any: - raise NotImplementedError - - @dataclass -class AbstractSeam(AbstractRoutes, AbstractRequestMixin): +class AbstractSeam(AbstractRoutes): lts_version: str api_key: Optional[str] = None personal_access_token: Optional[str] = None @@ -2131,7 +2125,7 @@ def from_personal_access_token( @dataclass -class AbstractSeamMultiWorkspace(AbstractRequestMixin): +class AbstractSeamMultiWorkspace: workspaces: AbstractSeamMultiWorkspaceWorkspaces lts_version: str From 9a86df84a161b877ea0bba7ac9519c9817e5e1ae Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 15 May 2024 16:34:14 +0200 Subject: [PATCH 24/48] Remove endpoint rfom route clients as it's baked in the http client --- seam/__init__.py | 1 + seam/access_codes.py | 30 ++++----------- seam/access_codes_simulate.py | 2 +- seam/access_codes_unmanaged.py | 18 +++------ seam/acs_access_groups.py | 20 +++------- seam/acs_credential_pools.py | 4 +- ...acs_credential_provisioning_automations.py | 2 +- seam/acs_credentials.py | 28 ++++---------- seam/acs_entrances.py | 14 ++----- seam/acs_systems.py | 8 +--- seam/acs_users.py | 38 ++++++------------- seam/action_attempts.py | 8 +--- seam/client_sessions.py | 28 ++++---------- seam/connect_webviews.py | 16 ++------ seam/connected_accounts.py | 16 ++------ seam/devices.py | 16 +++----- seam/devices_simulate.py | 4 +- seam/devices_unmanaged.py | 12 ++---- seam/events.py | 8 +--- seam/locks.py | 16 ++------ seam/networks.py | 8 +--- seam/noise_sensors_noise_thresholds.py | 10 ++--- seam/noise_sensors_simulate.py | 2 +- seam/phones.py | 8 +--- seam/phones_simulate.py | 2 +- seam/seam.py | 2 +- seam/thermostats.py | 32 ++++------------ seam/thermostats_climate_setting_schedules.py | 10 ++--- seam/user_identities.py | 38 ++++++------------- .../user_identities_enrollment_automations.py | 8 ++-- seam/webhooks.py | 20 +++------- seam/workspaces.py | 16 ++------ test/workspaces/test_workspaces_create.py | 22 +++++------ 33 files changed, 139 insertions(+), 328 deletions(-) diff --git a/seam/__init__.py b/seam/__init__.py index 8303af0..4a184c7 100644 --- a/seam/__init__.py +++ b/seam/__init__.py @@ -2,6 +2,7 @@ # type: ignore from seam.seam import Seam + # from seam.types import SeamApiException # from seam.seam_multi_workspace import SeamMultiWorkspace # from seam.options import SeamHttpInvalidOptionsError diff --git a/seam/access_codes.py b/seam/access_codes.py index 5bbb69a..d06399c 100644 --- a/seam/access_codes.py +++ b/seam/access_codes.py @@ -78,9 +78,7 @@ def create( if use_offline_access_code is not None: json_payload["use_offline_access_code"] = use_offline_access_code - res = self.seam.client.post( - self.seam.endpoint + "/access_codes/create", json=json_payload - ) + res = self.seam.client.post("/access_codes/create", json=json_payload) return AccessCode.from_dict(res["access_code"]) @@ -143,9 +141,7 @@ def create_multiple( if use_offline_access_code is not None: json_payload["use_offline_access_code"] = use_offline_access_code - res = self.seam.client.post( - self.seam.endpoint + "/access_codes/create_multiple", json=json_payload - ) + res = self.seam.client.post("/access_codes/create_multiple", json=json_payload) return [AccessCode.from_dict(item) for item in res["access_codes"]] @@ -165,9 +161,7 @@ def delete( if sync is not None: json_payload["sync"] = sync - self.seam.client.post( - self.seam.endpoint + "/access_codes/delete", json=json_payload - ) + self.seam.client.post("/access_codes/delete", json=json_payload) return None @@ -177,9 +171,7 @@ def generate_code(self, *, device_id: str) -> AccessCode: if device_id is not None: json_payload["device_id"] = device_id - res = self.seam.client.post( - self.seam.endpoint + "/access_codes/generate_code", json=json_payload - ) + res = self.seam.client.post("/access_codes/generate_code", json=json_payload) return AccessCode.from_dict(res["generated_code"]) @@ -199,9 +191,7 @@ def get( if device_id is not None: json_payload["device_id"] = device_id - res = self.seam.client.post( - self.seam.endpoint + "/access_codes/get", json=json_payload - ) + res = self.seam.client.post("/access_codes/get", json=json_payload) return AccessCode.from_dict(res["access_code"]) @@ -221,9 +211,7 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.client.post( - self.seam.endpoint + "/access_codes/list", json=json_payload - ) + res = self.seam.client.post("/access_codes/list", json=json_payload) return [AccessCode.from_dict(item) for item in res["access_codes"]] @@ -234,7 +222,7 @@ def pull_backup_access_code(self, *, access_code_id: str) -> AccessCode: json_payload["access_code_id"] = access_code_id res = self.seam.client.post( - self.seam.endpoint + "/access_codes/pull_backup_access_code", + "/access_codes/pull_backup_access_code", json=json_payload, ) @@ -303,8 +291,6 @@ def update( if use_offline_access_code is not None: json_payload["use_offline_access_code"] = use_offline_access_code - self.seam.client.post( - self.seam.endpoint + "/access_codes/update", json=json_payload - ) + self.seam.client.post("/access_codes/update", json=json_payload) return None diff --git a/seam/access_codes_simulate.py b/seam/access_codes_simulate.py index d202a6d..1c69b8e 100644 --- a/seam/access_codes_simulate.py +++ b/seam/access_codes_simulate.py @@ -22,7 +22,7 @@ def create_unmanaged_access_code( json_payload["name"] = name res = self.seam.client.post( - self.seam.endpoint + "/access_codes/simulate/create_unmanaged_access_code", + "/access_codes/simulate/create_unmanaged_access_code", json=json_payload, ) diff --git a/seam/access_codes_unmanaged.py b/seam/access_codes_unmanaged.py index 58a7b0b..62b3c12 100644 --- a/seam/access_codes_unmanaged.py +++ b/seam/access_codes_unmanaged.py @@ -34,7 +34,7 @@ def convert_to_managed( json_payload["sync"] = sync self.seam.client.post( - self.seam.endpoint + "/access_codes/unmanaged/convert_to_managed", + "/access_codes/unmanaged/convert_to_managed", json=json_payload, ) @@ -48,9 +48,7 @@ def delete(self, *, access_code_id: str, sync: Optional[bool] = None) -> None: if sync is not None: json_payload["sync"] = sync - self.seam.client.post( - self.seam.endpoint + "/access_codes/unmanaged/delete", json=json_payload - ) + self.seam.client.post("/access_codes/unmanaged/delete", json=json_payload) return None @@ -70,9 +68,7 @@ def get( if device_id is not None: json_payload["device_id"] = device_id - res = self.seam.client.post( - self.seam.endpoint + "/access_codes/unmanaged/get", json=json_payload - ) + res = self.seam.client.post("/access_codes/unmanaged/get", json=json_payload) return UnmanagedAccessCode.from_dict(res["access_code"]) @@ -86,9 +82,7 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.client.post( - self.seam.endpoint + "/access_codes/unmanaged/list", json=json_payload - ) + res = self.seam.client.post("/access_codes/unmanaged/list", json=json_payload) return [UnmanagedAccessCode.from_dict(item) for item in res["access_codes"]] @@ -116,8 +110,6 @@ def update( is_external_modification_allowed ) - self.seam.client.post( - self.seam.endpoint + "/access_codes/unmanaged/update", json=json_payload - ) + self.seam.client.post("/access_codes/unmanaged/update", json=json_payload) return None diff --git a/seam/acs_access_groups.py b/seam/acs_access_groups.py index 4691b20..725b9d4 100644 --- a/seam/acs_access_groups.py +++ b/seam/acs_access_groups.py @@ -17,9 +17,7 @@ def add_user(self, *, acs_access_group_id: str, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.client.post( - self.seam.endpoint + "/acs/access_groups/add_user", json=json_payload - ) + self.seam.client.post("/acs/access_groups/add_user", json=json_payload) return None @@ -29,9 +27,7 @@ def get(self, *, acs_access_group_id: str) -> AcsAccessGroup: if acs_access_group_id is not None: json_payload["acs_access_group_id"] = acs_access_group_id - res = self.seam.client.post( - self.seam.endpoint + "/acs/access_groups/get", json=json_payload - ) + res = self.seam.client.post("/acs/access_groups/get", json=json_payload) return AcsAccessGroup.from_dict(res["acs_access_group"]) @@ -45,9 +41,7 @@ def list( if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - res = self.seam.client.post( - self.seam.endpoint + "/acs/access_groups/list", json=json_payload - ) + res = self.seam.client.post("/acs/access_groups/list", json=json_payload) return [AcsAccessGroup.from_dict(item) for item in res["acs_access_groups"]] @@ -57,9 +51,7 @@ def list_users(self, *, acs_access_group_id: str) -> List[AcsUser]: if acs_access_group_id is not None: json_payload["acs_access_group_id"] = acs_access_group_id - res = self.seam.client.post( - self.seam.endpoint + "/acs/access_groups/list_users", json=json_payload - ) + res = self.seam.client.post("/acs/access_groups/list_users", json=json_payload) return [AcsUser.from_dict(item) for item in res["acs_users"]] @@ -71,8 +63,6 @@ def remove_user(self, *, acs_access_group_id: str, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.client.post( - self.seam.endpoint + "/acs/access_groups/remove_user", json=json_payload - ) + self.seam.client.post("/acs/access_groups/remove_user", json=json_payload) return None diff --git a/seam/acs_credential_pools.py b/seam/acs_credential_pools.py index cd809b0..3de3b32 100644 --- a/seam/acs_credential_pools.py +++ b/seam/acs_credential_pools.py @@ -15,9 +15,7 @@ def list(self, *, acs_system_id: str) -> List[AcsCredentialPool]: if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id - res = self.seam.client.post( - self.seam.endpoint + "/acs/credential_pools/list", json=json_payload - ) + res = self.seam.client.post("/acs/credential_pools/list", json=json_payload) return [ AcsCredentialPool.from_dict(item) for item in res["acs_credential_pools"] diff --git a/seam/acs_credential_provisioning_automations.py b/seam/acs_credential_provisioning_automations.py index 42419ba..0f3d1da 100644 --- a/seam/acs_credential_provisioning_automations.py +++ b/seam/acs_credential_provisioning_automations.py @@ -43,7 +43,7 @@ def launch( ) res = self.seam.client.post( - self.seam.endpoint + "/acs/credential_provisioning_automations/launch", + "/acs/credential_provisioning_automations/launch", json=json_payload, ) diff --git a/seam/acs_credentials.py b/seam/acs_credentials.py index 887070a..9b31275 100644 --- a/seam/acs_credentials.py +++ b/seam/acs_credentials.py @@ -17,9 +17,7 @@ def assign(self, *, acs_credential_id: str, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.client.post( - self.seam.endpoint + "/acs/credentials/assign", json=json_payload - ) + self.seam.client.post("/acs/credentials/assign", json=json_payload) return None @@ -61,9 +59,7 @@ def create( if visionline_metadata is not None: json_payload["visionline_metadata"] = visionline_metadata - res = self.seam.client.post( - self.seam.endpoint + "/acs/credentials/create", json=json_payload - ) + res = self.seam.client.post("/acs/credentials/create", json=json_payload) return AcsCredential.from_dict(res["acs_credential"]) @@ -73,9 +69,7 @@ def delete(self, *, acs_credential_id: str) -> None: if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id - self.seam.client.post( - self.seam.endpoint + "/acs/credentials/delete", json=json_payload - ) + self.seam.client.post("/acs/credentials/delete", json=json_payload) return None @@ -85,9 +79,7 @@ def get(self, *, acs_credential_id: str) -> AcsCredential: if acs_credential_id is not None: json_payload["acs_credential_id"] = acs_credential_id - res = self.seam.client.post( - self.seam.endpoint + "/acs/credentials/get", json=json_payload - ) + res = self.seam.client.post("/acs/credentials/get", json=json_payload) return AcsCredential.from_dict(res["acs_credential"]) @@ -112,9 +104,7 @@ def list( is_multi_phone_sync_credential ) - res = self.seam.client.post( - self.seam.endpoint + "/acs/credentials/list", json=json_payload - ) + res = self.seam.client.post("/acs/credentials/list", json=json_payload) return [AcsCredential.from_dict(item) for item in res["acs_credentials"]] @@ -126,9 +116,7 @@ def unassign(self, *, acs_credential_id: str, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.client.post( - self.seam.endpoint + "/acs/credentials/unassign", json=json_payload - ) + self.seam.client.post("/acs/credentials/unassign", json=json_payload) return None @@ -148,8 +136,6 @@ def update( if ends_at is not None: json_payload["ends_at"] = ends_at - self.seam.client.post( - self.seam.endpoint + "/acs/credentials/update", json=json_payload - ) + self.seam.client.post("/acs/credentials/update", json=json_payload) return None diff --git a/seam/acs_entrances.py b/seam/acs_entrances.py index 3b3ff54..85f24ae 100644 --- a/seam/acs_entrances.py +++ b/seam/acs_entrances.py @@ -15,9 +15,7 @@ def get(self, *, acs_entrance_id: str) -> AcsEntrance: if acs_entrance_id is not None: json_payload["acs_entrance_id"] = acs_entrance_id - res = self.seam.client.post( - self.seam.endpoint + "/acs/entrances/get", json=json_payload - ) + res = self.seam.client.post("/acs/entrances/get", json=json_payload) return AcsEntrance.from_dict(res["acs_entrance"]) @@ -29,9 +27,7 @@ def grant_access(self, *, acs_entrance_id: str, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.client.post( - self.seam.endpoint + "/acs/entrances/grant_access", json=json_payload - ) + self.seam.client.post("/acs/entrances/grant_access", json=json_payload) return None @@ -48,9 +44,7 @@ def list( if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id - res = self.seam.client.post( - self.seam.endpoint + "/acs/entrances/list", json=json_payload - ) + res = self.seam.client.post("/acs/entrances/list", json=json_payload) return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] @@ -65,7 +59,7 @@ def list_credentials_with_access( json_payload["include_if"] = include_if res = self.seam.client.post( - self.seam.endpoint + "/acs/entrances/list_credentials_with_access", + "/acs/entrances/list_credentials_with_access", json=json_payload, ) diff --git a/seam/acs_systems.py b/seam/acs_systems.py index 97ea818..a68d463 100644 --- a/seam/acs_systems.py +++ b/seam/acs_systems.py @@ -15,9 +15,7 @@ def get(self, *, acs_system_id: str) -> AcsSystem: if acs_system_id is not None: json_payload["acs_system_id"] = acs_system_id - res = self.seam.client.post( - self.seam.endpoint + "/acs/systems/get", json=json_payload - ) + res = self.seam.client.post("/acs/systems/get", json=json_payload) return AcsSystem.from_dict(res["acs_system"]) @@ -27,9 +25,7 @@ def list(self, *, connected_account_id: Optional[str] = None) -> List[AcsSystem] if connected_account_id is not None: json_payload["connected_account_id"] = connected_account_id - res = self.seam.client.post( - self.seam.endpoint + "/acs/systems/list", json=json_payload - ) + res = self.seam.client.post("/acs/systems/list", json=json_payload) return [AcsSystem.from_dict(item) for item in res["acs_systems"]] diff --git a/seam/acs_users.py b/seam/acs_users.py index 0607368..42da658 100644 --- a/seam/acs_users.py +++ b/seam/acs_users.py @@ -19,9 +19,7 @@ def add_to_access_group( if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.client.post( - self.seam.endpoint + "/acs/users/add_to_access_group", json=json_payload - ) + self.seam.client.post("/acs/users/add_to_access_group", json=json_payload) return None @@ -56,9 +54,7 @@ def create( if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - res = self.seam.client.post( - self.seam.endpoint + "/acs/users/create", json=json_payload - ) + res = self.seam.client.post("/acs/users/create", json=json_payload) return AcsUser.from_dict(res["acs_user"]) @@ -68,9 +64,7 @@ def delete(self, *, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.client.post( - self.seam.endpoint + "/acs/users/delete", json=json_payload - ) + self.seam.client.post("/acs/users/delete", json=json_payload) return None @@ -80,9 +74,7 @@ def get(self, *, acs_user_id: str) -> AcsUser: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - res = self.seam.client.post( - self.seam.endpoint + "/acs/users/get", json=json_payload - ) + res = self.seam.client.post("/acs/users/get", json=json_payload) return AcsUser.from_dict(res["acs_user"]) @@ -105,9 +97,7 @@ def list( if user_identity_phone_number is not None: json_payload["user_identity_phone_number"] = user_identity_phone_number - res = self.seam.client.post( - self.seam.endpoint + "/acs/users/list", json=json_payload - ) + res = self.seam.client.post("/acs/users/list", json=json_payload) return [AcsUser.from_dict(item) for item in res["acs_users"]] @@ -118,7 +108,7 @@ def list_accessible_entrances(self, *, acs_user_id: str) -> List[AcsEntrance]: json_payload["acs_user_id"] = acs_user_id res = self.seam.client.post( - self.seam.endpoint + "/acs/users/list_accessible_entrances", + "/acs/users/list_accessible_entrances", json=json_payload, ) @@ -135,7 +125,7 @@ def remove_from_access_group( json_payload["acs_user_id"] = acs_user_id self.seam.client.post( - self.seam.endpoint + "/acs/users/remove_from_access_group", + "/acs/users/remove_from_access_group", json=json_payload, ) @@ -148,7 +138,7 @@ def revoke_access_to_all_entrances(self, *, acs_user_id: str) -> None: json_payload["acs_user_id"] = acs_user_id self.seam.client.post( - self.seam.endpoint + "/acs/users/revoke_access_to_all_entrances", + "/acs/users/revoke_access_to_all_entrances", json=json_payload, ) @@ -160,9 +150,7 @@ def suspend(self, *, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.client.post( - self.seam.endpoint + "/acs/users/suspend", json=json_payload - ) + self.seam.client.post("/acs/users/suspend", json=json_payload) return None @@ -172,9 +160,7 @@ def unsuspend(self, *, acs_user_id: str) -> None: if acs_user_id is not None: json_payload["acs_user_id"] = acs_user_id - self.seam.client.post( - self.seam.endpoint + "/acs/users/unsuspend", json=json_payload - ) + self.seam.client.post("/acs/users/unsuspend", json=json_payload) return None @@ -206,8 +192,6 @@ def update( if phone_number is not None: json_payload["phone_number"] = phone_number - self.seam.client.post( - self.seam.endpoint + "/acs/users/update", json=json_payload - ) + self.seam.client.post("/acs/users/update", json=json_payload) return None diff --git a/seam/action_attempts.py b/seam/action_attempts.py index 0881fbc..d0a8fbd 100644 --- a/seam/action_attempts.py +++ b/seam/action_attempts.py @@ -43,9 +43,7 @@ def get( if action_attempt_id is not None: json_payload["action_attempt_id"] = action_attempt_id - res = self.seam.client.post( - self.seam.endpoint + "/action_attempts/get", json=json_payload - ) + res = self.seam.client.post("/action_attempts/get", json=json_payload) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), @@ -58,9 +56,7 @@ def list(self, *, action_attempt_ids: List[str]) -> List[ActionAttempt]: if action_attempt_ids is not None: json_payload["action_attempt_ids"] = action_attempt_ids - res = self.seam.client.post( - self.seam.endpoint + "/action_attempts/list", json=json_payload - ) + res = self.seam.client.post("/action_attempts/list", json=json_payload) return [ActionAttempt.from_dict(item) for item in res["action_attempts"]] diff --git a/seam/client_sessions.py b/seam/client_sessions.py index 7953f20..bc78838 100644 --- a/seam/client_sessions.py +++ b/seam/client_sessions.py @@ -31,9 +31,7 @@ def create( if user_identity_ids is not None: json_payload["user_identity_ids"] = user_identity_ids - res = self.seam.client.post( - self.seam.endpoint + "/client_sessions/create", json=json_payload - ) + res = self.seam.client.post("/client_sessions/create", json=json_payload) return ClientSession.from_dict(res["client_session"]) @@ -43,9 +41,7 @@ def delete(self, *, client_session_id: str) -> None: if client_session_id is not None: json_payload["client_session_id"] = client_session_id - self.seam.client.post( - self.seam.endpoint + "/client_sessions/delete", json=json_payload - ) + self.seam.client.post("/client_sessions/delete", json=json_payload) return None @@ -62,9 +58,7 @@ def get( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.client.post( - self.seam.endpoint + "/client_sessions/get", json=json_payload - ) + res = self.seam.client.post("/client_sessions/get", json=json_payload) return ClientSession.from_dict(res["client_session"]) @@ -90,9 +84,7 @@ def get_or_create( if user_identity_ids is not None: json_payload["user_identity_ids"] = user_identity_ids - res = self.seam.client.post( - self.seam.endpoint + "/client_sessions/get_or_create", json=json_payload - ) + res = self.seam.client.post("/client_sessions/get_or_create", json=json_payload) return ClientSession.from_dict(res["client_session"]) @@ -118,9 +110,7 @@ def grant_access( if user_identity_ids is not None: json_payload["user_identity_ids"] = user_identity_ids - self.seam.client.post( - self.seam.endpoint + "/client_sessions/grant_access", json=json_payload - ) + self.seam.client.post("/client_sessions/grant_access", json=json_payload) return None @@ -146,9 +136,7 @@ def list( if without_user_identifier_key is not None: json_payload["without_user_identifier_key"] = without_user_identifier_key - res = self.seam.client.post( - self.seam.endpoint + "/client_sessions/list", json=json_payload - ) + res = self.seam.client.post("/client_sessions/list", json=json_payload) return [ClientSession.from_dict(item) for item in res["client_sessions"]] @@ -158,8 +146,6 @@ def revoke(self, *, client_session_id: str) -> None: if client_session_id is not None: json_payload["client_session_id"] = client_session_id - self.seam.client.post( - self.seam.endpoint + "/client_sessions/revoke", json=json_payload - ) + self.seam.client.post("/client_sessions/revoke", json=json_payload) return None diff --git a/seam/connect_webviews.py b/seam/connect_webviews.py index ddde7fc..910a809 100644 --- a/seam/connect_webviews.py +++ b/seam/connect_webviews.py @@ -42,9 +42,7 @@ def create( if wait_for_device_creation is not None: json_payload["wait_for_device_creation"] = wait_for_device_creation - res = self.seam.client.post( - self.seam.endpoint + "/connect_webviews/create", json=json_payload - ) + res = self.seam.client.post("/connect_webviews/create", json=json_payload) return ConnectWebview.from_dict(res["connect_webview"]) @@ -54,9 +52,7 @@ def delete(self, *, connect_webview_id: str) -> None: if connect_webview_id is not None: json_payload["connect_webview_id"] = connect_webview_id - self.seam.client.post( - self.seam.endpoint + "/connect_webviews/delete", json=json_payload - ) + self.seam.client.post("/connect_webviews/delete", json=json_payload) return None @@ -66,9 +62,7 @@ def get(self, *, connect_webview_id: str) -> ConnectWebview: if connect_webview_id is not None: json_payload["connect_webview_id"] = connect_webview_id - res = self.seam.client.post( - self.seam.endpoint + "/connect_webviews/get", json=json_payload - ) + res = self.seam.client.post("/connect_webviews/get", json=json_payload) return ConnectWebview.from_dict(res["connect_webview"]) @@ -85,8 +79,6 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.client.post( - self.seam.endpoint + "/connect_webviews/list", json=json_payload - ) + res = self.seam.client.post("/connect_webviews/list", json=json_payload) return [ConnectWebview.from_dict(item) for item in res["connect_webviews"]] diff --git a/seam/connected_accounts.py b/seam/connected_accounts.py index 93fce0f..23805eb 100644 --- a/seam/connected_accounts.py +++ b/seam/connected_accounts.py @@ -17,9 +17,7 @@ def delete(self, *, connected_account_id: str, sync: Optional[bool] = None) -> N if sync is not None: json_payload["sync"] = sync - self.seam.client.post( - self.seam.endpoint + "/connected_accounts/delete", json=json_payload - ) + self.seam.client.post("/connected_accounts/delete", json=json_payload) return None @@ -33,9 +31,7 @@ def get( if email is not None: json_payload["email"] = email - res = self.seam.client.post( - self.seam.endpoint + "/connected_accounts/get", json=json_payload - ) + res = self.seam.client.post("/connected_accounts/get", json=json_payload) return ConnectedAccount.from_dict(res["connected_account"]) @@ -47,9 +43,7 @@ def list( if custom_metadata_has is not None: json_payload["custom_metadata_has"] = custom_metadata_has - res = self.seam.client.post( - self.seam.endpoint + "/connected_accounts/list", json=json_payload - ) + res = self.seam.client.post("/connected_accounts/list", json=json_payload) return [ConnectedAccount.from_dict(item) for item in res["connected_accounts"]] @@ -71,8 +65,6 @@ def update( if custom_metadata is not None: json_payload["custom_metadata"] = custom_metadata - res = self.seam.client.post( - self.seam.endpoint + "/connected_accounts/update", json=json_payload - ) + res = self.seam.client.post("/connected_accounts/update", json=json_payload) return ConnectedAccount.from_dict(res["connected_account"]) diff --git a/seam/devices.py b/seam/devices.py index 00206f8..7a11239 100644 --- a/seam/devices.py +++ b/seam/devices.py @@ -27,7 +27,7 @@ def delete(self, *, device_id: str) -> None: if device_id is not None: json_payload["device_id"] = device_id - self.seam.client.post(self.seam.endpoint + "/devices/delete", json=json_payload) + self.seam.client.post("/devices/delete", json=json_payload) return None @@ -41,9 +41,7 @@ def get( if name is not None: json_payload["name"] = name - res = self.seam.client.post( - self.seam.endpoint + "/devices/get", json=json_payload - ) + res = self.seam.client.post("/devices/get", json=json_payload) return Device.from_dict(res["device"]) @@ -93,9 +91,7 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.client.post( - self.seam.endpoint + "/devices/list", json=json_payload - ) + res = self.seam.client.post("/devices/list", json=json_payload) return [Device.from_dict(item) for item in res["devices"]] @@ -107,9 +103,7 @@ def list_device_providers( if provider_category is not None: json_payload["provider_category"] = provider_category - res = self.seam.client.post( - self.seam.endpoint + "/devices/list_device_providers", json=json_payload - ) + res = self.seam.client.post("/devices/list_device_providers", json=json_payload) return [DeviceProvider.from_dict(item) for item in res["device_providers"]] @@ -135,6 +129,6 @@ def update( if properties is not None: json_payload["properties"] = properties - self.seam.client.post(self.seam.endpoint + "/devices/update", json=json_payload) + self.seam.client.post("/devices/update", json=json_payload) return None diff --git a/seam/devices_simulate.py b/seam/devices_simulate.py index 163b9f1..745aaed 100644 --- a/seam/devices_simulate.py +++ b/seam/devices_simulate.py @@ -15,8 +15,6 @@ def remove(self, *, device_id: str) -> None: if device_id is not None: json_payload["device_id"] = device_id - self.seam.client.post( - self.seam.endpoint + "/devices/simulate/remove", json=json_payload - ) + self.seam.client.post("/devices/simulate/remove", json=json_payload) return None diff --git a/seam/devices_unmanaged.py b/seam/devices_unmanaged.py index dba76f9..ad7fcb1 100644 --- a/seam/devices_unmanaged.py +++ b/seam/devices_unmanaged.py @@ -19,9 +19,7 @@ def get( if name is not None: json_payload["name"] = name - res = self.seam.client.post( - self.seam.endpoint + "/devices/unmanaged/get", json=json_payload - ) + res = self.seam.client.post("/devices/unmanaged/get", json=json_payload) return UnmanagedDevice.from_dict(res["device"]) @@ -71,9 +69,7 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.client.post( - self.seam.endpoint + "/devices/unmanaged/list", json=json_payload - ) + res = self.seam.client.post("/devices/unmanaged/list", json=json_payload) return [UnmanagedDevice.from_dict(item) for item in res["devices"]] @@ -85,8 +81,6 @@ def update(self, *, device_id: str, is_managed: bool) -> None: if is_managed is not None: json_payload["is_managed"] = is_managed - self.seam.client.post( - self.seam.endpoint + "/devices/unmanaged/update", json=json_payload - ) + self.seam.client.post("/devices/unmanaged/update", json=json_payload) return None diff --git a/seam/events.py b/seam/events.py index dfdae79..c7e4b44 100644 --- a/seam/events.py +++ b/seam/events.py @@ -25,9 +25,7 @@ def get( if event_type is not None: json_payload["event_type"] = event_type - res = self.seam.client.post( - self.seam.endpoint + "/events/get", json=json_payload - ) + res = self.seam.client.post("/events/get", json=json_payload) return Event.from_dict(res["event"]) @@ -68,8 +66,6 @@ def list( if since is not None: json_payload["since"] = since - res = self.seam.client.post( - self.seam.endpoint + "/events/list", json=json_payload - ) + res = self.seam.client.post("/events/list", json=json_payload) return [Event.from_dict(item) for item in res["events"]] diff --git a/seam/locks.py b/seam/locks.py index d1d52dc..7ab4cb2 100644 --- a/seam/locks.py +++ b/seam/locks.py @@ -19,9 +19,7 @@ def get( if name is not None: json_payload["name"] = name - res = self.seam.client.post( - self.seam.endpoint + "/locks/get", json=json_payload - ) + res = self.seam.client.post("/locks/get", json=json_payload) return Device.from_dict(res["device"]) @@ -71,9 +69,7 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.client.post( - self.seam.endpoint + "/locks/list", json=json_payload - ) + res = self.seam.client.post("/locks/list", json=json_payload) return [Device.from_dict(item) for item in res["devices"]] @@ -91,9 +87,7 @@ def lock_door( if sync is not None: json_payload["sync"] = sync - res = self.seam.client.post( - self.seam.endpoint + "/locks/lock_door", json=json_payload - ) + res = self.seam.client.post("/locks/lock_door", json=json_payload) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), @@ -114,9 +108,7 @@ def unlock_door( if sync is not None: json_payload["sync"] = sync - res = self.seam.client.post( - self.seam.endpoint + "/locks/unlock_door", json=json_payload - ) + res = self.seam.client.post("/locks/unlock_door", json=json_payload) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), diff --git a/seam/networks.py b/seam/networks.py index fac0031..905c0c0 100644 --- a/seam/networks.py +++ b/seam/networks.py @@ -15,9 +15,7 @@ def get(self, *, network_id: str) -> Network: if network_id is not None: json_payload["network_id"] = network_id - res = self.seam.client.post( - self.seam.endpoint + "/networks/get", json=json_payload - ) + res = self.seam.client.post("/networks/get", json=json_payload) return Network.from_dict(res["network"]) @@ -26,8 +24,6 @@ def list( ) -> List[Network]: json_payload = {} - res = self.seam.client.post( - self.seam.endpoint + "/networks/list", json=json_payload - ) + res = self.seam.client.post("/networks/list", json=json_payload) return [Network.from_dict(item) for item in res["networks"]] diff --git a/seam/noise_sensors_noise_thresholds.py b/seam/noise_sensors_noise_thresholds.py index 462b142..0873e30 100644 --- a/seam/noise_sensors_noise_thresholds.py +++ b/seam/noise_sensors_noise_thresholds.py @@ -38,7 +38,7 @@ def create( json_payload["sync"] = sync res = self.seam.client.post( - self.seam.endpoint + "/noise_sensors/noise_thresholds/create", + "/noise_sensors/noise_thresholds/create", json=json_payload, ) @@ -57,7 +57,7 @@ def delete( json_payload["sync"] = sync self.seam.client.post( - self.seam.endpoint + "/noise_sensors/noise_thresholds/delete", + "/noise_sensors/noise_thresholds/delete", json=json_payload, ) @@ -70,7 +70,7 @@ def get(self, *, noise_threshold_id: str) -> NoiseThreshold: json_payload["noise_threshold_id"] = noise_threshold_id res = self.seam.client.post( - self.seam.endpoint + "/noise_sensors/noise_thresholds/get", + "/noise_sensors/noise_thresholds/get", json=json_payload, ) @@ -87,7 +87,7 @@ def list( json_payload["is_programmed"] = is_programmed res = self.seam.client.post( - self.seam.endpoint + "/noise_sensors/noise_thresholds/list", + "/noise_sensors/noise_thresholds/list", json=json_payload, ) @@ -125,7 +125,7 @@ def update( json_payload["sync"] = sync self.seam.client.post( - self.seam.endpoint + "/noise_sensors/noise_thresholds/update", + "/noise_sensors/noise_thresholds/update", json=json_payload, ) diff --git a/seam/noise_sensors_simulate.py b/seam/noise_sensors_simulate.py index 18dc32e..872e0dc 100644 --- a/seam/noise_sensors_simulate.py +++ b/seam/noise_sensors_simulate.py @@ -16,7 +16,7 @@ def trigger_noise_threshold(self, *, device_id: str) -> None: json_payload["device_id"] = device_id self.seam.client.post( - self.seam.endpoint + "/noise_sensors/simulate/trigger_noise_threshold", + "/noise_sensors/simulate/trigger_noise_threshold", json=json_payload, ) diff --git a/seam/phones.py b/seam/phones.py index a8f6eee..1bd41a8 100644 --- a/seam/phones.py +++ b/seam/phones.py @@ -21,9 +21,7 @@ def deactivate(self, *, device_id: str) -> None: if device_id is not None: json_payload["device_id"] = device_id - self.seam.client.post( - self.seam.endpoint + "/phones/deactivate", json=json_payload - ) + self.seam.client.post("/phones/deactivate", json=json_payload) return None @@ -33,8 +31,6 @@ def list(self, *, owner_user_identity_id: Optional[str] = None) -> List[Phone]: if owner_user_identity_id is not None: json_payload["owner_user_identity_id"] = owner_user_identity_id - res = self.seam.client.post( - self.seam.endpoint + "/phones/list", json=json_payload - ) + res = self.seam.client.post("/phones/list", json=json_payload) return [Phone.from_dict(item) for item in res["phones"]] diff --git a/seam/phones_simulate.py b/seam/phones_simulate.py index 5c38995..308a2dc 100644 --- a/seam/phones_simulate.py +++ b/seam/phones_simulate.py @@ -29,7 +29,7 @@ def create_sandbox_phone( json_payload["phone_metadata"] = phone_metadata res = self.seam.client.post( - self.seam.endpoint + "/phones/simulate/create_sandbox_phone", + "/phones/simulate/create_sandbox_phone", json=json_payload, ) diff --git a/seam/seam.py b/seam/seam.py index 4ef40b5..80aa25d 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -4,7 +4,7 @@ from seam.constants import LTS_VERSION from seam.parse_options import parse_options -from seam.request import SeamHttpClient +from seam.request import SeamHttpClient from seam.routes import Routes from seam.types import AbstractSeam diff --git a/seam/thermostats.py b/seam/thermostats.py index 1fcf710..79ade8d 100644 --- a/seam/thermostats.py +++ b/seam/thermostats.py @@ -37,9 +37,7 @@ def cool( if sync is not None: json_payload["sync"] = sync - res = self.seam.client.post( - self.seam.endpoint + "/thermostats/cool", json=json_payload - ) + res = self.seam.client.post("/thermostats/cool", json=json_payload) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), @@ -56,9 +54,7 @@ def get( if name is not None: json_payload["name"] = name - res = self.seam.client.post( - self.seam.endpoint + "/thermostats/get", json=json_payload - ) + res = self.seam.client.post("/thermostats/get", json=json_payload) return Device.from_dict(res["thermostat"]) @@ -82,9 +78,7 @@ def heat( if sync is not None: json_payload["sync"] = sync - res = self.seam.client.post( - self.seam.endpoint + "/thermostats/heat", json=json_payload - ) + res = self.seam.client.post("/thermostats/heat", json=json_payload) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), @@ -117,9 +111,7 @@ def heat_cool( if sync is not None: json_payload["sync"] = sync - res = self.seam.client.post( - self.seam.endpoint + "/thermostats/heat_cool", json=json_payload - ) + res = self.seam.client.post("/thermostats/heat_cool", json=json_payload) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), @@ -172,9 +164,7 @@ def list( if user_identifier_key is not None: json_payload["user_identifier_key"] = user_identifier_key - res = self.seam.client.post( - self.seam.endpoint + "/thermostats/list", json=json_payload - ) + res = self.seam.client.post("/thermostats/list", json=json_payload) return [Device.from_dict(item) for item in res["thermostats"]] @@ -192,9 +182,7 @@ def off( if sync is not None: json_payload["sync"] = sync - res = self.seam.client.post( - self.seam.endpoint + "/thermostats/off", json=json_payload - ) + res = self.seam.client.post("/thermostats/off", json=json_payload) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), @@ -221,9 +209,7 @@ def set_fan_mode( if sync is not None: json_payload["sync"] = sync - res = self.seam.client.post( - self.seam.endpoint + "/thermostats/set_fan_mode", json=json_payload - ) + res = self.seam.client.post("/thermostats/set_fan_mode", json=json_payload) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), @@ -240,8 +226,6 @@ def update( if device_id is not None: json_payload["device_id"] = device_id - self.seam.client.post( - self.seam.endpoint + "/thermostats/update", json=json_payload - ) + self.seam.client.post("/thermostats/update", json=json_payload) return None diff --git a/seam/thermostats_climate_setting_schedules.py b/seam/thermostats_climate_setting_schedules.py index e4a9271..1de390b 100644 --- a/seam/thermostats_climate_setting_schedules.py +++ b/seam/thermostats_climate_setting_schedules.py @@ -59,7 +59,7 @@ def create( json_payload["schedule_type"] = schedule_type res = self.seam.client.post( - self.seam.endpoint + "/thermostats/climate_setting_schedules/create", + "/thermostats/climate_setting_schedules/create", json=json_payload, ) @@ -72,7 +72,7 @@ def delete(self, *, climate_setting_schedule_id: str) -> None: json_payload["climate_setting_schedule_id"] = climate_setting_schedule_id self.seam.client.post( - self.seam.endpoint + "/thermostats/climate_setting_schedules/delete", + "/thermostats/climate_setting_schedules/delete", json=json_payload, ) @@ -92,7 +92,7 @@ def get( json_payload["device_id"] = device_id res = self.seam.client.post( - self.seam.endpoint + "/thermostats/climate_setting_schedules/get", + "/thermostats/climate_setting_schedules/get", json=json_payload, ) @@ -109,7 +109,7 @@ def list( json_payload["user_identifier_key"] = user_identifier_key res = self.seam.client.post( - self.seam.endpoint + "/thermostats/climate_setting_schedules/list", + "/thermostats/climate_setting_schedules/list", json=json_payload, ) @@ -165,7 +165,7 @@ def update( json_payload["schedule_type"] = schedule_type self.seam.client.post( - self.seam.endpoint + "/thermostats/climate_setting_schedules/update", + "/thermostats/climate_setting_schedules/update", json=json_payload, ) diff --git a/seam/user_identities.py b/seam/user_identities.py index ed1461d..813ec15 100644 --- a/seam/user_identities.py +++ b/seam/user_identities.py @@ -25,9 +25,7 @@ def add_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - self.seam.client.post( - self.seam.endpoint + "/user_identities/add_acs_user", json=json_payload - ) + self.seam.client.post("/user_identities/add_acs_user", json=json_payload) return None @@ -50,9 +48,7 @@ def create( if user_identity_key is not None: json_payload["user_identity_key"] = user_identity_key - res = self.seam.client.post( - self.seam.endpoint + "/user_identities/create", json=json_payload - ) + res = self.seam.client.post("/user_identities/create", json=json_payload) return UserIdentity.from_dict(res["user_identity"]) @@ -62,9 +58,7 @@ def delete(self, *, user_identity_id: str) -> None: if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - self.seam.client.post( - self.seam.endpoint + "/user_identities/delete", json=json_payload - ) + self.seam.client.post("/user_identities/delete", json=json_payload) return None @@ -81,9 +75,7 @@ def get( if user_identity_key is not None: json_payload["user_identity_key"] = user_identity_key - res = self.seam.client.post( - self.seam.endpoint + "/user_identities/get", json=json_payload - ) + res = self.seam.client.post("/user_identities/get", json=json_payload) return UserIdentity.from_dict(res["user_identity"]) @@ -96,7 +88,7 @@ def grant_access_to_device(self, *, device_id: str, user_identity_id: str) -> No json_payload["user_identity_id"] = user_identity_id self.seam.client.post( - self.seam.endpoint + "/user_identities/grant_access_to_device", + "/user_identities/grant_access_to_device", json=json_payload, ) @@ -112,9 +104,7 @@ def list( credential_manager_acs_system_id ) - res = self.seam.client.post( - self.seam.endpoint + "/user_identities/list", json=json_payload - ) + res = self.seam.client.post("/user_identities/list", json=json_payload) return [UserIdentity.from_dict(item) for item in res["user_identities"]] @@ -125,7 +115,7 @@ def list_accessible_devices(self, *, user_identity_id: str) -> List[Device]: json_payload["user_identity_id"] = user_identity_id res = self.seam.client.post( - self.seam.endpoint + "/user_identities/list_accessible_devices", + "/user_identities/list_accessible_devices", json=json_payload, ) @@ -138,7 +128,7 @@ def list_acs_systems(self, *, user_identity_id: str) -> List[AcsSystem]: json_payload["user_identity_id"] = user_identity_id res = self.seam.client.post( - self.seam.endpoint + "/user_identities/list_acs_systems", json=json_payload + "/user_identities/list_acs_systems", json=json_payload ) return [AcsSystem.from_dict(item) for item in res["acs_systems"]] @@ -150,7 +140,7 @@ def list_acs_users(self, *, user_identity_id: str) -> List[AcsUser]: json_payload["user_identity_id"] = user_identity_id res = self.seam.client.post( - self.seam.endpoint + "/user_identities/list_acs_users", json=json_payload + "/user_identities/list_acs_users", json=json_payload ) return [AcsUser.from_dict(item) for item in res["acs_users"]] @@ -163,9 +153,7 @@ def remove_acs_user(self, *, acs_user_id: str, user_identity_id: str) -> None: if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id - self.seam.client.post( - self.seam.endpoint + "/user_identities/remove_acs_user", json=json_payload - ) + self.seam.client.post("/user_identities/remove_acs_user", json=json_payload) return None @@ -178,7 +166,7 @@ def revoke_access_to_device(self, *, device_id: str, user_identity_id: str) -> N json_payload["user_identity_id"] = user_identity_id self.seam.client.post( - self.seam.endpoint + "/user_identities/revoke_access_to_device", + "/user_identities/revoke_access_to_device", json=json_payload, ) @@ -206,8 +194,6 @@ def update( if user_identity_key is not None: json_payload["user_identity_key"] = user_identity_key - self.seam.client.post( - self.seam.endpoint + "/user_identities/update", json=json_payload - ) + self.seam.client.post("/user_identities/update", json=json_payload) return None diff --git a/seam/user_identities_enrollment_automations.py b/seam/user_identities_enrollment_automations.py index d633f28..93ae5f4 100644 --- a/seam/user_identities_enrollment_automations.py +++ b/seam/user_identities_enrollment_automations.py @@ -16,7 +16,7 @@ def delete(self, *, enrollment_automation_id: str) -> None: json_payload["enrollment_automation_id"] = enrollment_automation_id self.seam.client.post( - self.seam.endpoint + "/user_identities/enrollment_automations/delete", + "/user_identities/enrollment_automations/delete", json=json_payload, ) @@ -29,7 +29,7 @@ def get(self, *, enrollment_automation_id: str) -> EnrollmentAutomation: json_payload["enrollment_automation_id"] = enrollment_automation_id res = self.seam.client.post( - self.seam.endpoint + "/user_identities/enrollment_automations/get", + "/user_identities/enrollment_automations/get", json=json_payload, ) @@ -64,7 +64,7 @@ def launch( ) self.seam.client.post( - self.seam.endpoint + "/user_identities/enrollment_automations/launch", + "/user_identities/enrollment_automations/launch", json=json_payload, ) @@ -77,7 +77,7 @@ def list(self, *, user_identity_id: str) -> List[EnrollmentAutomation]: json_payload["user_identity_id"] = user_identity_id res = self.seam.client.post( - self.seam.endpoint + "/user_identities/enrollment_automations/list", + "/user_identities/enrollment_automations/list", json=json_payload, ) diff --git a/seam/webhooks.py b/seam/webhooks.py index 1c7ac64..fe27f3e 100644 --- a/seam/webhooks.py +++ b/seam/webhooks.py @@ -17,9 +17,7 @@ def create(self, *, url: str, event_types: Optional[List[str]] = None) -> Webhoo if event_types is not None: json_payload["event_types"] = event_types - res = self.seam.client.post( - self.seam.endpoint + "/webhooks/create", json=json_payload - ) + res = self.seam.client.post("/webhooks/create", json=json_payload) return Webhook.from_dict(res["webhook"]) @@ -29,9 +27,7 @@ def delete(self, *, webhook_id: str) -> None: if webhook_id is not None: json_payload["webhook_id"] = webhook_id - self.seam.client.post( - self.seam.endpoint + "/webhooks/delete", json=json_payload - ) + self.seam.client.post("/webhooks/delete", json=json_payload) return None @@ -41,9 +37,7 @@ def get(self, *, webhook_id: str) -> Webhook: if webhook_id is not None: json_payload["webhook_id"] = webhook_id - res = self.seam.client.post( - self.seam.endpoint + "/webhooks/get", json=json_payload - ) + res = self.seam.client.post("/webhooks/get", json=json_payload) return Webhook.from_dict(res["webhook"]) @@ -52,9 +46,7 @@ def list( ) -> List[Webhook]: json_payload = {} - res = self.seam.client.post( - self.seam.endpoint + "/webhooks/list", json=json_payload - ) + res = self.seam.client.post("/webhooks/list", json=json_payload) return [Webhook.from_dict(item) for item in res["webhooks"]] @@ -66,8 +58,6 @@ def update(self, *, event_types: List[str], webhook_id: str) -> None: if webhook_id is not None: json_payload["webhook_id"] = webhook_id - self.seam.client.post( - self.seam.endpoint + "/webhooks/update", json=json_payload - ) + self.seam.client.post("/webhooks/update", json=json_payload) return None diff --git a/seam/workspaces.py b/seam/workspaces.py index 257d1c6..5a1bc43 100644 --- a/seam/workspaces.py +++ b/seam/workspaces.py @@ -31,9 +31,7 @@ def create( if webview_primary_button_color is not None: json_payload["webview_primary_button_color"] = webview_primary_button_color - res = self.seam.client.post( - self.seam.endpoint + "/workspaces/create", json=json_payload - ) + res = self.seam.client.post("/workspaces/create", json=json_payload) return Workspace.from_dict(res["workspace"]) @@ -42,9 +40,7 @@ def get( ) -> Workspace: json_payload = {} - res = self.seam.client.post( - self.seam.endpoint + "/workspaces/get", json=json_payload - ) + res = self.seam.client.post("/workspaces/get", json=json_payload) return Workspace.from_dict(res["workspace"]) @@ -53,9 +49,7 @@ def list( ) -> List[Workspace]: json_payload = {} - res = self.seam.client.post( - self.seam.endpoint + "/workspaces/list", json=json_payload - ) + res = self.seam.client.post("/workspaces/list", json=json_payload) return [Workspace.from_dict(item) for item in res["workspaces"]] @@ -64,9 +58,7 @@ def reset_sandbox( ) -> ActionAttempt: json_payload = {} - res = self.seam.client.post( - self.seam.endpoint + "/workspaces/reset_sandbox", json=json_payload - ) + res = self.seam.client.post("/workspaces/reset_sandbox", json=json_payload) return self.seam.action_attempts.decide_and_wait( action_attempt=ActionAttempt.from_dict(res["action_attempt"]), diff --git a/test/workspaces/test_workspaces_create.py b/test/workspaces/test_workspaces_create.py index 5232cb2..48716ad 100644 --- a/test/workspaces/test_workspaces_create.py +++ b/test/workspaces/test_workspaces_create.py @@ -5,17 +5,17 @@ def test_workspaces_create(seam: Seam): - # r = "".join(random.choices(string.ascii_uppercase + string.digits, k=10)) - # seam = SeamMultiWorkspace( - # endpoint=f"https://{r}.fakeseamconnect.seam.vc", - # personal_access_token="seam_at1_shorttoken_longtoken", - # ) + r = "".join(random.choices(string.ascii_uppercase + string.digits, k=10)) + seam = SeamMultiWorkspace( + endpoint=f"https://{r}.fakeseamconnect.seam.vc", + personal_access_token="seam_at1_shorttoken_longtoken", + ) - # workspace = seam.workspaces.create( - # name="Test Workspace", - # connect_partner_name="Example Partner", - # is_sandbox=True, - # ) + workspace = seam.workspaces.create( + name="Test Workspace", + connect_partner_name="Example Partner", + is_sandbox=True, + ) - # assert workspace.workspace_id + assert workspace.workspace_id pass From 2989550efe8d93f76a3561581866b55e9598741f Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 15 May 2024 16:39:19 +0200 Subject: [PATCH 25/48] Mark certain files as not generated --- .gitattributes | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index b8dad61..e136faf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,4 @@ -seam/** linguist-generated +seam/** linguist-generated=true +seam/seam.py linguist-generated=false +seam/request.py linguist-generated=false +seam/seam_multi_workspace.py linguist-generated=false From e04798e63fb19bb7b69c11f10d88cbc9e087bafe Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 15 May 2024 16:44:20 +0200 Subject: [PATCH 26/48] Fix client_options default value to fix linting --- seam/seam.py | 6 +++++- seam/seam_multi_workspace.py | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/seam/seam.py b/seam/seam.py index 80aa25d..6de7854 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -25,7 +25,7 @@ def __init__( endpoint: Optional[str] = None, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, client: Optional[requests.Session] = None, - client_options: Optional[Dict[str, Any]] = {}, + client_options: Optional[Dict[str, Any]] = None, ): """ Parameters @@ -52,6 +52,10 @@ def __init__( workspace_id=workspace_id, endpoint=endpoint, ) + + if client_options is None: + client_options = {} + self.client = client or SeamHttpClient( base_url=endpoint, auth_headers=auth_headers, **client_options ) diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py index 8feff62..c199ffd 100644 --- a/seam/seam_multi_workspace.py +++ b/seam/seam_multi_workspace.py @@ -37,7 +37,7 @@ def __init__( endpoint: Optional[str] = None, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, client: Optional[requests.Session] = None, - client_options: Optional[Dict[str, Any]] = {}, + client_options: Optional[Dict[str, Any]] = None, ): """ Parameters @@ -56,6 +56,10 @@ def __init__( personal_access_token ) endpoint = get_endpoint(endpoint) + + if client_options is None: + client_options = {} + self.client = client or SeamHttpClient( base_url=endpoint, auth_headers=auth_headers, **client_options ) From 5ebe8414dbc09d72d5afdb28741df3857a6adea4 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Thu, 16 May 2024 17:55:25 +0200 Subject: [PATCH 27/48] Remove AbstractRequestMixin --- seam/seam.py | 2 +- seam/types.py | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/seam/seam.py b/seam/seam.py index f225c23..7ed2418 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -5,7 +5,7 @@ from .constants import LTS_VERSION from .parse_options import parse_options from .request import SeamHttpClient -from .routes import Routes +from .routes.routes import Routes from .types import AbstractSeam diff --git a/seam/types.py b/seam/types.py index 672ac2a..d793b04 100644 --- a/seam/types.py +++ b/seam/types.py @@ -24,13 +24,7 @@ def __init__( ) -class AbstractRequestMixin(abc.ABC): - @abc.abstractmethod - def make_request(self, method: str, path: str, **kwargs): - raise NotImplementedError - - -class AbstractSeam(AbstractRoutes, AbstractRequestMixin): +class AbstractSeam(AbstractRoutes): lts_version: str wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] @@ -90,7 +84,7 @@ def list( raise NotImplementedError() -class AbstractSeamMultiWorkspace(AbstractRequestMixin): +class AbstractSeamMultiWorkspace(): lts_version: str wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] From adb64da1844f06210cd613aae49fb9d9c4ab0458 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Thu, 16 May 2024 18:54:02 +0200 Subject: [PATCH 28/48] Swap requests for niquests --- poetry.lock | 338 ++++++++++++++++++++++++++++++----- pyproject.toml | 2 +- seam/request.py | 2 +- seam/seam.py | 2 +- seam/seam_multi_workspace.py | 2 +- seam/types.py | 2 +- 6 files changed, 303 insertions(+), 45 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3f3b685..3516b8a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "annotated-types" @@ -71,17 +71,6 @@ d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] -[[package]] -name = "certifi" -version = "2024.2.2" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, -] - [[package]] name = "charset-normalizer" version = "3.3.2" @@ -338,15 +327,26 @@ files = [ [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + [[package]] name = "idna" -version = "3.6" +version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] @@ -374,6 +374,94 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] +[[package]] +name = "jh2" +version = "5.0.3" +description = "HTTP/2 State-Machine based protocol implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jh2-5.0.3-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:764acdd572413198eb7a1299d08d32b0819c33220604f76ba7ea722443c3b929"}, + {file = "jh2-5.0.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:f83bdbbae9fac7766e2edcac9a275af5a70e8e7188296c84cbeb552e1f1f2e8d"}, + {file = "jh2-5.0.3-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a921112bbafaea4d5ef9e2a25f03cacdaa1795b6a961f0fe430b8de15b939b3a"}, + {file = "jh2-5.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18cdf52edf8e636f3a4a74d92eb62bc6692a2c78e288b0724341c82b078bb261"}, + {file = "jh2-5.0.3-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9ccb5fc490722b5ca0966a2402ae90a7bc70ec8a4a9bce224948db211f5fa2a9"}, + {file = "jh2-5.0.3-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7c7dcecef260a792da2b7653011d09bcad5e1455e38fd194ee07fcb01a803fc3"}, + {file = "jh2-5.0.3-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aa0af8f8a1a27dc7e166840fbdda46abc673d5cd8e2319ac08a3c7d5e9e9920"}, + {file = "jh2-5.0.3-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78228b1a255581e93144d52feda6e8605fcfbfae7aa289def8879a7be6ca8a74"}, + {file = "jh2-5.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2be4f69c7c8d87d28948065a038d84555a0784acf3886e9c18707f34ceb7c1b"}, + {file = "jh2-5.0.3-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f83206f4e18d4836ec9de573c9c0c27c02e277b59685d9d490f7187968b780fa"}, + {file = "jh2-5.0.3-cp37-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:be4e8c8ccb401cbd6386a406c21a87f690d68f1fdcc1698dcc813429bf5a9ce3"}, + {file = "jh2-5.0.3-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b6facef13abe549d70fd722ab668fae822cfdcade6199a12e7ec06fe0ba44326"}, + {file = "jh2-5.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:3d2e48899378c3026d3640f1be1a043038af74f4076f1fb0c1b7bc13fb9c0619"}, + {file = "jh2-5.0.3-cp37-abi3-win_arm64.whl", hash = "sha256:2faaf1792220ffd5dcae8e88dd8f3b2b72771589121dbabc92fb503f488021c2"}, + {file = "jh2-5.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:72db414df9a49a3498b7e083002cd9a3eefc4bce33789d7ae31d0e1c92229f0e"}, + {file = "jh2-5.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6fa5629d6e18c6be93cc17da6b31a21511405e443df0a6936a0795807bf949f2"}, + {file = "jh2-5.0.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:a5d5326a15eed6b4dd64598522b741b65168093705e4276f964627ada3281f48"}, + {file = "jh2-5.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af39530c7173dd41383ac9910164695768418bd5910bc1e8e628383d82656881"}, + {file = "jh2-5.0.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c091dd40414b028793d3867968842a55c09e26bcbbba76e6daf8080b47384fd"}, + {file = "jh2-5.0.3-pp310-pypy310_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9e4b981ddce810d7a691f535c150212bef8a70c61081007c13b19ab30e44409b"}, + {file = "jh2-5.0.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57f8d46fac15bbe1fe6c576b00ef2aaf616c359cc2fb8a468d46b05e19495bf5"}, + {file = "jh2-5.0.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7b3476e3f13ce6751a45a82861a5fd64b38ef166c40974d0c97a0762293e12"}, + {file = "jh2-5.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498a7e463f2be67ec1085833e99582bcb1e3ea1c2933f640f7c4896e307673eb"}, + {file = "jh2-5.0.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:37e9d6f59e2c305f33793ea24fb588300828a83f9f51d47671e8335fc49a59e0"}, + {file = "jh2-5.0.3-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:09b89d2700e9530f13b10dd1b8cb0e5389e3da833b6aa7d0bef74ef39229c7ba"}, + {file = "jh2-5.0.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:a68cf6e7baf185f831b3eccdbfe91b7d48ad6af78b4b929d6df89d92d9a4ca62"}, + {file = "jh2-5.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7669f95cfac416a3e97b224d214c9838bb5b6b9e35a1892637337fe774d0406b"}, + {file = "jh2-5.0.3-pp37-pypy37_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:af5560b7856c6a1e17071d86e16e288f6ca3f465a29fd683347f84cf4ad8a99b"}, + {file = "jh2-5.0.3-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:769f569fd7dfbe027ddf28c5c73dda48494bb3ffef0be6f60d5661fa6c754da9"}, + {file = "jh2-5.0.3-pp37-pypy37_pp73-macosx_11_0_arm64.whl", hash = "sha256:48befaf5dd60aa3b623e8cfafdb097516820f9b5c2ae38d399b6e4eed1cfdb46"}, + {file = "jh2-5.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac6259ba5591120b3663089ac1ba9a521249794d753343c59447808b744e2755"}, + {file = "jh2-5.0.3-pp37-pypy37_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dc1f2442d6eb9ab8f54d328ec938918ca273290d1e99329387967dd74e1b054e"}, + {file = "jh2-5.0.3-pp37-pypy37_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:557c40acd62c51001cdad2674c5a9d400127796ce7fbe8d82698ff8bec478092"}, + {file = "jh2-5.0.3-pp37-pypy37_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e175649572bc2a7c60a7e03d8bd1d42d0f64634d31d0d7930b65e1b31f8e43d"}, + {file = "jh2-5.0.3-pp37-pypy37_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0125bbc6d41bd55f088e952fbfec0652eb0ac45632604d6644c45e2ccb83507b"}, + {file = "jh2-5.0.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ecdf049246a0bc01bd404737b1506770577e982802872539f7734368877623a"}, + {file = "jh2-5.0.3-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:70a401591577502f3efb2fb247378fa5a0adfd48578e15f7365d6db656447e66"}, + {file = "jh2-5.0.3-pp37-pypy37_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3ba6a00394c6cb5d1409fbb910eba2ff2a216bb553af198ccc8f64af63133f67"}, + {file = "jh2-5.0.3-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:4eb16b0f95181c7bb19115446b5987b694727c6afd618ee900e895a54d101188"}, + {file = "jh2-5.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e967d4a1bdb7a8726e0349dea82eba530dbed8fed4cad118f81e867f84c0446e"}, + {file = "jh2-5.0.3-pp38-pypy38_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:2fcc906639e5987ee0eacb12ba5a7678ac76677986dbeeb41dc7d1b44926ac43"}, + {file = "jh2-5.0.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:39dbcd14e8313370c83287080ddac77a42cace50ef223852d6b0bea73df8ac3e"}, + {file = "jh2-5.0.3-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0ceddd1be2a9153d5eec25ac3aca846f35bc171325e549509cac75ecd26930d"}, + {file = "jh2-5.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8aa14f5b3077526fed01464e4400860c9f88f73b0499f87e50e4cbf0d851def"}, + {file = "jh2-5.0.3-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:89cec07a5e15660c10c29537d6df2b61daf9673cbd014971664b50d3c5eedc34"}, + {file = "jh2-5.0.3-pp38-pypy38_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:fc81fe2ef8a2c011a67468a169790973d0595731599c7c21d0d223b29c886d43"}, + {file = "jh2-5.0.3-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:024862bbaa795549823386bc30b0bb7c1807df5df75b4e5ce97f216812796fca"}, + {file = "jh2-5.0.3-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7417bd01c897c5afe9da8270ca4fc0485378a05bee0d4c0ad435c48aee841ad"}, + {file = "jh2-5.0.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f67738cb9e669c3b28ab1f7c611df0c3e5cb6da06df5a7f303e8510b890764a"}, + {file = "jh2-5.0.3-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fa6d7060c225d2f1c888ecb8e834a4685d4468fe11968d56a5eb3376e45d287c"}, + {file = "jh2-5.0.3-pp38-pypy38_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:32f0700ecca8707b9561328705efa9ad1626ca829b2c4964e753d579337d53c2"}, + {file = "jh2-5.0.3-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:969f62d321bd82207be836f99bbbb854ec5596ca82a2c905c404fed5e4a9289c"}, + {file = "jh2-5.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:1d1194a66b25fea05f34e0bd1cf5ffc72ec67fd3e584d4c1293e9962815ec180"}, + {file = "jh2-5.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b1b47bba88666287ad998dd561292dbe38ad767072a4ecdfe90a98f2f2e95579"}, + {file = "jh2-5.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:97a57f39f6fbfb96966edf0d6879afed5784582573644cd5c2f58907f6e45759"}, + {file = "jh2-5.0.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0ab31da34be3c726acda1eed2fd9ad9daf14f4f45bb15fdce7ed877ce82f677"}, + {file = "jh2-5.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:090755e41a0c0b7021dfdfda46970e3920d6524a528d0456a0759310b2cd3a30"}, + {file = "jh2-5.0.3-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e8903186182de97107260540796f6579b7322cb82d7d3e8aca11fd09c2847fe"}, + {file = "jh2-5.0.3-pp39-pypy39_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:85714f220fbd7ab7d17ab83cbbdac4d43e0fc699738da06b018a9aa01adad5f0"}, + {file = "jh2-5.0.3-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d59abb4b1adfd9e7f0b6df22f4f6ea260128d7605110a39bab29dbed32a5ab28"}, + {file = "jh2-5.0.3-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe75dbd44d1c53341acd5505afd29af8172e7ce67a7d21fd4466d7db26be3766"}, + {file = "jh2-5.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c58aafe13f208769f16fa612095a64aed27b35a241eaffcef10c105b5c48c03"}, + {file = "jh2-5.0.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:83fa9a815edbe52b6c0c096a20c1bb3f9669b706e8a69775dae63c973a60b2ea"}, + {file = "jh2-5.0.3-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:b4d6d2e8a14526951b15cbeaae89625838570affb429ab90df32b6a34a7d417b"}, + {file = "jh2-5.0.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ea555676ce7eb72d5be9b5107286c83c72cc12bf7dbdcc9d2381672451c077a4"}, + {file = "jh2-5.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ac1e1db4a92fa1b4625e57de2ad36b6376dfcc53be6298afbd82ee96f172d80f"}, + {file = "jh2-5.0.3-py3-none-any.whl", hash = "sha256:4ac75f013a1600d8111306fa5d3b35ce08bbbd8fde2aca096a0ddf6a415e999f"}, + {file = "jh2-5.0.3.tar.gz", hash = "sha256:c13d97a3f82a02e6a2a89606f1ffe1771670266dc7746140e00e66c4dad12b14"}, +] + +[[package]] +name = "kiss-headers" +version = "2.4.3" +description = "Object-oriented HTTP and IMAP (structured) headers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "kiss_headers-2.4.3-py3-none-any.whl", hash = "sha256:9d800b77532068e8748be9f96f30eaeb547cdc5345e4689ddf07b77071256239"}, + {file = "kiss_headers-2.4.3.tar.gz", hash = "sha256:70c689ce167ac83146f094ea916b40a3767d67c2e05a4cb95b0fd2e33bf243f1"}, +] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -450,6 +538,30 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "niquests" +version = "3.6.4" +description = "Niquests is a simple, yet elegant, HTTP library. It is a drop-in replacement for Requests, which is under feature freeze." +optional = false +python-versions = ">=3.7" +files = [ + {file = "niquests-3.6.4-py3-none-any.whl", hash = "sha256:fd26b1e4d757f3cce4200cc2a540d99b46d8c3c0129c0ce23b86b52b1a96d7a4"}, + {file = "niquests-3.6.4.tar.gz", hash = "sha256:275095fe027c5112bf8cc7b9c93fb09cf2221b0d22875f05d06493235b9e7a75"}, +] + +[package.dependencies] +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +kiss-headers = ">=2,<4" +urllib3-future = ">=2.7.905,<3" +wassima = ">=1.0.1,<2" + +[package.extras] +http3 = ["urllib3-future[qh3]"] +ocsp = ["urllib3-future[qh3]"] +socks = ["urllib3-future[socks]"] +speedups = ["orjson (>=3,<4)", "urllib3-future[brotli,zstd]"] + [[package]] name = "packaging" version = "24.0" @@ -643,8 +755,8 @@ astroid = ">=3.1.0,<=3.2.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" mccabe = ">=0.6,<0.8" @@ -729,26 +841,80 @@ pytest = ">=2.6.4" watchdog = ">=0.6.0" [[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." +name = "qh3" +version = "1.0.7" +description = "A lightway and fast implementation of QUIC and HTTP/3" optional = false python-versions = ">=3.7" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "qh3-1.0.7-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:392cbfc95c832f78b840a5b17d743d4dcf8d47d7217d17370b939a8717939fa6"}, + {file = "qh3-1.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:75bea34952369975a646379aa2b6438f557b4da7a76ddb59973000d96ea8063e"}, + {file = "qh3-1.0.7-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9d40552d28eaa89c819edfc3bc3752ed3d7da59119840d8fe09790b9c76f5819"}, + {file = "qh3-1.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f1283bbab24b26565fb9b90278ea96c7940396d31ee9fac169e9c7e1b36fd96"}, + {file = "qh3-1.0.7-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0f7f910a1523b1d2f16e20034452b63335f90431868365bee0fe29d8e6473438"}, + {file = "qh3-1.0.7-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:92d87664b758bc1f8b4bcfa60cce866c1e96938d59af3de25189661e263cc510"}, + {file = "qh3-1.0.7-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6f56c34e8d7009596a61a800c80f478a9e9ceccfdc11b28544b59ad904a5ff6"}, + {file = "qh3-1.0.7-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4da2ba491c7fb240556a996b0a10cf7b6ce17816a9c2a53a02b08a40eb2fa37"}, + {file = "qh3-1.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586cfd89068fd44e5471b31bd7d4c4b01b80a85ac28e26b14da6ede4583e8017"}, + {file = "qh3-1.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:1835f149b1d359a9de915207bc0543004edcb3077390614bca5d52c637914ad3"}, + {file = "qh3-1.0.7-cp37-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:1e7618c6059a5f838b858ea834ef6bc66d1e95bdeb8ea466217d74aedf471415"}, + {file = "qh3-1.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ebf612690cfefc66d19648f67bd0a7a83112230c322e21245a5536ca05aa9564"}, + {file = "qh3-1.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:7ef77f2663f60ac3b46fc13ad67076dcff8dfe37886c1793fa7411c50385b061"}, + {file = "qh3-1.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4fb6c73399313881b80708354af6ac5d837cf9733189476f919d7c9b88e5ccba"}, + {file = "qh3-1.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4bbd1a4a5b49cbcfd02a57b07eab729cec7b4a75f2180a03fa0d200d46833cbb"}, + {file = "qh3-1.0.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:0ccbcf47a1311deece090ac6c83918741be4b568f7018748d3f3762c70a45c3c"}, + {file = "qh3-1.0.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6e5871df72c6303e9c68e8ab50d25152fc4d4ee0d8ebdced20724922fbaeb53"}, + {file = "qh3-1.0.7-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:981725de5210eb8f902fa5015b029072a532c4dd44770bc6d2b45b569b91212a"}, + {file = "qh3-1.0.7-pp310-pypy310_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4018dc5023738b4da756297787e6606a771777b576d61da95226c2ec0d5d50ef"}, + {file = "qh3-1.0.7-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:debbcd38adf35bb2589095f78ef59690f507b1fd6aa71440c80ad17a15485fbc"}, + {file = "qh3-1.0.7-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c83a719c48dbd3d8d592193e83360c11657a5c1bda31e519a8b8d7b1f60ae627"}, + {file = "qh3-1.0.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6487edea20ac7bea0e820bafb9bd4957d80a341facd7d64163953adcf560bf95"}, + {file = "qh3-1.0.7-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e70de70904cca4f53dec7ddbc15e8d2fe0e4cad01c05063cddb1087460a367a"}, + {file = "qh3-1.0.7-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8e03f81f60ef0772e2a32e3e56fbcc907f111f4057909fbb013e2f65516b5799"}, + {file = "qh3-1.0.7-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:89ece443484baff18ce12d08cbcf5fa8bcfbfbc6517e1f2e020666c6adf003e9"}, + {file = "qh3-1.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0ce94a6fbd6bd2715e66d28560af8c41bd99bd419bb89abc45f761932dc8cb54"}, + {file = "qh3-1.0.7-pp37-pypy37_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:6c8b427f31c486fc63898ec15b5d12636f29d09275e186da5cacbf4296143e7d"}, + {file = "qh3-1.0.7-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:8bf1b4f50639ee5277e4baef3740c4790f59760c4a537cef9e8096b2c7c2ca69"}, + {file = "qh3-1.0.7-pp37-pypy37_pp73-macosx_11_0_arm64.whl", hash = "sha256:acb42fc6d6251ee9af39a4330c9fe3a4958517b76ee7f4c66787a8b099bdf0f4"}, + {file = "qh3-1.0.7-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5a807d4bac257e1e68e690464444361320ce730f68d8d1821a1e70a3cd795cf"}, + {file = "qh3-1.0.7-pp37-pypy37_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d51d9402d5544540363424cfe9c06f7c1e7e0b1bfaaa7128e5034d27a0f2cef"}, + {file = "qh3-1.0.7-pp37-pypy37_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:fbcb8a29285b8a9d1587948b30f2520bc564d0341f860c2fc57a5d53a77a2e23"}, + {file = "qh3-1.0.7-pp37-pypy37_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0753e77e9bbb6140c05adb3fbd19dbba6b2f5f27598e608e2a543165ede4b337"}, + {file = "qh3-1.0.7-pp37-pypy37_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25a1e9a27efd7fe55e521f4f68b9bc7a4ef6b1a0f51cb8a98134fcf6e9bec6ab"}, + {file = "qh3-1.0.7-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6ad8a4c31b5579b65482172e7e7f945ec7b29030046ce29d7fce396246fbb7"}, + {file = "qh3-1.0.7-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:43911c6f206ac2654b857bef14dda7394509f9746f145ccb4e496f32a9d67422"}, + {file = "qh3-1.0.7-pp37-pypy37_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:bda728efcf03e49ec16d1f0a8d3953fcc113df5d3055b63c838d7dc7834c357e"}, + {file = "qh3-1.0.7-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:03f7ea1cedacbfe6252f202ac65d0040a8942ce627df241bf74a0f2ad7c4a73f"}, + {file = "qh3-1.0.7-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:5f82a48b9c06b51ab482fedd4b0476b0c0ce0f4a0d47c5fef924adc8cd795a89"}, + {file = "qh3-1.0.7-pp38-pypy38_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:091247b9bb24e6abb055772a502ea70857e7dabdf1dd993053db680c7bdd3718"}, + {file = "qh3-1.0.7-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c73458b6a79652a63a64b715d8047f07d7f61ab091a87f32c2303f9cb9d77bbd"}, + {file = "qh3-1.0.7-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:733553159d65b447bf7c7aa84cde6b158e057025b602192c43b41083f3e06b4a"}, + {file = "qh3-1.0.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308154b300b28436a415f2163b942e0c809f8cbeede1a940be563d64462e35b2"}, + {file = "qh3-1.0.7-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d5db32a2a9ea4210bb5a3c6c037e02d11f6709be38e077eee77cdf0e626dc7f7"}, + {file = "qh3-1.0.7-pp38-pypy38_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57d3013bb1f03cf1a2c8a1bb7d536722eaa059c58d3f6918fc5b62f8469d4f6c"}, + {file = "qh3-1.0.7-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaca92cfbaabf2a8792325243e63cf7512c4760aed2371a5fc9ce32612245bfe"}, + {file = "qh3-1.0.7-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7da2e218fed81602d4f1034cd9698afee2c56270b1520cfde09e14d47dbdb2d2"}, + {file = "qh3-1.0.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04a0005c4ccf5d9510491d5e731021af1f764c23e2bb456f3b5674311a126e90"}, + {file = "qh3-1.0.7-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:15b54ed14df131266e65d7ef09b7bb3f1fb4d74d4fb632b51d233c58be050368"}, + {file = "qh3-1.0.7-pp38-pypy38_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:bf675b50dbf971d21e8fc4ccf6fc18c77942f6ac20358ac6ca3a98857e467f42"}, + {file = "qh3-1.0.7-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:a438f7cb5923ec519c3a1bd4e8db67067abe5d1d3d0499bffd19213b52ee654f"}, + {file = "qh3-1.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:53ccddf58ea50250609da3753977ad501783fa0cf2dc15960add5c15e2313b47"}, + {file = "qh3-1.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:168bd4f481c6b7f780e4f46f399d6c7faadebe4227b5130d0d7c335d10dc13e3"}, + {file = "qh3-1.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:188ef7bd33b1b85af93058ce365ff7b65377a99580fdc37bf9d18de98ac90ebd"}, + {file = "qh3-1.0.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:63407c2c680ac4a713f502a23658e9e26818133312b78765bb5efe21978feb02"}, + {file = "qh3-1.0.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:703775328761d10a6ba2e412a6c44304e28b4d05799765496c7a21569ac7f732"}, + {file = "qh3-1.0.7-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ffe51161a782c99cfe792100af43f8577ac3f9fcc247e80a860981cd8e4bc8f7"}, + {file = "qh3-1.0.7-pp39-pypy39_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:567a2a76b2c5ef42a2c9adef9596c8ebbdb614660363bcd020096ea38969bede"}, + {file = "qh3-1.0.7-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9aa47c11d2d4c2ed75506333fd83c481017f6da7f45002e24e391842d8dc989a"}, + {file = "qh3-1.0.7-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67433e71e5bf6a76f41f1bde9c69aaf017ae3b4ae5e9f3e4a123a45619dc5783"}, + {file = "qh3-1.0.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2569550be0faa50e723a1bbf3a9061b66a7546d514fea81bb9125e16f6a7d37"}, + {file = "qh3-1.0.7-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a5aea4ed5b8880210aa34957c33b4c576b2e68b9cbc48013974d34a41d71d17f"}, + {file = "qh3-1.0.7-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:be0675ced825a4a659888541060031621b575a99bee4efc1e435ffa7815a696d"}, + {file = "qh3-1.0.7-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ecf9eb27fcbbd9c8cf4a374c5e0d034ec0c3e576f1ed028f7ce84a3484fb06fa"}, + {file = "qh3-1.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:89f00a47cef7197ea6367bb0a1ecb9517aeaf8c8dd26202cfbf3e3346d0f4aeb"}, + {file = "qh3-1.0.7.tar.gz", hash = "sha256:eb527d8317746209509b9c575527577cdc9b3cfb0f49294fc1cd109b0570362c"}, ] -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - [[package]] name = "rich" version = "13.7.1" @@ -924,22 +1090,114 @@ mypy-extensions = ">=0.3.0" typing-extensions = ">=3.7.4" [[package]] -name = "urllib3" -version = "2.2.1" -description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "urllib3-future" +version = "2.7.908" +description = "urllib3.future is a powerful HTTP 1.1, 2, and 3 client with both sync and async interfaces" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3_future-2.7.908-py3-none-any.whl", hash = "sha256:c35af8521f5a833b3664c6574893a30e73608d3e070e920296400ab698a60b4c"}, + {file = "urllib3_future-2.7.908.tar.gz", hash = "sha256:2ab62b2a20dcf66cb5e02f0454c81cf5858f6e7064d7760dc19bedcbbd7523be"}, ] +[package.dependencies] +h11 = ">=0.11.0,<1.0.0" +jh2 = ">=5.0.3,<6.0.0" +qh3 = {version = ">=1.0.3,<2.0.0", markers = "(platform_system == \"Darwin\" or platform_system == \"Windows\" or platform_system == \"Linux\") and (platform_machine == \"x86_64\" or platform_machine == \"s390x\" or platform_machine == \"aarch64\" or platform_machine == \"armv7l\" or platform_machine == \"ppc64le\" or platform_machine == \"ppc64\" or platform_machine == \"AMD64\" or platform_machine == \"arm64\") and (platform_python_implementation == \"CPython\" or (platform_python_implementation == \"PyPy\" and python_version < \"3.11\"))"} + [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +qh3 = ["qh3 (>=1.0.3,<2.0.0)"] +socks = ["python-socks (>=2.0,<3.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "wassima" +version = "1.1.1" +description = "Access your OS root certificates with the atmost ease" +optional = false +python-versions = ">=3.7" +files = [ + {file = "wassima-1.1.1-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:1f6f5198ddb3d68d7b6fe9229c55a2c83cb56232b72dcdd4b2ebc7540138aa20"}, + {file = "wassima-1.1.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:583e9e510cb88f067e9a48b39ac58549a258623d1e07eb6bb0512280a10c0e8f"}, + {file = "wassima-1.1.1-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:59c73287561dcbd0102ff897b136af0ef5e5879192f0908c597d85b8269701b0"}, + {file = "wassima-1.1.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50afde20c335c18c39a47ff9c9d1c481864275e6ec1fb50b23ff4d693428b4fd"}, + {file = "wassima-1.1.1-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b2edfdc6ec4f07a35fd4b84c2b282f856062a832ced48eddce1a44c82525f275"}, + {file = "wassima-1.1.1-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f41980d3d83bc446e28822c2f4a395f787814273f623811e4ed1035dfde7b267"}, + {file = "wassima-1.1.1-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a04100cdd5ad8f7d1e85b1b679bc7b5db95c788d24aed6e8b63a8ad47fccf62e"}, + {file = "wassima-1.1.1-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af205bfad17f814a05341bc1889ff61c40f44b37139c58c9c47aa593e83e2f"}, + {file = "wassima-1.1.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdba17f5d0844468c8bb7b5103505985bdedcad5dd90cd722475386f115332d2"}, + {file = "wassima-1.1.1-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:32bca7ed733a94c32d6c78d10c902dd4dc2dcf5785b560d82915de1544e076ce"}, + {file = "wassima-1.1.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ae474c2458addbe1b657ee080dc73a52ef002504c055f71176a246aecf2b714f"}, + {file = "wassima-1.1.1-cp37-abi3-musllinux_1_1_armv7l.whl", hash = "sha256:c2baa1316b40044caecb9fd5f574527784673b809515668bb7d9631c29f03d39"}, + {file = "wassima-1.1.1-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:bc2d94894c6c270787b010b12243ce4a2ffe6a62b1f5682c912ea4540fe5d6df"}, + {file = "wassima-1.1.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:8f81f8dc770e7988321003776b2d3fff65339de4216e6f9b39d831333390c728"}, + {file = "wassima-1.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:878ae83bb9130b0c86426fc7682b397e6c8fd7a0457c277485e96b792f7f12b1"}, + {file = "wassima-1.1.1-cp37-abi3-win_arm64.whl", hash = "sha256:491d0541b5995618efc85c9b249f1a9fa33744973e42bd523bf52c01adcd078e"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:84a1607d508024febacc192414797daa0bce64bbf24e7ae93e182aecadaf200a"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a63866dec44e5d0ef9606b43a33d3017d0de7d931a128a3d1b0c28ad5714e8ce"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:85244bda65f8a39ad700aa941f1f0123f588933c79afbb4f65f600fa6c83863d"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:665f0b4a424b56e907bd579d8144b339f1383a6dd53bd4ebdd6b6f2653cd6d3e"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:41630a1f13b356f19cda2ab406ca93185a426fc65ba608d58000c125f07cdf2e"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:028f07252188a7b6a1e73c955959b3028d6f5633e798eb6b27ee96716c62ab31"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec076533caed061bdec324b0536bf01b704edf4a6bfd022462bc64233d9660e2"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c99e98dd7960d7dd7342f5fa569a4cfcd5e9333f74f1a9115a483fa6c03a5f8"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1aa554e79832c8f94fbedc77a53ff4207cfce45ebd0c3a550d6c0eb8fbd82118"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7cac5cc91a6dc12b675c6749e5e0a36f35b4976dd37df36d50246fd8c3707866"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2763d8f5cc58c4a9ee986c39709d3af91a9dc215c8a8895b992783a828a35824"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8d2d8cbec28a9f810207ca2647a0cf6c674b95162f2269d35846421d5948a1e6"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-musllinux_1_1_i686.whl", hash = "sha256:0353ba99c1db703902d084a81875ed3e06a3831f3eb039240607198e8f6bbb5c"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:95d1b7144286c090b943fc42711da922384f7e4e629a5dd2e8f05b66d008cf76"}, + {file = "wassima-1.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bdb76ee185736ed98ed92b8200b380b732b4e3df08219ea9491c6c7c9a983778"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:1f7d14d3019f179b5747168282fcd4c0cc3a2363eb974bcbc532b546a4513c24"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e26e96df5d5358f093314ac162aed33ae3c04d80e9bdd1c1c2cd07c8e0109f36"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-macosx_11_0_arm64.whl", hash = "sha256:18857ddbc6d257541027ffc59dc6e87c21bf0ba5a10abde5073d01cec0352faf"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86fc536580e31c81717212751fab16ff962f16302f71a71a2786a4c743ac1e1a"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f2ce27271a38fd21e2dc3cc5167431a63473ebeec1c02975e4000c95d04e35a9"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4ed19ff2afc209ea837fefbfe9eec01eaa6b8af46fe8dbdb8a692a894646013b"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c08969d10347b71b25c31e9ce73416002ff9d27fa22aa1e652ecd8cffda0a92"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c24eea8459388db8ec524c32eca1e06fff2ea274b2ca92431f5a9a5b05b4b6e6"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cb04b797b1cc0504d0c34625b1e939c236e001a73db0615825c0b1f9b926992"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e289c87ef4a738593bc0ce455c564e1c17d4acffab27d010751cd60b47c235cd"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:797200152716971b9d7f06bf1bcc3767b2998afa73bb1515c3bdc7828e70a711"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0b080a60c5019dcd11e4dd2870479a1876d09c1caa2f1d2cfb9645a10e7880b8"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-musllinux_1_1_i686.whl", hash = "sha256:b870df6e1166b86522298d5a3d945ce65743be2d16586b9d2eabbb49ec7dbf2c"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:14d8395e06d0db4a54f99058bf00818420d4cfda8fbcc48b56e8ca73d5f2551a"}, + {file = "wassima-1.1.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:5166da190adff31bdc432d4675e75945e88341a2334e5420a50f0f9758fb55b9"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:dfe2cc6069aa074f2262ef128ad31d4212d850ea3c5c793525ed93a29b356658"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:829c11682c5ef7a90bc248a56ef50ef1290335b554626c1221f8a8c244d69cbb"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:e22c247574d35c2f450ede7662e630d9fd07e2230434aea142afd3cf819a3f3b"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e996e4772a41edffcce071360bcc2359deae3e6dd23ad1b101f4bb5168e7d0c6"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:49cf2bc1f7e4cd622600bfbbb709320d5b60469dfaeef5493cc464771ffd027a"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8866a5f401b790f47fb084227e6c8576c25f48b1e50161a50705d603b67147cb"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ab2757fdbf1ea4c6623c575866b8c6c0ea1c8c99470e2e3c67ffae63f73f85"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a45b6986634634404da64c34386a71130666ac930e9a84be4050b8777ba8b9d"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:890121a09213b98755eab091c72d9e2fdfe7f659d153051dc8a8adeef366e36f"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:36194a67632b472f455a36b89efff7b9e1b521df4d24a3ba2789b2bb322ac81e"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:856ffd4b95ae31518e7c7109796feef27801da79c40ee1c75f166c9655934cd2"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:6b0142f9121095b5807159621d6c37cf1defb409a6cd245d0f496d40072f67c1"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-musllinux_1_1_i686.whl", hash = "sha256:0612efb6f2e9339a3748fa8dc622f0be5f71b95b4471e1c1fbf5006a7e98a060"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:18cdae934705ec6a8df947f25c5230d6da812717ac23b5f51ad570c63bf7e3d7"}, + {file = "wassima-1.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:21e87777709d5c7d60503c589deef6a214ff8b94fa3e75ad5ff19518aeb0d4b1"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3565a0a206003acfa9eade388c7374add98800025bb04f568e57e11be27167ee"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:29b55e43836bf64c7f2a022e8dd358abdab05894cb444a4ad5d550ac0f8869d3"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:929216bccf041e21ad27e28aff46e9744f001792eb5ef2868a826a18fa2f58c7"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd201e7ba92dfb919b5200b844536133015e8f231954160e2d57ebf6e54d3cc2"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f6cb1b86329dd39d4e097d5d5abae9da47ce41485a4fd138c631b6e07389b8c"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:39511a31adbd988ec9706459f162b0258ef8be231837cf3e1adfce80858484ec"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd1f0d44b4a9274c89faa2cb2dc193d7e71e263cdd82184cb34bcabd6acfdbfd"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1736dd1a018dc4a9a71c51de7a3dd1bed76b31d35c7360de974c73542ea47e8"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36a5b0eb6713803ff0034ee5317950a36f2edcde73be8ecdcc959c2fb5e1fc39"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:83352eef88ce33fd7729215f4aea0bece6f05cb9226c710cf4fa4cd0a6a69e93"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:824af089a281d6cfb15d12708eed3cae211a15bbf3a0925d52fa242c10672091"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:5298e90f925205b037e44efcd843d2414aad720d6755fa8d604b1385272d0f9e"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-musllinux_1_1_i686.whl", hash = "sha256:eafece652771c9e7d1922348e67c57481a1a488faa98235ff0f3bdd6732a7fbe"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b00e2db17d32c2b97ce1429fd25e868c2dc0380ecf55ffae9949ff866053364c"}, + {file = "wassima-1.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e8d67bb4ba6941fa16418e9af884aa2f3b002af1edb25c82dbdc0f96fc7ad594"}, + {file = "wassima-1.1.1-py3-none-any.whl", hash = "sha256:b5b67d9128d728d35a0dd5b0ed071a0feaf2c7e0a7416660864f180b752623df"}, + {file = "wassima-1.1.1.tar.gz", hash = "sha256:b673f31051fd1b9292bd6e05853016b401ac703c377a7d0657242eb41ce6121b"}, +] + [[package]] name = "watchdog" version = "4.0.0" @@ -984,4 +1242,4 @@ watchmedo = ["PyYAML (>=3.10)"] [metadata] lock-version = "2.0" python-versions = "^3.9.0" -content-hash = "52cb0df4fecd9e307c6cfb9617f66e90f3d305468a33ae6646705d809a39e996" +content-hash = "9d9ab727a4d522a566ad2e05e3a936578e254b40f541d539bf9e9285a05dda98" diff --git a/pyproject.toml b/pyproject.toml index 3f345db..50afdb4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ repository = "https://github.com/seamapi/python-next" [tool.poetry.dependencies] python = "^3.9.0" dataclasses-json = "^0.6.4" -requests = "^2.26.0" +niquests = "^3.6.4" [tool.poetry.group.dev.dependencies] black = "^24.3.0" diff --git a/seam/request.py b/seam/request.py index f25dfa7..692bc54 100644 --- a/seam/request.py +++ b/seam/request.py @@ -1,6 +1,6 @@ from typing import Dict from urllib.parse import urljoin -import requests +import niquests as requests from importlib.metadata import version from seam.constants import LTS_VERSION diff --git a/seam/seam.py b/seam/seam.py index 7ed2418..25b8cdd 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -1,5 +1,5 @@ from typing import Any, Optional, Union, Dict -import requests +import niquests as requests from typing_extensions import Self from .constants import LTS_VERSION diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py index 5c659ec..5fd904d 100644 --- a/seam/seam_multi_workspace.py +++ b/seam/seam_multi_workspace.py @@ -1,5 +1,5 @@ from typing import Any, Dict, Optional, Union -import requests +import niquests as requests from typing_extensions import Self from .auth import get_auth_headers_for_multi_workspace_personal_access_token diff --git a/seam/types.py b/seam/types.py index d793b04..8401253 100644 --- a/seam/types.py +++ b/seam/types.py @@ -1,5 +1,5 @@ from typing import Any, Dict, List, Optional, Union -import requests +import niquests as requests from typing_extensions import Self import abc From 893d48b5be61b6ec5349dfd4e2998d355aee2ad6 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Thu, 16 May 2024 19:29:18 +0200 Subject: [PATCH 29/48] Uncomment innit imports --- seam/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/seam/__init__.py b/seam/__init__.py index 4a184c7..7e777bd 100644 --- a/seam/__init__.py +++ b/seam/__init__.py @@ -3,12 +3,12 @@ from seam.seam import Seam -# from seam.types import SeamApiException -# from seam.seam_multi_workspace import SeamMultiWorkspace -# from seam.options import SeamHttpInvalidOptionsError -# from seam.auth import SeamHttpInvalidTokenError -# from seam.utils.action_attempt_errors import ( -# SeamActionAttemptError, -# SeamActionAttemptFailedError, -# SeamActionAttemptTimeoutError, -# ) +from seam.types import SeamApiException +from seam.seam_multi_workspace import SeamMultiWorkspace +from seam.options import SeamHttpInvalidOptionsError +from seam.auth import SeamHttpInvalidTokenError +from seam.routes.action_attempts import ( + SeamActionAttemptError, + SeamActionAttemptFailedError, + SeamActionAttemptTimeoutError, +) From 623315361e4addb611c69a5b69a476bd0f27416a Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Thu, 16 May 2024 19:29:56 +0200 Subject: [PATCH 30/48] Remove duplicat SeamApiException definition --- seam/types.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/seam/types.py b/seam/types.py index 8401253..560752d 100644 --- a/seam/types.py +++ b/seam/types.py @@ -84,7 +84,7 @@ def list( raise NotImplementedError() -class AbstractSeamMultiWorkspace(): +class AbstractSeamMultiWorkspace: lts_version: str wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] @@ -108,19 +108,3 @@ def from_personal_access_token( wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, ) -> Self: raise NotImplementedError - - -class SeamApiException(Exception): - def __init__( - self, - response, - ): - self.status_code = response.status_code - self.request_id = response.headers.get("seam-request-id", None) - self.metadata = None - if "application/json" in response.headers["content-type"]: - parsed_response = response.json() - self.metadata = parsed_response.get("error", None) - super().__init__( - f"SeamApiException: status={self.status_code}, request_id={self.request_id}, metadata={self.metadata}" - ) From e5cf4127a0f0e240f63107d929e03121388df7a8 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Thu, 16 May 2024 19:30:10 +0200 Subject: [PATCH 31/48] Add docstring for client and client_options --- seam/seam.py | 4 ++++ seam/seam_multi_workspace.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/seam/seam.py b/seam/seam.py index 25b8cdd..621036f 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -40,6 +40,10 @@ def __init__( The API endpoint to which the request should be sent. wait_for_action_attempt : bool or dict, optional Controls whether to wait for an action attempt to complete, either as a boolean or as a dictionary specifying `timeout` and `poll_interval`. Defaults to `False`. + client : requests.Session, optional + A pre-configured requests session to be used for making HTTP requests. If not provided, a new `SeamHttpClient` instance will be created using the `client_options` and other relevant parameters. + client_options : dict, optional + A dictionary of options that will be passed to the `SeamHttpClient` constructor when initializing a new requests session client. This allows for customization of the HTTP client, such as setting additional headers or configuring timeouts. For detailed information on available options, refer to the niquests library documentation [here](https://github.com/jawah/niquests/blob/954764c22f3c51ab51add3c6e3635bbfb3e1ee23/src/niquests/sessions.py#L252). If client is provided, this parameter will be ignored. """ Routes.__init__(self) diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py index 5fd904d..c0d512c 100644 --- a/seam/seam_multi_workspace.py +++ b/seam/seam_multi_workspace.py @@ -48,6 +48,10 @@ def __init__( The API endpoint to which the request should be sent. wait_for_action_attempt : bool or dict, optional Controls whether to wait for an action attempt to complete, either as a boolean or as a dictionary specifying `timeout` and `poll_interval`. Defaults to `False`. + client : requests.Session, optional + A pre-configured requests session to be used for making HTTP requests. If not provided, a new `SeamHttpClient` instance will be created using the `client_options` and other relevant parameters. + client_options : dict, optional + A dictionary of options that will be passed to the `SeamHttpClient` constructor when initializing a new requests session client. This allows for customization of the HTTP client, such as setting additional headers or configuring timeouts. For detailed information on available options, refer to the niquests library documentation [here](https://github.com/jawah/niquests/blob/954764c22f3c51ab51add3c6e3635bbfb3e1ee23/src/niquests/sessions.py#L252). If client is provided, this parameter will be ignored. """ self.lts_version = SeamMultiWorkspace.lts_version From f9e888c682a1c3e6dce6636de7db94e9af1d4efc Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Thu, 16 May 2024 19:34:47 +0200 Subject: [PATCH 32/48] Revert generate-routes script --- generate-routes.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/generate-routes.js b/generate-routes.js index a2e4ca0..c3aacef 100644 --- a/generate-routes.js +++ b/generate-routes.js @@ -11,8 +11,7 @@ import { deleteAsync } from 'del' const libName = 'seam' const rootPath = dirname(fileURLToPath(import.meta.url)) -const routesPath = resolve(libName, 'routes') -const outputPath = resolve(rootPath, routesPath) +const outputPath = resolve(rootPath, libName, 'routes') await deleteAsync(outputPath) @@ -20,11 +19,8 @@ const fileSystem = await generateSdk({ openApiSpecObject: openapi, }) -const files = Object.entries(fileSystem) - .filter(([fileName]) => fileName.startsWith(`${routesPath}/`)) - .map(([fileName, contents]) => [ - fileName.replace(/^seam\/routes\//, ''), - contents, - ]) +const files = Object.entries(fileSystem).filter(([fileName]) => + fileName.startsWith(`${libName}/routes/`), +) -writeFs(resolve(rootPath, 'seam'), Object.fromEntries(files)) +writeFs(rootPath, Object.fromEntries(files)) From e1af1de9406ac65a0f30efa3938ebc4cc0d75a01 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Thu, 16 May 2024 19:43:33 +0200 Subject: [PATCH 33/48] Revert package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 079fc94..d85a577 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "format": "prettier --write --ignore-path .gitignore ." }, "devDependencies": { - "@seamapi/nextlove-sdk-generator": "file:.yalc/@seamapi/nextlove-sdk-generator", + "@seamapi/nextlove-sdk-generator": "1.11.0", "@seamapi/types": "1.172.0", "del": "^7.1.0", "prettier": "^3.2.5" From 257321f6c499c40a5f5010f0d26888134e5fa8a6 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 17 May 2024 11:30:29 +0200 Subject: [PATCH 34/48] Add AbstractSeamHttpClient --- seam/__init__.py | 1 - seam/request.py | 4 ++-- seam/types.py | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/seam/__init__.py b/seam/__init__.py index 7e777bd..a85c80f 100644 --- a/seam/__init__.py +++ b/seam/__init__.py @@ -2,7 +2,6 @@ # type: ignore from seam.seam import Seam - from seam.types import SeamApiException from seam.seam_multi_workspace import SeamMultiWorkspace from seam.options import SeamHttpInvalidOptionsError diff --git a/seam/request.py b/seam/request.py index 692bc54..7c9aa6a 100644 --- a/seam/request.py +++ b/seam/request.py @@ -4,7 +4,7 @@ from importlib.metadata import version from seam.constants import LTS_VERSION -from seam.types import SeamApiException +from seam.types import AbstractSeamHttpClient, SeamApiException SDK_HEADERS = { "seam-sdk-name": "seamapi/python", @@ -13,7 +13,7 @@ } -class SeamHttpClient(requests.Session): +class SeamHttpClient(requests.Session, AbstractSeamHttpClient): def __init__(self, base_url: str, auth_headers: Dict[str, str], **kwargs): super().__init__(**kwargs) diff --git a/seam/types.py b/seam/types.py index 560752d..c7d257c 100644 --- a/seam/types.py +++ b/seam/types.py @@ -24,6 +24,20 @@ def __init__( ) +class AbstractSeamHttpClient(abc.ABC): + @abc.abstractmethod + def __init__(self, base_url: str, auth_headers: Dict[str, str], **kwargs): + raise NotImplementedError + + @abc.abstractmethod + def request(self, method: str, url: str, *args, **kwargs): + raise NotImplementedError + + @abc.abstractmethod + def _handle_response(self, response: requests.Response): + raise NotImplementedError + + class AbstractSeam(AbstractRoutes): lts_version: str wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] From b1deeb2271c1a3234ae412191a71422aadf8c51c Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 17 May 2024 12:08:18 +0200 Subject: [PATCH 35/48] Update client_options docstring --- seam/seam.py | 2 +- seam/seam_multi_workspace.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/seam/seam.py b/seam/seam.py index 621036f..96fa7f5 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -43,7 +43,7 @@ def __init__( client : requests.Session, optional A pre-configured requests session to be used for making HTTP requests. If not provided, a new `SeamHttpClient` instance will be created using the `client_options` and other relevant parameters. client_options : dict, optional - A dictionary of options that will be passed to the `SeamHttpClient` constructor when initializing a new requests session client. This allows for customization of the HTTP client, such as setting additional headers or configuring timeouts. For detailed information on available options, refer to the niquests library documentation [here](https://github.com/jawah/niquests/blob/954764c22f3c51ab51add3c6e3635bbfb3e1ee23/src/niquests/sessions.py#L252). If client is provided, this parameter will be ignored. + A dictionary of options that will be passed to the `SeamHttpClient` constructor when initializing a new requests session client. This allows for customization of the HTTP client, such as setting additional headers or configuring timeouts. For detailed information on available options, refer to the niquests library [repo](https://github.com/jawah/niquests). If client is provided, this parameter will be ignored. """ Routes.__init__(self) diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py index c0d512c..4567d12 100644 --- a/seam/seam_multi_workspace.py +++ b/seam/seam_multi_workspace.py @@ -51,7 +51,7 @@ def __init__( client : requests.Session, optional A pre-configured requests session to be used for making HTTP requests. If not provided, a new `SeamHttpClient` instance will be created using the `client_options` and other relevant parameters. client_options : dict, optional - A dictionary of options that will be passed to the `SeamHttpClient` constructor when initializing a new requests session client. This allows for customization of the HTTP client, such as setting additional headers or configuring timeouts. For detailed information on available options, refer to the niquests library documentation [here](https://github.com/jawah/niquests/blob/954764c22f3c51ab51add3c6e3635bbfb3e1ee23/src/niquests/sessions.py#L252). If client is provided, this parameter will be ignored. + A dictionary of options that will be passed to the `SeamHttpClient` constructor when initializing a new requests session client. This allows for customization of the HTTP client, such as setting additional headers or configuring timeouts. For detailed information on available options, refer to the niquests library [repo](https://github.com/jawah/niquests). If client is provided, this parameter will be ignored. """ self.lts_version = SeamMultiWorkspace.lts_version From 0b77f91ed1d5b4b6e05cdee6b6d9bded597c1b94 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 17 May 2024 14:48:04 +0200 Subject: [PATCH 36/48] Add seam/exceptions.py --- seam/exceptions.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 seam/exceptions.py diff --git a/seam/exceptions.py b/seam/exceptions.py new file mode 100644 index 0000000..2351116 --- /dev/null +++ b/seam/exceptions.py @@ -0,0 +1,23 @@ +from .routes.models import ActionAttempt + + +# Action Attempt +class SeamActionAttemptError(Exception): + def __init__(self, message: str, action_attempt: ActionAttempt): + super().__init__(message) + self.name = self.__class__.__name__ + self.action_attempt = action_attempt + + +class SeamActionAttemptFailedError(SeamActionAttemptError): + def __init__(self, action_attempt: ActionAttempt): + super().__init__(action_attempt.error.message, action_attempt) + self.name = self.__class__.__name__ + self.code = action_attempt.error.type + + +class SeamActionAttemptTimeoutError(SeamActionAttemptError): + def __init__(self, action_attempt: ActionAttempt, timeout: str): + message = f"Timed out waiting for action attempt after {timeout}s" + super().__init__(message, action_attempt) + self.name = self.__class__.__name__ From 36486375c5c903b99b4902a4ec7a99e0a6c05b70 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 17 May 2024 14:49:33 +0200 Subject: [PATCH 37/48] Bump generator to 1.11.1 to update imports and structure --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index df68a02..55c86d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "@seamapi/python", "devDependencies": { - "@seamapi/nextlove-sdk-generator": "1.11.0", + "@seamapi/nextlove-sdk-generator": "1.11.1", "@seamapi/types": "1.172.0", "del": "^7.1.0", "prettier": "^3.2.5" @@ -416,9 +416,9 @@ } }, "node_modules/@seamapi/nextlove-sdk-generator": { - "version": "1.11.0", + "version": "1.11.1", "resolved": "https://registry.npmjs.org/@seamapi/nextlove-sdk-generator/-/nextlove-sdk-generator-1.11.0.tgz", - "integrity": "sha512-eaBaTri2n961aLr32DU36iM4rMjpBIyikJe5yk1aueol96Ks5tIFtIh+T4XZvEU6IWwSI1aG43MQFeocLKSOXA==", + "integrity": "sha512-Na6ZTUMecOBA8UEqWSITiHfI7k+tUadqK3CVaThmqrKZ+OtHsQZS0zbN/oBUWpzY3SmjhQ7woxL8RELafNkOEg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index d85a577..aaed178 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "format": "prettier --write --ignore-path .gitignore ." }, "devDependencies": { - "@seamapi/nextlove-sdk-generator": "1.11.0", + "@seamapi/nextlove-sdk-generator": "1.11.1", "@seamapi/types": "1.172.0", "del": "^7.1.0", "prettier": "^3.2.5" From dd0768b795bffca60f65de76e64cea79c0f2a9f2 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 17 May 2024 14:51:35 +0200 Subject: [PATCH 38/48] Update generator's tarball link --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 55c86d5..cfd49e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -417,7 +417,7 @@ }, "node_modules/@seamapi/nextlove-sdk-generator": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@seamapi/nextlove-sdk-generator/-/nextlove-sdk-generator-1.11.0.tgz", + "resolved": "https://registry.npmjs.org/@seamapi/nextlove-sdk-generator/-/nextlove-sdk-generator-1.11.1.tgz", "integrity": "sha512-Na6ZTUMecOBA8UEqWSITiHfI7k+tUadqK3CVaThmqrKZ+OtHsQZS0zbN/oBUWpzY3SmjhQ7woxL8RELafNkOEg==", "dev": true, "license": "MIT", From be94343b2f9e5c533135f4b364f36d3ae07a562e Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Fri, 17 May 2024 12:52:22 +0000 Subject: [PATCH 39/48] ci: Generate code --- seam/routes/{routes.py => __init__.py} | 2 +- seam/routes/access_codes.py | 8 ++--- seam/routes/access_codes_simulate.py | 4 +-- seam/routes/access_codes_unmanaged.py | 4 +-- seam/routes/acs.py | 18 +++++------ seam/routes/acs_access_groups.py | 4 +-- seam/routes/acs_credential_pools.py | 4 +-- ...acs_credential_provisioning_automations.py | 6 ++-- seam/routes/acs_credentials.py | 4 +-- seam/routes/acs_entrances.py | 4 +-- seam/routes/acs_systems.py | 4 +-- seam/routes/acs_users.py | 4 +-- seam/routes/action_attempts.py | 32 ++++--------------- seam/routes/client_sessions.py | 4 +-- seam/routes/connect_webviews.py | 4 +-- seam/routes/connected_accounts.py | 4 +-- seam/routes/devices.py | 8 ++--- seam/routes/devices_simulate.py | 4 +-- seam/routes/devices_unmanaged.py | 4 +-- seam/routes/events.py | 4 +-- seam/routes/locks.py | 4 +-- seam/routes/{types.py => models.py} | 2 +- seam/routes/networks.py | 4 +-- seam/routes/noise_sensors.py | 8 ++--- seam/routes/noise_sensors_noise_thresholds.py | 4 +-- seam/routes/noise_sensors_simulate.py | 4 +-- seam/routes/phones.py | 6 ++-- seam/routes/phones_simulate.py | 4 +-- seam/routes/thermostats.py | 8 ++--- .../thermostats_climate_setting_schedules.py | 7 ++-- seam/routes/user_identities.py | 14 ++------ .../user_identities_enrollment_automations.py | 7 ++-- seam/routes/utils/deep_attr_dict.py | 26 --------------- seam/routes/webhooks.py | 4 +-- seam/routes/workspaces.py | 4 +-- 35 files changed, 87 insertions(+), 149 deletions(-) rename seam/routes/{routes.py => __init__.py} (97%) rename seam/routes/{types.py => models.py} (99%) delete mode 100644 seam/routes/utils/deep_attr_dict.py diff --git a/seam/routes/routes.py b/seam/routes/__init__.py similarity index 97% rename from seam/routes/routes.py rename to seam/routes/__init__.py index 35ba96e..cda43ec 100644 --- a/seam/routes/routes.py +++ b/seam/routes/__init__.py @@ -1,4 +1,4 @@ -from .types import AbstractRoutes +from .models import AbstractRoutes from .access_codes import AccessCodes from .acs import Acs from .action_attempts import ActionAttempts diff --git a/seam/routes/access_codes.py b/seam/routes/access_codes.py index d0f1abb..56a45ad 100644 --- a/seam/routes/access_codes.py +++ b/seam/routes/access_codes.py @@ -1,8 +1,8 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractAccessCodes, AccessCode from typing import Optional, Any, List, Dict, Union -from seam.routes.access_codes_simulate import AccessCodesSimulate -from seam.routes.access_codes_unmanaged import AccessCodesUnmanaged +from ..models import AbstractSeam as Seam +from .models import AbstractAccessCodes, AccessCode +from .access_codes_simulate import AccessCodesSimulate +from .access_codes_unmanaged import AccessCodesUnmanaged class AccessCodes(AbstractAccessCodes): diff --git a/seam/routes/access_codes_simulate.py b/seam/routes/access_codes_simulate.py index 00ecb28..08fe050 100644 --- a/seam/routes/access_codes_simulate.py +++ b/seam/routes/access_codes_simulate.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractAccessCodesSimulate, UnmanagedAccessCode from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractAccessCodesSimulate, UnmanagedAccessCode class AccessCodesSimulate(AbstractAccessCodesSimulate): diff --git a/seam/routes/access_codes_unmanaged.py b/seam/routes/access_codes_unmanaged.py index 05243d1..0ec6e56 100644 --- a/seam/routes/access_codes_unmanaged.py +++ b/seam/routes/access_codes_unmanaged.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractAccessCodesUnmanaged, UnmanagedAccessCode from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractAccessCodesUnmanaged, UnmanagedAccessCode class AccessCodesUnmanaged(AbstractAccessCodesUnmanaged): diff --git a/seam/routes/acs.py b/seam/routes/acs.py index c99783a..52c878e 100644 --- a/seam/routes/acs.py +++ b/seam/routes/acs.py @@ -1,15 +1,15 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractAcs from typing import Optional, Any, List, Dict, Union -from seam.routes.acs_access_groups import AcsAccessGroups -from seam.routes.acs_credential_pools import AcsCredentialPools -from seam.routes.acs_credential_provisioning_automations import ( +from ..models import AbstractSeam as Seam +from .models import AbstractAcs +from .acs_access_groups import AcsAccessGroups +from .acs_credential_pools import AcsCredentialPools +from .acs_credential_provisioning_automations import ( AcsCredentialProvisioningAutomations, ) -from seam.routes.acs_credentials import AcsCredentials -from seam.routes.acs_entrances import AcsEntrances -from seam.routes.acs_systems import AcsSystems -from seam.routes.acs_users import AcsUsers +from .acs_credentials import AcsCredentials +from .acs_entrances import AcsEntrances +from .acs_systems import AcsSystems +from .acs_users import AcsUsers class Acs(AbstractAcs): diff --git a/seam/routes/acs_access_groups.py b/seam/routes/acs_access_groups.py index 897eb64..404d387 100644 --- a/seam/routes/acs_access_groups.py +++ b/seam/routes/acs_access_groups.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractAcsAccessGroups, AcsAccessGroup, AcsUser from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractAcsAccessGroups, AcsAccessGroup, AcsUser class AcsAccessGroups(AbstractAcsAccessGroups): diff --git a/seam/routes/acs_credential_pools.py b/seam/routes/acs_credential_pools.py index c0764b6..4173665 100644 --- a/seam/routes/acs_credential_pools.py +++ b/seam/routes/acs_credential_pools.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractAcsCredentialPools, AcsCredentialPool from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractAcsCredentialPools, AcsCredentialPool class AcsCredentialPools(AbstractAcsCredentialPools): diff --git a/seam/routes/acs_credential_provisioning_automations.py b/seam/routes/acs_credential_provisioning_automations.py index df0214c..219803b 100644 --- a/seam/routes/acs_credential_provisioning_automations.py +++ b/seam/routes/acs_credential_provisioning_automations.py @@ -1,9 +1,9 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import ( +from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import ( AbstractAcsCredentialProvisioningAutomations, AcsCredentialProvisioningAutomation, ) -from typing import Optional, Any, List, Dict, Union class AcsCredentialProvisioningAutomations( diff --git a/seam/routes/acs_credentials.py b/seam/routes/acs_credentials.py index abc9289..1939e14 100644 --- a/seam/routes/acs_credentials.py +++ b/seam/routes/acs_credentials.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractAcsCredentials, AcsCredential from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractAcsCredentials, AcsCredential class AcsCredentials(AbstractAcsCredentials): diff --git a/seam/routes/acs_entrances.py b/seam/routes/acs_entrances.py index dd976ad..3709a2b 100644 --- a/seam/routes/acs_entrances.py +++ b/seam/routes/acs_entrances.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractAcsEntrances, AcsEntrance, AcsCredential from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractAcsEntrances, AcsEntrance, AcsCredential class AcsEntrances(AbstractAcsEntrances): diff --git a/seam/routes/acs_systems.py b/seam/routes/acs_systems.py index 9896ebd..03dca36 100644 --- a/seam/routes/acs_systems.py +++ b/seam/routes/acs_systems.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractAcsSystems, AcsSystem from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractAcsSystems, AcsSystem class AcsSystems(AbstractAcsSystems): diff --git a/seam/routes/acs_users.py b/seam/routes/acs_users.py index 01c3ef5..c418265 100644 --- a/seam/routes/acs_users.py +++ b/seam/routes/acs_users.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractAcsUsers, AcsUser, AcsEntrance from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractAcsUsers, AcsUser, AcsEntrance class AcsUsers(AbstractAcsUsers): diff --git a/seam/routes/action_attempts.py b/seam/routes/action_attempts.py index a9e110c..88b80fa 100644 --- a/seam/routes/action_attempts.py +++ b/seam/routes/action_attempts.py @@ -1,29 +1,9 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractActionAttempts, ActionAttempt from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractActionAttempts, ActionAttempt import time - - -class SeamActionAttemptError(Exception): - def __init__(self, message: str, action_attempt: ActionAttempt): - super().__init__(message) - self.name = self.__class__.__name__ - self.action_attempt = action_attempt - - -class SeamActionAttemptFailedError(SeamActionAttemptError): - def __init__(self, action_attempt: ActionAttempt): - super().__init__(action_attempt.error.message, action_attempt) - self.name = self.__class__.__name__ - self.code = action_attempt.error.type - - -class SeamActionAttemptTimeoutError(SeamActionAttemptError): - def __init__(self, action_attempt: ActionAttempt, timeout: str): - message = f"Timed out waiting for action attempt after {timeout}s" - super().__init__(message, action_attempt) - self.name = self.__class__.__name__ +from ..exceptions import SeamActionAttemptFailedError, SeamActionAttemptTimeoutError class ActionAttempts(AbstractActionAttempts): @@ -36,7 +16,7 @@ def get( self, *, action_attempt_id: str, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None ) -> ActionAttempt: json_payload = {} @@ -65,7 +45,7 @@ def poll_until_ready( *, action_attempt_id: str, timeout: Optional[float] = 5.0, - polling_interval: Optional[float] = 0.5, + polling_interval: Optional[float] = 0.5 ) -> ActionAttempt: seam = self.seam time_waiting = 0.0 @@ -95,7 +75,7 @@ def decide_and_wait( self, *, action_attempt: ActionAttempt, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None ) -> ActionAttempt: wait_decision = ( self.seam.wait_for_action_attempt diff --git a/seam/routes/client_sessions.py b/seam/routes/client_sessions.py index 9b14ad4..7d631a6 100644 --- a/seam/routes/client_sessions.py +++ b/seam/routes/client_sessions.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractClientSessions, ClientSession from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractClientSessions, ClientSession class ClientSessions(AbstractClientSessions): diff --git a/seam/routes/connect_webviews.py b/seam/routes/connect_webviews.py index bdc2fd9..fb6fbf2 100644 --- a/seam/routes/connect_webviews.py +++ b/seam/routes/connect_webviews.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractConnectWebviews, ConnectWebview from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractConnectWebviews, ConnectWebview class ConnectWebviews(AbstractConnectWebviews): diff --git a/seam/routes/connected_accounts.py b/seam/routes/connected_accounts.py index 2f6bc96..e87b41d 100644 --- a/seam/routes/connected_accounts.py +++ b/seam/routes/connected_accounts.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractConnectedAccounts, ConnectedAccount from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractConnectedAccounts, ConnectedAccount class ConnectedAccounts(AbstractConnectedAccounts): diff --git a/seam/routes/devices.py b/seam/routes/devices.py index 98156b8..34731ee 100644 --- a/seam/routes/devices.py +++ b/seam/routes/devices.py @@ -1,8 +1,8 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractDevices, Device, DeviceProvider from typing import Optional, Any, List, Dict, Union -from seam.routes.devices_simulate import DevicesSimulate -from seam.routes.devices_unmanaged import DevicesUnmanaged +from ..models import AbstractSeam as Seam +from .models import AbstractDevices, Device, DeviceProvider +from .devices_simulate import DevicesSimulate +from .devices_unmanaged import DevicesUnmanaged class Devices(AbstractDevices): diff --git a/seam/routes/devices_simulate.py b/seam/routes/devices_simulate.py index 537125a..fb20f0a 100644 --- a/seam/routes/devices_simulate.py +++ b/seam/routes/devices_simulate.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractDevicesSimulate from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractDevicesSimulate class DevicesSimulate(AbstractDevicesSimulate): diff --git a/seam/routes/devices_unmanaged.py b/seam/routes/devices_unmanaged.py index f68f940..6e295cb 100644 --- a/seam/routes/devices_unmanaged.py +++ b/seam/routes/devices_unmanaged.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractDevicesUnmanaged, UnmanagedDevice from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractDevicesUnmanaged, UnmanagedDevice class DevicesUnmanaged(AbstractDevicesUnmanaged): diff --git a/seam/routes/events.py b/seam/routes/events.py index bc56b8e..377a502 100644 --- a/seam/routes/events.py +++ b/seam/routes/events.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractEvents, Event from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractEvents, Event class Events(AbstractEvents): diff --git a/seam/routes/locks.py b/seam/routes/locks.py index afc19a6..6bad53a 100644 --- a/seam/routes/locks.py +++ b/seam/routes/locks.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractLocks, Device, ActionAttempt from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractLocks, Device, ActionAttempt class Locks(AbstractLocks): diff --git a/seam/routes/types.py b/seam/routes/models.py similarity index 99% rename from seam/routes/types.py rename to seam/routes/models.py index 5219144..71d8be9 100644 --- a/seam/routes/types.py +++ b/seam/routes/models.py @@ -2,7 +2,7 @@ from typing_extensions import Self import abc from dataclasses import dataclass -from seam.routes.utils.deep_attr_dict import DeepAttrDict +from ..utils.deep_attr_dict import DeepAttrDict @dataclass diff --git a/seam/routes/networks.py b/seam/routes/networks.py index 325e82d..10de1db 100644 --- a/seam/routes/networks.py +++ b/seam/routes/networks.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractNetworks, Network from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractNetworks, Network class Networks(AbstractNetworks): diff --git a/seam/routes/noise_sensors.py b/seam/routes/noise_sensors.py index afbd775..fefabef 100644 --- a/seam/routes/noise_sensors.py +++ b/seam/routes/noise_sensors.py @@ -1,8 +1,8 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractNoiseSensors from typing import Optional, Any, List, Dict, Union -from seam.routes.noise_sensors_noise_thresholds import NoiseSensorsNoiseThresholds -from seam.routes.noise_sensors_simulate import NoiseSensorsSimulate +from ..models import AbstractSeam as Seam +from .models import AbstractNoiseSensors +from .noise_sensors_noise_thresholds import NoiseSensorsNoiseThresholds +from .noise_sensors_simulate import NoiseSensorsSimulate class NoiseSensors(AbstractNoiseSensors): diff --git a/seam/routes/noise_sensors_noise_thresholds.py b/seam/routes/noise_sensors_noise_thresholds.py index 09aca91..7edafe3 100644 --- a/seam/routes/noise_sensors_noise_thresholds.py +++ b/seam/routes/noise_sensors_noise_thresholds.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractNoiseSensorsNoiseThresholds, NoiseThreshold from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractNoiseSensorsNoiseThresholds, NoiseThreshold class NoiseSensorsNoiseThresholds(AbstractNoiseSensorsNoiseThresholds): diff --git a/seam/routes/noise_sensors_simulate.py b/seam/routes/noise_sensors_simulate.py index 001fcb6..8aa2318 100644 --- a/seam/routes/noise_sensors_simulate.py +++ b/seam/routes/noise_sensors_simulate.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractNoiseSensorsSimulate from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractNoiseSensorsSimulate class NoiseSensorsSimulate(AbstractNoiseSensorsSimulate): diff --git a/seam/routes/phones.py b/seam/routes/phones.py index 8350159..b1babe5 100644 --- a/seam/routes/phones.py +++ b/seam/routes/phones.py @@ -1,7 +1,7 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractPhones, Phone from typing import Optional, Any, List, Dict, Union -from seam.routes.phones_simulate import PhonesSimulate +from ..models import AbstractSeam as Seam +from .models import AbstractPhones, Phone +from .phones_simulate import PhonesSimulate class Phones(AbstractPhones): diff --git a/seam/routes/phones_simulate.py b/seam/routes/phones_simulate.py index f302435..f4db7ab 100644 --- a/seam/routes/phones_simulate.py +++ b/seam/routes/phones_simulate.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractPhonesSimulate, Phone from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractPhonesSimulate, Phone class PhonesSimulate(AbstractPhonesSimulate): diff --git a/seam/routes/thermostats.py b/seam/routes/thermostats.py index b54fddc..fa29ca7 100644 --- a/seam/routes/thermostats.py +++ b/seam/routes/thermostats.py @@ -1,9 +1,7 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractThermostats, ActionAttempt, Device from typing import Optional, Any, List, Dict, Union -from seam.routes.thermostats_climate_setting_schedules import ( - ThermostatsClimateSettingSchedules, -) +from ..models import AbstractSeam as Seam +from .models import AbstractThermostats, ActionAttempt, Device +from .thermostats_climate_setting_schedules import ThermostatsClimateSettingSchedules class Thermostats(AbstractThermostats): diff --git a/seam/routes/thermostats_climate_setting_schedules.py b/seam/routes/thermostats_climate_setting_schedules.py index 30e6a03..90e3fb0 100644 --- a/seam/routes/thermostats_climate_setting_schedules.py +++ b/seam/routes/thermostats_climate_setting_schedules.py @@ -1,9 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import ( - AbstractThermostatsClimateSettingSchedules, - ClimateSettingSchedule, -) from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractThermostatsClimateSettingSchedules, ClimateSettingSchedule class ThermostatsClimateSettingSchedules(AbstractThermostatsClimateSettingSchedules): diff --git a/seam/routes/user_identities.py b/seam/routes/user_identities.py index fe186c9..50986dd 100644 --- a/seam/routes/user_identities.py +++ b/seam/routes/user_identities.py @@ -1,15 +1,7 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import ( - AbstractUserIdentities, - UserIdentity, - Device, - AcsSystem, - AcsUser, -) from typing import Optional, Any, List, Dict, Union -from seam.routes.user_identities_enrollment_automations import ( - UserIdentitiesEnrollmentAutomations, -) +from ..models import AbstractSeam as Seam +from .models import AbstractUserIdentities, UserIdentity, Device, AcsSystem, AcsUser +from .user_identities_enrollment_automations import UserIdentitiesEnrollmentAutomations class UserIdentities(AbstractUserIdentities): diff --git a/seam/routes/user_identities_enrollment_automations.py b/seam/routes/user_identities_enrollment_automations.py index dce3940..b8ff459 100644 --- a/seam/routes/user_identities_enrollment_automations.py +++ b/seam/routes/user_identities_enrollment_automations.py @@ -1,9 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import ( - AbstractUserIdentitiesEnrollmentAutomations, - EnrollmentAutomation, -) from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractUserIdentitiesEnrollmentAutomations, EnrollmentAutomation class UserIdentitiesEnrollmentAutomations(AbstractUserIdentitiesEnrollmentAutomations): diff --git a/seam/routes/utils/deep_attr_dict.py b/seam/routes/utils/deep_attr_dict.py deleted file mode 100644 index 341f0a9..0000000 --- a/seam/routes/utils/deep_attr_dict.py +++ /dev/null @@ -1,26 +0,0 @@ -# https://stackoverflow.com/a/3031270/559475 -class DeepAttrDict(dict): - MARKER = object() - - def __init__(self, value=None): - if value is None: - pass - elif isinstance(value, dict): - for key in value: - self.__setitem__(key, value[key]) - else: - raise TypeError("expected dict") - - def __setitem__(self, key, value): - if isinstance(value, dict) and not isinstance(value, DeepAttrDict): - value = DeepAttrDict(value) - super(DeepAttrDict, self).__setitem__(key, value) - - def __getitem__(self, key): - found = self.get(key, DeepAttrDict.MARKER) - if found is DeepAttrDict.MARKER: - found = DeepAttrDict() - super(DeepAttrDict, self).__setitem__(key, found) - return found - - __setattr__, __getattr__ = __setitem__, __getitem__ diff --git a/seam/routes/webhooks.py b/seam/routes/webhooks.py index d764eff..2845885 100644 --- a/seam/routes/webhooks.py +++ b/seam/routes/webhooks.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractWebhooks, Webhook from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractWebhooks, Webhook class Webhooks(AbstractWebhooks): diff --git a/seam/routes/workspaces.py b/seam/routes/workspaces.py index 660d94b..850f890 100644 --- a/seam/routes/workspaces.py +++ b/seam/routes/workspaces.py @@ -1,6 +1,6 @@ -from seam.types import AbstractSeam as Seam -from seam.routes.types import AbstractWorkspaces, Workspace, ActionAttempt from typing import Optional, Any, List, Dict, Union +from ..models import AbstractSeam as Seam +from .models import AbstractWorkspaces, Workspace, ActionAttempt class Workspaces(AbstractWorkspaces): From 2c66246ef134edb580e12244a9b0ee73f8e337f1 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 17 May 2024 14:53:41 +0200 Subject: [PATCH 40/48] Add deep_attr_dict to /seam/utils --- seam/utils/deep_attr_dict.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 seam/utils/deep_attr_dict.py diff --git a/seam/utils/deep_attr_dict.py b/seam/utils/deep_attr_dict.py new file mode 100644 index 0000000..341f0a9 --- /dev/null +++ b/seam/utils/deep_attr_dict.py @@ -0,0 +1,26 @@ +# https://stackoverflow.com/a/3031270/559475 +class DeepAttrDict(dict): + MARKER = object() + + def __init__(self, value=None): + if value is None: + pass + elif isinstance(value, dict): + for key in value: + self.__setitem__(key, value[key]) + else: + raise TypeError("expected dict") + + def __setitem__(self, key, value): + if isinstance(value, dict) and not isinstance(value, DeepAttrDict): + value = DeepAttrDict(value) + super(DeepAttrDict, self).__setitem__(key, value) + + def __getitem__(self, key): + found = self.get(key, DeepAttrDict.MARKER) + if found is DeepAttrDict.MARKER: + found = DeepAttrDict() + super(DeepAttrDict, self).__setitem__(key, found) + return found + + __setattr__, __getattr__ = __setitem__, __getitem__ From 710edff8ae5e80882ad6fbcd7231a355c05c271a Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 17 May 2024 14:54:29 +0200 Subject: [PATCH 41/48] Renam types.py to models.py --- seam/models.py | 115 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 seam/models.py diff --git a/seam/models.py b/seam/models.py new file mode 100644 index 0000000..90ed91d --- /dev/null +++ b/seam/models.py @@ -0,0 +1,115 @@ +from typing import Any, Dict, List, Optional, Union +from typing_extensions import Self +import abc + +from .routes.models import AbstractRoutes, Workspace + + +class SeamApiException(Exception): + def __init__( + self, + response, + ): + self.status_code = response.status_code + self.request_id = response.headers.get("seam-request-id", None) + + self.metadata = None + if "application/json" in response.headers["content-type"]: + parsed_response = response.json() + self.metadata = parsed_response.get("error", None) + + super().__init__( + f"SeamApiException: status={self.status_code}, request_id={self.request_id}, metadata={self.metadata}" + ) + + +class AbstractRequestMixin(abc.ABC): + @abc.abstractmethod + def make_request(self, method: str, path: str, **kwargs): + raise NotImplementedError + + +class AbstractSeam(AbstractRoutes, AbstractRequestMixin): + lts_version: str + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] + + @abc.abstractmethod + def __init__( + self, + api_key: Optional[str] = None, + *, + personal_access_token: Optional[str] = None, + workspace_id: Optional[str] = None, + endpoint: Optional[str] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + ): + raise NotImplementedError + + @classmethod + @abc.abstractmethod + def from_api_key( + cls, + api_key: str, + *, + endpoint: Optional[str] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + ) -> Self: + raise NotImplementedError + + @classmethod + @abc.abstractmethod + def from_personal_access_token( + cls, + personal_access_token: str, + workspace_id: str, + *, + endpoint: Optional[str] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + ) -> Self: + raise NotImplementedError + + +class AbstractSeamMultiWorkspaceWorkspaces(abc.ABC): + @abc.abstractmethod + def create( + self, + *, + connect_partner_name: str, + name: str, + is_sandbox: Optional[bool] = None, + webview_logo_shape: Optional[str] = None, + webview_primary_button_color: Optional[str] = None, + ) -> Workspace: + raise NotImplementedError() + + @abc.abstractmethod + def list( + self, + ) -> List[Workspace]: + raise NotImplementedError() + + +class AbstractSeamMultiWorkspace(AbstractRequestMixin): + lts_version: str + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] + + @abc.abstractmethod + def __init__( + self, + personal_access_token: str, + *, + endpoint: Optional[str] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + ): + raise NotImplementedError + + @classmethod + @abc.abstractmethod + def from_personal_access_token( + cls, + personal_access_token: str, + *, + endpoint: Optional[str] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, + ) -> Self: + raise NotImplementedError From e40ae9ede845ce03e21dd16fe25d52375b5b2aa1 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 17 May 2024 14:54:37 +0200 Subject: [PATCH 42/48] Update top level imports --- seam/__init__.py | 12 ++++++------ seam/seam.py | 2 +- seam/types.py | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/seam/__init__.py b/seam/__init__.py index a85c80f..daed9ad 100644 --- a/seam/__init__.py +++ b/seam/__init__.py @@ -1,12 +1,12 @@ # flake8: noqa # type: ignore -from seam.seam import Seam -from seam.types import SeamApiException -from seam.seam_multi_workspace import SeamMultiWorkspace -from seam.options import SeamHttpInvalidOptionsError -from seam.auth import SeamHttpInvalidTokenError -from seam.routes.action_attempts import ( +from .seam import Seam +from .types import SeamApiException +from .seam_multi_workspace import SeamMultiWorkspace +from .options import SeamHttpInvalidOptionsError +from .auth import SeamHttpInvalidTokenError +from .exceptions import ( SeamActionAttemptError, SeamActionAttemptFailedError, SeamActionAttemptTimeoutError, diff --git a/seam/seam.py b/seam/seam.py index 3cb5cb8..b9f04d6 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -4,7 +4,7 @@ from .constants import LTS_VERSION from .parse_options import parse_options from .request import RequestMixin -from .routes.routes import Routes +from .routes import Routes from .types import AbstractSeam diff --git a/seam/types.py b/seam/types.py index 5afcd03..b08a6f4 100644 --- a/seam/types.py +++ b/seam/types.py @@ -2,7 +2,8 @@ from typing_extensions import Self import abc -from seam.routes.types import AbstractRoutes, Workspace +from .routes import AbstractRoutes +from .models import Workspace class SeamApiException(Exception): From e905135c1184d868babcca5f8ebdf14abf228581 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 17 May 2024 14:58:05 +0200 Subject: [PATCH 43/48] Remove seam/types.py, use seam/models.py --- seam/__init__.py | 2 +- seam/request.py | 2 +- seam/seam.py | 2 +- seam/seam_multi_workspace.py | 2 +- seam/types.py | 116 ----------------------------------- 5 files changed, 4 insertions(+), 120 deletions(-) delete mode 100644 seam/types.py diff --git a/seam/__init__.py b/seam/__init__.py index daed9ad..63bde96 100644 --- a/seam/__init__.py +++ b/seam/__init__.py @@ -2,7 +2,7 @@ # type: ignore from .seam import Seam -from .types import SeamApiException +from .models import SeamApiException from .seam_multi_workspace import SeamMultiWorkspace from .options import SeamHttpInvalidOptionsError from .auth import SeamHttpInvalidTokenError diff --git a/seam/request.py b/seam/request.py index 11dcc84..21c1409 100644 --- a/seam/request.py +++ b/seam/request.py @@ -1,7 +1,7 @@ import requests from importlib.metadata import version -from seam.types import AbstractRequestMixin, SeamApiException +from .models import AbstractRequestMixin, SeamApiException class RequestMixin(AbstractRequestMixin): diff --git a/seam/seam.py b/seam/seam.py index b9f04d6..2c4683e 100644 --- a/seam/seam.py +++ b/seam/seam.py @@ -5,7 +5,7 @@ from .parse_options import parse_options from .request import RequestMixin from .routes import Routes -from .types import AbstractSeam +from .models import AbstractSeam class Seam(AbstractSeam, RequestMixin): diff --git a/seam/seam_multi_workspace.py b/seam/seam_multi_workspace.py index d5e240f..96bdc3e 100644 --- a/seam/seam_multi_workspace.py +++ b/seam/seam_multi_workspace.py @@ -5,7 +5,7 @@ from .constants import LTS_VERSION from .options import get_endpoint from .request import RequestMixin -from .types import AbstractSeamMultiWorkspace +from .models import AbstractSeamMultiWorkspace from .routes.workspaces import Workspaces diff --git a/seam/types.py b/seam/types.py deleted file mode 100644 index b08a6f4..0000000 --- a/seam/types.py +++ /dev/null @@ -1,116 +0,0 @@ -from typing import Any, Dict, List, Optional, Union -from typing_extensions import Self -import abc - -from .routes import AbstractRoutes -from .models import Workspace - - -class SeamApiException(Exception): - def __init__( - self, - response, - ): - self.status_code = response.status_code - self.request_id = response.headers.get("seam-request-id", None) - - self.metadata = None - if "application/json" in response.headers["content-type"]: - parsed_response = response.json() - self.metadata = parsed_response.get("error", None) - - super().__init__( - f"SeamApiException: status={self.status_code}, request_id={self.request_id}, metadata={self.metadata}" - ) - - -class AbstractRequestMixin(abc.ABC): - @abc.abstractmethod - def make_request(self, method: str, path: str, **kwargs): - raise NotImplementedError - - -class AbstractSeam(AbstractRoutes, AbstractRequestMixin): - lts_version: str - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] - - @abc.abstractmethod - def __init__( - self, - api_key: Optional[str] = None, - *, - personal_access_token: Optional[str] = None, - workspace_id: Optional[str] = None, - endpoint: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, - ): - raise NotImplementedError - - @classmethod - @abc.abstractmethod - def from_api_key( - cls, - api_key: str, - *, - endpoint: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, - ) -> Self: - raise NotImplementedError - - @classmethod - @abc.abstractmethod - def from_personal_access_token( - cls, - personal_access_token: str, - workspace_id: str, - *, - endpoint: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, - ) -> Self: - raise NotImplementedError - - -class AbstractSeamMultiWorkspaceWorkspaces(abc.ABC): - @abc.abstractmethod - def create( - self, - *, - connect_partner_name: str, - name: str, - is_sandbox: Optional[bool] = None, - webview_logo_shape: Optional[str] = None, - webview_primary_button_color: Optional[str] = None, - ) -> Workspace: - raise NotImplementedError() - - @abc.abstractmethod - def list( - self, - ) -> List[Workspace]: - raise NotImplementedError() - - -class AbstractSeamMultiWorkspace(AbstractRequestMixin): - lts_version: str - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] - - @abc.abstractmethod - def __init__( - self, - personal_access_token: str, - *, - endpoint: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, - ): - raise NotImplementedError - - @classmethod - @abc.abstractmethod - def from_personal_access_token( - cls, - personal_access_token: str, - *, - endpoint: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, - ) -> Self: - raise NotImplementedError From 3ddcdd4f4b0496b349b44165417b61e6247ec6fe Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 17 May 2024 15:09:45 +0200 Subject: [PATCH 44/48] Update test/events/test_events.py import --- test/events/test_events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/events/test_events.py b/test/events/test_events.py index 02f1cf7..bee416c 100644 --- a/test/events/test_events.py +++ b/test/events/test_events.py @@ -1,7 +1,7 @@ import time from seam import Seam -from seam.types import SeamApiException +from seam.models import SeamApiException SINCE = "2021-01-01T00:00:00.000Z" EVENT_TYPE = "device.connected" From ed6bfe23533df43c396ceb08889c2984924f0b5a Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 17 May 2024 15:16:27 +0200 Subject: [PATCH 45/48] Update response type on SeamApiException --- seam/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seam/models.py b/seam/models.py index 90ed91d..53804ac 100644 --- a/seam/models.py +++ b/seam/models.py @@ -1,6 +1,7 @@ from typing import Any, Dict, List, Optional, Union from typing_extensions import Self import abc +from niquests import Response from .routes.models import AbstractRoutes, Workspace @@ -8,7 +9,7 @@ class SeamApiException(Exception): def __init__( self, - response, + response: Response, ): self.status_code = response.status_code self.request_id = response.headers.get("seam-request-id", None) From 6d62541f767a8d451de48fe68678688f9b03586d Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Fri, 17 May 2024 15:20:39 +0200 Subject: [PATCH 46/48] Move SeamApiException to exceptions.py and update imports --- seam/__init__.py | 2 +- seam/exceptions.py | 20 ++++++++++++++++++ seam/models.py | 21 +------------------ seam/request.py | 3 ++- test/access_codes/test_access_codes.py | 2 +- .../test_connected_accounts.py | 2 +- test/devices/test_devices.py | 2 +- test/events/test_events.py | 3 +-- .../noise_thresholds/test_noise_thresholds.py | 2 +- 9 files changed, 29 insertions(+), 28 deletions(-) diff --git a/seam/__init__.py b/seam/__init__.py index 63bde96..d3c2324 100644 --- a/seam/__init__.py +++ b/seam/__init__.py @@ -2,11 +2,11 @@ # type: ignore from .seam import Seam -from .models import SeamApiException from .seam_multi_workspace import SeamMultiWorkspace from .options import SeamHttpInvalidOptionsError from .auth import SeamHttpInvalidTokenError from .exceptions import ( + SeamApiException, SeamActionAttemptError, SeamActionAttemptFailedError, SeamActionAttemptTimeoutError, diff --git a/seam/exceptions.py b/seam/exceptions.py index 2351116..df54dee 100644 --- a/seam/exceptions.py +++ b/seam/exceptions.py @@ -1,6 +1,26 @@ +from niquests import Response from .routes.models import ActionAttempt +# HTTP +class SeamApiException(Exception): + def __init__( + self, + response: Response, + ): + self.status_code = response.status_code + self.request_id = response.headers.get("seam-request-id", None) + + self.metadata = None + if "application/json" in response.headers["content-type"]: + parsed_response = response.json() + self.metadata = parsed_response.get("error", None) + + super().__init__( + f"SeamApiException: status={self.status_code}, request_id={self.request_id}, metadata={self.metadata}" + ) + + # Action Attempt class SeamActionAttemptError(Exception): def __init__(self, message: str, action_attempt: ActionAttempt): diff --git a/seam/models.py b/seam/models.py index 53804ac..4c6d993 100644 --- a/seam/models.py +++ b/seam/models.py @@ -1,29 +1,10 @@ -from typing import Any, Dict, List, Optional, Union +from typing import Dict, List, Optional, Union from typing_extensions import Self import abc -from niquests import Response from .routes.models import AbstractRoutes, Workspace -class SeamApiException(Exception): - def __init__( - self, - response: Response, - ): - self.status_code = response.status_code - self.request_id = response.headers.get("seam-request-id", None) - - self.metadata = None - if "application/json" in response.headers["content-type"]: - parsed_response = response.json() - self.metadata = parsed_response.get("error", None) - - super().__init__( - f"SeamApiException: status={self.status_code}, request_id={self.request_id}, metadata={self.metadata}" - ) - - class AbstractRequestMixin(abc.ABC): @abc.abstractmethod def make_request(self, method: str, path: str, **kwargs): diff --git a/seam/request.py b/seam/request.py index 21c1409..fcee805 100644 --- a/seam/request.py +++ b/seam/request.py @@ -1,7 +1,8 @@ import requests from importlib.metadata import version -from .models import AbstractRequestMixin, SeamApiException +from .models import AbstractRequestMixin +from .exceptions import SeamApiException class RequestMixin(AbstractRequestMixin): diff --git a/test/access_codes/test_access_codes.py b/test/access_codes/test_access_codes.py index a325a4c..0c53eaa 100644 --- a/test/access_codes/test_access_codes.py +++ b/test/access_codes/test_access_codes.py @@ -1,5 +1,5 @@ from seam import Seam -from seam.types import SeamApiException +from seam.exceptions import SeamApiException import pytest diff --git a/test/connected_accounts/test_connected_accounts.py b/test/connected_accounts/test_connected_accounts.py index d45e5f1..ef51adc 100644 --- a/test/connected_accounts/test_connected_accounts.py +++ b/test/connected_accounts/test_connected_accounts.py @@ -1,5 +1,5 @@ from seam import Seam -from seam.types import SeamApiException +from seam.exceptions import SeamApiException EMAIL = "john@example.com" diff --git a/test/devices/test_devices.py b/test/devices/test_devices.py index 4e15938..03b83b5 100644 --- a/test/devices/test_devices.py +++ b/test/devices/test_devices.py @@ -1,5 +1,5 @@ from seam import Seam -from seam.types import SeamApiException +from seam.exceptions import SeamApiException def test_devices(seam: Seam): diff --git a/test/events/test_events.py b/test/events/test_events.py index bee416c..daf23e2 100644 --- a/test/events/test_events.py +++ b/test/events/test_events.py @@ -1,7 +1,6 @@ -import time from seam import Seam -from seam.models import SeamApiException +from seam.exceptions import SeamApiException SINCE = "2021-01-01T00:00:00.000Z" EVENT_TYPE = "device.connected" diff --git a/test/noise_sensors/noise_thresholds/test_noise_thresholds.py b/test/noise_sensors/noise_thresholds/test_noise_thresholds.py index a869b56..e882327 100644 --- a/test/noise_sensors/noise_thresholds/test_noise_thresholds.py +++ b/test/noise_sensors/noise_thresholds/test_noise_thresholds.py @@ -1,6 +1,6 @@ import time from seam import Seam -from seam.types import SeamApiException +from seam.exceptions import SeamApiException import pytest From 089e262965e24f516f15e0aebce1fea473a39b55 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Mon, 20 May 2024 13:09:56 +0200 Subject: [PATCH 47/48] Update imports in tests --- test/access_codes/test_access_codes.py | 3 +-- test/connected_accounts/test_connected_accounts.py | 3 +-- test/devices/test_devices.py | 3 +-- test/events/test_events.py | 3 +-- test/noise_sensors/noise_thresholds/test_noise_thresholds.py | 3 +-- test/utils/test_deep_attr_dict.py | 2 +- test/workspaces/test_workspaces_create.py | 3 +-- 7 files changed, 7 insertions(+), 13 deletions(-) diff --git a/test/access_codes/test_access_codes.py b/test/access_codes/test_access_codes.py index 0c53eaa..573a952 100644 --- a/test/access_codes/test_access_codes.py +++ b/test/access_codes/test_access_codes.py @@ -1,5 +1,4 @@ -from seam import Seam -from seam.exceptions import SeamApiException +from seam import Seam, SeamApiException import pytest diff --git a/test/connected_accounts/test_connected_accounts.py b/test/connected_accounts/test_connected_accounts.py index ef51adc..49bc8dd 100644 --- a/test/connected_accounts/test_connected_accounts.py +++ b/test/connected_accounts/test_connected_accounts.py @@ -1,5 +1,4 @@ -from seam import Seam -from seam.exceptions import SeamApiException +from seam import Seam, SeamApiException EMAIL = "john@example.com" diff --git a/test/devices/test_devices.py b/test/devices/test_devices.py index 03b83b5..e990f77 100644 --- a/test/devices/test_devices.py +++ b/test/devices/test_devices.py @@ -1,5 +1,4 @@ -from seam import Seam -from seam.exceptions import SeamApiException +from seam import Seam, SeamApiException def test_devices(seam: Seam): diff --git a/test/events/test_events.py b/test/events/test_events.py index daf23e2..644e242 100644 --- a/test/events/test_events.py +++ b/test/events/test_events.py @@ -1,6 +1,5 @@ -from seam import Seam +from seam import Seam, SeamApiException -from seam.exceptions import SeamApiException SINCE = "2021-01-01T00:00:00.000Z" EVENT_TYPE = "device.connected" diff --git a/test/noise_sensors/noise_thresholds/test_noise_thresholds.py b/test/noise_sensors/noise_thresholds/test_noise_thresholds.py index e882327..9f06ece 100644 --- a/test/noise_sensors/noise_thresholds/test_noise_thresholds.py +++ b/test/noise_sensors/noise_thresholds/test_noise_thresholds.py @@ -1,6 +1,5 @@ import time -from seam import Seam -from seam.exceptions import SeamApiException +from seam import Seam, SeamApiException import pytest diff --git a/test/utils/test_deep_attr_dict.py b/test/utils/test_deep_attr_dict.py index 48767fe..afdc331 100644 --- a/test/utils/test_deep_attr_dict.py +++ b/test/utils/test_deep_attr_dict.py @@ -1,4 +1,4 @@ -from seam.routes.utils.deep_attr_dict import DeepAttrDict +from seam.utils.deep_attr_dict import DeepAttrDict def test_deep_attr_dict(): diff --git a/test/workspaces/test_workspaces_create.py b/test/workspaces/test_workspaces_create.py index f755022..1f5ebdf 100644 --- a/test/workspaces/test_workspaces_create.py +++ b/test/workspaces/test_workspaces_create.py @@ -1,7 +1,6 @@ import random import string -from seam import Seam -from seam.seam_multi_workspace import SeamMultiWorkspace +from seam import Seam, SeamMultiWorkspace def test_workspaces_create(seam: Seam): From 3c31b65be4527ecbbd47827e8a43a0a81862a5be Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Mon, 20 May 2024 13:12:20 +0200 Subject: [PATCH 48/48] More relative imports --- seam/auth.py | 4 ++-- seam/options.py | 2 +- seam/parse_options.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/seam/auth.py b/seam/auth.py index 90c4605..0daf860 100644 --- a/seam/auth.py +++ b/seam/auth.py @@ -1,10 +1,10 @@ from typing import Optional -from seam.options import ( +from .options import ( SeamHttpInvalidOptionsError, is_seam_http_options_with_api_key, is_seam_http_options_with_personal_access_token, ) -from seam.token import ( +from .token import ( is_jwt, is_access_token, is_client_session_token, diff --git a/seam/options.py b/seam/options.py index e7b5ba7..c9b0a1f 100644 --- a/seam/options.py +++ b/seam/options.py @@ -1,7 +1,7 @@ import os from typing import Optional -from seam.constants import DEFAULT_ENDPOINT +from .constants import DEFAULT_ENDPOINT def get_endpoint(endpoint: Optional[str] = None): diff --git a/seam/parse_options.py b/seam/parse_options.py index 4e40ca8..afd0e56 100644 --- a/seam/parse_options.py +++ b/seam/parse_options.py @@ -1,8 +1,8 @@ import os from typing import Optional -from seam.auth import get_auth_headers -from seam.options import get_endpoint +from .auth import get_auth_headers +from .options import get_endpoint def parse_options(