From 1667495efb0b419c07fb7d91e87459a98e09510f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 May 2024 09:44:47 +0000 Subject: [PATCH 1/2] feat(deps-dev): bump the seam group across 1 directory with 2 updates Bumps the seam group with 2 updates in the / directory: [@seamapi/nextlove-sdk-generator](https://github.com/seamapi/nextlove-sdk-generator) and [@seamapi/types](https://github.com/seamapi/types). Updates `@seamapi/nextlove-sdk-generator` from 1.10.5 to 1.11.0 - [Commits](https://github.com/seamapi/nextlove-sdk-generator/commits) Updates `@seamapi/types` from 1.164.0 to 1.174.0 - [Release notes](https://github.com/seamapi/types/releases) - [Changelog](https://github.com/seamapi/types/blob/main/.releaserc.json) - [Commits](https://github.com/seamapi/types/compare/v1.164.0...v1.174.0) --- updated-dependencies: - dependency-name: "@seamapi/nextlove-sdk-generator" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: seam - dependency-name: "@seamapi/types" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: seam ... Signed-off-by: dependabot[bot] --- package-lock.json | 17 ++++++++--------- package.json | 4 ++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index c322364..13a5b66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,8 +6,8 @@ "": { "name": "@seamapi/python", "devDependencies": { - "@seamapi/nextlove-sdk-generator": "^1.10.5", - "@seamapi/types": "1.164.0", + "@seamapi/nextlove-sdk-generator": "^1.11.0", + "@seamapi/types": "1.174.0", "del": "^7.1.0", "prettier": "^3.2.5" } @@ -416,11 +416,10 @@ } }, "node_modules/@seamapi/nextlove-sdk-generator": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/@seamapi/nextlove-sdk-generator/-/nextlove-sdk-generator-1.10.5.tgz", - "integrity": "sha512-kXOETQ9VQP1+I00tw8Xq7AytDaTA/U5WWmGTIZLmBNO3Oa4g5B2YP666Z5pdfvqCU48djiYT3OYY9cHacp3Pog==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@seamapi/nextlove-sdk-generator/-/nextlove-sdk-generator-1.11.0.tgz", + "integrity": "sha512-eaBaTri2n961aLr32DU36iM4rMjpBIyikJe5yk1aueol96Ks5tIFtIh+T4XZvEU6IWwSI1aG43MQFeocLKSOXA==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.walk": "^2.0.0", "axios": "^1.5.0", @@ -438,9 +437,9 @@ } }, "node_modules/@seamapi/types": { - "version": "1.164.0", - "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.164.0.tgz", - "integrity": "sha512-/07lxxOUCUv+/QwRPjF/0KaIrr5SFlIQ5eU7K5A7vX/N9nYHEXSRNbr2LIDBBLNo6A2fefDpcPfxFzDw6xENiA==", + "version": "1.174.0", + "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.174.0.tgz", + "integrity": "sha512-IpcM/22LHPz4aj0Hesf1MrthRqWK1EO+fqXRwCKvtPoM7NbgDN5Z260PMSou9EsxZRNoXzIWzboqMC3pPaYcig==", "dev": true, "engines": { "node": ">=18.12.0", diff --git a/package.json b/package.json index 69c42ae..0c4f665 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "format": "prettier --write --ignore-path .gitignore ." }, "devDependencies": { - "@seamapi/nextlove-sdk-generator": "^1.10.5", - "@seamapi/types": "1.164.0", + "@seamapi/nextlove-sdk-generator": "^1.11.0", + "@seamapi/types": "1.174.0", "del": "^7.1.0", "prettier": "^3.2.5" } From 4462e79b56f10f94d59c9055466a8a7b7f5f90a6 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 16 May 2024 09:45:24 +0000 Subject: [PATCH 2/2] ci: Generate code --- seam/__init__.py | 11 - seam/auth.py | 101 --------- seam/options.py | 66 ------ seam/parse_options.py | 26 --- seam/{ => routes}/access_codes.py | 7 +- seam/{ => routes}/access_codes_simulate.py | 7 +- seam/{ => routes}/access_codes_unmanaged.py | 7 +- seam/{ => routes}/acs.py | 17 +- seam/{ => routes}/acs_access_groups.py | 8 +- seam/{ => routes}/acs_credential_pools.py | 7 +- ...acs_credential_provisioning_automations.py | 4 +- seam/{ => routes}/acs_credentials.py | 25 ++- seam/{ => routes}/acs_entrances.py | 8 +- seam/{ => routes}/acs_systems.py | 19 +- seam/{ => routes}/acs_users.py | 3 +- seam/{ => routes}/action_attempts.py | 33 ++- seam/{ => routes}/client_sessions.py | 3 +- seam/{ => routes}/connect_webviews.py | 3 +- seam/{ => routes}/connected_accounts.py | 3 +- seam/{ => routes}/devices.py | 7 +- seam/{ => routes}/devices_simulate.py | 3 +- seam/{ => routes}/devices_unmanaged.py | 3 +- seam/{ => routes}/events.py | 3 +- seam/{ => routes}/locks.py | 3 +- seam/{ => routes}/networks.py | 3 +- seam/{ => routes}/noise_sensors.py | 7 +- .../noise_sensors_noise_thresholds.py | 7 +- seam/{ => routes}/noise_sensors_simulate.py | 3 +- seam/{ => routes}/phones.py | 5 +- seam/{ => routes}/phones_simulate.py | 8 +- seam/{ => routes}/routes.py | 0 seam/{ => routes}/thermostats.py | 5 +- .../thermostats_climate_setting_schedules.py | 4 +- seam/{ => routes}/types.py | 199 ++++++++---------- seam/{ => routes}/user_identities.py | 6 +- .../user_identities_enrollment_automations.py | 4 +- seam/{ => routes}/utils/deep_attr_dict.py | 0 seam/{ => routes}/webhooks.py | 3 +- seam/{ => routes}/workspaces.py | 8 +- seam/seam.py | 118 ----------- seam/token.py | 47 ----- seam/utils/action_attempt_errors.py | 22 -- 42 files changed, 228 insertions(+), 598 deletions(-) delete mode 100644 seam/__init__.py delete mode 100644 seam/auth.py delete mode 100644 seam/options.py delete mode 100644 seam/parse_options.py rename seam/{ => routes}/access_codes.py (97%) rename seam/{ => routes}/access_codes_simulate.py (86%) rename seam/{ => routes}/access_codes_unmanaged.py (96%) rename seam/{ => routes}/acs.py (74%) rename seam/{ => routes}/acs_access_groups.py (95%) rename seam/{ => routes}/acs_credential_pools.py (83%) rename seam/{ => routes}/acs_credential_provisioning_automations.py (95%) rename seam/{ => routes}/acs_credentials.py (85%) rename seam/{ => routes}/acs_entrances.py (94%) rename seam/{ => routes}/acs_systems.py (59%) rename seam/{ => routes}/acs_users.py (98%) rename seam/{ => routes}/action_attempts.py (74%) rename seam/{ => routes}/client_sessions.py (98%) rename seam/{ => routes}/connect_webviews.py (96%) rename seam/{ => routes}/connected_accounts.py (95%) rename seam/{ => routes}/devices.py (95%) rename seam/{ => routes}/devices_simulate.py (82%) rename seam/{ => routes}/devices_unmanaged.py (96%) rename seam/{ => routes}/events.py (96%) rename seam/{ => routes}/locks.py (97%) rename seam/{ => routes}/networks.py (87%) rename seam/{ => routes}/noise_sensors.py (67%) rename seam/{ => routes}/noise_sensors_noise_thresholds.py (97%) rename seam/{ => routes}/noise_sensors_simulate.py (83%) rename seam/{ => routes}/phones.py (86%) rename seam/{ => routes}/phones_simulate.py (76%) rename seam/{ => routes}/routes.py (100%) rename seam/{ => routes}/thermostats.py (97%) rename seam/{ => routes}/thermostats_climate_setting_schedules.py (98%) rename seam/{ => routes}/types.py (93%) rename seam/{ => routes}/user_identities.py (97%) rename seam/{ => routes}/user_identities_enrollment_automations.py (97%) rename seam/{ => routes}/utils/deep_attr_dict.py (100%) rename seam/{ => routes}/webhooks.py (94%) rename seam/{ => routes}/workspaces.py (94%) delete mode 100644 seam/seam.py delete mode 100644 seam/token.py delete mode 100644 seam/utils/action_attempt_errors.py diff --git a/seam/__init__.py b/seam/__init__.py deleted file mode 100644 index 05e3fa2..0000000 --- a/seam/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# flake8: noqa -# type: ignore - -from seam.seam import Seam, SeamApiException -from seam.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 deleted file mode 100644 index 41c733e..0000000 --- a/seam/auth.py +++ /dev/null @@ -1,101 +0,0 @@ -from typing import Optional -from seam.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/options.py b/seam/options.py deleted file mode 100644 index ee7b4fa..0000000 --- a/seam/options.py +++ /dev/null @@ -1,66 +0,0 @@ -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 deleted file mode 100644 index fa5c1b1..0000000 --- a/seam/parse_options.py +++ /dev/null @@ -1,26 +0,0 @@ -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" - - -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") - - 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 diff --git a/seam/access_codes.py b/seam/routes/access_codes.py similarity index 97% rename from seam/access_codes.py rename to seam/routes/access_codes.py index 46cc290..d0f1abb 100644 --- a/seam/access_codes.py +++ b/seam/routes/access_codes.py @@ -1,7 +1,8 @@ -from seam.types import AbstractAccessCodes, AbstractSeam as Seam, AccessCode +from seam.types import AbstractSeam as Seam +from seam.routes.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 +from seam.routes.access_codes_simulate import AccessCodesSimulate +from seam.routes.access_codes_unmanaged import AccessCodesUnmanaged class AccessCodes(AbstractAccessCodes): diff --git a/seam/access_codes_simulate.py b/seam/routes/access_codes_simulate.py similarity index 86% rename from seam/access_codes_simulate.py rename to seam/routes/access_codes_simulate.py index e9d6e44..00ecb28 100644 --- a/seam/access_codes_simulate.py +++ b/seam/routes/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.routes.types import AbstractAccessCodesSimulate, UnmanagedAccessCode from typing import Optional, Any, List, Dict, Union diff --git a/seam/access_codes_unmanaged.py b/seam/routes/access_codes_unmanaged.py similarity index 96% rename from seam/access_codes_unmanaged.py rename to seam/routes/access_codes_unmanaged.py index b50e424..05243d1 100644 --- a/seam/access_codes_unmanaged.py +++ b/seam/routes/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.routes.types import AbstractAccessCodesUnmanaged, UnmanagedAccessCode from typing import Optional, Any, List, Dict, Union diff --git a/seam/acs.py b/seam/routes/acs.py similarity index 74% rename from seam/acs.py rename to seam/routes/acs.py index 616b4b2..c99783a 100644 --- a/seam/acs.py +++ b/seam/routes/acs.py @@ -1,14 +1,15 @@ -from seam.types import AbstractAcs, AbstractSeam as Seam +from seam.types import AbstractSeam as Seam +from seam.routes.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 -from seam.acs_credential_provisioning_automations import ( +from seam.routes.acs_access_groups import AcsAccessGroups +from seam.routes.acs_credential_pools import AcsCredentialPools +from seam.routes.acs_credential_provisioning_automations import ( AcsCredentialProvisioningAutomations, ) -from seam.acs_credentials import AcsCredentials -from seam.acs_entrances import AcsEntrances -from seam.acs_systems import AcsSystems -from seam.acs_users import AcsUsers +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 class Acs(AbstractAcs): diff --git a/seam/acs_access_groups.py b/seam/routes/acs_access_groups.py similarity index 95% rename from seam/acs_access_groups.py rename to seam/routes/acs_access_groups.py index 6716143..897eb64 100644 --- a/seam/acs_access_groups.py +++ b/seam/routes/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.routes.types import AbstractAcsAccessGroups, AcsAccessGroup, AcsUser from typing import Optional, Any, List, Dict, Union diff --git a/seam/acs_credential_pools.py b/seam/routes/acs_credential_pools.py similarity index 83% rename from seam/acs_credential_pools.py rename to seam/routes/acs_credential_pools.py index 4d37488..c0764b6 100644 --- a/seam/acs_credential_pools.py +++ b/seam/routes/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.routes.types import AbstractAcsCredentialPools, AcsCredentialPool from typing import Optional, Any, List, Dict, Union diff --git a/seam/acs_credential_provisioning_automations.py b/seam/routes/acs_credential_provisioning_automations.py similarity index 95% rename from seam/acs_credential_provisioning_automations.py rename to seam/routes/acs_credential_provisioning_automations.py index 33e9a05..df0214c 100644 --- a/seam/acs_credential_provisioning_automations.py +++ b/seam/routes/acs_credential_provisioning_automations.py @@ -1,6 +1,6 @@ -from seam.types import ( +from seam.types import AbstractSeam as Seam +from seam.routes.types import ( AbstractAcsCredentialProvisioningAutomations, - AbstractSeam as Seam, AcsCredentialProvisioningAutomation, ) from typing import Optional, Any, List, Dict, Union diff --git a/seam/acs_credentials.py b/seam/routes/acs_credentials.py similarity index 85% rename from seam/acs_credentials.py rename to seam/routes/acs_credentials.py index cb3edb4..ab198e8 100644 --- a/seam/acs_credentials.py +++ b/seam/routes/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.routes.types import AbstractAcsCredentials, AcsCredential, AcsEntrance from typing import Optional, Any, List, Dict, Union @@ -109,6 +110,18 @@ def list( return [AcsCredential.from_dict(item) for item in res["acs_credentials"]] + def list_accessible_entrances(self, *, acs_credential_id: str) -> List[AcsEntrance]: + json_payload = {} + + if acs_credential_id is not None: + json_payload["acs_credential_id"] = acs_credential_id + + res = self.seam.make_request( + "POST", "/acs/credentials/list_accessible_entrances", json=json_payload + ) + + return [AcsEntrance.from_dict(item) for item in res["acs_entrances"]] + def unassign(self, *, acs_credential_id: str, acs_user_id: str) -> None: json_payload = {} @@ -121,13 +134,21 @@ def unassign(self, *, acs_credential_id: str, acs_user_id: str) -> None: 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) diff --git a/seam/acs_entrances.py b/seam/routes/acs_entrances.py similarity index 94% rename from seam/acs_entrances.py rename to seam/routes/acs_entrances.py index a54bd86..dd976ad 100644 --- a/seam/acs_entrances.py +++ b/seam/routes/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.routes.types import AbstractAcsEntrances, AcsEntrance, AcsCredential from typing import Optional, Any, List, Dict, Union diff --git a/seam/acs_systems.py b/seam/routes/acs_systems.py similarity index 59% rename from seam/acs_systems.py rename to seam/routes/acs_systems.py index c5b0207..9896ebd 100644 --- a/seam/acs_systems.py +++ b/seam/routes/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.routes.types import AbstractAcsSystems, AcsSystem from typing import Optional, Any, List, Dict, Union @@ -27,3 +28,19 @@ def list(self, *, connected_account_id: Optional[str] = None) -> List[AcsSystem] res = self.seam.make_request("POST", "/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.make_request( + "POST", + "/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/routes/acs_users.py similarity index 98% rename from seam/acs_users.py rename to seam/routes/acs_users.py index 8c98b38..01c3ef5 100644 --- a/seam/acs_users.py +++ b/seam/routes/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.routes.types import AbstractAcsUsers, AcsUser, AcsEntrance from typing import Optional, Any, List, Dict, Union diff --git a/seam/action_attempts.py b/seam/routes/action_attempts.py similarity index 74% rename from seam/action_attempts.py rename to seam/routes/action_attempts.py index 0078204..a9e110c 100644 --- a/seam/action_attempts.py +++ b/seam/routes/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.routes.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,7 +36,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 = {} @@ -48,7 +65,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 +95,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/routes/client_sessions.py similarity index 98% rename from seam/client_sessions.py rename to seam/routes/client_sessions.py index 09587e0..9b14ad4 100644 --- a/seam/client_sessions.py +++ b/seam/routes/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.routes.types import AbstractClientSessions, ClientSession from typing import Optional, Any, List, Dict, Union diff --git a/seam/connect_webviews.py b/seam/routes/connect_webviews.py similarity index 96% rename from seam/connect_webviews.py rename to seam/routes/connect_webviews.py index dd1ded4..bdc2fd9 100644 --- a/seam/connect_webviews.py +++ b/seam/routes/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.routes.types import AbstractConnectWebviews, ConnectWebview from typing import Optional, Any, List, Dict, Union diff --git a/seam/connected_accounts.py b/seam/routes/connected_accounts.py similarity index 95% rename from seam/connected_accounts.py rename to seam/routes/connected_accounts.py index 0c27810..2f6bc96 100644 --- a/seam/connected_accounts.py +++ b/seam/routes/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.routes.types import AbstractConnectedAccounts, ConnectedAccount from typing import Optional, Any, List, Dict, Union diff --git a/seam/devices.py b/seam/routes/devices.py similarity index 95% rename from seam/devices.py rename to seam/routes/devices.py index 6225426..98156b8 100644 --- a/seam/devices.py +++ b/seam/routes/devices.py @@ -1,7 +1,8 @@ -from seam.types import AbstractDevices, AbstractSeam as Seam, Device, DeviceProvider +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.devices_simulate import DevicesSimulate -from seam.devices_unmanaged import DevicesUnmanaged +from seam.routes.devices_simulate import DevicesSimulate +from seam.routes.devices_unmanaged import DevicesUnmanaged class Devices(AbstractDevices): diff --git a/seam/devices_simulate.py b/seam/routes/devices_simulate.py similarity index 82% rename from seam/devices_simulate.py rename to seam/routes/devices_simulate.py index ec8f76d..537125a 100644 --- a/seam/devices_simulate.py +++ b/seam/routes/devices_simulate.py @@ -1,4 +1,5 @@ -from seam.types import AbstractDevicesSimulate, AbstractSeam as Seam +from seam.types import AbstractSeam as Seam +from seam.routes.types import AbstractDevicesSimulate from typing import Optional, Any, List, Dict, Union diff --git a/seam/devices_unmanaged.py b/seam/routes/devices_unmanaged.py similarity index 96% rename from seam/devices_unmanaged.py rename to seam/routes/devices_unmanaged.py index 0b3b2eb..f68f940 100644 --- a/seam/devices_unmanaged.py +++ b/seam/routes/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.routes.types import AbstractDevicesUnmanaged, UnmanagedDevice from typing import Optional, Any, List, Dict, Union diff --git a/seam/events.py b/seam/routes/events.py similarity index 96% rename from seam/events.py rename to seam/routes/events.py index affa12b..bc56b8e 100644 --- a/seam/events.py +++ b/seam/routes/events.py @@ -1,4 +1,5 @@ -from seam.types import AbstractEvents, AbstractSeam as Seam, Event +from seam.types import AbstractSeam as Seam +from seam.routes.types import AbstractEvents, Event from typing import Optional, Any, List, Dict, Union diff --git a/seam/locks.py b/seam/routes/locks.py similarity index 97% rename from seam/locks.py rename to seam/routes/locks.py index cbe6e3d..afc19a6 100644 --- a/seam/locks.py +++ b/seam/routes/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.routes.types import AbstractLocks, Device, ActionAttempt from typing import Optional, Any, List, Dict, Union diff --git a/seam/networks.py b/seam/routes/networks.py similarity index 87% rename from seam/networks.py rename to seam/routes/networks.py index 689ac9c..325e82d 100644 --- a/seam/networks.py +++ b/seam/routes/networks.py @@ -1,4 +1,5 @@ -from seam.types import AbstractNetworks, AbstractSeam as Seam, Network +from seam.types import AbstractSeam as Seam +from seam.routes.types import AbstractNetworks, Network from typing import Optional, Any, List, Dict, Union diff --git a/seam/noise_sensors.py b/seam/routes/noise_sensors.py similarity index 67% rename from seam/noise_sensors.py rename to seam/routes/noise_sensors.py index 46c43ae..afbd775 100644 --- a/seam/noise_sensors.py +++ b/seam/routes/noise_sensors.py @@ -1,7 +1,8 @@ -from seam.types import AbstractNoiseSensors, AbstractSeam as Seam +from seam.types import AbstractSeam as Seam +from seam.routes.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 +from seam.routes.noise_sensors_noise_thresholds import NoiseSensorsNoiseThresholds +from seam.routes.noise_sensors_simulate import NoiseSensorsSimulate class NoiseSensors(AbstractNoiseSensors): diff --git a/seam/noise_sensors_noise_thresholds.py b/seam/routes/noise_sensors_noise_thresholds.py similarity index 97% rename from seam/noise_sensors_noise_thresholds.py rename to seam/routes/noise_sensors_noise_thresholds.py index 36ff6d7..09aca91 100644 --- a/seam/noise_sensors_noise_thresholds.py +++ b/seam/routes/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.routes.types import AbstractNoiseSensorsNoiseThresholds, NoiseThreshold from typing import Optional, Any, List, Dict, Union diff --git a/seam/noise_sensors_simulate.py b/seam/routes/noise_sensors_simulate.py similarity index 83% rename from seam/noise_sensors_simulate.py rename to seam/routes/noise_sensors_simulate.py index 03dc979..001fcb6 100644 --- a/seam/noise_sensors_simulate.py +++ b/seam/routes/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.routes.types import AbstractNoiseSensorsSimulate from typing import Optional, Any, List, Dict, Union diff --git a/seam/phones.py b/seam/routes/phones.py similarity index 86% rename from seam/phones.py rename to seam/routes/phones.py index 02a0f7b..8350159 100644 --- a/seam/phones.py +++ b/seam/routes/phones.py @@ -1,6 +1,7 @@ -from seam.types import AbstractPhones, AbstractSeam as Seam, Phone +from seam.types import AbstractSeam as Seam +from seam.routes.types import AbstractPhones, Phone from typing import Optional, Any, List, Dict, Union -from seam.phones_simulate import PhonesSimulate +from seam.routes.phones_simulate import PhonesSimulate class Phones(AbstractPhones): diff --git a/seam/phones_simulate.py b/seam/routes/phones_simulate.py similarity index 76% rename from seam/phones_simulate.py rename to seam/routes/phones_simulate.py index e062afb..4d0e8e4 100644 --- a/seam/phones_simulate.py +++ b/seam/routes/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.routes.types import AbstractPhonesSimulate, Phone from typing import Optional, Any, List, Dict, Union @@ -11,6 +12,7 @@ def __init__(self, seam: Seam): def create_sandbox_phone( self, *, + credential_manager_acs_system_id: str, user_identity_id: str, assa_abloy_metadata: Optional[Dict[str, Any]] = None, custom_sdk_installation_id: Optional[str] = None, @@ -18,6 +20,10 @@ def create_sandbox_phone( ) -> Phone: json_payload = {} + if credential_manager_acs_system_id is not None: + json_payload["credential_manager_acs_system_id"] = ( + credential_manager_acs_system_id + ) if user_identity_id is not None: json_payload["user_identity_id"] = user_identity_id if assa_abloy_metadata is not None: diff --git a/seam/routes.py b/seam/routes/routes.py similarity index 100% rename from seam/routes.py rename to seam/routes/routes.py diff --git a/seam/thermostats.py b/seam/routes/thermostats.py similarity index 97% rename from seam/thermostats.py rename to seam/routes/thermostats.py index 396bf3b..b54fddc 100644 --- a/seam/thermostats.py +++ b/seam/routes/thermostats.py @@ -1,6 +1,7 @@ -from seam.types import AbstractThermostats, AbstractSeam as Seam, ActionAttempt, Device +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.thermostats_climate_setting_schedules import ( +from seam.routes.thermostats_climate_setting_schedules import ( ThermostatsClimateSettingSchedules, ) diff --git a/seam/thermostats_climate_setting_schedules.py b/seam/routes/thermostats_climate_setting_schedules.py similarity index 98% rename from seam/thermostats_climate_setting_schedules.py rename to seam/routes/thermostats_climate_setting_schedules.py index 4342155..30e6a03 100644 --- a/seam/thermostats_climate_setting_schedules.py +++ b/seam/routes/thermostats_climate_setting_schedules.py @@ -1,6 +1,6 @@ -from seam.types import ( +from seam.types import AbstractSeam as Seam +from seam.routes.types import ( AbstractThermostatsClimateSettingSchedules, - AbstractSeam as Seam, ClimateSettingSchedule, ) from typing import Optional, Any, List, Dict, Union diff --git a/seam/types.py b/seam/routes/types.py similarity index 93% rename from seam/types.py rename to seam/routes/types.py index ad9e5b6..24cae40 100644 --- a/seam/types.py +++ b/seam/routes/types.py @@ -2,7 +2,7 @@ from typing_extensions import Self import abc from dataclasses import dataclass -from seam.utils.deep_attr_dict import DeepAttrDict +from seam.routes.utils.deep_attr_dict import DeepAttrDict @dataclass @@ -190,6 +190,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] @@ -200,6 +201,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)), ) @@ -549,9 +551,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 @@ -560,9 +567,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), @@ -809,24 +821,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 @@ -846,7 +840,7 @@ def convert_to_managed( allow_external_modification: Optional[bool] = None, force: Optional[bool] = None, is_external_modification_allowed: Optional[bool] = None, - sync: Optional[bool] = None, + sync: Optional[bool] = None ) -> None: raise NotImplementedError() @@ -860,7 +854,7 @@ def get( *, access_code_id: Optional[str] = None, code: Optional[str] = None, - device_id: Optional[str] = None, + device_id: Optional[str] = None ) -> UnmanagedAccessCode: raise NotImplementedError() @@ -878,7 +872,7 @@ def update( is_managed: bool, allow_external_modification: Optional[bool] = None, force: Optional[bool] = None, - is_external_modification_allowed: Optional[bool] = None, + is_external_modification_allowed: Optional[bool] = None ) -> None: raise NotImplementedError() @@ -925,7 +919,7 @@ def launch( user_identity_id: str, acs_credential_pool_id: Optional[str] = None, create_credential_manager_user: Optional[bool] = None, - credential_manager_acs_user_id: Optional[str] = None, + credential_manager_acs_user_id: Optional[str] = None ) -> AcsCredentialProvisioningAutomation: raise NotImplementedError() @@ -948,7 +942,7 @@ def create( ends_at: Optional[str] = None, is_multi_phone_sync_credential: Optional[bool] = None, starts_at: Optional[str] = None, - visionline_metadata: Optional[Dict[str, Any]] = None, + visionline_metadata: Optional[Dict[str, Any]] = None ) -> AcsCredential: raise NotImplementedError() @@ -967,16 +961,26 @@ def list( acs_user_id: Optional[str] = None, acs_system_id: Optional[str] = None, user_identity_id: Optional[str] = None, - is_multi_phone_sync_credential: Optional[bool] = None, + is_multi_phone_sync_credential: Optional[bool] = None ) -> List[AcsCredential]: raise NotImplementedError() + @abc.abstractmethod + def list_accessible_entrances(self, *, acs_credential_id: str) -> List[AcsEntrance]: + raise NotImplementedError() + @abc.abstractmethod 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() @@ -995,7 +999,7 @@ def list( self, *, acs_credential_id: Optional[str] = None, - acs_system_id: Optional[str] = None, + acs_system_id: Optional[str] = None ) -> List[AcsEntrance]: raise NotImplementedError() @@ -1016,6 +1020,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): @@ -1036,7 +1046,7 @@ def create( email_address: Optional[str] = None, full_name: Optional[str] = None, phone_number: Optional[str] = None, - user_identity_id: Optional[str] = None, + user_identity_id: Optional[str] = None ) -> AcsUser: raise NotImplementedError() @@ -1055,7 +1065,7 @@ def list( acs_system_id: Optional[str] = None, user_identity_email_address: Optional[str] = None, user_identity_id: Optional[str] = None, - user_identity_phone_number: Optional[str] = None, + user_identity_phone_number: Optional[str] = None ) -> List[AcsUser]: raise NotImplementedError() @@ -1091,7 +1101,7 @@ def update( email_address: Optional[str] = None, full_name: Optional[str] = None, hid_acs_system_id: Optional[str] = None, - phone_number: Optional[str] = None, + phone_number: Optional[str] = None ) -> None: raise NotImplementedError() @@ -1103,7 +1113,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: raise NotImplementedError() @@ -1117,7 +1127,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: raise NotImplementedError() @@ -1126,7 +1136,7 @@ def decide_and_wait( self, *, action_attempt: ActionAttempt, - wait_for_action_attempt: Union[bool, Dict[str, float]], + wait_for_action_attempt: Union[bool, Dict[str, float]] ) -> ActionAttempt: raise NotImplementedError() @@ -1141,7 +1151,7 @@ def create( connected_account_ids: Optional[List[str]] = None, expires_at: Optional[str] = None, user_identifier_key: Optional[str] = None, - user_identity_ids: Optional[List[str]] = None, + user_identity_ids: Optional[List[str]] = None ) -> ClientSession: raise NotImplementedError() @@ -1154,7 +1164,7 @@ def get( self, *, client_session_id: Optional[str] = None, - user_identifier_key: Optional[str] = None, + user_identifier_key: Optional[str] = None ) -> ClientSession: raise NotImplementedError() @@ -1166,7 +1176,7 @@ def get_or_create( connected_account_ids: Optional[List[str]] = None, expires_at: Optional[str] = None, user_identifier_key: Optional[str] = None, - user_identity_ids: Optional[List[str]] = None, + user_identity_ids: Optional[List[str]] = None ) -> ClientSession: raise NotImplementedError() @@ -1178,7 +1188,7 @@ def grant_access( connect_webview_ids: Optional[List[str]] = None, connected_account_ids: Optional[List[str]] = None, user_identifier_key: Optional[str] = None, - user_identity_ids: Optional[List[str]] = None, + user_identity_ids: Optional[List[str]] = None ) -> None: raise NotImplementedError() @@ -1190,7 +1200,7 @@ def list( connect_webview_id: Optional[str] = None, user_identifier_key: Optional[str] = None, user_identity_id: Optional[str] = None, - without_user_identifier_key: Optional[bool] = None, + without_user_identifier_key: Optional[bool] = None ) -> List[ClientSession]: raise NotImplementedError() @@ -1212,7 +1222,7 @@ def create( custom_redirect_url: Optional[str] = None, device_selection_mode: Optional[str] = None, provider_category: Optional[str] = None, - wait_for_device_creation: Optional[bool] = None, + wait_for_device_creation: Optional[bool] = None ) -> ConnectWebview: raise NotImplementedError() @@ -1229,7 +1239,7 @@ def list( self, *, custom_metadata_has: Optional[Dict[str, Any]] = None, - user_identifier_key: Optional[str] = None, + user_identifier_key: Optional[str] = None ) -> List[ConnectWebview]: raise NotImplementedError() @@ -1258,7 +1268,7 @@ def update( *, connected_account_id: str, automatically_manage_new_devices: Optional[bool] = None, - custom_metadata: Optional[Dict[str, Any]] = None, + custom_metadata: Optional[Dict[str, Any]] = None ) -> ConnectedAccount: raise NotImplementedError() @@ -1294,7 +1304,7 @@ def list( include_if: Optional[List[str]] = None, limit: Optional[float] = None, manufacturer: Optional[str] = None, - user_identifier_key: Optional[str] = None, + user_identifier_key: Optional[str] = None ) -> List[UnmanagedDevice]: raise NotImplementedError() @@ -1311,7 +1321,7 @@ def get( *, device_id: Optional[str] = None, event_id: Optional[str] = None, - event_type: Optional[str] = None, + event_type: Optional[str] = None ) -> Event: raise NotImplementedError() @@ -1328,7 +1338,7 @@ def list( event_type: Optional[str] = None, event_types: Optional[List[str]] = None, limit: Optional[float] = None, - since: Optional[str] = None, + since: Optional[str] = None ) -> List[Event]: raise NotImplementedError() @@ -1357,7 +1367,7 @@ def list( include_if: Optional[List[str]] = None, limit: Optional[float] = None, manufacturer: Optional[str] = None, - user_identifier_key: Optional[str] = None, + user_identifier_key: Optional[str] = None ) -> List[Device]: raise NotImplementedError() @@ -1367,7 +1377,7 @@ def lock_door( *, device_id: str, sync: Optional[bool] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None ) -> ActionAttempt: raise NotImplementedError() @@ -1377,7 +1387,7 @@ def unlock_door( *, device_id: str, sync: Optional[bool] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None ) -> ActionAttempt: raise NotImplementedError() @@ -1407,7 +1417,7 @@ def create( name: Optional[str] = None, noise_threshold_decibels: Optional[float] = None, noise_threshold_nrs: Optional[float] = None, - sync: Optional[bool] = None, + sync: Optional[bool] = None ) -> NoiseThreshold: raise NotImplementedError() @@ -1438,7 +1448,7 @@ def update( noise_threshold_decibels: Optional[float] = None, noise_threshold_nrs: Optional[float] = None, starts_daily_at: Optional[str] = None, - sync: Optional[bool] = None, + sync: Optional[bool] = None ) -> None: raise NotImplementedError() @@ -1456,10 +1466,11 @@ class AbstractPhonesSimulate(abc.ABC): def create_sandbox_phone( self, *, + credential_manager_acs_system_id: str, user_identity_id: str, assa_abloy_metadata: Optional[Dict[str, Any]] = None, custom_sdk_installation_id: Optional[str] = None, - phone_metadata: Optional[Dict[str, Any]] = None, + phone_metadata: Optional[Dict[str, Any]] = None ) -> Phone: raise NotImplementedError() @@ -1482,7 +1493,7 @@ def create( hvac_mode_setting: Optional[str] = None, manual_override_allowed: Optional[bool] = None, name: Optional[str] = None, - schedule_type: Optional[str] = None, + schedule_type: Optional[str] = None ) -> ClimateSettingSchedule: raise NotImplementedError() @@ -1495,7 +1506,7 @@ def get( self, *, climate_setting_schedule_id: Optional[str] = None, - device_id: Optional[str] = None, + device_id: Optional[str] = None ) -> ClimateSettingSchedule: raise NotImplementedError() @@ -1521,7 +1532,7 @@ def update( name: Optional[str] = None, schedule_ends_at: Optional[str] = None, schedule_starts_at: Optional[str] = None, - schedule_type: Optional[str] = None, + schedule_type: Optional[str] = None ) -> None: raise NotImplementedError() @@ -1544,7 +1555,7 @@ def launch( user_identity_id: str, acs_credential_pool_id: Optional[str] = None, create_credential_manager_user: Optional[bool] = None, - credential_manager_acs_user_id: Optional[str] = None, + credential_manager_acs_user_id: Optional[str] = None ) -> None: raise NotImplementedError() @@ -1588,7 +1599,7 @@ def create( name: str, is_sandbox: Optional[bool] = None, webview_logo_shape: Optional[str] = None, - webview_primary_button_color: Optional[str] = None, + webview_primary_button_color: Optional[str] = None ) -> Workspace: raise NotImplementedError() @@ -1642,7 +1653,7 @@ def cool( cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, sync: Optional[bool] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None ) -> ActionAttempt: raise NotImplementedError() @@ -1660,7 +1671,7 @@ def heat( heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, sync: Optional[bool] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None ) -> ActionAttempt: raise NotImplementedError() @@ -1674,7 +1685,7 @@ def heat_cool( heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, sync: Optional[bool] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None ) -> ActionAttempt: raise NotImplementedError() @@ -1694,7 +1705,7 @@ def list( include_if: Optional[List[str]] = None, limit: Optional[float] = None, manufacturer: Optional[str] = None, - user_identifier_key: Optional[str] = None, + user_identifier_key: Optional[str] = None ) -> List[Device]: raise NotImplementedError() @@ -1704,7 +1715,7 @@ def off( *, device_id: str, sync: Optional[bool] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None ) -> ActionAttempt: raise NotImplementedError() @@ -1716,7 +1727,7 @@ def set_fan_mode( fan_mode: Optional[str] = None, fan_mode_setting: Optional[str] = None, sync: Optional[bool] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, + wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None ) -> ActionAttempt: raise NotImplementedError() @@ -1745,7 +1756,7 @@ def create( email_address: Optional[str] = None, full_name: Optional[str] = None, phone_number: Optional[str] = None, - user_identity_key: Optional[str] = None, + user_identity_key: Optional[str] = None ) -> UserIdentity: raise NotImplementedError() @@ -1758,7 +1769,7 @@ def get( self, *, user_identity_id: Optional[str] = None, - user_identity_key: Optional[str] = None, + user_identity_key: Optional[str] = None ) -> UserIdentity: raise NotImplementedError() @@ -1800,7 +1811,7 @@ def update( email_address: Optional[str] = None, full_name: Optional[str] = None, phone_number: Optional[str] = None, - user_identity_key: Optional[str] = None, + user_identity_key: Optional[str] = None ) -> None: raise NotImplementedError() @@ -1836,7 +1847,7 @@ def create( starts_at: Optional[str] = None, sync: Optional[bool] = None, use_backup_access_code_pool: Optional[bool] = None, - use_offline_access_code: Optional[bool] = None, + use_offline_access_code: Optional[bool] = None ) -> AccessCode: raise NotImplementedError() @@ -1859,7 +1870,7 @@ def create_multiple( preferred_code_length: Optional[float] = None, starts_at: Optional[str] = None, use_backup_access_code_pool: Optional[bool] = None, - use_offline_access_code: Optional[bool] = None, + use_offline_access_code: Optional[bool] = None ) -> List[AccessCode]: raise NotImplementedError() @@ -1869,7 +1880,7 @@ def delete( *, access_code_id: str, device_id: Optional[str] = None, - sync: Optional[bool] = None, + sync: Optional[bool] = None ) -> None: raise NotImplementedError() @@ -1883,7 +1894,7 @@ def get( *, access_code_id: Optional[str] = None, code: Optional[str] = None, - device_id: Optional[str] = None, + device_id: Optional[str] = None ) -> AccessCode: raise NotImplementedError() @@ -1893,7 +1904,7 @@ def list( *, access_code_ids: Optional[List[str]] = None, device_id: Optional[str] = None, - user_identifier_key: Optional[str] = None, + user_identifier_key: Optional[str] = None ) -> List[AccessCode]: raise NotImplementedError() @@ -1922,7 +1933,7 @@ def update( sync: Optional[bool] = None, type: Optional[str] = None, use_backup_access_code_pool: Optional[bool] = None, - use_offline_access_code: Optional[bool] = None, + use_offline_access_code: Optional[bool] = None ) -> None: raise NotImplementedError() @@ -1965,7 +1976,7 @@ def list( include_if: Optional[List[str]] = None, limit: Optional[float] = None, manufacturer: Optional[str] = None, - user_identifier_key: Optional[str] = None, + user_identifier_key: Optional[str] = None ) -> List[Device]: raise NotImplementedError() @@ -1983,7 +1994,7 @@ def update( custom_metadata: Optional[Dict[str, Any]] = None, is_managed: Optional[bool] = None, name: Optional[str] = None, - properties: Optional[Dict[str, Any]] = None, + properties: Optional[Dict[str, Any]] = None ) -> None: raise NotImplementedError() @@ -2061,47 +2072,3 @@ class AbstractRoutes(abc.ABC): user_identities: AbstractUserIdentities webhooks: AbstractWebhooks workspaces: AbstractWorkspaces - - -class AbstractSeam(AbstractRoutes): - lts_version: str - - @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 - - @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 diff --git a/seam/user_identities.py b/seam/routes/user_identities.py similarity index 97% rename from seam/user_identities.py rename to seam/routes/user_identities.py index be10fb7..fe186c9 100644 --- a/seam/user_identities.py +++ b/seam/routes/user_identities.py @@ -1,13 +1,13 @@ -from seam.types import ( +from seam.types import AbstractSeam as Seam +from seam.routes.types import ( AbstractUserIdentities, - AbstractSeam as Seam, UserIdentity, Device, AcsSystem, AcsUser, ) from typing import Optional, Any, List, Dict, Union -from seam.user_identities_enrollment_automations import ( +from seam.routes.user_identities_enrollment_automations import ( UserIdentitiesEnrollmentAutomations, ) diff --git a/seam/user_identities_enrollment_automations.py b/seam/routes/user_identities_enrollment_automations.py similarity index 97% rename from seam/user_identities_enrollment_automations.py rename to seam/routes/user_identities_enrollment_automations.py index e006e05..dce3940 100644 --- a/seam/user_identities_enrollment_automations.py +++ b/seam/routes/user_identities_enrollment_automations.py @@ -1,6 +1,6 @@ -from seam.types import ( +from seam.types import AbstractSeam as Seam +from seam.routes.types import ( AbstractUserIdentitiesEnrollmentAutomations, - AbstractSeam as Seam, EnrollmentAutomation, ) from typing import Optional, Any, List, Dict, Union diff --git a/seam/utils/deep_attr_dict.py b/seam/routes/utils/deep_attr_dict.py similarity index 100% rename from seam/utils/deep_attr_dict.py rename to seam/routes/utils/deep_attr_dict.py diff --git a/seam/webhooks.py b/seam/routes/webhooks.py similarity index 94% rename from seam/webhooks.py rename to seam/routes/webhooks.py index ae85f53..d764eff 100644 --- a/seam/webhooks.py +++ b/seam/routes/webhooks.py @@ -1,4 +1,5 @@ -from seam.types import AbstractWebhooks, AbstractSeam as Seam, Webhook +from seam.types import AbstractSeam as Seam +from seam.routes.types import AbstractWebhooks, Webhook from typing import Optional, Any, List, Dict, Union diff --git a/seam/workspaces.py b/seam/routes/workspaces.py similarity index 94% rename from seam/workspaces.py rename to seam/routes/workspaces.py index 43f1467..660d94b 100644 --- a/seam/workspaces.py +++ b/seam/routes/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.routes.types import AbstractWorkspaces, Workspace, ActionAttempt from typing import Optional, Any, List, Dict, Union diff --git a/seam/seam.py b/seam/seam.py deleted file mode 100644 index 35ace66..0000000 --- a/seam/seam.py +++ /dev/null @@ -1,118 +0,0 @@ -import requests -from importlib.metadata import version -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 - - -class Seam(AbstractSeam): - """ - Initial Seam class used to interact with Seam API - """ - - 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, - endpoint: Optional[str] = None, - wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = False, - ): - """ - Parameters - ---------- - api_key : str, optional - API key. - personal_access_token : str, optional - Personal access token. - workspace_id : str, optional - 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) - - self.lts_version = Seam.lts_version - self.wait_for_action_attempt = wait_for_action_attempt - auth_headers, endpoint = parse_options( - api_key=api_key, - personal_access_token=personal_access_token, - 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 - - @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 deleted file mode 100644 index 9ec8f3b..0000000 --- a/seam/token.py +++ /dev/null @@ -1,47 +0,0 @@ -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/utils/action_attempt_errors.py b/seam/utils/action_attempt_errors.py deleted file mode 100644 index b047bb0..0000000 --- a/seam/utils/action_attempt_errors.py +++ /dev/null @@ -1,22 +0,0 @@ -from seam.types import ActionAttempt - - -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__