From 5e4870fa61767dc889e9e3e3fca61109b05579c1 Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Sun, 3 May 2026 22:58:02 +0000 Subject: [PATCH] feat!(generated): regenerate from spec (5 changes) --- .last-synced-sha | 2 +- .oagen-manifest.json | 61 +- src/workos/_client.py | 22 +- src/workos/api_keys/_resource.py | 183 ++-- src/workos/api_keys/models/__init__.py | 12 +- src/workos/api_keys/models/api_key.py | 2 +- .../api_keys/models/organization_api_key.py | 75 ++ ...owner.py => organization_api_key_owner.py} | 2 +- .../models/organization_api_key_with_value.py | 79 ++ .../organization_api_key_with_value_owner.py | 6 + .../models/organizations_api_keys_order.py | 4 +- src/workos/audit_logs/_resource.py | 21 +- .../audit_logs/models/audit_logs_order.py | 4 +- src/workos/authorization/_resource.py | 125 +-- .../models/authorization_order.py | 4 +- .../authorization/models/permissions_order.py | 4 +- ..._organization_membership_base_list_data.py | 7 + src/workos/common/__init__.py | 2 + src/workos/common/models/__init__.py | 4 + .../models/api_key_created_data_owner.py | 2 +- src/workos/common/models/directory_user.py | 7 + .../common/models/dsync_user_updated_data.py | 7 + .../models/event_context_actor_source.py | 5 +- .../common/models/vault_byok_key_deleted.py | 60 ++ .../models/vault_byok_key_deleted_data.py | 41 + ...ault_byok_key_deleted_data_key_provider.py | 31 + ...erification_completed_data_key_provider.py | 36 +- src/workos/connect/_resource.py | 25 +- .../connect/models/applications_order.py | 30 +- src/workos/directory_sync/_resource.py | 17 +- .../models/directories_order.py | 4 +- .../models/directory_groups_order.py | 4 +- .../models/directory_user_with_groups.py | 9 +- .../models/directory_users_order.py | 4 +- src/workos/events/models/event_schema.py | 3 + src/workos/events/models/events_order.py | 4 +- src/workos/feature_flags/_resource.py | 29 +- .../models/feature_flags_order.py | 4 +- .../organizations_feature_flags_order.py | 4 +- ...er_management_users_feature_flags_order.py | 4 +- src/workos/groups/_resource.py | 33 +- src/workos/groups/models/groups_order.py | 4 +- src/workos/multi_factor_auth/_resource.py | 25 +- ...ement_multi_factor_authentication_order.py | 4 +- src/workos/organization_domains/_resource.py | 13 +- src/workos/organizations/_resource.py | 21 +- .../models/organizations_order.py | 4 +- src/workos/pipes/_resource.py | 21 +- src/workos/radar/_resource.py | 13 +- src/workos/sso/_resource.py | 9 +- src/workos/sso/models/connections_order.py | 4 +- src/workos/sso/models/profile.py | 7 + src/workos/user_management/_resource.py | 358 ++++++- src/workos/user_management/models/__init__.py | 8 + .../user_management/models/api_keys_order.py | 29 + .../models/create_user_api_key.py | 40 + .../models/organization_membership.py | 5 + .../user_management/models/user_api_key.py | 73 ++ .../models/user_api_key_owner.py | 39 + .../models/user_api_key_with_value.py} | 14 +- .../models/user_api_key_with_value_owner.py | 6 + .../user_management_invitations_order.py | 4 +- ...anagement_organization_membership_order.py | 4 +- ...ent_users_authorized_applications_order.py | 4 +- .../models/user_management_users_order.py | 4 +- .../_resource.py | 5 +- ...nt_organization_membership_groups_order.py | 4 +- src/workos/webhooks/_resource.py | 9 +- src/workos/webhooks/models/webhooks_order.py | 4 +- tests/fixtures/create_user_api_key.json | 8 + tests/fixtures/directory_user.json | 1 + .../fixtures/directory_user_with_groups.json | 1 + tests/fixtures/dsync_group_user_added.json | 1 + .../fixtures/dsync_group_user_added_data.json | 1 + tests/fixtures/dsync_group_user_removed.json | 1 + .../dsync_group_user_removed_data.json | 1 + tests/fixtures/dsync_user_created.json | 1 + tests/fixtures/dsync_user_deleted.json | 1 + tests/fixtures/dsync_user_updated.json | 1 + tests/fixtures/dsync_user_updated_data.json | 1 + tests/fixtures/jwt_template_response.json | 2 +- .../list_directory_user_with_groups.json | 1 + tests/fixtures/list_organization_api_key.json | 25 + tests/fixtures/list_user_api_key.json | 26 + .../list_user_organization_membership.json | 19 +- ...rganization_membership_base_list_data.json | 21 +- tests/fixtures/organization_api_key.json | 17 + .../fixtures/organization_api_key_owner.json | 4 + .../organization_api_key_with_value.json | 18 + ...organization_api_key_with_value_owner.json | 4 + tests/fixtures/organization_membership.json | 17 + tests/fixtures/profile.json | 1 + tests/fixtures/sso_token_response.json | 1 + tests/fixtures/update_jwt_template.json | 2 +- tests/fixtures/user_api_key.json | 18 + tests/fixtures/user_api_key_owner.json | 5 + tests/fixtures/user_api_key_with_value.json | 19 + .../user_api_key_with_value_owner.json | 5 + .../user_organization_membership.json | 19 +- ...rganization_membership_base_list_data.json | 21 +- tests/fixtures/vault_byok_key_deleted.json | 30 + .../fixtures/vault_byok_key_deleted_data.json | 4 + tests/test_api_keys.py | 168 ++-- tests/test_models_round_trip.py | 892 +++++++++++++----- tests/test_user_management.py | 123 +++ 105 files changed, 2440 insertions(+), 757 deletions(-) create mode 100644 src/workos/api_keys/models/organization_api_key.py rename src/workos/api_keys/models/{api_key_with_value_owner.py => organization_api_key_owner.py} (73%) create mode 100644 src/workos/api_keys/models/organization_api_key_with_value.py create mode 100644 src/workos/api_keys/models/organization_api_key_with_value_owner.py create mode 100644 src/workos/common/models/vault_byok_key_deleted.py create mode 100644 src/workos/common/models/vault_byok_key_deleted_data.py create mode 100644 src/workos/common/models/vault_byok_key_deleted_data_key_provider.py create mode 100644 src/workos/user_management/models/api_keys_order.py create mode 100644 src/workos/user_management/models/create_user_api_key.py create mode 100644 src/workos/user_management/models/user_api_key.py create mode 100644 src/workos/user_management/models/user_api_key_owner.py rename src/workos/{api_keys/models/api_key_with_value.py => user_management/models/user_api_key_with_value.py} (87%) create mode 100644 src/workos/user_management/models/user_api_key_with_value_owner.py create mode 100644 tests/fixtures/create_user_api_key.json create mode 100644 tests/fixtures/list_organization_api_key.json create mode 100644 tests/fixtures/list_user_api_key.json create mode 100644 tests/fixtures/organization_api_key.json create mode 100644 tests/fixtures/organization_api_key_owner.json create mode 100644 tests/fixtures/organization_api_key_with_value.json create mode 100644 tests/fixtures/organization_api_key_with_value_owner.json create mode 100644 tests/fixtures/user_api_key.json create mode 100644 tests/fixtures/user_api_key_owner.json create mode 100644 tests/fixtures/user_api_key_with_value.json create mode 100644 tests/fixtures/user_api_key_with_value_owner.json create mode 100644 tests/fixtures/vault_byok_key_deleted.json create mode 100644 tests/fixtures/vault_byok_key_deleted_data.json diff --git a/.last-synced-sha b/.last-synced-sha index f72eb0e2..33f76880 100644 --- a/.last-synced-sha +++ b/.last-synced-sha @@ -1 +1 @@ -92db0495807c86fbbc4d45bd266a6c1f5bcbb59c +ff939ff075453287993e1e6182f1d6f23c67ab80 diff --git a/.oagen-manifest.json b/.oagen-manifest.json index 8f408c22..11f41617 100644 --- a/.oagen-manifest.json +++ b/.oagen-manifest.json @@ -1,7 +1,7 @@ { "version": 2, "language": "python", - "generatedAt": "2026-05-01T16:11:18.992Z", + "generatedAt": "2026-05-03T22:57:53.434Z", "files": [ "src/workos/_client.py", "src/workos/admin_portal/__init__.py", @@ -18,9 +18,11 @@ "src/workos/api_keys/models/api_key.py", "src/workos/api_keys/models/api_key_owner.py", "src/workos/api_keys/models/api_key_validation_response.py", - "src/workos/api_keys/models/api_key_with_value.py", - "src/workos/api_keys/models/api_key_with_value_owner.py", "src/workos/api_keys/models/create_organization_api_key.py", + "src/workos/api_keys/models/organization_api_key.py", + "src/workos/api_keys/models/organization_api_key_owner.py", + "src/workos/api_keys/models/organization_api_key_with_value.py", + "src/workos/api_keys/models/organization_api_key_with_value_owner.py", "src/workos/api_keys/models/organizations_api_keys_order.py", "src/workos/api_keys/models/validate_api_key.py", "src/workos/audit_logs/__init__.py", @@ -378,6 +380,9 @@ "src/workos/common/models/user_sessions_auth_method.py", "src/workos/common/models/user_sessions_status.py", "src/workos/common/models/user_updated.py", + "src/workos/common/models/vault_byok_key_deleted.py", + "src/workos/common/models/vault_byok_key_deleted_data.py", + "src/workos/common/models/vault_byok_key_deleted_data_key_provider.py", "src/workos/common/models/vault_byok_key_verification_completed.py", "src/workos/common/models/vault_byok_key_verification_completed_data.py", "src/workos/common/models/vault_byok_key_verification_completed_data_key_provider.py", @@ -565,6 +570,7 @@ "src/workos/user_management/__init__.py", "src/workos/user_management/_resource.py", "src/workos/user_management/models/__init__.py", + "src/workos/user_management/models/api_keys_order.py", "src/workos/user_management/models/authenticate_response.py", "src/workos/user_management/models/authenticate_response_impersonator.py", "src/workos/user_management/models/authenticate_response_oauth_token.py", @@ -578,6 +584,7 @@ "src/workos/user_management/models/create_password_reset_token.py", "src/workos/user_management/models/create_redirect_uri.py", "src/workos/user_management/models/create_user.py", + "src/workos/user_management/models/create_user_api_key.py", "src/workos/user_management/models/create_user_invite_options.py", "src/workos/user_management/models/create_user_organization_membership.py", "src/workos/user_management/models/device_authorization_response.py", @@ -610,6 +617,10 @@ "src/workos/user_management/models/update_user.py", "src/workos/user_management/models/update_user_organization_membership.py", "src/workos/user_management/models/user.py", + "src/workos/user_management/models/user_api_key.py", + "src/workos/user_management/models/user_api_key_owner.py", + "src/workos/user_management/models/user_api_key_with_value.py", + "src/workos/user_management/models/user_api_key_with_value_owner.py", "src/workos/user_management/models/user_identities_get_item.py", "src/workos/user_management/models/user_invite.py", "src/workos/user_management/models/user_management_authentication_provider.py", @@ -654,8 +665,6 @@ "tests/fixtures/api_key_revoked_data.json", "tests/fixtures/api_key_revoked_data_owner.json", "tests/fixtures/api_key_validation_response.json", - "tests/fixtures/api_key_with_value.json", - "tests/fixtures/api_key_with_value_owner.json", "tests/fixtures/application_credentials_list_item.json", "tests/fixtures/assign_role.json", "tests/fixtures/audit_log_action_json.json", @@ -781,6 +790,7 @@ "tests/fixtures/create_redirect_uri.json", "tests/fixtures/create_role.json", "tests/fixtures/create_user.json", + "tests/fixtures/create_user_api_key.json", "tests/fixtures/create_user_invite_options.json", "tests/fixtures/create_user_organization_membership.json", "tests/fixtures/create_webhook_endpoint.json", @@ -895,7 +905,6 @@ "tests/fixtures/jwks_response.json", "tests/fixtures/jwks_response_keys.json", "tests/fixtures/jwt_template_response.json", - "tests/fixtures/list_api_key.json", "tests/fixtures/list_audit_log_action_json.json", "tests/fixtures/list_audit_log_schema_json.json", "tests/fixtures/list_authentication_factor.json", @@ -911,8 +920,10 @@ "tests/fixtures/list_flag.json", "tests/fixtures/list_group.json", "tests/fixtures/list_organization.json", + "tests/fixtures/list_organization_api_key.json", "tests/fixtures/list_role_assignment.json", "tests/fixtures/list_user.json", + "tests/fixtures/list_user_api_key.json", "tests/fixtures/list_user_invite.json", "tests/fixtures/list_user_organization_membership.json", "tests/fixtures/list_user_organization_membership_base_list_data.json", @@ -925,6 +936,10 @@ "tests/fixtures/mfa_totp_session_authenticate_request.json", "tests/fixtures/new_connect_application_secret.json", "tests/fixtures/organization.json", + "tests/fixtures/organization_api_key.json", + "tests/fixtures/organization_api_key_owner.json", + "tests/fixtures/organization_api_key_with_value.json", + "tests/fixtures/organization_api_key_with_value_owner.json", "tests/fixtures/organization_created.json", "tests/fixtures/organization_created_data.json", "tests/fixtures/organization_created_data_domain.json", @@ -1032,6 +1047,10 @@ "tests/fixtures/update_user_organization_membership.json", "tests/fixtures/update_webhook_endpoint.json", "tests/fixtures/user.json", + "tests/fixtures/user_api_key.json", + "tests/fixtures/user_api_key_owner.json", + "tests/fixtures/user_api_key_with_value.json", + "tests/fixtures/user_api_key_with_value_owner.json", "tests/fixtures/user_authentication_factor_enroll_response.json", "tests/fixtures/user_consent_option.json", "tests/fixtures/user_consent_option_choice.json", @@ -1047,6 +1066,8 @@ "tests/fixtures/user_sessions_list_item.json", "tests/fixtures/user_updated.json", "tests/fixtures/validate_api_key.json", + "tests/fixtures/vault_byok_key_deleted.json", + "tests/fixtures/vault_byok_key_deleted_data.json", "tests/fixtures/vault_byok_key_verification_completed.json", "tests/fixtures/vault_byok_key_verification_completed_data.json", "tests/fixtures/vault_data_created.json", @@ -1098,14 +1119,6 @@ "tests/test_widgets.py" ], "operations": { - "POST /api_keys/validations": { - "sdkMethod": "create_validation", - "service": "api_keys" - }, - "DELETE /api_keys/{id}": { - "sdkMethod": "delete_api_key", - "service": "api_keys" - }, "POST /auth/challenges/{id}/verify": { "sdkMethod": "verify_challenge", "service": "multi_factor_auth" @@ -1642,6 +1655,10 @@ "sdkMethod": "revoke_invitation", "service": "user_management" }, + "GET /user_management/jwt_template": { + "sdkMethod": "list_jwt_template", + "service": "user_management" + }, "PUT /user_management/jwt_template": { "sdkMethod": "update_jwt_template", "service": "user_management" @@ -1742,6 +1759,22 @@ "sdkMethod": "create_token", "service": "widgets" }, + "POST /api_keys/validations": { + "sdkMethod": "create_validation", + "service": "api_keys" + }, + "DELETE /api_keys/{id}": { + "sdkMethod": "delete_api_key", + "service": "api_keys" + }, + "GET /user_management/users/{userId}/api_keys": { + "sdkMethod": "list_user_api_keys", + "service": "user_management" + }, + "POST /user_management/users/{userId}/api_keys": { + "sdkMethod": "create_user_api_key", + "service": "user_management" + }, "GET /audit_logs/actions": { "sdkMethod": "list_actions", "service": "audit_logs" diff --git a/src/workos/_client.py b/src/workos/_client.py index f59743fa..baa25896 100644 --- a/src/workos/_client.py +++ b/src/workos/_client.py @@ -8,7 +8,6 @@ WorkOSClient as _SyncBase, AsyncWorkOSClient as _AsyncBase, ) -from .api_keys._resource import ApiKeys, AsyncApiKeys from .multi_factor_auth._resource import MultiFactorAuth, AsyncMultiFactorAuth from .connect._resource import Connect, AsyncConnect from .authorization._resource import Authorization, AsyncAuthorization @@ -22,6 +21,7 @@ AsyncOrganizationDomains, ) from .organizations._resource import Organizations, AsyncOrganizations +from .api_keys._resource import ApiKeys, AsyncApiKeys from .groups._resource import Groups, AsyncGroups from .admin_portal._resource import AdminPortal, AsyncAdminPortal from .radar._resource import Radar, AsyncRadar @@ -42,11 +42,6 @@ class WorkOSClient(_SyncBase): """Synchronous WorkOS API client with service accessors.""" - @functools.cached_property - def api_keys(self) -> ApiKeys: - """Api Keys API resources.""" - return ApiKeys(self) - @functools.cached_property def multi_factor_auth(self) -> MultiFactorAuth: """Multi Factor Auth API resources.""" @@ -97,6 +92,11 @@ def organizations(self) -> Organizations: """Organizations API resources.""" return Organizations(self) + @functools.cached_property + def api_keys(self) -> ApiKeys: + """Api Keys API resources.""" + return ApiKeys(self) + @functools.cached_property def groups(self) -> Groups: """Groups API resources.""" @@ -172,11 +172,6 @@ def pkce(self) -> PKCE: class AsyncWorkOSClient(_AsyncBase): """Asynchronous WorkOS API client with service accessors.""" - @functools.cached_property - def api_keys(self) -> AsyncApiKeys: - """Api Keys API resources.""" - return AsyncApiKeys(self) - @functools.cached_property def multi_factor_auth(self) -> AsyncMultiFactorAuth: """Multi Factor Auth API resources.""" @@ -227,6 +222,11 @@ def organizations(self) -> AsyncOrganizations: """Organizations API resources.""" return AsyncOrganizations(self) + @functools.cached_property + def api_keys(self) -> AsyncApiKeys: + """Api Keys API resources.""" + return AsyncApiKeys(self) + @functools.cached_property def groups(self) -> AsyncGroups: """Groups API resources.""" diff --git a/src/workos/api_keys/_resource.py b/src/workos/api_keys/_resource.py index 982c12f3..ff546f6d 100644 --- a/src/workos/api_keys/_resource.py +++ b/src/workos/api_keys/_resource.py @@ -3,12 +3,17 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient from .._types import RequestOptions, enum_value -from .models import ApiKey, ApiKeyValidationResponse, ApiKeyWithValue +from .models import ( + ApiKeyValidationResponse, + OrganizationApiKey, + OrganizationApiKeyWithValue, +) from .models import OrganizationsApiKeysOrder from .._pagination import AsyncPage, SyncPage @@ -19,66 +24,6 @@ class ApiKeys: def __init__(self, client: "WorkOSClient") -> None: self._client = client - def create_validation( - self, - *, - value: str, - request_options: Optional[RequestOptions] = None, - ) -> ApiKeyValidationResponse: - """Validate API key - - Validate an API key value and return the API key object if valid. - - Args: - value: The value for an API key. - request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. - - Returns: - ApiKeyValidationResponse - - Raises: - AuthenticationError: If the API key is invalid (401). - UnprocessableEntityError: If the request data is unprocessable (422). - RateLimitExceededError: If rate limited (429). - ServerError: If the server returns a 5xx error. - """ - body: Dict[str, Any] = { - "value": value, - } - return self._client.request( - method="post", - path="api_keys/validations", - body=body, - model=ApiKeyValidationResponse, - request_options=request_options, - ) - - def delete_api_key( - self, - id: str, - *, - request_options: Optional[RequestOptions] = None, - ) -> None: - """Delete an API key - - Permanently deletes an API key. This action cannot be undone. Once deleted, any requests using this API key will fail authentication. - - Args: - id: The unique ID of the API key. - request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. - - Raises: - NotFoundError: If the resource is not found (404). - AuthenticationError: If the API key is invalid (401). - RateLimitExceededError: If rate limited (429). - ServerError: If the server returns a 5xx error. - """ - self._client.request( - method="delete", - path=f"api_keys/{id}", - request_options=request_options, - ) - def list_organization_api_keys( self, organization_id: str, @@ -88,7 +33,7 @@ def list_organization_api_keys( after: Optional[str] = None, order: Optional[Union[OrganizationsApiKeysOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, - ) -> SyncPage[ApiKey]: + ) -> SyncPage[OrganizationApiKey]: """List API keys for an organization Get a list of all API keys for an organization. @@ -102,7 +47,7 @@ def list_organization_api_keys( request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - SyncPage[ApiKey] + SyncPage[OrganizationApiKey] Raises: NotFoundError: If the resource is not found (404). @@ -122,8 +67,8 @@ def list_organization_api_keys( } return self._client.request_page( method="get", - path=f"organizations/{organization_id}/api_keys", - model=ApiKey, + path=f"organizations/{quote(str(organization_id), safe='')}/api_keys", + model=OrganizationApiKey, params=params, request_options=request_options, ) @@ -135,7 +80,7 @@ def create_organization_api_key( name: str, permissions: Optional[List[str]] = None, request_options: Optional[RequestOptions] = None, - ) -> ApiKeyWithValue: + ) -> OrganizationApiKeyWithValue: """Create an API key for an organization Create a new API key for an organization. @@ -147,7 +92,7 @@ def create_organization_api_key( request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ApiKeyWithValue + OrganizationApiKeyWithValue Raises: NotFoundError: If the resource is not found (404). @@ -166,20 +111,13 @@ def create_organization_api_key( } return self._client.request( method="post", - path=f"organizations/{organization_id}/api_keys", + path=f"organizations/{quote(str(organization_id), safe='')}/api_keys", body=body, - model=ApiKeyWithValue, + model=OrganizationApiKeyWithValue, request_options=request_options, ) - -class AsyncApiKeys: - """Api Keys API resources (async).""" - - def __init__(self, client: "AsyncWorkOSClient") -> None: - self._client = client - - async def create_validation( + def create_validation( self, *, value: str, @@ -205,7 +143,7 @@ async def create_validation( body: Dict[str, Any] = { "value": value, } - return await self._client.request( + return self._client.request( method="post", path="api_keys/validations", body=body, @@ -213,7 +151,7 @@ async def create_validation( request_options=request_options, ) - async def delete_api_key( + def delete_api_key( self, id: str, *, @@ -233,12 +171,19 @@ async def delete_api_key( RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. """ - await self._client.request( + self._client.request( method="delete", - path=f"api_keys/{id}", + path=f"api_keys/{quote(str(id), safe='')}", request_options=request_options, ) + +class AsyncApiKeys: + """Api Keys API resources (async).""" + + def __init__(self, client: "AsyncWorkOSClient") -> None: + self._client = client + async def list_organization_api_keys( self, organization_id: str, @@ -248,7 +193,7 @@ async def list_organization_api_keys( after: Optional[str] = None, order: Optional[Union[OrganizationsApiKeysOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, - ) -> AsyncPage[ApiKey]: + ) -> AsyncPage[OrganizationApiKey]: """List API keys for an organization Get a list of all API keys for an organization. @@ -262,7 +207,7 @@ async def list_organization_api_keys( request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - AsyncPage[ApiKey] + AsyncPage[OrganizationApiKey] Raises: NotFoundError: If the resource is not found (404). @@ -282,8 +227,8 @@ async def list_organization_api_keys( } return await self._client.request_page( method="get", - path=f"organizations/{organization_id}/api_keys", - model=ApiKey, + path=f"organizations/{quote(str(organization_id), safe='')}/api_keys", + model=OrganizationApiKey, params=params, request_options=request_options, ) @@ -295,7 +240,7 @@ async def create_organization_api_key( name: str, permissions: Optional[List[str]] = None, request_options: Optional[RequestOptions] = None, - ) -> ApiKeyWithValue: + ) -> OrganizationApiKeyWithValue: """Create an API key for an organization Create a new API key for an organization. @@ -307,7 +252,7 @@ async def create_organization_api_key( request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ApiKeyWithValue + OrganizationApiKeyWithValue Raises: NotFoundError: If the resource is not found (404). @@ -326,8 +271,68 @@ async def create_organization_api_key( } return await self._client.request( method="post", - path=f"organizations/{organization_id}/api_keys", + path=f"organizations/{quote(str(organization_id), safe='')}/api_keys", + body=body, + model=OrganizationApiKeyWithValue, + request_options=request_options, + ) + + async def create_validation( + self, + *, + value: str, + request_options: Optional[RequestOptions] = None, + ) -> ApiKeyValidationResponse: + """Validate API key + + Validate an API key value and return the API key object if valid. + + Args: + value: The value for an API key. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + ApiKeyValidationResponse + + Raises: + AuthenticationError: If the API key is invalid (401). + UnprocessableEntityError: If the request data is unprocessable (422). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + body: Dict[str, Any] = { + "value": value, + } + return await self._client.request( + method="post", + path="api_keys/validations", body=body, - model=ApiKeyWithValue, + model=ApiKeyValidationResponse, + request_options=request_options, + ) + + async def delete_api_key( + self, + id: str, + *, + request_options: Optional[RequestOptions] = None, + ) -> None: + """Delete an API key + + Permanently deletes an API key. This action cannot be undone. Once deleted, any requests using this API key will fail authentication. + + Args: + id: The unique ID of the API key. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Raises: + NotFoundError: If the resource is not found (404). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + await self._client.request( + method="delete", + path=f"api_keys/{quote(str(id), safe='')}", request_options=request_options, ) diff --git a/src/workos/api_keys/models/__init__.py b/src/workos/api_keys/models/__init__.py index 86ecd07b..18c64dee 100644 --- a/src/workos/api_keys/models/__init__.py +++ b/src/workos/api_keys/models/__init__.py @@ -5,11 +5,19 @@ from .api_key_validation_response import ( ApiKeyValidationResponse as ApiKeyValidationResponse, ) -from .api_key_with_value import ApiKeyWithValue as ApiKeyWithValue -from .api_key_with_value_owner import ApiKeyWithValueOwner as ApiKeyWithValueOwner from .create_organization_api_key import ( CreateOrganizationApiKey as CreateOrganizationApiKey, ) +from .organization_api_key import OrganizationApiKey as OrganizationApiKey +from .organization_api_key_owner import ( + OrganizationApiKeyOwner as OrganizationApiKeyOwner, +) +from .organization_api_key_with_value import ( + OrganizationApiKeyWithValue as OrganizationApiKeyWithValue, +) +from .organization_api_key_with_value_owner import ( + OrganizationApiKeyWithValueOwner as OrganizationApiKeyWithValueOwner, +) from .organizations_api_keys_order import ( OrganizationsApiKeysOrder as OrganizationsApiKeysOrder, ) diff --git a/src/workos/api_keys/models/api_key.py b/src/workos/api_keys/models/api_key.py index c59bbf72..fb19da8d 100644 --- a/src/workos/api_keys/models/api_key.py +++ b/src/workos/api_keys/models/api_key.py @@ -14,7 +14,7 @@ @dataclass(slots=True) class ApiKey: - """The API Key object if the value is valid, or `null` if invalid.""" + """Api Key model.""" object: Literal["api_key"] """Distinguishes the API Key object.""" diff --git a/src/workos/api_keys/models/organization_api_key.py b/src/workos/api_keys/models/organization_api_key.py new file mode 100644 index 00000000..493e26ce --- /dev/null +++ b/src/workos/api_keys/models/organization_api_key.py @@ -0,0 +1,75 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from typing import cast +from typing import Any, Dict, List, Literal, Optional +from workos._types import _raise_deserialize_error +from workos._types import _format_datetime, _parse_datetime + +from .organization_api_key_owner import OrganizationApiKeyOwner + + +@dataclass(slots=True) +class OrganizationApiKey: + """Organization Api Key model.""" + + object: Literal["api_key"] + """Distinguishes the API Key object.""" + id: str + """Unique identifier of the API Key.""" + owner: "OrganizationApiKeyOwner" + """The entity that owns the API Key.""" + name: str + """A descriptive name for the API Key.""" + obfuscated_value: str + """An obfuscated representation of the API Key value.""" + last_used_at: Optional[datetime] + """Timestamp of when the API Key was last used.""" + permissions: List[str] + """The permission slugs assigned to the API Key.""" + created_at: datetime + """An ISO 8601 timestamp.""" + updated_at: datetime + """An ISO 8601 timestamp.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "OrganizationApiKey": + """Deserialize from a dictionary.""" + try: + return cls( + object=data.get("object", "api_key"), + id=data["id"], + owner=OrganizationApiKeyOwner.from_dict( + cast(Dict[str, Any], data["owner"]) + ), + name=data["name"], + obfuscated_value=data["obfuscated_value"], + last_used_at=_parse_datetime(_v_last_used_at) + if (_v_last_used_at := data["last_used_at"]) is not None + else None, + permissions=data["permissions"], + created_at=_parse_datetime(data["created_at"]), + updated_at=_parse_datetime(data["updated_at"]), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("OrganizationApiKey", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["object"] = self.object + result["id"] = self.id + result["owner"] = self.owner.to_dict() + result["name"] = self.name + result["obfuscated_value"] = self.obfuscated_value + if self.last_used_at is not None: + result["last_used_at"] = _format_datetime(self.last_used_at) + else: + result["last_used_at"] = None + result["permissions"] = self.permissions + result["created_at"] = _format_datetime(self.created_at) + result["updated_at"] = _format_datetime(self.updated_at) + return result diff --git a/src/workos/api_keys/models/api_key_with_value_owner.py b/src/workos/api_keys/models/organization_api_key_owner.py similarity index 73% rename from src/workos/api_keys/models/api_key_with_value_owner.py rename to src/workos/api_keys/models/organization_api_key_owner.py index 574963ce..591f3357 100644 --- a/src/workos/api_keys/models/api_key_with_value_owner.py +++ b/src/workos/api_keys/models/organization_api_key_owner.py @@ -3,4 +3,4 @@ from typing import TypeAlias from workos.common.models.api_key_created_data_owner import ApiKeyCreatedDataOwner -ApiKeyWithValueOwner: TypeAlias = ApiKeyCreatedDataOwner +OrganizationApiKeyOwner: TypeAlias = ApiKeyCreatedDataOwner diff --git a/src/workos/api_keys/models/organization_api_key_with_value.py b/src/workos/api_keys/models/organization_api_key_with_value.py new file mode 100644 index 00000000..acd75504 --- /dev/null +++ b/src/workos/api_keys/models/organization_api_key_with_value.py @@ -0,0 +1,79 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from typing import cast +from typing import Any, Dict, List, Literal, Optional +from workos._types import _raise_deserialize_error +from workos._types import _format_datetime, _parse_datetime + +from .organization_api_key_with_value_owner import OrganizationApiKeyWithValueOwner + + +@dataclass(slots=True) +class OrganizationApiKeyWithValue: + """Organization Api Key With Value model.""" + + object: Literal["api_key"] + """Distinguishes the API Key object.""" + id: str + """Unique identifier of the API Key.""" + owner: "OrganizationApiKeyWithValueOwner" + """The entity that owns the API Key.""" + name: str + """A descriptive name for the API Key.""" + obfuscated_value: str + """An obfuscated representation of the API Key value.""" + last_used_at: Optional[datetime] + """Timestamp of when the API Key was last used.""" + permissions: List[str] + """The permission slugs assigned to the API Key.""" + created_at: datetime + """An ISO 8601 timestamp.""" + updated_at: datetime + """An ISO 8601 timestamp.""" + value: str + """The full API Key value. Only returned once at creation time.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "OrganizationApiKeyWithValue": + """Deserialize from a dictionary.""" + try: + return cls( + object=data.get("object", "api_key"), + id=data["id"], + owner=OrganizationApiKeyWithValueOwner.from_dict( + cast(Dict[str, Any], data["owner"]) + ), + name=data["name"], + obfuscated_value=data["obfuscated_value"], + last_used_at=_parse_datetime(_v_last_used_at) + if (_v_last_used_at := data["last_used_at"]) is not None + else None, + permissions=data["permissions"], + created_at=_parse_datetime(data["created_at"]), + updated_at=_parse_datetime(data["updated_at"]), + value=data["value"], + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("OrganizationApiKeyWithValue", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["object"] = self.object + result["id"] = self.id + result["owner"] = self.owner.to_dict() + result["name"] = self.name + result["obfuscated_value"] = self.obfuscated_value + if self.last_used_at is not None: + result["last_used_at"] = _format_datetime(self.last_used_at) + else: + result["last_used_at"] = None + result["permissions"] = self.permissions + result["created_at"] = _format_datetime(self.created_at) + result["updated_at"] = _format_datetime(self.updated_at) + result["value"] = self.value + return result diff --git a/src/workos/api_keys/models/organization_api_key_with_value_owner.py b/src/workos/api_keys/models/organization_api_key_with_value_owner.py new file mode 100644 index 00000000..217abfdd --- /dev/null +++ b/src/workos/api_keys/models/organization_api_key_with_value_owner.py @@ -0,0 +1,6 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias +from workos.common.models.api_key_created_data_owner import ApiKeyCreatedDataOwner + +OrganizationApiKeyWithValueOwner: TypeAlias = ApiKeyCreatedDataOwner diff --git a/src/workos/api_keys/models/organizations_api_keys_order.py b/src/workos/api_keys/models/organizations_api_keys_order.py index 1ee9eb61..0c837d32 100644 --- a/src/workos/api_keys/models/organizations_api_keys_order.py +++ b/src/workos/api_keys/models/organizations_api_keys_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -OrganizationsApiKeysOrder: TypeAlias = ApplicationsOrder +OrganizationsApiKeysOrder: TypeAlias = ApiKeysOrder __all__ = ["OrganizationsApiKeysOrder"] diff --git a/src/workos/audit_logs/_resource.py b/src/workos/audit_logs/_resource.py index e70ccd18..13f1f7cb 100644 --- a/src/workos/audit_logs/_resource.py +++ b/src/workos/audit_logs/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -53,7 +54,7 @@ def get_organization_audit_logs_retention( """ return self._client.request( method="get", - path=f"organizations/{id}/audit_logs_retention", + path=f"organizations/{quote(str(id), safe='')}/audit_logs_retention", model=AuditLogsRetentionJson, request_options=request_options, ) @@ -89,7 +90,7 @@ def update_organization_audit_logs_retention( } return self._client.request( method="put", - path=f"organizations/{id}/audit_logs_retention", + path=f"organizations/{quote(str(id), safe='')}/audit_logs_retention", body=body, model=AuditLogsRetentionJson, request_options=request_options, @@ -187,7 +188,7 @@ def list_action_schemas( } return self._client.request_page( method="get", - path=f"audit_logs/actions/{action_name}/schemas", + path=f"audit_logs/actions/{quote(str(action_name), safe='')}/schemas", model=AuditLogSchemaJson, params=params, request_options=request_options, @@ -233,7 +234,7 @@ def create_schema( } return self._client.request( method="post", - path=f"audit_logs/actions/{action_name}/schemas", + path=f"audit_logs/actions/{quote(str(action_name), safe='')}/schemas", body=body, model=AuditLogSchemaJson, request_options=request_options, @@ -371,7 +372,7 @@ def get_export( """ return self._client.request( method="get", - path=f"audit_logs/exports/{audit_log_export_id}", + path=f"audit_logs/exports/{quote(str(audit_log_export_id), safe='')}", model=AuditLogExportJson, request_options=request_options, ) @@ -408,7 +409,7 @@ async def get_organization_audit_logs_retention( """ return await self._client.request( method="get", - path=f"organizations/{id}/audit_logs_retention", + path=f"organizations/{quote(str(id), safe='')}/audit_logs_retention", model=AuditLogsRetentionJson, request_options=request_options, ) @@ -444,7 +445,7 @@ async def update_organization_audit_logs_retention( } return await self._client.request( method="put", - path=f"organizations/{id}/audit_logs_retention", + path=f"organizations/{quote(str(id), safe='')}/audit_logs_retention", body=body, model=AuditLogsRetentionJson, request_options=request_options, @@ -542,7 +543,7 @@ async def list_action_schemas( } return await self._client.request_page( method="get", - path=f"audit_logs/actions/{action_name}/schemas", + path=f"audit_logs/actions/{quote(str(action_name), safe='')}/schemas", model=AuditLogSchemaJson, params=params, request_options=request_options, @@ -588,7 +589,7 @@ async def create_schema( } return await self._client.request( method="post", - path=f"audit_logs/actions/{action_name}/schemas", + path=f"audit_logs/actions/{quote(str(action_name), safe='')}/schemas", body=body, model=AuditLogSchemaJson, request_options=request_options, @@ -726,7 +727,7 @@ async def get_export( """ return await self._client.request( method="get", - path=f"audit_logs/exports/{audit_log_export_id}", + path=f"audit_logs/exports/{quote(str(audit_log_export_id), safe='')}", model=AuditLogExportJson, request_options=request_options, ) diff --git a/src/workos/audit_logs/models/audit_logs_order.py b/src/workos/audit_logs/models/audit_logs_order.py index e1193053..5d037273 100644 --- a/src/workos/audit_logs/models/audit_logs_order.py +++ b/src/workos/audit_logs/models/audit_logs_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -AuditLogsOrder: TypeAlias = ApplicationsOrder +AuditLogsOrder: TypeAlias = ApiKeysOrder __all__ = ["AuditLogsOrder"] diff --git a/src/workos/authorization/_resource.py b/src/workos/authorization/_resource.py index 55a229c8..38f48a39 100644 --- a/src/workos/authorization/_resource.py +++ b/src/workos/authorization/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -113,7 +114,7 @@ def check( body["resource_type_slug"] = resource_target.resource_type_slug return self._client.request( method="post", - path=f"authorization/organization_memberships/{organization_membership_id}/check", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/check", body=body, model=AuthorizationCheck, request_options=request_options, @@ -181,7 +182,7 @@ def list_resources_for_membership( ) return self._client.request_page( method="get", - path=f"authorization/organization_memberships/{organization_membership_id}/resources", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/resources", model=AuthorizationResource, params=params, request_options=request_options, @@ -234,7 +235,7 @@ def list_effective_permissions( } return self._client.request_page( method="get", - path=f"authorization/organization_memberships/{organization_membership_id}/resources/{resource_id}/permissions", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/resources/{quote(str(resource_id), safe='')}/permissions", model=AuthorizationPermission, params=params, request_options=request_options, @@ -289,7 +290,7 @@ def list_effective_permissions_by_external_id( } return self._client.request_page( method="get", - path=f"authorization/organization_memberships/{organization_membership_id}/resources/{resource_type_slug}/{external_id}/permissions", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/resources/{quote(str(resource_type_slug), safe='')}/{quote(str(external_id), safe='')}/permissions", model=AuthorizationPermission, params=params, request_options=request_options, @@ -339,7 +340,7 @@ def list_role_assignments( } return self._client.request_page( method="get", - path=f"authorization/organization_memberships/{organization_membership_id}/role_assignments", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/role_assignments", model=RoleAssignment, params=params, request_options=request_options, @@ -384,7 +385,7 @@ def assign_role( body["resource_type_slug"] = resource_target.resource_type_slug return self._client.request( method="post", - path=f"authorization/organization_memberships/{organization_membership_id}/role_assignments", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/role_assignments", body=body, model=RoleAssignment, request_options=request_options, @@ -426,7 +427,7 @@ def remove_role( body["resource_type_slug"] = resource_target.resource_type_slug self._client.request( method="delete", - path=f"authorization/organization_memberships/{organization_membership_id}/role_assignments", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/role_assignments", body=body, request_options=request_options, ) @@ -456,7 +457,7 @@ def remove_role_assignment( """ self._client.request( method="delete", - path=f"authorization/organization_memberships/{organization_membership_id}/role_assignments/{role_assignment_id}", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/role_assignments/{quote(str(role_assignment_id), safe='')}", request_options=request_options, ) @@ -486,7 +487,7 @@ def list_organization_roles( """ return self._client.request( method="get", - path=f"authorization/organizations/{organization_id}/roles", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles", model=RoleList, request_options=request_options, ) @@ -538,7 +539,7 @@ def create_organization_role( } return self._client.request( method="post", - path=f"authorization/organizations/{organization_id}/roles", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles", body=body, model=Role, request_options=request_options, @@ -572,7 +573,7 @@ def get_organization_role( """ return self._client.request( method="get", - path=f"authorization/organizations/{organization_id}/roles/{slug}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles/{quote(str(slug), safe='')}", model=Role, request_options=request_options, ) @@ -619,7 +620,7 @@ def update_organization_role( } return self._client.request( method="patch", - path=f"authorization/organizations/{organization_id}/roles/{slug}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles/{quote(str(slug), safe='')}", body=body, model=Role, request_options=request_options, @@ -652,7 +653,7 @@ def delete_organization_role( """ self._client.request( method="delete", - path=f"authorization/organizations/{organization_id}/roles/{slug}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles/{quote(str(slug), safe='')}", request_options=request_options, ) @@ -691,7 +692,7 @@ def add_organization_role_permission( } return self._client.request( method="post", - path=f"authorization/organizations/{organization_id}/roles/{slug}/permissions", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles/{quote(str(slug), safe='')}/permissions", body=body, model=Role, request_options=request_options, @@ -731,7 +732,7 @@ def set_organization_role_permissions( } return self._client.request( method="put", - path=f"authorization/organizations/{organization_id}/roles/{slug}/permissions", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles/{quote(str(slug), safe='')}/permissions", body=body, model=Role, request_options=request_options, @@ -764,7 +765,7 @@ def remove_organization_role_permission( """ self._client.request( method="delete", - path=f"authorization/organizations/{organization_id}/roles/{slug}/permissions/{permission_slug}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles/{quote(str(slug), safe='')}/permissions/{quote(str(permission_slug), safe='')}", request_options=request_options, ) @@ -798,7 +799,7 @@ def get_resource_by_external_id( """ return self._client.request( method="get", - path=f"authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/resources/{quote(str(resource_type_slug), safe='')}/{quote(str(external_id), safe='')}", model=AuthorizationResource, request_options=request_options, ) @@ -862,7 +863,7 @@ def update_resource_by_external_id( ) return self._client.request( method="patch", - path=f"authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/resources/{quote(str(resource_type_slug), safe='')}/{quote(str(external_id), safe='')}", body=body, model=AuthorizationResource, request_options=request_options, @@ -906,7 +907,7 @@ def delete_resource_by_external_id( } self._client.request( method="delete", - path=f"authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/resources/{quote(str(resource_type_slug), safe='')}/{quote(str(external_id), safe='')}", params=params, request_options=request_options, ) @@ -969,7 +970,7 @@ def list_memberships_for_resource_by_external_id( } return self._client.request_page( method="get", - path=f"authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}/organization_memberships", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/resources/{quote(str(resource_type_slug), safe='')}/{quote(str(external_id), safe='')}/organization_memberships", model=UserOrganizationMembershipBaseListData, params=params, request_options=request_options, @@ -1138,7 +1139,7 @@ def get_resource( """ return self._client.request( method="get", - path=f"authorization/resources/{resource_id}", + path=f"authorization/resources/{quote(str(resource_id), safe='')}", model=AuthorizationResource, request_options=request_options, ) @@ -1198,7 +1199,7 @@ def update_resource( ) return self._client.request( method="patch", - path=f"authorization/resources/{resource_id}", + path=f"authorization/resources/{quote(str(resource_id), safe='')}", body=body, model=AuthorizationResource, request_options=request_options, @@ -1238,7 +1239,7 @@ def delete_resource( } self._client.request( method="delete", - path=f"authorization/resources/{resource_id}", + path=f"authorization/resources/{quote(str(resource_id), safe='')}", params=params, request_options=request_options, ) @@ -1297,7 +1298,7 @@ def list_memberships_for_resource( } return self._client.request_page( method="get", - path=f"authorization/resources/{resource_id}/organization_memberships", + path=f"authorization/resources/{quote(str(resource_id), safe='')}/organization_memberships", model=UserOrganizationMembershipBaseListData, params=params, request_options=request_options, @@ -1405,7 +1406,7 @@ def get_environment_role( """ return self._client.request( method="get", - path=f"authorization/roles/{slug}", + path=f"authorization/roles/{quote(str(slug), safe='')}", model=Role, request_options=request_options, ) @@ -1450,7 +1451,7 @@ def update_environment_role( } return self._client.request( method="patch", - path=f"authorization/roles/{slug}", + path=f"authorization/roles/{quote(str(slug), safe='')}", body=body, model=Role, request_options=request_options, @@ -1489,7 +1490,7 @@ def add_environment_role_permission( } return self._client.request( method="post", - path=f"authorization/roles/{slug}/permissions", + path=f"authorization/roles/{quote(str(slug), safe='')}/permissions", body=body, model=Role, request_options=request_options, @@ -1528,7 +1529,7 @@ def set_environment_role_permissions( } return self._client.request( method="put", - path=f"authorization/roles/{slug}/permissions", + path=f"authorization/roles/{quote(str(slug), safe='')}/permissions", body=body, model=Role, request_options=request_options, @@ -1656,7 +1657,7 @@ def get_permission( """ return self._client.request( method="get", - path=f"authorization/permissions/{slug}", + path=f"authorization/permissions/{quote(str(slug), safe='')}", model=AuthorizationPermission, request_options=request_options, ) @@ -1700,7 +1701,7 @@ def update_permission( } return self._client.request( method="patch", - path=f"authorization/permissions/{slug}", + path=f"authorization/permissions/{quote(str(slug), safe='')}", body=body, model=AuthorizationPermission, request_options=request_options, @@ -1729,7 +1730,7 @@ def delete_permission( """ self._client.request( method="delete", - path=f"authorization/permissions/{slug}", + path=f"authorization/permissions/{quote(str(slug), safe='')}", request_options=request_options, ) @@ -1779,7 +1780,7 @@ async def check( body["resource_type_slug"] = resource_target.resource_type_slug return await self._client.request( method="post", - path=f"authorization/organization_memberships/{organization_membership_id}/check", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/check", body=body, model=AuthorizationCheck, request_options=request_options, @@ -1847,7 +1848,7 @@ async def list_resources_for_membership( ) return await self._client.request_page( method="get", - path=f"authorization/organization_memberships/{organization_membership_id}/resources", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/resources", model=AuthorizationResource, params=params, request_options=request_options, @@ -1900,7 +1901,7 @@ async def list_effective_permissions( } return await self._client.request_page( method="get", - path=f"authorization/organization_memberships/{organization_membership_id}/resources/{resource_id}/permissions", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/resources/{quote(str(resource_id), safe='')}/permissions", model=AuthorizationPermission, params=params, request_options=request_options, @@ -1955,7 +1956,7 @@ async def list_effective_permissions_by_external_id( } return await self._client.request_page( method="get", - path=f"authorization/organization_memberships/{organization_membership_id}/resources/{resource_type_slug}/{external_id}/permissions", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/resources/{quote(str(resource_type_slug), safe='')}/{quote(str(external_id), safe='')}/permissions", model=AuthorizationPermission, params=params, request_options=request_options, @@ -2005,7 +2006,7 @@ async def list_role_assignments( } return await self._client.request_page( method="get", - path=f"authorization/organization_memberships/{organization_membership_id}/role_assignments", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/role_assignments", model=RoleAssignment, params=params, request_options=request_options, @@ -2050,7 +2051,7 @@ async def assign_role( body["resource_type_slug"] = resource_target.resource_type_slug return await self._client.request( method="post", - path=f"authorization/organization_memberships/{organization_membership_id}/role_assignments", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/role_assignments", body=body, model=RoleAssignment, request_options=request_options, @@ -2092,7 +2093,7 @@ async def remove_role( body["resource_type_slug"] = resource_target.resource_type_slug await self._client.request( method="delete", - path=f"authorization/organization_memberships/{organization_membership_id}/role_assignments", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/role_assignments", body=body, request_options=request_options, ) @@ -2122,7 +2123,7 @@ async def remove_role_assignment( """ await self._client.request( method="delete", - path=f"authorization/organization_memberships/{organization_membership_id}/role_assignments/{role_assignment_id}", + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/role_assignments/{quote(str(role_assignment_id), safe='')}", request_options=request_options, ) @@ -2152,7 +2153,7 @@ async def list_organization_roles( """ return await self._client.request( method="get", - path=f"authorization/organizations/{organization_id}/roles", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles", model=RoleList, request_options=request_options, ) @@ -2204,7 +2205,7 @@ async def create_organization_role( } return await self._client.request( method="post", - path=f"authorization/organizations/{organization_id}/roles", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles", body=body, model=Role, request_options=request_options, @@ -2238,7 +2239,7 @@ async def get_organization_role( """ return await self._client.request( method="get", - path=f"authorization/organizations/{organization_id}/roles/{slug}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles/{quote(str(slug), safe='')}", model=Role, request_options=request_options, ) @@ -2285,7 +2286,7 @@ async def update_organization_role( } return await self._client.request( method="patch", - path=f"authorization/organizations/{organization_id}/roles/{slug}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles/{quote(str(slug), safe='')}", body=body, model=Role, request_options=request_options, @@ -2318,7 +2319,7 @@ async def delete_organization_role( """ await self._client.request( method="delete", - path=f"authorization/organizations/{organization_id}/roles/{slug}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles/{quote(str(slug), safe='')}", request_options=request_options, ) @@ -2357,7 +2358,7 @@ async def add_organization_role_permission( } return await self._client.request( method="post", - path=f"authorization/organizations/{organization_id}/roles/{slug}/permissions", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles/{quote(str(slug), safe='')}/permissions", body=body, model=Role, request_options=request_options, @@ -2397,7 +2398,7 @@ async def set_organization_role_permissions( } return await self._client.request( method="put", - path=f"authorization/organizations/{organization_id}/roles/{slug}/permissions", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles/{quote(str(slug), safe='')}/permissions", body=body, model=Role, request_options=request_options, @@ -2430,7 +2431,7 @@ async def remove_organization_role_permission( """ await self._client.request( method="delete", - path=f"authorization/organizations/{organization_id}/roles/{slug}/permissions/{permission_slug}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/roles/{quote(str(slug), safe='')}/permissions/{quote(str(permission_slug), safe='')}", request_options=request_options, ) @@ -2464,7 +2465,7 @@ async def get_resource_by_external_id( """ return await self._client.request( method="get", - path=f"authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/resources/{quote(str(resource_type_slug), safe='')}/{quote(str(external_id), safe='')}", model=AuthorizationResource, request_options=request_options, ) @@ -2528,7 +2529,7 @@ async def update_resource_by_external_id( ) return await self._client.request( method="patch", - path=f"authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/resources/{quote(str(resource_type_slug), safe='')}/{quote(str(external_id), safe='')}", body=body, model=AuthorizationResource, request_options=request_options, @@ -2572,7 +2573,7 @@ async def delete_resource_by_external_id( } await self._client.request( method="delete", - path=f"authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/resources/{quote(str(resource_type_slug), safe='')}/{quote(str(external_id), safe='')}", params=params, request_options=request_options, ) @@ -2635,7 +2636,7 @@ async def list_memberships_for_resource_by_external_id( } return await self._client.request_page( method="get", - path=f"authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}/organization_memberships", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/resources/{quote(str(resource_type_slug), safe='')}/{quote(str(external_id), safe='')}/organization_memberships", model=UserOrganizationMembershipBaseListData, params=params, request_options=request_options, @@ -2804,7 +2805,7 @@ async def get_resource( """ return await self._client.request( method="get", - path=f"authorization/resources/{resource_id}", + path=f"authorization/resources/{quote(str(resource_id), safe='')}", model=AuthorizationResource, request_options=request_options, ) @@ -2864,7 +2865,7 @@ async def update_resource( ) return await self._client.request( method="patch", - path=f"authorization/resources/{resource_id}", + path=f"authorization/resources/{quote(str(resource_id), safe='')}", body=body, model=AuthorizationResource, request_options=request_options, @@ -2904,7 +2905,7 @@ async def delete_resource( } await self._client.request( method="delete", - path=f"authorization/resources/{resource_id}", + path=f"authorization/resources/{quote(str(resource_id), safe='')}", params=params, request_options=request_options, ) @@ -2963,7 +2964,7 @@ async def list_memberships_for_resource( } return await self._client.request_page( method="get", - path=f"authorization/resources/{resource_id}/organization_memberships", + path=f"authorization/resources/{quote(str(resource_id), safe='')}/organization_memberships", model=UserOrganizationMembershipBaseListData, params=params, request_options=request_options, @@ -3071,7 +3072,7 @@ async def get_environment_role( """ return await self._client.request( method="get", - path=f"authorization/roles/{slug}", + path=f"authorization/roles/{quote(str(slug), safe='')}", model=Role, request_options=request_options, ) @@ -3116,7 +3117,7 @@ async def update_environment_role( } return await self._client.request( method="patch", - path=f"authorization/roles/{slug}", + path=f"authorization/roles/{quote(str(slug), safe='')}", body=body, model=Role, request_options=request_options, @@ -3155,7 +3156,7 @@ async def add_environment_role_permission( } return await self._client.request( method="post", - path=f"authorization/roles/{slug}/permissions", + path=f"authorization/roles/{quote(str(slug), safe='')}/permissions", body=body, model=Role, request_options=request_options, @@ -3194,7 +3195,7 @@ async def set_environment_role_permissions( } return await self._client.request( method="put", - path=f"authorization/roles/{slug}/permissions", + path=f"authorization/roles/{quote(str(slug), safe='')}/permissions", body=body, model=Role, request_options=request_options, @@ -3322,7 +3323,7 @@ async def get_permission( """ return await self._client.request( method="get", - path=f"authorization/permissions/{slug}", + path=f"authorization/permissions/{quote(str(slug), safe='')}", model=AuthorizationPermission, request_options=request_options, ) @@ -3366,7 +3367,7 @@ async def update_permission( } return await self._client.request( method="patch", - path=f"authorization/permissions/{slug}", + path=f"authorization/permissions/{quote(str(slug), safe='')}", body=body, model=AuthorizationPermission, request_options=request_options, @@ -3395,6 +3396,6 @@ async def delete_permission( """ await self._client.request( method="delete", - path=f"authorization/permissions/{slug}", + path=f"authorization/permissions/{quote(str(slug), safe='')}", request_options=request_options, ) diff --git a/src/workos/authorization/models/authorization_order.py b/src/workos/authorization/models/authorization_order.py index 9920b5c9..be9c66f7 100644 --- a/src/workos/authorization/models/authorization_order.py +++ b/src/workos/authorization/models/authorization_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -AuthorizationOrder: TypeAlias = ApplicationsOrder +AuthorizationOrder: TypeAlias = ApiKeysOrder __all__ = ["AuthorizationOrder"] diff --git a/src/workos/authorization/models/permissions_order.py b/src/workos/authorization/models/permissions_order.py index cc601f83..26d8cecd 100644 --- a/src/workos/authorization/models/permissions_order.py +++ b/src/workos/authorization/models/permissions_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -PermissionsOrder: TypeAlias = ApplicationsOrder +PermissionsOrder: TypeAlias = ApiKeysOrder __all__ = ["PermissionsOrder"] diff --git a/src/workos/authorization/models/user_organization_membership_base_list_data.py b/src/workos/authorization/models/user_organization_membership_base_list_data.py index 0cc63eaa..ddc996bf 100644 --- a/src/workos/authorization/models/user_organization_membership_base_list_data.py +++ b/src/workos/authorization/models/user_organization_membership_base_list_data.py @@ -5,9 +5,12 @@ from dataclasses import dataclass from datetime import datetime from enum import Enum +from typing import cast from typing import Any, Dict, Literal, Optional from workos._types import _raise_deserialize_error from workos._types import _format_datetime, _parse_datetime + +from workos.user_management.models.user import User from workos.common.models.user_organization_membership_base_list_data_status import ( UserOrganizationMembershipBaseListDataStatus, ) @@ -33,6 +36,8 @@ class UserOrganizationMembershipBaseListData: """An ISO 8601 timestamp.""" updated_at: datetime """An ISO 8601 timestamp.""" + user: "User" + """The user that belongs to the organization through this membership.""" organization_name: Optional[str] = None """The name of the organization which the user belongs to.""" custom_attributes: Optional[Dict[str, Any]] = None @@ -53,6 +58,7 @@ def from_dict( directory_managed=data["directory_managed"], created_at=_parse_datetime(data["created_at"]), updated_at=_parse_datetime(data["updated_at"]), + user=User.from_dict(cast(Dict[str, Any], data["user"])), organization_name=data.get("organization_name"), custom_attributes=data.get("custom_attributes"), ) @@ -72,6 +78,7 @@ def to_dict(self) -> Dict[str, Any]: result["directory_managed"] = self.directory_managed result["created_at"] = _format_datetime(self.created_at) result["updated_at"] = _format_datetime(self.updated_at) + result["user"] = self.user.to_dict() if self.organization_name is not None: result["organization_name"] = self.organization_name if self.custom_attributes is not None: diff --git a/src/workos/common/__init__.py b/src/workos/common/__init__.py index 8d5ff562..97be36e7 100644 --- a/src/workos/common/__init__.py +++ b/src/workos/common/__init__.py @@ -361,6 +361,8 @@ from .models import UserSessionsAuthMethod as UserSessionsAuthMethod from .models import UserSessionsStatus as UserSessionsStatus from .models import UserUpdated as UserUpdated +from .models import VaultByokKeyDeleted as VaultByokKeyDeleted +from .models import VaultByokKeyDeletedData as VaultByokKeyDeletedData from .models import ( VaultByokKeyVerificationCompleted as VaultByokKeyVerificationCompleted, ) diff --git a/src/workos/common/models/__init__.py b/src/workos/common/models/__init__.py index f27d1492..17796729 100644 --- a/src/workos/common/models/__init__.py +++ b/src/workos/common/models/__init__.py @@ -555,6 +555,10 @@ from .user_sessions_auth_method import UserSessionsAuthMethod as UserSessionsAuthMethod from .user_sessions_status import UserSessionsStatus as UserSessionsStatus from .user_updated import UserUpdated as UserUpdated +from .vault_byok_key_deleted import VaultByokKeyDeleted as VaultByokKeyDeleted +from .vault_byok_key_deleted_data import ( + VaultByokKeyDeletedData as VaultByokKeyDeletedData, +) from .vault_byok_key_verification_completed import ( VaultByokKeyVerificationCompleted as VaultByokKeyVerificationCompleted, ) diff --git a/src/workos/common/models/api_key_created_data_owner.py b/src/workos/common/models/api_key_created_data_owner.py index df51ebf1..e99edf06 100644 --- a/src/workos/common/models/api_key_created_data_owner.py +++ b/src/workos/common/models/api_key_created_data_owner.py @@ -9,7 +9,7 @@ @dataclass(slots=True) class ApiKeyCreatedDataOwner: - """The owner of the API key.""" + """Api Key Created Data Owner model.""" type: Literal["organization"] """The type of the API key owner.""" diff --git a/src/workos/common/models/directory_user.py b/src/workos/common/models/directory_user.py index 37772c9f..056d8ec8 100644 --- a/src/workos/common/models/directory_user.py +++ b/src/workos/common/models/directory_user.py @@ -43,6 +43,8 @@ class DirectoryUser: """The first name of the user.""" last_name: Optional[str] = None """The last name of the user.""" + name: Optional[str] = None + """The full name of the user.""" emails: Optional[List["DirectoryUserEmail"]] = None """A list of email addresses for the user. @@ -80,6 +82,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "DirectoryUser": updated_at=_parse_datetime(data["updated_at"]), first_name=data.get("first_name"), last_name=data.get("last_name"), + name=data.get("name"), emails=[ DirectoryUserEmail.from_dict(cast(Dict[str, Any], item)) for item in cast(list[Any], _v_emails) @@ -128,6 +131,10 @@ def to_dict(self) -> Dict[str, Any]: result["last_name"] = self.last_name else: result["last_name"] = None + if self.name is not None: + result["name"] = self.name + else: + result["name"] = None if self.emails is not None: result["emails"] = [item.to_dict() for item in self.emails] if self.job_title is not None: diff --git a/src/workos/common/models/dsync_user_updated_data.py b/src/workos/common/models/dsync_user_updated_data.py index 90956d01..29a78971 100644 --- a/src/workos/common/models/dsync_user_updated_data.py +++ b/src/workos/common/models/dsync_user_updated_data.py @@ -43,6 +43,8 @@ class DsyncUserUpdatedData: """The first name of the user.""" last_name: Optional[str] = None """The last name of the user.""" + name: Optional[str] = None + """The full name of the user.""" emails: Optional[List["DsyncUserUpdatedDataEmail"]] = None """A list of email addresses for the user. @@ -81,6 +83,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "DsyncUserUpdatedData": updated_at=_parse_datetime(data["updated_at"]), first_name=data.get("first_name"), last_name=data.get("last_name"), + name=data.get("name"), emails=[ DsyncUserUpdatedDataEmail.from_dict(cast(Dict[str, Any], item)) for item in cast(list[Any], _v_emails) @@ -130,6 +133,10 @@ def to_dict(self) -> Dict[str, Any]: result["last_name"] = self.last_name else: result["last_name"] = None + if self.name is not None: + result["name"] = self.name + else: + result["name"] = None if self.emails is not None: result["emails"] = [item.to_dict() for item in self.emails] if self.job_title is not None: diff --git a/src/workos/common/models/event_context_actor_source.py b/src/workos/common/models/event_context_actor_source.py index f270625e..a70e58e7 100644 --- a/src/workos/common/models/event_context_actor_source.py +++ b/src/workos/common/models/event_context_actor_source.py @@ -14,6 +14,7 @@ class EventContextActorSource(str, Enum): API = "api" DASHBOARD = "dashboard" + ADMIN_PORTAL = "admin_portal" SYSTEM = "system" @classmethod @@ -26,4 +27,6 @@ def _missing_(cls, value: object) -> Optional["EventContextActorSource"]: return unknown -EventContextActorSourceLiteral: TypeAlias = Literal["api", "dashboard", "system"] +EventContextActorSourceLiteral: TypeAlias = Literal[ + "api", "dashboard", "admin_portal", "system" +] diff --git a/src/workos/common/models/vault_byok_key_deleted.py b/src/workos/common/models/vault_byok_key_deleted.py new file mode 100644 index 00000000..6cc0012e --- /dev/null +++ b/src/workos/common/models/vault_byok_key_deleted.py @@ -0,0 +1,60 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from typing import cast +from typing import Any, Dict, Literal, Optional +from workos._types import _raise_deserialize_error +from workos._types import _format_datetime, _parse_datetime + +from .event_context import EventContext +from .vault_byok_key_deleted_data import VaultByokKeyDeletedData + + +@dataclass(slots=True) +class VaultByokKeyDeleted: + """Vault Byok Key Deleted model.""" + + id: str + """Unique identifier for the event.""" + event: Literal["vault.byok_key.deleted"] + data: "VaultByokKeyDeletedData" + """The event payload.""" + created_at: datetime + """An ISO 8601 timestamp.""" + object: Literal["event"] + """Distinguishes the Event object.""" + context: Optional["EventContext"] = None + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "VaultByokKeyDeleted": + """Deserialize from a dictionary.""" + try: + return cls( + id=data["id"], + event=data.get("event", "vault.byok_key.deleted"), + data=VaultByokKeyDeletedData.from_dict( + cast(Dict[str, Any], data["data"]) + ), + created_at=_parse_datetime(data["created_at"]), + object=data.get("object", "event"), + context=EventContext.from_dict(cast(Dict[str, Any], _v_context)) + if (_v_context := data.get("context")) is not None + else None, + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("VaultByokKeyDeleted", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["id"] = self.id + result["event"] = self.event + result["data"] = self.data.to_dict() + result["created_at"] = _format_datetime(self.created_at) + result["object"] = self.object + if self.context is not None: + result["context"] = self.context.to_dict() + return result diff --git a/src/workos/common/models/vault_byok_key_deleted_data.py b/src/workos/common/models/vault_byok_key_deleted_data.py new file mode 100644 index 00000000..7bd73efb --- /dev/null +++ b/src/workos/common/models/vault_byok_key_deleted_data.py @@ -0,0 +1,41 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import Any, Dict +from workos._types import _raise_deserialize_error +from .vault_byok_key_deleted_data_key_provider import VaultByokKeyDeletedDataKeyProvider + + +@dataclass(slots=True) +class VaultByokKeyDeletedData: + """The event payload.""" + + organization_id: str + """The unique identifier of the organization.""" + key_provider: "VaultByokKeyDeletedDataKeyProvider" + """The external key provider used for BYOK.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "VaultByokKeyDeletedData": + """Deserialize from a dictionary.""" + try: + return cls( + organization_id=data["organization_id"], + key_provider=VaultByokKeyDeletedDataKeyProvider(data["key_provider"]), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("VaultByokKeyDeletedData", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["organization_id"] = self.organization_id + result["key_provider"] = ( + self.key_provider.value + if isinstance(self.key_provider, Enum) + else self.key_provider + ) + return result diff --git a/src/workos/common/models/vault_byok_key_deleted_data_key_provider.py b/src/workos/common/models/vault_byok_key_deleted_data_key_provider.py new file mode 100644 index 00000000..26957ec9 --- /dev/null +++ b/src/workos/common/models/vault_byok_key_deleted_data_key_provider.py @@ -0,0 +1,31 @@ +# This file is auto-generated by oagen. Do not edit. + +"""Enumeration of vault byok key deleted data key provider values.""" + +from __future__ import annotations + +from enum import Enum +from typing import Optional +from typing import Literal, TypeAlias + + +class VaultByokKeyDeletedDataKeyProvider(str, Enum): + """Known values for VaultByokKeyDeletedDataKeyProvider.""" + + AWS_KMS = "AWS_KMS" + GCP_KMS = "GCP_KMS" + AZURE_KEY_VAULT = "AZURE_KEY_VAULT" + + @classmethod + def _missing_(cls, value: object) -> Optional["VaultByokKeyDeletedDataKeyProvider"]: + if not isinstance(value, str): + return None + unknown = str.__new__(cls, value) + unknown._name_ = value.upper() + unknown._value_ = value + return unknown + + +VaultByokKeyDeletedDataKeyProviderLiteral: TypeAlias = Literal[ + "AWS_KMS", "GCP_KMS", "AZURE_KEY_VAULT" +] diff --git a/src/workos/common/models/vault_byok_key_verification_completed_data_key_provider.py b/src/workos/common/models/vault_byok_key_verification_completed_data_key_provider.py index c63ba21d..d8569afc 100644 --- a/src/workos/common/models/vault_byok_key_verification_completed_data_key_provider.py +++ b/src/workos/common/models/vault_byok_key_verification_completed_data_key_provider.py @@ -1,33 +1,9 @@ # This file is auto-generated by oagen. Do not edit. -"""Enumeration of vault byok key verification completed data key provider values.""" +from typing import TypeAlias +from .vault_byok_key_deleted_data_key_provider import VaultByokKeyDeletedDataKeyProvider -from __future__ import annotations - -from enum import Enum -from typing import Optional -from typing import Literal, TypeAlias - - -class VaultByokKeyVerificationCompletedDataKeyProvider(str, Enum): - """Known values for VaultByokKeyVerificationCompletedDataKeyProvider.""" - - AWS_KMS = "AWS_KMS" - GCP_KMS = "GCP_KMS" - AZURE_KEY_VAULT = "AZURE_KEY_VAULT" - - @classmethod - def _missing_( - cls, value: object - ) -> Optional["VaultByokKeyVerificationCompletedDataKeyProvider"]: - if not isinstance(value, str): - return None - unknown = str.__new__(cls, value) - unknown._name_ = value.upper() - unknown._value_ = value - return unknown - - -VaultByokKeyVerificationCompletedDataKeyProviderLiteral: TypeAlias = Literal[ - "AWS_KMS", "GCP_KMS", "AZURE_KEY_VAULT" -] +VaultByokKeyVerificationCompletedDataKeyProvider: TypeAlias = ( + VaultByokKeyDeletedDataKeyProvider +) +__all__ = ["VaultByokKeyVerificationCompletedDataKeyProvider"] diff --git a/src/workos/connect/_resource.py b/src/workos/connect/_resource.py index e7b474de..96827a45 100644 --- a/src/workos/connect/_resource.py +++ b/src/workos/connect/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -261,7 +262,7 @@ def get_application( """ return self._client.request( method="get", - path=f"connect/applications/{id}", + path=f"connect/applications/{quote(str(id), safe='')}", model=ConnectApplication, request_options=request_options, ) @@ -312,7 +313,7 @@ def update_application( } return self._client.request( method="put", - path=f"connect/applications/{id}", + path=f"connect/applications/{quote(str(id), safe='')}", body=body, model=ConnectApplication, request_options=request_options, @@ -340,7 +341,7 @@ def delete_application( """ self._client.request( method="delete", - path=f"connect/applications/{id}", + path=f"connect/applications/{quote(str(id), safe='')}", request_options=request_options, ) @@ -369,7 +370,7 @@ def list_application_client_secrets( """ raw = self._client.request_list( method="get", - path=f"connect/applications/{id}/client_secrets", + path=f"connect/applications/{quote(str(id), safe='')}/client_secrets", request_options=request_options, ) return [ @@ -404,7 +405,7 @@ def create_application_client_secret( body: Dict[str, Any] = {} return self._client.request( method="post", - path=f"connect/applications/{id}/client_secrets", + path=f"connect/applications/{quote(str(id), safe='')}/client_secrets", body=body, model=NewConnectApplicationSecret, request_options=request_options, @@ -432,7 +433,7 @@ def delete_client_secret( """ self._client.request( method="delete", - path=f"connect/client_secrets/{id}", + path=f"connect/client_secrets/{quote(str(id), safe='')}", request_options=request_options, ) @@ -675,7 +676,7 @@ async def get_application( """ return await self._client.request( method="get", - path=f"connect/applications/{id}", + path=f"connect/applications/{quote(str(id), safe='')}", model=ConnectApplication, request_options=request_options, ) @@ -726,7 +727,7 @@ async def update_application( } return await self._client.request( method="put", - path=f"connect/applications/{id}", + path=f"connect/applications/{quote(str(id), safe='')}", body=body, model=ConnectApplication, request_options=request_options, @@ -754,7 +755,7 @@ async def delete_application( """ await self._client.request( method="delete", - path=f"connect/applications/{id}", + path=f"connect/applications/{quote(str(id), safe='')}", request_options=request_options, ) @@ -783,7 +784,7 @@ async def list_application_client_secrets( """ raw = await self._client.request_list( method="get", - path=f"connect/applications/{id}/client_secrets", + path=f"connect/applications/{quote(str(id), safe='')}/client_secrets", request_options=request_options, ) return [ @@ -818,7 +819,7 @@ async def create_application_client_secret( body: Dict[str, Any] = {} return await self._client.request( method="post", - path=f"connect/applications/{id}/client_secrets", + path=f"connect/applications/{quote(str(id), safe='')}/client_secrets", body=body, model=NewConnectApplicationSecret, request_options=request_options, @@ -846,6 +847,6 @@ async def delete_client_secret( """ await self._client.request( method="delete", - path=f"connect/client_secrets/{id}", + path=f"connect/client_secrets/{quote(str(id), safe='')}", request_options=request_options, ) diff --git a/src/workos/connect/models/applications_order.py b/src/workos/connect/models/applications_order.py index 15ae48f3..262bcdc1 100644 --- a/src/workos/connect/models/applications_order.py +++ b/src/workos/connect/models/applications_order.py @@ -1,29 +1,7 @@ # This file is auto-generated by oagen. Do not edit. -"""Enumeration of applications order values.""" +from typing import TypeAlias +from workos.user_management.models.api_keys_order import ApiKeysOrder -from __future__ import annotations - -from enum import Enum -from typing import Optional -from typing import Literal, TypeAlias - - -class ApplicationsOrder(str, Enum): - """Known values for ApplicationsOrder.""" - - NORMAL = "normal" - DESC = "desc" - ASC = "asc" - - @classmethod - def _missing_(cls, value: object) -> Optional["ApplicationsOrder"]: - if not isinstance(value, str): - return None - unknown = str.__new__(cls, value) - unknown._name_ = value.upper() - unknown._value_ = value - return unknown - - -ApplicationsOrderLiteral: TypeAlias = Literal["normal", "desc", "asc"] +ApplicationsOrder: TypeAlias = ApiKeysOrder +__all__ = ["ApplicationsOrder"] diff --git a/src/workos/directory_sync/_resource.py b/src/workos/directory_sync/_resource.py index d183730e..30331454 100644 --- a/src/workos/directory_sync/_resource.py +++ b/src/workos/directory_sync/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -102,7 +103,7 @@ def get_directory( """ return self._client.request( method="get", - path=f"directories/{id}", + path=f"directories/{quote(str(id), safe='')}", model=Directory, request_options=request_options, ) @@ -129,7 +130,7 @@ def delete_directory( """ self._client.request( method="delete", - path=f"directories/{id}", + path=f"directories/{quote(str(id), safe='')}", request_options=request_options, ) @@ -214,7 +215,7 @@ def get_group( """ return self._client.request( method="get", - path=f"directory_groups/{id}", + path=f"directory_groups/{quote(str(id), safe='')}", model=DirectoryGroup, request_options=request_options, ) @@ -300,7 +301,7 @@ def get_user( """ return self._client.request( method="get", - path=f"directory_users/{id}", + path=f"directory_users/{quote(str(id), safe='')}", model=DirectoryUserWithGroups, request_options=request_options, ) @@ -395,7 +396,7 @@ async def get_directory( """ return await self._client.request( method="get", - path=f"directories/{id}", + path=f"directories/{quote(str(id), safe='')}", model=Directory, request_options=request_options, ) @@ -422,7 +423,7 @@ async def delete_directory( """ await self._client.request( method="delete", - path=f"directories/{id}", + path=f"directories/{quote(str(id), safe='')}", request_options=request_options, ) @@ -507,7 +508,7 @@ async def get_group( """ return await self._client.request( method="get", - path=f"directory_groups/{id}", + path=f"directory_groups/{quote(str(id), safe='')}", model=DirectoryGroup, request_options=request_options, ) @@ -593,7 +594,7 @@ async def get_user( """ return await self._client.request( method="get", - path=f"directory_users/{id}", + path=f"directory_users/{quote(str(id), safe='')}", model=DirectoryUserWithGroups, request_options=request_options, ) diff --git a/src/workos/directory_sync/models/directories_order.py b/src/workos/directory_sync/models/directories_order.py index 9b78ed24..b1fc780c 100644 --- a/src/workos/directory_sync/models/directories_order.py +++ b/src/workos/directory_sync/models/directories_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -DirectoriesOrder: TypeAlias = ApplicationsOrder +DirectoriesOrder: TypeAlias = ApiKeysOrder __all__ = ["DirectoriesOrder"] diff --git a/src/workos/directory_sync/models/directory_groups_order.py b/src/workos/directory_sync/models/directory_groups_order.py index 35c06d3f..6cbb6329 100644 --- a/src/workos/directory_sync/models/directory_groups_order.py +++ b/src/workos/directory_sync/models/directory_groups_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -DirectoryGroupsOrder: TypeAlias = ApplicationsOrder +DirectoryGroupsOrder: TypeAlias = ApiKeysOrder __all__ = ["DirectoryGroupsOrder"] diff --git a/src/workos/directory_sync/models/directory_user_with_groups.py b/src/workos/directory_sync/models/directory_user_with_groups.py index 58be6a89..764c55fe 100644 --- a/src/workos/directory_sync/models/directory_user_with_groups.py +++ b/src/workos/directory_sync/models/directory_user_with_groups.py @@ -46,6 +46,8 @@ class DirectoryUserWithGroups: """The first name of the user.""" last_name: Optional[str] = None """The last name of the user.""" + name: Optional[str] = None + """The full name of the user.""" emails: Optional[List["DirectoryUserWithGroupsEmail"]] = None """A list of email addresses for the user. @@ -66,7 +68,7 @@ class DirectoryUserWithGroups: roles: Optional[List["SlimRole"]] = None """All roles assigned to the user.""" groups: Optional[List["DirectoryGroup"]] = None - """The directory groups the user belongs to. Use the List Directory Groups endpoint with a user filter instead. + """The directory groups the user belongs to. Deprecated: starting May 1, 2026, this field returns an empty array by default for newly created teams. Existing teams currently depending on this field should migrate to the new access pattern for better throughput performance — the field is unbounded by user, so users with many group memberships produce large, slow response payloads. Use the List Directory Groups endpoint with a `user` filter to fetch a user's group memberships. .. deprecated:: This field is deprecated.""" @@ -87,6 +89,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "DirectoryUserWithGroups": updated_at=_parse_datetime(data["updated_at"]), first_name=data.get("first_name"), last_name=data.get("last_name"), + name=data.get("name"), emails=[ DirectoryUserWithGroupsEmail.from_dict(cast(Dict[str, Any], item)) for item in cast(list[Any], _v_emails) @@ -141,6 +144,10 @@ def to_dict(self) -> Dict[str, Any]: result["last_name"] = self.last_name else: result["last_name"] = None + if self.name is not None: + result["name"] = self.name + else: + result["name"] = None if self.emails is not None: result["emails"] = [item.to_dict() for item in self.emails] if self.job_title is not None: diff --git a/src/workos/directory_sync/models/directory_users_order.py b/src/workos/directory_sync/models/directory_users_order.py index 5409eaca..d2e8173b 100644 --- a/src/workos/directory_sync/models/directory_users_order.py +++ b/src/workos/directory_sync/models/directory_users_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -DirectoryUsersOrder: TypeAlias = ApplicationsOrder +DirectoryUsersOrder: TypeAlias = ApiKeysOrder __all__ = ["DirectoryUsersOrder"] diff --git a/src/workos/events/models/event_schema.py b/src/workos/events/models/event_schema.py index 6dabf048..25fa0079 100644 --- a/src/workos/events/models/event_schema.py +++ b/src/workos/events/models/event_schema.py @@ -119,6 +119,7 @@ from workos.common.models.user_created import UserCreated from workos.common.models.user_deleted import UserDeleted from workos.common.models.user_updated import UserUpdated +from workos.common.models.vault_byok_key_deleted import VaultByokKeyDeleted from workos.common.models.vault_byok_key_verification_completed import ( VaultByokKeyVerificationCompleted, ) @@ -233,6 +234,7 @@ def to_dict(self) -> Dict[str, Any]: UserCreated, UserDeleted, UserUpdated, + VaultByokKeyDeleted, VaultByokKeyVerificationCompleted, VaultDataCreated, VaultDataDeleted, @@ -333,6 +335,7 @@ class EventSchema: "user.created": UserCreated, "user.deleted": UserDeleted, "user.updated": UserUpdated, + "vault.byok_key.deleted": VaultByokKeyDeleted, "vault.byok_key.verification_completed": VaultByokKeyVerificationCompleted, "vault.data.created": VaultDataCreated, "vault.data.deleted": VaultDataDeleted, diff --git a/src/workos/events/models/events_order.py b/src/workos/events/models/events_order.py index b272b239..232be5b3 100644 --- a/src/workos/events/models/events_order.py +++ b/src/workos/events/models/events_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -EventsOrder: TypeAlias = ApplicationsOrder +EventsOrder: TypeAlias = ApiKeysOrder __all__ = ["EventsOrder"] diff --git a/src/workos/feature_flags/_resource.py b/src/workos/feature_flags/_resource.py index b1da5d77..d9ce7646 100644 --- a/src/workos/feature_flags/_resource.py +++ b/src/workos/feature_flags/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -97,7 +98,7 @@ def get_feature_flag( """ return self._client.request( method="get", - path=f"feature-flags/{slug}", + path=f"feature-flags/{quote(str(slug), safe='')}", model=Flag, request_options=request_options, ) @@ -127,7 +128,7 @@ def disable_feature_flag( """ return self._client.request( method="put", - path=f"feature-flags/{slug}/disable", + path=f"feature-flags/{quote(str(slug), safe='')}/disable", model=FeatureFlag, request_options=request_options, ) @@ -157,7 +158,7 @@ def enable_feature_flag( """ return self._client.request( method="put", - path=f"feature-flags/{slug}/enable", + path=f"feature-flags/{quote(str(slug), safe='')}/enable", model=FeatureFlag, request_options=request_options, ) @@ -188,7 +189,7 @@ def add_flag_target( """ self._client.request( method="post", - path=f"feature-flags/{slug}/targets/{resource_id}", + path=f"feature-flags/{quote(str(slug), safe='')}/targets/{quote(str(resource_id), safe='')}", request_options=request_options, ) @@ -218,7 +219,7 @@ def remove_flag_target( """ self._client.request( method="delete", - path=f"feature-flags/{slug}/targets/{resource_id}", + path=f"feature-flags/{quote(str(slug), safe='')}/targets/{quote(str(resource_id), safe='')}", request_options=request_options, ) @@ -265,7 +266,7 @@ def list_organization_feature_flags( } return self._client.request_page( method="get", - path=f"organizations/{organization_id}/feature-flags", + path=f"organizations/{quote(str(organization_id), safe='')}/feature-flags", model=Flag, params=params, request_options=request_options, @@ -314,7 +315,7 @@ def list_user_feature_flags( } return self._client.request_page( method="get", - path=f"user_management/users/{user_id}/feature-flags", + path=f"user_management/users/{quote(str(user_id), safe='')}/feature-flags", model=Flag, params=params, request_options=request_options, @@ -401,7 +402,7 @@ async def get_feature_flag( """ return await self._client.request( method="get", - path=f"feature-flags/{slug}", + path=f"feature-flags/{quote(str(slug), safe='')}", model=Flag, request_options=request_options, ) @@ -431,7 +432,7 @@ async def disable_feature_flag( """ return await self._client.request( method="put", - path=f"feature-flags/{slug}/disable", + path=f"feature-flags/{quote(str(slug), safe='')}/disable", model=FeatureFlag, request_options=request_options, ) @@ -461,7 +462,7 @@ async def enable_feature_flag( """ return await self._client.request( method="put", - path=f"feature-flags/{slug}/enable", + path=f"feature-flags/{quote(str(slug), safe='')}/enable", model=FeatureFlag, request_options=request_options, ) @@ -492,7 +493,7 @@ async def add_flag_target( """ await self._client.request( method="post", - path=f"feature-flags/{slug}/targets/{resource_id}", + path=f"feature-flags/{quote(str(slug), safe='')}/targets/{quote(str(resource_id), safe='')}", request_options=request_options, ) @@ -522,7 +523,7 @@ async def remove_flag_target( """ await self._client.request( method="delete", - path=f"feature-flags/{slug}/targets/{resource_id}", + path=f"feature-flags/{quote(str(slug), safe='')}/targets/{quote(str(resource_id), safe='')}", request_options=request_options, ) @@ -569,7 +570,7 @@ async def list_organization_feature_flags( } return await self._client.request_page( method="get", - path=f"organizations/{organization_id}/feature-flags", + path=f"organizations/{quote(str(organization_id), safe='')}/feature-flags", model=Flag, params=params, request_options=request_options, @@ -618,7 +619,7 @@ async def list_user_feature_flags( } return await self._client.request_page( method="get", - path=f"user_management/users/{user_id}/feature-flags", + path=f"user_management/users/{quote(str(user_id), safe='')}/feature-flags", model=Flag, params=params, request_options=request_options, diff --git a/src/workos/feature_flags/models/feature_flags_order.py b/src/workos/feature_flags/models/feature_flags_order.py index 52e0eb14..b1621e5f 100644 --- a/src/workos/feature_flags/models/feature_flags_order.py +++ b/src/workos/feature_flags/models/feature_flags_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -FeatureFlagsOrder: TypeAlias = ApplicationsOrder +FeatureFlagsOrder: TypeAlias = ApiKeysOrder __all__ = ["FeatureFlagsOrder"] diff --git a/src/workos/feature_flags/models/organizations_feature_flags_order.py b/src/workos/feature_flags/models/organizations_feature_flags_order.py index cfe72a19..b1360b40 100644 --- a/src/workos/feature_flags/models/organizations_feature_flags_order.py +++ b/src/workos/feature_flags/models/organizations_feature_flags_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -OrganizationsFeatureFlagsOrder: TypeAlias = ApplicationsOrder +OrganizationsFeatureFlagsOrder: TypeAlias = ApiKeysOrder __all__ = ["OrganizationsFeatureFlagsOrder"] diff --git a/src/workos/feature_flags/models/user_management_users_feature_flags_order.py b/src/workos/feature_flags/models/user_management_users_feature_flags_order.py index 8ff0e415..6809b8c6 100644 --- a/src/workos/feature_flags/models/user_management_users_feature_flags_order.py +++ b/src/workos/feature_flags/models/user_management_users_feature_flags_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -UserManagementUsersFeatureFlagsOrder: TypeAlias = ApplicationsOrder +UserManagementUsersFeatureFlagsOrder: TypeAlias = ApiKeysOrder __all__ = ["UserManagementUsersFeatureFlagsOrder"] diff --git a/src/workos/groups/_resource.py b/src/workos/groups/_resource.py index c7d4dde5..7e1d729a 100644 --- a/src/workos/groups/_resource.py +++ b/src/workos/groups/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -66,7 +67,7 @@ def list_organization_groups( } return self._client.request_page( method="get", - path=f"organizations/{organization_id}/groups", + path=f"organizations/{quote(str(organization_id), safe='')}/groups", model=Group, params=params, request_options=request_options, @@ -112,7 +113,7 @@ def create_organization_group( } return self._client.request( method="post", - path=f"organizations/{organization_id}/groups", + path=f"organizations/{quote(str(organization_id), safe='')}/groups", body=body, model=Group, request_options=request_options, @@ -146,7 +147,7 @@ def get_organization_group( """ return self._client.request( method="get", - path=f"organizations/{organization_id}/groups/{group_id}", + path=f"organizations/{quote(str(organization_id), safe='')}/groups/{quote(str(group_id), safe='')}", model=Group, request_options=request_options, ) @@ -193,7 +194,7 @@ def update_organization_group( } return self._client.request( method="patch", - path=f"organizations/{organization_id}/groups/{group_id}", + path=f"organizations/{quote(str(organization_id), safe='')}/groups/{quote(str(group_id), safe='')}", body=body, model=Group, request_options=request_options, @@ -224,7 +225,7 @@ def delete_organization_group( """ self._client.request( method="delete", - path=f"organizations/{organization_id}/groups/{group_id}", + path=f"organizations/{quote(str(organization_id), safe='')}/groups/{quote(str(group_id), safe='')}", request_options=request_options, ) @@ -274,7 +275,7 @@ def list_group_organization_memberships( } return self._client.request_page( method="get", - path=f"organizations/{organization_id}/groups/{group_id}/organization-memberships", + path=f"organizations/{quote(str(organization_id), safe='')}/groups/{quote(str(group_id), safe='')}/organization-memberships", model=UserOrganizationMembershipBaseListData, params=params, request_options=request_options, @@ -314,7 +315,7 @@ def create_group_organization_membership( } return self._client.request( method="post", - path=f"organizations/{organization_id}/groups/{group_id}/organization-memberships", + path=f"organizations/{quote(str(organization_id), safe='')}/groups/{quote(str(group_id), safe='')}/organization-memberships", body=body, model=Group, request_options=request_options, @@ -347,7 +348,7 @@ def delete_group_organization_membership( """ self._client.request( method="delete", - path=f"organizations/{organization_id}/groups/{group_id}/organization-memberships/{om_id}", + path=f"organizations/{quote(str(organization_id), safe='')}/groups/{quote(str(group_id), safe='')}/organization-memberships/{quote(str(om_id), safe='')}", request_options=request_options, ) @@ -402,7 +403,7 @@ async def list_organization_groups( } return await self._client.request_page( method="get", - path=f"organizations/{organization_id}/groups", + path=f"organizations/{quote(str(organization_id), safe='')}/groups", model=Group, params=params, request_options=request_options, @@ -448,7 +449,7 @@ async def create_organization_group( } return await self._client.request( method="post", - path=f"organizations/{organization_id}/groups", + path=f"organizations/{quote(str(organization_id), safe='')}/groups", body=body, model=Group, request_options=request_options, @@ -482,7 +483,7 @@ async def get_organization_group( """ return await self._client.request( method="get", - path=f"organizations/{organization_id}/groups/{group_id}", + path=f"organizations/{quote(str(organization_id), safe='')}/groups/{quote(str(group_id), safe='')}", model=Group, request_options=request_options, ) @@ -529,7 +530,7 @@ async def update_organization_group( } return await self._client.request( method="patch", - path=f"organizations/{organization_id}/groups/{group_id}", + path=f"organizations/{quote(str(organization_id), safe='')}/groups/{quote(str(group_id), safe='')}", body=body, model=Group, request_options=request_options, @@ -560,7 +561,7 @@ async def delete_organization_group( """ await self._client.request( method="delete", - path=f"organizations/{organization_id}/groups/{group_id}", + path=f"organizations/{quote(str(organization_id), safe='')}/groups/{quote(str(group_id), safe='')}", request_options=request_options, ) @@ -610,7 +611,7 @@ async def list_group_organization_memberships( } return await self._client.request_page( method="get", - path=f"organizations/{organization_id}/groups/{group_id}/organization-memberships", + path=f"organizations/{quote(str(organization_id), safe='')}/groups/{quote(str(group_id), safe='')}/organization-memberships", model=UserOrganizationMembershipBaseListData, params=params, request_options=request_options, @@ -650,7 +651,7 @@ async def create_group_organization_membership( } return await self._client.request( method="post", - path=f"organizations/{organization_id}/groups/{group_id}/organization-memberships", + path=f"organizations/{quote(str(organization_id), safe='')}/groups/{quote(str(group_id), safe='')}/organization-memberships", body=body, model=Group, request_options=request_options, @@ -683,6 +684,6 @@ async def delete_group_organization_membership( """ await self._client.request( method="delete", - path=f"organizations/{organization_id}/groups/{group_id}/organization-memberships/{om_id}", + path=f"organizations/{quote(str(organization_id), safe='')}/groups/{quote(str(group_id), safe='')}/organization-memberships/{quote(str(om_id), safe='')}", request_options=request_options, ) diff --git a/src/workos/groups/models/groups_order.py b/src/workos/groups/models/groups_order.py index 6a6b4d04..b022c256 100644 --- a/src/workos/groups/models/groups_order.py +++ b/src/workos/groups/models/groups_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -GroupsOrder: TypeAlias = ApplicationsOrder +GroupsOrder: TypeAlias = ApiKeysOrder __all__ = ["GroupsOrder"] diff --git a/src/workos/multi_factor_auth/_resource.py b/src/workos/multi_factor_auth/_resource.py index d89a5e09..60ddf4f7 100644 --- a/src/workos/multi_factor_auth/_resource.py +++ b/src/workos/multi_factor_auth/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -60,7 +61,7 @@ def verify_challenge( } return self._client.request( method="post", - path=f"auth/challenges/{id}/verify", + path=f"auth/challenges/{quote(str(id), safe='')}/verify", body=body, model=AuthenticationChallengeVerifyResponse, request_options=request_options, @@ -141,7 +142,7 @@ def get_factor( """ return self._client.request( method="get", - path=f"auth/factors/{id}", + path=f"auth/factors/{quote(str(id), safe='')}", model=AuthenticationFactor, request_options=request_options, ) @@ -168,7 +169,7 @@ def delete_factor( """ self._client.request( method="delete", - path=f"auth/factors/{id}", + path=f"auth/factors/{quote(str(id), safe='')}", request_options=request_options, ) @@ -207,7 +208,7 @@ def challenge_factor( } return self._client.request( method="post", - path=f"auth/factors/{id}/challenge", + path=f"auth/factors/{quote(str(id), safe='')}/challenge", body=body, model=AuthenticationChallenge, request_options=request_options, @@ -258,7 +259,7 @@ def list_user_auth_factors( } return self._client.request_page( method="get", - path=f"user_management/users/{userland_user_id}/auth_factors", + path=f"user_management/users/{quote(str(userland_user_id), safe='')}/auth_factors", model=AuthenticationFactor, params=params, request_options=request_options, @@ -307,7 +308,7 @@ def create_user_auth_factor( } return self._client.request( method="post", - path=f"user_management/users/{userland_user_id}/auth_factors", + path=f"user_management/users/{quote(str(userland_user_id), safe='')}/auth_factors", body=body, model=UserAuthenticationFactorEnrollResponse, request_options=request_options, @@ -352,7 +353,7 @@ async def verify_challenge( } return await self._client.request( method="post", - path=f"auth/challenges/{id}/verify", + path=f"auth/challenges/{quote(str(id), safe='')}/verify", body=body, model=AuthenticationChallengeVerifyResponse, request_options=request_options, @@ -433,7 +434,7 @@ async def get_factor( """ return await self._client.request( method="get", - path=f"auth/factors/{id}", + path=f"auth/factors/{quote(str(id), safe='')}", model=AuthenticationFactor, request_options=request_options, ) @@ -460,7 +461,7 @@ async def delete_factor( """ await self._client.request( method="delete", - path=f"auth/factors/{id}", + path=f"auth/factors/{quote(str(id), safe='')}", request_options=request_options, ) @@ -499,7 +500,7 @@ async def challenge_factor( } return await self._client.request( method="post", - path=f"auth/factors/{id}/challenge", + path=f"auth/factors/{quote(str(id), safe='')}/challenge", body=body, model=AuthenticationChallenge, request_options=request_options, @@ -550,7 +551,7 @@ async def list_user_auth_factors( } return await self._client.request_page( method="get", - path=f"user_management/users/{userland_user_id}/auth_factors", + path=f"user_management/users/{quote(str(userland_user_id), safe='')}/auth_factors", model=AuthenticationFactor, params=params, request_options=request_options, @@ -599,7 +600,7 @@ async def create_user_auth_factor( } return await self._client.request( method="post", - path=f"user_management/users/{userland_user_id}/auth_factors", + path=f"user_management/users/{quote(str(userland_user_id), safe='')}/auth_factors", body=body, model=UserAuthenticationFactorEnrollResponse, request_options=request_options, diff --git a/src/workos/multi_factor_auth/models/user_management_multi_factor_authentication_order.py b/src/workos/multi_factor_auth/models/user_management_multi_factor_authentication_order.py index 6113602c..0707ba31 100644 --- a/src/workos/multi_factor_auth/models/user_management_multi_factor_authentication_order.py +++ b/src/workos/multi_factor_auth/models/user_management_multi_factor_authentication_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -UserManagementMultiFactorAuthenticationOrder: TypeAlias = ApplicationsOrder +UserManagementMultiFactorAuthenticationOrder: TypeAlias = ApiKeysOrder __all__ = ["UserManagementMultiFactorAuthenticationOrder"] diff --git a/src/workos/organization_domains/_resource.py b/src/workos/organization_domains/_resource.py index ab3cc141..37f9ca02 100644 --- a/src/workos/organization_domains/_resource.py +++ b/src/workos/organization_domains/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, Optional +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -79,7 +80,7 @@ def get_organization_domain( """ return self._client.request( method="get", - path=f"organization_domains/{id}", + path=f"organization_domains/{quote(str(id), safe='')}", model=OrganizationDomainStandAlone, request_options=request_options, ) @@ -106,7 +107,7 @@ def delete_organization_domain( """ self._client.request( method="delete", - path=f"organization_domains/{id}", + path=f"organization_domains/{quote(str(id), safe='')}", request_options=request_options, ) @@ -135,7 +136,7 @@ def verify_organization_domain( """ return self._client.request( method="post", - path=f"organization_domains/{id}/verify", + path=f"organization_domains/{quote(str(id), safe='')}/verify", model=OrganizationDomainStandAlone, request_options=request_options, ) @@ -209,7 +210,7 @@ async def get_organization_domain( """ return await self._client.request( method="get", - path=f"organization_domains/{id}", + path=f"organization_domains/{quote(str(id), safe='')}", model=OrganizationDomainStandAlone, request_options=request_options, ) @@ -236,7 +237,7 @@ async def delete_organization_domain( """ await self._client.request( method="delete", - path=f"organization_domains/{id}", + path=f"organization_domains/{quote(str(id), safe='')}", request_options=request_options, ) @@ -265,7 +266,7 @@ async def verify_organization_domain( """ return await self._client.request( method="post", - path=f"organization_domains/{id}/verify", + path=f"organization_domains/{quote(str(id), safe='')}/verify", model=OrganizationDomainStandAlone, request_options=request_options, ) diff --git a/src/workos/organizations/_resource.py b/src/workos/organizations/_resource.py index e0065743..f92f91a2 100644 --- a/src/workos/organizations/_resource.py +++ b/src/workos/organizations/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -156,7 +157,7 @@ def get_organization_by_external_id( """ return self._client.request( method="get", - path=f"organizations/external_id/{external_id}", + path=f"organizations/external_id/{quote(str(external_id), safe='')}", model=Organization, request_options=request_options, ) @@ -186,7 +187,7 @@ def get_organization( """ return self._client.request( method="get", - path=f"organizations/{id}", + path=f"organizations/{quote(str(id), safe='')}", model=Organization, request_options=request_options, ) @@ -249,7 +250,7 @@ def update_organization( } return self._client.request( method="put", - path=f"organizations/{id}", + path=f"organizations/{quote(str(id), safe='')}", body=body, model=Organization, request_options=request_options, @@ -277,7 +278,7 @@ def delete_organization( """ self._client.request( method="delete", - path=f"organizations/{id}", + path=f"organizations/{quote(str(id), safe='')}", request_options=request_options, ) @@ -306,7 +307,7 @@ def get_audit_log_configuration( """ return self._client.request( method="get", - path=f"organizations/{id}/audit_log_configuration", + path=f"organizations/{quote(str(id), safe='')}/audit_log_configuration", model=AuditLogConfiguration, request_options=request_options, ) @@ -455,7 +456,7 @@ async def get_organization_by_external_id( """ return await self._client.request( method="get", - path=f"organizations/external_id/{external_id}", + path=f"organizations/external_id/{quote(str(external_id), safe='')}", model=Organization, request_options=request_options, ) @@ -485,7 +486,7 @@ async def get_organization( """ return await self._client.request( method="get", - path=f"organizations/{id}", + path=f"organizations/{quote(str(id), safe='')}", model=Organization, request_options=request_options, ) @@ -548,7 +549,7 @@ async def update_organization( } return await self._client.request( method="put", - path=f"organizations/{id}", + path=f"organizations/{quote(str(id), safe='')}", body=body, model=Organization, request_options=request_options, @@ -576,7 +577,7 @@ async def delete_organization( """ await self._client.request( method="delete", - path=f"organizations/{id}", + path=f"organizations/{quote(str(id), safe='')}", request_options=request_options, ) @@ -605,7 +606,7 @@ async def get_audit_log_configuration( """ return await self._client.request( method="get", - path=f"organizations/{id}/audit_log_configuration", + path=f"organizations/{quote(str(id), safe='')}/audit_log_configuration", model=AuditLogConfiguration, request_options=request_options, ) diff --git a/src/workos/organizations/models/organizations_order.py b/src/workos/organizations/models/organizations_order.py index 0fc8338e..cb6d4884 100644 --- a/src/workos/organizations/models/organizations_order.py +++ b/src/workos/organizations/models/organizations_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -OrganizationsOrder: TypeAlias = ApplicationsOrder +OrganizationsOrder: TypeAlias = ApiKeysOrder __all__ = ["OrganizationsOrder"] diff --git a/src/workos/pipes/_resource.py b/src/workos/pipes/_resource.py index d48766c6..9d834e07 100644 --- a/src/workos/pipes/_resource.py +++ b/src/workos/pipes/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, Optional +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -64,7 +65,7 @@ def authorize_data_integration( } return self._client.request( method="post", - path=f"data-integrations/{slug}/authorize", + path=f"data-integrations/{quote(str(slug), safe='')}/authorize", body=body, model=DataIntegrationAuthorizeUrlResponse, request_options=request_options, @@ -108,7 +109,7 @@ def create_data_integration_token( } return self._client.request( method="post", - path=f"data-integrations/{slug}/token", + path=f"data-integrations/{quote(str(slug), safe='')}/token", body=body, model=DataIntegrationAccessTokenResponse, request_options=request_options, @@ -150,7 +151,7 @@ def get_user_connected_account( } return self._client.request( method="get", - path=f"user_management/users/{user_id}/connected_accounts/{slug}", + path=f"user_management/users/{quote(str(user_id), safe='')}/connected_accounts/{quote(str(slug), safe='')}", params=params, model=ConnectedAccount, request_options=request_options, @@ -189,7 +190,7 @@ def delete_user_connected_account( } self._client.request( method="delete", - path=f"user_management/users/{user_id}/connected_accounts/{slug}", + path=f"user_management/users/{quote(str(user_id), safe='')}/connected_accounts/{quote(str(slug), safe='')}", params=params, request_options=request_options, ) @@ -228,7 +229,7 @@ def list_user_data_providers( } return self._client.request( method="get", - path=f"user_management/users/{user_id}/data_providers", + path=f"user_management/users/{quote(str(user_id), safe='')}/data_providers", params=params, model=DataIntegrationsListResponse, request_options=request_options, @@ -283,7 +284,7 @@ async def authorize_data_integration( } return await self._client.request( method="post", - path=f"data-integrations/{slug}/authorize", + path=f"data-integrations/{quote(str(slug), safe='')}/authorize", body=body, model=DataIntegrationAuthorizeUrlResponse, request_options=request_options, @@ -327,7 +328,7 @@ async def create_data_integration_token( } return await self._client.request( method="post", - path=f"data-integrations/{slug}/token", + path=f"data-integrations/{quote(str(slug), safe='')}/token", body=body, model=DataIntegrationAccessTokenResponse, request_options=request_options, @@ -369,7 +370,7 @@ async def get_user_connected_account( } return await self._client.request( method="get", - path=f"user_management/users/{user_id}/connected_accounts/{slug}", + path=f"user_management/users/{quote(str(user_id), safe='')}/connected_accounts/{quote(str(slug), safe='')}", params=params, model=ConnectedAccount, request_options=request_options, @@ -408,7 +409,7 @@ async def delete_user_connected_account( } await self._client.request( method="delete", - path=f"user_management/users/{user_id}/connected_accounts/{slug}", + path=f"user_management/users/{quote(str(user_id), safe='')}/connected_accounts/{quote(str(slug), safe='')}", params=params, request_options=request_options, ) @@ -447,7 +448,7 @@ async def list_user_data_providers( } return await self._client.request( method="get", - path=f"user_management/users/{user_id}/data_providers", + path=f"user_management/users/{quote(str(user_id), safe='')}/data_providers", params=params, model=DataIntegrationsListResponse, request_options=request_options, diff --git a/src/workos/radar/_resource.py b/src/workos/radar/_resource.py index bddd81ed..f90d25a4 100644 --- a/src/workos/radar/_resource.py +++ b/src/workos/radar/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -115,7 +116,7 @@ def update_attempt( } self._client.request( method="put", - path=f"radar/attempts/{id}", + path=f"radar/attempts/{quote(str(id), safe='')}", body=body, request_options=request_options, ) @@ -152,7 +153,7 @@ def add_list_entry( } return self._client.request( method="post", - path=f"radar/lists/{enum_value(type)}/{enum_value(action)}", + path=f"radar/lists/{quote(str(enum_value(type)), safe='')}/{quote(str(enum_value(action)), safe='')}", body=body, model=RadarListEntryAlreadyPresentResponse, request_options=request_options, @@ -188,7 +189,7 @@ def remove_list_entry( } self._client.request( method="delete", - path=f"radar/lists/{enum_value(type)}/{enum_value(action)}", + path=f"radar/lists/{quote(str(enum_value(type)), safe='')}/{quote(str(enum_value(action)), safe='')}", body=body, request_options=request_options, ) @@ -291,7 +292,7 @@ async def update_attempt( } await self._client.request( method="put", - path=f"radar/attempts/{id}", + path=f"radar/attempts/{quote(str(id), safe='')}", body=body, request_options=request_options, ) @@ -328,7 +329,7 @@ async def add_list_entry( } return await self._client.request( method="post", - path=f"radar/lists/{enum_value(type)}/{enum_value(action)}", + path=f"radar/lists/{quote(str(enum_value(type)), safe='')}/{quote(str(enum_value(action)), safe='')}", body=body, model=RadarListEntryAlreadyPresentResponse, request_options=request_options, @@ -364,7 +365,7 @@ async def remove_list_entry( } await self._client.request( method="delete", - path=f"radar/lists/{enum_value(type)}/{enum_value(action)}", + path=f"radar/lists/{quote(str(enum_value(type)), safe='')}/{quote(str(enum_value(action)), safe='')}", body=body, request_options=request_options, ) diff --git a/src/workos/sso/_resource.py b/src/workos/sso/_resource.py index 7cfb5015..40479448 100644 --- a/src/workos/sso/_resource.py +++ b/src/workos/sso/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -107,7 +108,7 @@ def get_connection( """ return self._client.request( method="get", - path=f"connections/{id}", + path=f"connections/{quote(str(id), safe='')}", model=Connection, request_options=request_options, ) @@ -135,7 +136,7 @@ def delete_connection( """ self._client.request( method="delete", - path=f"connections/{id}", + path=f"connections/{quote(str(id), safe='')}", request_options=request_options, ) @@ -524,7 +525,7 @@ async def get_connection( """ return await self._client.request( method="get", - path=f"connections/{id}", + path=f"connections/{quote(str(id), safe='')}", model=Connection, request_options=request_options, ) @@ -552,7 +553,7 @@ async def delete_connection( """ await self._client.request( method="delete", - path=f"connections/{id}", + path=f"connections/{quote(str(id), safe='')}", request_options=request_options, ) diff --git a/src/workos/sso/models/connections_order.py b/src/workos/sso/models/connections_order.py index d7c89f0d..f8dbcad2 100644 --- a/src/workos/sso/models/connections_order.py +++ b/src/workos/sso/models/connections_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -ConnectionsOrder: TypeAlias = ApplicationsOrder +ConnectionsOrder: TypeAlias = ApiKeysOrder __all__ = ["ConnectionsOrder"] diff --git a/src/workos/sso/models/profile.py b/src/workos/sso/models/profile.py index b6fb6750..ba036263 100644 --- a/src/workos/sso/models/profile.py +++ b/src/workos/sso/models/profile.py @@ -34,6 +34,8 @@ class Profile: """The user's first name.""" last_name: Optional[str] """The user's last name.""" + name: Optional[str] + """The user's full name.""" raw_attributes: Dict[str, Any] """The complete set of raw attributes returned by the identity provider.""" role: Optional["SlimRole"] = None @@ -59,6 +61,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "Profile": email=data["email"], first_name=data["first_name"], last_name=data["last_name"], + name=data["name"], raw_attributes=data["raw_attributes"], role=SlimRole.from_dict(cast(Dict[str, Any], _v_role)) if (_v_role := data.get("role")) is not None @@ -100,6 +103,10 @@ def to_dict(self) -> Dict[str, Any]: result["last_name"] = self.last_name else: result["last_name"] = None + if self.name is not None: + result["name"] = self.name + else: + result["name"] = None result["raw_attributes"] = self.raw_attributes if self.role is not None: result["role"] = self.role.to_dict() diff --git a/src/workos/user_management/_resource.py b/src/workos/user_management/_resource.py index 8c383d77..82b9cc7b 100644 --- a/src/workos/user_management/_resource.py +++ b/src/workos/user_management/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union, cast +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -34,6 +35,8 @@ MFATotpSessionAuthenticateRequest, OrganizationSelectionSessionAuthenticateRequest, User, + UserApiKey, + UserApiKeyWithValue, UserIdentitiesGetItem, UserInvite, UserOrganizationMembership, @@ -41,6 +44,7 @@ VerifyEmailResponse, ) from .models import ( + ApiKeysOrder, UserManagementAuthenticationProvider, UserManagementAuthenticationScreenHint, UserManagementInvitationsOrder, @@ -128,7 +132,7 @@ def get_jwks( """ return self._client.request( method="get", - path=f"sso/jwks/{client_id}", + path=f"sso/jwks/{quote(str(client_id), safe='')}", model=JwksResponse, request_options=request_options, ) @@ -713,7 +717,7 @@ def get_email_verification( """ return self._client.request( method="get", - path=f"user_management/email_verification/{id}", + path=f"user_management/email_verification/{quote(str(id), safe='')}", model=EmailVerification, request_options=request_options, ) @@ -819,7 +823,7 @@ def get_password_reset( """ return self._client.request( method="get", - path=f"user_management/password_reset/{id}", + path=f"user_management/password_reset/{quote(str(id), safe='')}", model=PasswordReset, request_options=request_options, ) @@ -968,7 +972,7 @@ def get_user_by_external_id( """ return self._client.request( method="get", - path=f"user_management/users/external_id/{external_id}", + path=f"user_management/users/external_id/{quote(str(external_id), safe='')}", model=User, request_options=request_options, ) @@ -998,7 +1002,7 @@ def get_user( """ return self._client.request( method="get", - path=f"user_management/users/{id}", + path=f"user_management/users/{quote(str(id), safe='')}", model=User, request_options=request_options, ) @@ -1064,7 +1068,7 @@ def update_user( body["password_hash_type"] = enum_value(password.password_hash_type) return self._client.request( method="put", - path=f"user_management/users/{id}", + path=f"user_management/users/{quote(str(id), safe='')}", body=body, model=User, request_options=request_options, @@ -1092,7 +1096,7 @@ def delete_user( """ self._client.request( method="delete", - path=f"user_management/users/{id}", + path=f"user_management/users/{quote(str(id), safe='')}", request_options=request_options, ) @@ -1129,7 +1133,7 @@ def confirm_email_change( } return self._client.request( method="post", - path=f"user_management/users/{id}/email_change/confirm", + path=f"user_management/users/{quote(str(id), safe='')}/email_change/confirm", body=body, model=EmailChangeConfirmation, request_options=request_options, @@ -1167,7 +1171,7 @@ def send_email_change( } return self._client.request( method="post", - path=f"user_management/users/{id}/email_change/send", + path=f"user_management/users/{quote(str(id), safe='')}/email_change/send", body=body, model=EmailChange, request_options=request_options, @@ -1205,7 +1209,7 @@ def verify_email( } return self._client.request( method="post", - path=f"user_management/users/{id}/email_verification/confirm", + path=f"user_management/users/{quote(str(id), safe='')}/email_verification/confirm", body=body, model=VerifyEmailResponse, request_options=request_options, @@ -1237,7 +1241,7 @@ def send_verification_email( """ return self._client.request( method="post", - path=f"user_management/users/{id}/email_verification/send", + path=f"user_management/users/{quote(str(id), safe='')}/email_verification/send", model=SendVerificationEmailResponse, request_options=request_options, ) @@ -1267,7 +1271,7 @@ def get_user_identities( """ raw = self._client.request_list( method="get", - path=f"user_management/users/{id}/identities", + path=f"user_management/users/{quote(str(id), safe='')}/identities", request_options=request_options, ) return [ @@ -1318,7 +1322,7 @@ def list_sessions( } return self._client.request_page( method="get", - path=f"user_management/users/{id}/sessions", + path=f"user_management/users/{quote(str(id), safe='')}/sessions", model=UserSessionsListItem, params=params, request_options=request_options, @@ -1457,7 +1461,7 @@ def find_invitation_by_token( """ return self._client.request( method="get", - path=f"user_management/invitations/by_token/{token}", + path=f"user_management/invitations/by_token/{quote(str(token), safe='')}", model=UserInvite, request_options=request_options, ) @@ -1487,7 +1491,7 @@ def get_invitation( """ return self._client.request( method="get", - path=f"user_management/invitations/{id}", + path=f"user_management/invitations/{quote(str(id), safe='')}", model=UserInvite, request_options=request_options, ) @@ -1518,7 +1522,7 @@ def accept_invitation( """ return self._client.request( method="post", - path=f"user_management/invitations/{id}/accept", + path=f"user_management/invitations/{quote(str(id), safe='')}/accept", model=Invitation, request_options=request_options, ) @@ -1559,7 +1563,7 @@ def resend_invitation( } return self._client.request( method="post", - path=f"user_management/invitations/{id}/resend", + path=f"user_management/invitations/{quote(str(id), safe='')}/resend", body=body, model=UserInvite, request_options=request_options, @@ -1590,11 +1594,36 @@ def revoke_invitation( """ return self._client.request( method="post", - path=f"user_management/invitations/{id}/revoke", + path=f"user_management/invitations/{quote(str(id), safe='')}/revoke", model=Invitation, request_options=request_options, ) + def list_jwt_template( + self, + *, + request_options: Optional[RequestOptions] = None, + ) -> JWTTemplateResponse: + """Get JWT template + + Get the JWT template for the current environment. + + Returns: + JWTTemplateResponse + + Raises: + NotFoundError: If the resource is not found (404). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + return self._client.request( + method="get", + path="user_management/jwt_template", + model=JWTTemplateResponse, + request_options=request_options, + ) + def update_jwt_template( self, *, @@ -1696,7 +1725,7 @@ def get_magic_auth( """ return self._client.request( method="get", - path=f"user_management/magic_auth/{id}", + path=f"user_management/magic_auth/{quote(str(id), safe='')}", model=MagicAuth, request_options=request_options, ) @@ -1836,7 +1865,7 @@ def get_organization_membership( """ return self._client.request( method="get", - path=f"user_management/organization_memberships/{id}", + path=f"user_management/organization_memberships/{quote(str(id), safe='')}", model=UserOrganizationMembership, request_options=request_options, ) @@ -1875,7 +1904,7 @@ def update_organization_membership( body["role_slugs"] = role.role_slugs return self._client.request( method="put", - path=f"user_management/organization_memberships/{id}", + path=f"user_management/organization_memberships/{quote(str(id), safe='')}", body=body, model=UserOrganizationMembership, request_options=request_options, @@ -1903,7 +1932,7 @@ def delete_organization_membership( """ self._client.request( method="delete", - path=f"user_management/organization_memberships/{id}", + path=f"user_management/organization_memberships/{quote(str(id), safe='')}", request_options=request_options, ) @@ -1939,7 +1968,7 @@ def deactivate_organization_membership( """ return self._client.request( method="put", - path=f"user_management/organization_memberships/{id}/deactivate", + path=f"user_management/organization_memberships/{quote(str(id), safe='')}/deactivate", model=OrganizationMembership, request_options=request_options, ) @@ -1976,7 +2005,7 @@ def reactivate_organization_membership( """ return self._client.request( method="put", - path=f"user_management/organization_memberships/{id}/reactivate", + path=f"user_management/organization_memberships/{quote(str(id), safe='')}/reactivate", model=UserOrganizationMembership, request_options=request_options, ) @@ -2062,7 +2091,7 @@ def list_user_authorized_applications( } return self._client.request_page( method="get", - path=f"user_management/users/{user_id}/authorized_applications", + path=f"user_management/users/{quote(str(user_id), safe='')}/authorized_applications", model=AuthorizedConnectApplicationListData, params=params, request_options=request_options, @@ -2092,7 +2121,107 @@ def delete_user_authorized_application( """ self._client.request( method="delete", - path=f"user_management/users/{user_id}/authorized_applications/{application_id}", + path=f"user_management/users/{quote(str(user_id), safe='')}/authorized_applications/{quote(str(application_id), safe='')}", + request_options=request_options, + ) + + def list_user_api_keys( + self, + user_id: str, + *, + limit: Optional[int] = None, + before: Optional[str] = None, + after: Optional[str] = None, + order: Optional[Union[ApiKeysOrder, str]] = "desc", + organization_id: Optional[str] = None, + request_options: Optional[RequestOptions] = None, + ) -> SyncPage[UserApiKey]: + """List API keys for a user + + Get a list of API keys owned by a specific user. + + Args: + user_id: Unique identifier of the user. + limit: Upper limit on the number of objects to return, between `1` and `100`. Defaults to `10`. + before: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. + after: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. + order: Order the results by the creation time. Defaults to `desc`. + organization_id: The ID of the organization to filter user API keys by. When provided, only API keys created against that organization membership are returned. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + SyncPage[UserApiKey] + + Raises: + NotFoundError: If the resource is not found (404). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + params = { + k: v + for k, v in { + "limit": limit, + "before": before, + "after": after, + "order": enum_value(order) if order is not None else None, + "organization_id": organization_id, + }.items() + if v is not None + } + return self._client.request_page( + method="get", + path=f"user_management/users/{quote(str(user_id), safe='')}/api_keys", + model=UserApiKey, + params=params, + request_options=request_options, + ) + + def create_user_api_key( + self, + user_id: str, + *, + name: str, + organization_id: str, + permissions: Optional[List[str]] = None, + request_options: Optional[RequestOptions] = None, + ) -> UserApiKeyWithValue: + """Create an API key for a user + + Create a new API key owned by a user. The user must have an active membership in the specified organization. + + Args: + user_id: Unique identifier of the user. + name: A descriptive name for the API key. + organization_id: The ID of the organization the user API key is associated with. The user must have an active membership in this organization. + permissions: The permission slugs to assign to the API key. Each permission must be enabled for user API keys. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + UserApiKeyWithValue + + Raises: + BadRequestError: If the request is malformed (400). + NotFoundError: If the resource is not found (404). + UnprocessableEntityError: If the request data is unprocessable (422). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + body: Dict[str, Any] = { + k: v + for k, v in { + "name": name, + "organization_id": organization_id, + "permissions": permissions, + }.items() + if v is not None + } + return self._client.request( + method="post", + path=f"user_management/users/{quote(str(user_id), safe='')}/api_keys", + body=body, + model=UserApiKeyWithValue, request_options=request_options, ) @@ -2313,7 +2442,7 @@ async def get_jwks( """ return await self._client.request( method="get", - path=f"sso/jwks/{client_id}", + path=f"sso/jwks/{quote(str(client_id), safe='')}", model=JwksResponse, request_options=request_options, ) @@ -2898,7 +3027,7 @@ async def get_email_verification( """ return await self._client.request( method="get", - path=f"user_management/email_verification/{id}", + path=f"user_management/email_verification/{quote(str(id), safe='')}", model=EmailVerification, request_options=request_options, ) @@ -3004,7 +3133,7 @@ async def get_password_reset( """ return await self._client.request( method="get", - path=f"user_management/password_reset/{id}", + path=f"user_management/password_reset/{quote(str(id), safe='')}", model=PasswordReset, request_options=request_options, ) @@ -3153,7 +3282,7 @@ async def get_user_by_external_id( """ return await self._client.request( method="get", - path=f"user_management/users/external_id/{external_id}", + path=f"user_management/users/external_id/{quote(str(external_id), safe='')}", model=User, request_options=request_options, ) @@ -3183,7 +3312,7 @@ async def get_user( """ return await self._client.request( method="get", - path=f"user_management/users/{id}", + path=f"user_management/users/{quote(str(id), safe='')}", model=User, request_options=request_options, ) @@ -3249,7 +3378,7 @@ async def update_user( body["password_hash_type"] = enum_value(password.password_hash_type) return await self._client.request( method="put", - path=f"user_management/users/{id}", + path=f"user_management/users/{quote(str(id), safe='')}", body=body, model=User, request_options=request_options, @@ -3277,7 +3406,7 @@ async def delete_user( """ await self._client.request( method="delete", - path=f"user_management/users/{id}", + path=f"user_management/users/{quote(str(id), safe='')}", request_options=request_options, ) @@ -3314,7 +3443,7 @@ async def confirm_email_change( } return await self._client.request( method="post", - path=f"user_management/users/{id}/email_change/confirm", + path=f"user_management/users/{quote(str(id), safe='')}/email_change/confirm", body=body, model=EmailChangeConfirmation, request_options=request_options, @@ -3352,7 +3481,7 @@ async def send_email_change( } return await self._client.request( method="post", - path=f"user_management/users/{id}/email_change/send", + path=f"user_management/users/{quote(str(id), safe='')}/email_change/send", body=body, model=EmailChange, request_options=request_options, @@ -3390,7 +3519,7 @@ async def verify_email( } return await self._client.request( method="post", - path=f"user_management/users/{id}/email_verification/confirm", + path=f"user_management/users/{quote(str(id), safe='')}/email_verification/confirm", body=body, model=VerifyEmailResponse, request_options=request_options, @@ -3422,7 +3551,7 @@ async def send_verification_email( """ return await self._client.request( method="post", - path=f"user_management/users/{id}/email_verification/send", + path=f"user_management/users/{quote(str(id), safe='')}/email_verification/send", model=SendVerificationEmailResponse, request_options=request_options, ) @@ -3452,7 +3581,7 @@ async def get_user_identities( """ raw = await self._client.request_list( method="get", - path=f"user_management/users/{id}/identities", + path=f"user_management/users/{quote(str(id), safe='')}/identities", request_options=request_options, ) return [ @@ -3503,7 +3632,7 @@ async def list_sessions( } return await self._client.request_page( method="get", - path=f"user_management/users/{id}/sessions", + path=f"user_management/users/{quote(str(id), safe='')}/sessions", model=UserSessionsListItem, params=params, request_options=request_options, @@ -3642,7 +3771,7 @@ async def find_invitation_by_token( """ return await self._client.request( method="get", - path=f"user_management/invitations/by_token/{token}", + path=f"user_management/invitations/by_token/{quote(str(token), safe='')}", model=UserInvite, request_options=request_options, ) @@ -3672,7 +3801,7 @@ async def get_invitation( """ return await self._client.request( method="get", - path=f"user_management/invitations/{id}", + path=f"user_management/invitations/{quote(str(id), safe='')}", model=UserInvite, request_options=request_options, ) @@ -3703,7 +3832,7 @@ async def accept_invitation( """ return await self._client.request( method="post", - path=f"user_management/invitations/{id}/accept", + path=f"user_management/invitations/{quote(str(id), safe='')}/accept", model=Invitation, request_options=request_options, ) @@ -3744,7 +3873,7 @@ async def resend_invitation( } return await self._client.request( method="post", - path=f"user_management/invitations/{id}/resend", + path=f"user_management/invitations/{quote(str(id), safe='')}/resend", body=body, model=UserInvite, request_options=request_options, @@ -3775,11 +3904,36 @@ async def revoke_invitation( """ return await self._client.request( method="post", - path=f"user_management/invitations/{id}/revoke", + path=f"user_management/invitations/{quote(str(id), safe='')}/revoke", model=Invitation, request_options=request_options, ) + async def list_jwt_template( + self, + *, + request_options: Optional[RequestOptions] = None, + ) -> JWTTemplateResponse: + """Get JWT template + + Get the JWT template for the current environment. + + Returns: + JWTTemplateResponse + + Raises: + NotFoundError: If the resource is not found (404). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + return await self._client.request( + method="get", + path="user_management/jwt_template", + model=JWTTemplateResponse, + request_options=request_options, + ) + async def update_jwt_template( self, *, @@ -3881,7 +4035,7 @@ async def get_magic_auth( """ return await self._client.request( method="get", - path=f"user_management/magic_auth/{id}", + path=f"user_management/magic_auth/{quote(str(id), safe='')}", model=MagicAuth, request_options=request_options, ) @@ -4021,7 +4175,7 @@ async def get_organization_membership( """ return await self._client.request( method="get", - path=f"user_management/organization_memberships/{id}", + path=f"user_management/organization_memberships/{quote(str(id), safe='')}", model=UserOrganizationMembership, request_options=request_options, ) @@ -4060,7 +4214,7 @@ async def update_organization_membership( body["role_slugs"] = role.role_slugs return await self._client.request( method="put", - path=f"user_management/organization_memberships/{id}", + path=f"user_management/organization_memberships/{quote(str(id), safe='')}", body=body, model=UserOrganizationMembership, request_options=request_options, @@ -4088,7 +4242,7 @@ async def delete_organization_membership( """ await self._client.request( method="delete", - path=f"user_management/organization_memberships/{id}", + path=f"user_management/organization_memberships/{quote(str(id), safe='')}", request_options=request_options, ) @@ -4124,7 +4278,7 @@ async def deactivate_organization_membership( """ return await self._client.request( method="put", - path=f"user_management/organization_memberships/{id}/deactivate", + path=f"user_management/organization_memberships/{quote(str(id), safe='')}/deactivate", model=OrganizationMembership, request_options=request_options, ) @@ -4161,7 +4315,7 @@ async def reactivate_organization_membership( """ return await self._client.request( method="put", - path=f"user_management/organization_memberships/{id}/reactivate", + path=f"user_management/organization_memberships/{quote(str(id), safe='')}/reactivate", model=UserOrganizationMembership, request_options=request_options, ) @@ -4247,7 +4401,7 @@ async def list_user_authorized_applications( } return await self._client.request_page( method="get", - path=f"user_management/users/{user_id}/authorized_applications", + path=f"user_management/users/{quote(str(user_id), safe='')}/authorized_applications", model=AuthorizedConnectApplicationListData, params=params, request_options=request_options, @@ -4277,7 +4431,107 @@ async def delete_user_authorized_application( """ await self._client.request( method="delete", - path=f"user_management/users/{user_id}/authorized_applications/{application_id}", + path=f"user_management/users/{quote(str(user_id), safe='')}/authorized_applications/{quote(str(application_id), safe='')}", + request_options=request_options, + ) + + async def list_user_api_keys( + self, + user_id: str, + *, + limit: Optional[int] = None, + before: Optional[str] = None, + after: Optional[str] = None, + order: Optional[Union[ApiKeysOrder, str]] = "desc", + organization_id: Optional[str] = None, + request_options: Optional[RequestOptions] = None, + ) -> AsyncPage[UserApiKey]: + """List API keys for a user + + Get a list of API keys owned by a specific user. + + Args: + user_id: Unique identifier of the user. + limit: Upper limit on the number of objects to return, between `1` and `100`. Defaults to `10`. + before: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. + after: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. + order: Order the results by the creation time. Defaults to `desc`. + organization_id: The ID of the organization to filter user API keys by. When provided, only API keys created against that organization membership are returned. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + AsyncPage[UserApiKey] + + Raises: + NotFoundError: If the resource is not found (404). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + params = { + k: v + for k, v in { + "limit": limit, + "before": before, + "after": after, + "order": enum_value(order) if order is not None else None, + "organization_id": organization_id, + }.items() + if v is not None + } + return await self._client.request_page( + method="get", + path=f"user_management/users/{quote(str(user_id), safe='')}/api_keys", + model=UserApiKey, + params=params, + request_options=request_options, + ) + + async def create_user_api_key( + self, + user_id: str, + *, + name: str, + organization_id: str, + permissions: Optional[List[str]] = None, + request_options: Optional[RequestOptions] = None, + ) -> UserApiKeyWithValue: + """Create an API key for a user + + Create a new API key owned by a user. The user must have an active membership in the specified organization. + + Args: + user_id: Unique identifier of the user. + name: A descriptive name for the API key. + organization_id: The ID of the organization the user API key is associated with. The user must have an active membership in this organization. + permissions: The permission slugs to assign to the API key. Each permission must be enabled for user API keys. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + UserApiKeyWithValue + + Raises: + BadRequestError: If the request is malformed (400). + NotFoundError: If the resource is not found (404). + UnprocessableEntityError: If the request data is unprocessable (422). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + body: Dict[str, Any] = { + k: v + for k, v in { + "name": name, + "organization_id": organization_id, + "permissions": permissions, + }.items() + if v is not None + } + return await self._client.request( + method="post", + path=f"user_management/users/{quote(str(user_id), safe='')}/api_keys", + body=body, + model=UserApiKeyWithValue, request_options=request_options, ) diff --git a/src/workos/user_management/models/__init__.py b/src/workos/user_management/models/__init__.py index 12ba3b65..31a384c8 100644 --- a/src/workos/user_management/models/__init__.py +++ b/src/workos/user_management/models/__init__.py @@ -1,5 +1,6 @@ # This file is auto-generated by oagen. Do not edit. +from .api_keys_order import ApiKeysOrder as ApiKeysOrder from .authenticate_response import AuthenticateResponse as AuthenticateResponse from .authenticate_response_impersonator import ( AuthenticateResponseImpersonator as AuthenticateResponseImpersonator, @@ -25,6 +26,7 @@ ) from .create_redirect_uri import CreateRedirectUri as CreateRedirectUri from .create_user import CreateUser as CreateUser +from .create_user_api_key import CreateUserApiKey as CreateUserApiKey from .create_user_invite_options import ( CreateUserInviteOptions as CreateUserInviteOptions, ) @@ -89,6 +91,12 @@ OrganizationSelectionSessionAuthenticateRequest as OrganizationSelectionSessionAuthenticateRequest, ) from .user import User as User +from .user_api_key import UserApiKey as UserApiKey +from .user_api_key_owner import UserApiKeyOwner as UserApiKeyOwner +from .user_api_key_with_value import UserApiKeyWithValue as UserApiKeyWithValue +from .user_api_key_with_value_owner import ( + UserApiKeyWithValueOwner as UserApiKeyWithValueOwner, +) from .user_identities_get_item import UserIdentitiesGetItem as UserIdentitiesGetItem from .user_invite import UserInvite as UserInvite from .user_management_authentication_provider import ( diff --git a/src/workos/user_management/models/api_keys_order.py b/src/workos/user_management/models/api_keys_order.py new file mode 100644 index 00000000..7330e00b --- /dev/null +++ b/src/workos/user_management/models/api_keys_order.py @@ -0,0 +1,29 @@ +# This file is auto-generated by oagen. Do not edit. + +"""Enumeration of api keys order values.""" + +from __future__ import annotations + +from enum import Enum +from typing import Optional +from typing import Literal, TypeAlias + + +class ApiKeysOrder(str, Enum): + """Known values for ApiKeysOrder.""" + + NORMAL = "normal" + DESC = "desc" + ASC = "asc" + + @classmethod + def _missing_(cls, value: object) -> Optional["ApiKeysOrder"]: + if not isinstance(value, str): + return None + unknown = str.__new__(cls, value) + unknown._name_ = value.upper() + unknown._value_ = value + return unknown + + +ApiKeysOrderLiteral: TypeAlias = Literal["normal", "desc", "asc"] diff --git a/src/workos/user_management/models/create_user_api_key.py b/src/workos/user_management/models/create_user_api_key.py new file mode 100644 index 00000000..1dfcb28d --- /dev/null +++ b/src/workos/user_management/models/create_user_api_key.py @@ -0,0 +1,40 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Dict, List, Optional +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class CreateUserApiKey: + """Create User Api Key model.""" + + name: str + """A descriptive name for the API key.""" + organization_id: str + """The ID of the organization the user API key is associated with. The user must have an active membership in this organization.""" + permissions: Optional[List[str]] = None + """The permission slugs to assign to the API key. Each permission must be enabled for user API keys.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "CreateUserApiKey": + """Deserialize from a dictionary.""" + try: + return cls( + name=data["name"], + organization_id=data["organization_id"], + permissions=data.get("permissions"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("CreateUserApiKey", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["name"] = self.name + result["organization_id"] = self.organization_id + if self.permissions is not None: + result["permissions"] = self.permissions + return result diff --git a/src/workos/user_management/models/organization_membership.py b/src/workos/user_management/models/organization_membership.py index 5375c21a..04b39bf1 100644 --- a/src/workos/user_management/models/organization_membership.py +++ b/src/workos/user_management/models/organization_membership.py @@ -11,6 +11,7 @@ from workos._types import _format_datetime, _parse_datetime from workos.authorization.models.slim_role import SlimRole +from .user import User from workos.common.models.organization_membership_status import ( OrganizationMembershipStatus, ) @@ -38,6 +39,8 @@ class OrganizationMembership: """An ISO 8601 timestamp.""" role: "SlimRole" """The primary role assigned to the user within the organization.""" + user: "User" + """The user that belongs to the organization through this membership.""" organization_name: Optional[str] = None """The name of the organization which the user belongs to.""" custom_attributes: Optional[Dict[str, Any]] = None @@ -57,6 +60,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "OrganizationMembership": created_at=_parse_datetime(data["created_at"]), updated_at=_parse_datetime(data["updated_at"]), role=SlimRole.from_dict(cast(Dict[str, Any], data["role"])), + user=User.from_dict(cast(Dict[str, Any], data["user"])), organization_name=data.get("organization_name"), custom_attributes=data.get("custom_attributes"), ) @@ -77,6 +81,7 @@ def to_dict(self) -> Dict[str, Any]: result["created_at"] = _format_datetime(self.created_at) result["updated_at"] = _format_datetime(self.updated_at) result["role"] = self.role.to_dict() + result["user"] = self.user.to_dict() if self.organization_name is not None: result["organization_name"] = self.organization_name if self.custom_attributes is not None: diff --git a/src/workos/user_management/models/user_api_key.py b/src/workos/user_management/models/user_api_key.py new file mode 100644 index 00000000..a3fd779a --- /dev/null +++ b/src/workos/user_management/models/user_api_key.py @@ -0,0 +1,73 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from typing import cast +from typing import Any, Dict, List, Literal, Optional +from workos._types import _raise_deserialize_error +from workos._types import _format_datetime, _parse_datetime + +from .user_api_key_owner import UserApiKeyOwner + + +@dataclass(slots=True) +class UserApiKey: + """User Api Key model.""" + + object: Literal["api_key"] + """Distinguishes the API Key object.""" + id: str + """Unique identifier of the API Key.""" + owner: "UserApiKeyOwner" + """The entity that owns the API Key.""" + name: str + """A descriptive name for the API Key.""" + obfuscated_value: str + """An obfuscated representation of the API Key value.""" + last_used_at: Optional[datetime] + """Timestamp of when the API Key was last used.""" + permissions: List[str] + """The permission slugs assigned to the API Key.""" + created_at: datetime + """An ISO 8601 timestamp.""" + updated_at: datetime + """An ISO 8601 timestamp.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "UserApiKey": + """Deserialize from a dictionary.""" + try: + return cls( + object=data.get("object", "api_key"), + id=data["id"], + owner=UserApiKeyOwner.from_dict(cast(Dict[str, Any], data["owner"])), + name=data["name"], + obfuscated_value=data["obfuscated_value"], + last_used_at=_parse_datetime(_v_last_used_at) + if (_v_last_used_at := data["last_used_at"]) is not None + else None, + permissions=data["permissions"], + created_at=_parse_datetime(data["created_at"]), + updated_at=_parse_datetime(data["updated_at"]), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("UserApiKey", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["object"] = self.object + result["id"] = self.id + result["owner"] = self.owner.to_dict() + result["name"] = self.name + result["obfuscated_value"] = self.obfuscated_value + if self.last_used_at is not None: + result["last_used_at"] = _format_datetime(self.last_used_at) + else: + result["last_used_at"] = None + result["permissions"] = self.permissions + result["created_at"] = _format_datetime(self.created_at) + result["updated_at"] = _format_datetime(self.updated_at) + return result diff --git a/src/workos/user_management/models/user_api_key_owner.py b/src/workos/user_management/models/user_api_key_owner.py new file mode 100644 index 00000000..6f8a3f24 --- /dev/null +++ b/src/workos/user_management/models/user_api_key_owner.py @@ -0,0 +1,39 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Dict, Literal +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class UserApiKeyOwner: + """The entity that owns the API Key.""" + + type: Literal["user"] + """The type of the API Key owner.""" + id: str + """Unique identifier of the API Key owner.""" + organization_id: str + """Unique identifier of the organization the API Key can access.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "UserApiKeyOwner": + """Deserialize from a dictionary.""" + try: + return cls( + type=data.get("type", "user"), + id=data["id"], + organization_id=data["organization_id"], + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("UserApiKeyOwner", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["type"] = self.type + result["id"] = self.id + result["organization_id"] = self.organization_id + return result diff --git a/src/workos/api_keys/models/api_key_with_value.py b/src/workos/user_management/models/user_api_key_with_value.py similarity index 87% rename from src/workos/api_keys/models/api_key_with_value.py rename to src/workos/user_management/models/user_api_key_with_value.py index 1d7d53a7..be464c3c 100644 --- a/src/workos/api_keys/models/api_key_with_value.py +++ b/src/workos/user_management/models/user_api_key_with_value.py @@ -9,18 +9,18 @@ from workos._types import _raise_deserialize_error from workos._types import _format_datetime, _parse_datetime -from .api_key_with_value_owner import ApiKeyWithValueOwner +from .user_api_key_with_value_owner import UserApiKeyWithValueOwner @dataclass(slots=True) -class ApiKeyWithValue: - """Api Key With Value model.""" +class UserApiKeyWithValue: + """User Api Key With Value model.""" object: Literal["api_key"] """Distinguishes the API Key object.""" id: str """Unique identifier of the API Key.""" - owner: "ApiKeyWithValueOwner" + owner: "UserApiKeyWithValueOwner" """The entity that owns the API Key.""" name: str """A descriptive name for the API Key.""" @@ -38,13 +38,13 @@ class ApiKeyWithValue: """The full API Key value. Only returned once at creation time.""" @classmethod - def from_dict(cls, data: Dict[str, Any]) -> "ApiKeyWithValue": + def from_dict(cls, data: Dict[str, Any]) -> "UserApiKeyWithValue": """Deserialize from a dictionary.""" try: return cls( object=data.get("object", "api_key"), id=data["id"], - owner=ApiKeyWithValueOwner.from_dict( + owner=UserApiKeyWithValueOwner.from_dict( cast(Dict[str, Any], data["owner"]) ), name=data["name"], @@ -58,7 +58,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "ApiKeyWithValue": value=data["value"], ) except (KeyError, ValueError) as e: - _raise_deserialize_error("ApiKeyWithValue", e) + _raise_deserialize_error("UserApiKeyWithValue", e) def to_dict(self) -> Dict[str, Any]: """Serialize to a dictionary.""" diff --git a/src/workos/user_management/models/user_api_key_with_value_owner.py b/src/workos/user_management/models/user_api_key_with_value_owner.py new file mode 100644 index 00000000..42bd59df --- /dev/null +++ b/src/workos/user_management/models/user_api_key_with_value_owner.py @@ -0,0 +1,6 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias +from .user_api_key_owner import UserApiKeyOwner + +UserApiKeyWithValueOwner: TypeAlias = UserApiKeyOwner diff --git a/src/workos/user_management/models/user_management_invitations_order.py b/src/workos/user_management/models/user_management_invitations_order.py index a33850b2..fd4572e9 100644 --- a/src/workos/user_management/models/user_management_invitations_order.py +++ b/src/workos/user_management/models/user_management_invitations_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from .api_keys_order import ApiKeysOrder -UserManagementInvitationsOrder: TypeAlias = ApplicationsOrder +UserManagementInvitationsOrder: TypeAlias = ApiKeysOrder __all__ = ["UserManagementInvitationsOrder"] diff --git a/src/workos/user_management/models/user_management_organization_membership_order.py b/src/workos/user_management/models/user_management_organization_membership_order.py index 7f54ab09..70d8f492 100644 --- a/src/workos/user_management/models/user_management_organization_membership_order.py +++ b/src/workos/user_management/models/user_management_organization_membership_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from .api_keys_order import ApiKeysOrder -UserManagementOrganizationMembershipOrder: TypeAlias = ApplicationsOrder +UserManagementOrganizationMembershipOrder: TypeAlias = ApiKeysOrder __all__ = ["UserManagementOrganizationMembershipOrder"] diff --git a/src/workos/user_management/models/user_management_users_authorized_applications_order.py b/src/workos/user_management/models/user_management_users_authorized_applications_order.py index a4c0ef90..957118db 100644 --- a/src/workos/user_management/models/user_management_users_authorized_applications_order.py +++ b/src/workos/user_management/models/user_management_users_authorized_applications_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from .api_keys_order import ApiKeysOrder -UserManagementUsersAuthorizedApplicationsOrder: TypeAlias = ApplicationsOrder +UserManagementUsersAuthorizedApplicationsOrder: TypeAlias = ApiKeysOrder __all__ = ["UserManagementUsersAuthorizedApplicationsOrder"] diff --git a/src/workos/user_management/models/user_management_users_order.py b/src/workos/user_management/models/user_management_users_order.py index 0e7922fa..098ce5b5 100644 --- a/src/workos/user_management/models/user_management_users_order.py +++ b/src/workos/user_management/models/user_management_users_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from .api_keys_order import ApiKeysOrder -UserManagementUsersOrder: TypeAlias = ApplicationsOrder +UserManagementUsersOrder: TypeAlias = ApiKeysOrder __all__ = ["UserManagementUsersOrder"] diff --git a/src/workos/user_management_organization_membership_groups/_resource.py b/src/workos/user_management_organization_membership_groups/_resource.py index c63f2e5f..1c0ffede 100644 --- a/src/workos/user_management_organization_membership_groups/_resource.py +++ b/src/workos/user_management_organization_membership_groups/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -64,7 +65,7 @@ def list_organization_membership_groups( } return self._client.request_page( method="get", - path=f"user_management/organization_memberships/{om_id}/groups", + path=f"user_management/organization_memberships/{quote(str(om_id), safe='')}/groups", model=Group, params=params, request_options=request_options, @@ -122,7 +123,7 @@ async def list_organization_membership_groups( } return await self._client.request_page( method="get", - path=f"user_management/organization_memberships/{om_id}/groups", + path=f"user_management/organization_memberships/{quote(str(om_id), safe='')}/groups", model=Group, params=params, request_options=request_options, diff --git a/src/workos/user_management_organization_membership_groups/models/user_management_organization_membership_groups_order.py b/src/workos/user_management_organization_membership_groups/models/user_management_organization_membership_groups_order.py index eb8d238e..c76897e8 100644 --- a/src/workos/user_management_organization_membership_groups/models/user_management_organization_membership_groups_order.py +++ b/src/workos/user_management_organization_membership_groups/models/user_management_organization_membership_groups_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -UserManagementOrganizationMembershipGroupsOrder: TypeAlias = ApplicationsOrder +UserManagementOrganizationMembershipGroupsOrder: TypeAlias = ApiKeysOrder __all__ = ["UserManagementOrganizationMembershipGroupsOrder"] diff --git a/src/workos/webhooks/_resource.py b/src/workos/webhooks/_resource.py index 7f0a27f2..8640de19 100644 --- a/src/workos/webhooks/_resource.py +++ b/src/workos/webhooks/_resource.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient @@ -158,7 +159,7 @@ def update_webhook_endpoint( } return self._client.request( method="patch", - path=f"webhook_endpoints/{id}", + path=f"webhook_endpoints/{quote(str(id), safe='')}", body=body, model=WebhookEndpointJson, request_options=request_options, @@ -186,7 +187,7 @@ def delete_webhook_endpoint( """ self._client.request( method="delete", - path=f"webhook_endpoints/{id}", + path=f"webhook_endpoints/{quote(str(id), safe='')}", request_options=request_options, ) @@ -415,7 +416,7 @@ async def update_webhook_endpoint( } return await self._client.request( method="patch", - path=f"webhook_endpoints/{id}", + path=f"webhook_endpoints/{quote(str(id), safe='')}", body=body, model=WebhookEndpointJson, request_options=request_options, @@ -443,7 +444,7 @@ async def delete_webhook_endpoint( """ await self._client.request( method="delete", - path=f"webhook_endpoints/{id}", + path=f"webhook_endpoints/{quote(str(id), safe='')}", request_options=request_options, ) diff --git a/src/workos/webhooks/models/webhooks_order.py b/src/workos/webhooks/models/webhooks_order.py index 41161321..6ef84faa 100644 --- a/src/workos/webhooks/models/webhooks_order.py +++ b/src/workos/webhooks/models/webhooks_order.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder +from workos.user_management.models.api_keys_order import ApiKeysOrder -WebhooksOrder: TypeAlias = ApplicationsOrder +WebhooksOrder: TypeAlias = ApiKeysOrder __all__ = ["WebhooksOrder"] diff --git a/tests/fixtures/create_user_api_key.json b/tests/fixtures/create_user_api_key.json new file mode 100644 index 00000000..4bdce131 --- /dev/null +++ b/tests/fixtures/create_user_api_key.json @@ -0,0 +1,8 @@ +{ + "name": "Production API Key", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "permissions": [ + "posts:read", + "posts:write" + ] +} diff --git a/tests/fixtures/directory_user.json b/tests/fixtures/directory_user.json index 68d037ee..67263ead 100644 --- a/tests/fixtures/directory_user.json +++ b/tests/fixtures/directory_user.json @@ -7,6 +7,7 @@ "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": true, diff --git a/tests/fixtures/directory_user_with_groups.json b/tests/fixtures/directory_user_with_groups.json index d7d35220..d39d14b9 100644 --- a/tests/fixtures/directory_user_with_groups.json +++ b/tests/fixtures/directory_user_with_groups.json @@ -7,6 +7,7 @@ "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": true, diff --git a/tests/fixtures/dsync_group_user_added.json b/tests/fixtures/dsync_group_user_added.json index 61c5aac6..50e7ed56 100644 --- a/tests/fixtures/dsync_group_user_added.json +++ b/tests/fixtures/dsync_group_user_added.json @@ -12,6 +12,7 @@ "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": true, diff --git a/tests/fixtures/dsync_group_user_added_data.json b/tests/fixtures/dsync_group_user_added_data.json index 5ef37883..e0efa882 100644 --- a/tests/fixtures/dsync_group_user_added_data.json +++ b/tests/fixtures/dsync_group_user_added_data.json @@ -9,6 +9,7 @@ "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": true, diff --git a/tests/fixtures/dsync_group_user_removed.json b/tests/fixtures/dsync_group_user_removed.json index 3b991f47..4f5b9dff 100644 --- a/tests/fixtures/dsync_group_user_removed.json +++ b/tests/fixtures/dsync_group_user_removed.json @@ -12,6 +12,7 @@ "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": true, diff --git a/tests/fixtures/dsync_group_user_removed_data.json b/tests/fixtures/dsync_group_user_removed_data.json index 5ef37883..e0efa882 100644 --- a/tests/fixtures/dsync_group_user_removed_data.json +++ b/tests/fixtures/dsync_group_user_removed_data.json @@ -9,6 +9,7 @@ "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": true, diff --git a/tests/fixtures/dsync_user_created.json b/tests/fixtures/dsync_user_created.json index aa1bbc76..87059515 100644 --- a/tests/fixtures/dsync_user_created.json +++ b/tests/fixtures/dsync_user_created.json @@ -10,6 +10,7 @@ "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": true, diff --git a/tests/fixtures/dsync_user_deleted.json b/tests/fixtures/dsync_user_deleted.json index 26942d25..f18a02dc 100644 --- a/tests/fixtures/dsync_user_deleted.json +++ b/tests/fixtures/dsync_user_deleted.json @@ -10,6 +10,7 @@ "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": true, diff --git a/tests/fixtures/dsync_user_updated.json b/tests/fixtures/dsync_user_updated.json index 6c0266bf..143dfefb 100644 --- a/tests/fixtures/dsync_user_updated.json +++ b/tests/fixtures/dsync_user_updated.json @@ -10,6 +10,7 @@ "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": true, diff --git a/tests/fixtures/dsync_user_updated_data.json b/tests/fixtures/dsync_user_updated_data.json index 0073a535..b8a7ffa6 100644 --- a/tests/fixtures/dsync_user_updated_data.json +++ b/tests/fixtures/dsync_user_updated_data.json @@ -7,6 +7,7 @@ "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": true, diff --git a/tests/fixtures/jwt_template_response.json b/tests/fixtures/jwt_template_response.json index b691ce73..00ce7dab 100644 --- a/tests/fixtures/jwt_template_response.json +++ b/tests/fixtures/jwt_template_response.json @@ -1,6 +1,6 @@ { "object": "jwt_template", - "content": "{\"iss\": \"{{environment.id}}\", \"sub\": \"{{user.id}}\"}", + "content": "{\"urn:myapp:full_name\": \"{{user.first_name}} {{user.last_name}}\", \"urn:myapp:email\": \"{{user.email}}\"}", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } diff --git a/tests/fixtures/list_directory_user_with_groups.json b/tests/fixtures/list_directory_user_with_groups.json index 58432bad..dbb44d56 100644 --- a/tests/fixtures/list_directory_user_with_groups.json +++ b/tests/fixtures/list_directory_user_with_groups.json @@ -9,6 +9,7 @@ "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": true, diff --git a/tests/fixtures/list_organization_api_key.json b/tests/fixtures/list_organization_api_key.json new file mode 100644 index 00000000..f6b9c99c --- /dev/null +++ b/tests/fixtures/list_organization_api_key.json @@ -0,0 +1,25 @@ +{ + "data": [ + { + "object": "api_key", + "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", + "owner": { + "type": "organization", + "id": "org_01EHZNVPK3SFK441A1RGBFSHRT" + }, + "name": "Production API Key", + "obfuscated_value": "sk_...3456", + "last_used_at": null, + "permissions": [ + "posts:read", + "posts:write" + ], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" + } + ], + "list_metadata": { + "before": null, + "after": null + } +} diff --git a/tests/fixtures/list_user_api_key.json b/tests/fixtures/list_user_api_key.json new file mode 100644 index 00000000..a6409aaa --- /dev/null +++ b/tests/fixtures/list_user_api_key.json @@ -0,0 +1,26 @@ +{ + "data": [ + { + "object": "api_key", + "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", + "owner": { + "type": "user", + "id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" + }, + "name": "Production API Key", + "obfuscated_value": "sk_...3456", + "last_used_at": null, + "permissions": [ + "posts:read", + "posts:write" + ], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" + } + ], + "list_metadata": { + "before": null, + "after": null + } +} diff --git a/tests/fixtures/list_user_organization_membership.json b/tests/fixtures/list_user_organization_membership.json index cdb823aa..b0764c60 100644 --- a/tests/fixtures/list_user_organization_membership.json +++ b/tests/fixtures/list_user_organization_membership.json @@ -3,7 +3,7 @@ { "object": "organization_membership", "id": "om_01HXYZ123456789ABCDEFGHIJ", - "user_id": "user_01EHQTV6MWP9P1F4ZXGXMC8ABB", + "user_id": "user_01E4ZCR3C56J083X43JQXF3JK5", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", "status": "active", "directory_managed": false, @@ -17,6 +17,23 @@ "updated_at": "2026-01-15T12:00:00.000Z", "role": { "slug": "admin" + }, + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": true, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": { + "timezone": "America/New_York" + }, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" } } ], diff --git a/tests/fixtures/list_user_organization_membership_base_list_data.json b/tests/fixtures/list_user_organization_membership_base_list_data.json index b992a1b9..f6dc992a 100644 --- a/tests/fixtures/list_user_organization_membership_base_list_data.json +++ b/tests/fixtures/list_user_organization_membership_base_list_data.json @@ -3,7 +3,7 @@ { "object": "organization_membership", "id": "om_01HXYZ123456789ABCDEFGHIJ", - "user_id": "user_01EHQTV6MWP9P1F4ZXGXMC8ABB", + "user_id": "user_01E4ZCR3C56J083X43JQXF3JK5", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", "status": "active", "directory_managed": false, @@ -14,7 +14,24 @@ "location": "Brooklyn" }, "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" + "updated_at": "2026-01-15T12:00:00.000Z", + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": true, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": { + "timezone": "America/New_York" + }, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" + } } ], "list_metadata": { diff --git a/tests/fixtures/organization_api_key.json b/tests/fixtures/organization_api_key.json new file mode 100644 index 00000000..e05b1db2 --- /dev/null +++ b/tests/fixtures/organization_api_key.json @@ -0,0 +1,17 @@ +{ + "object": "api_key", + "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", + "owner": { + "type": "organization", + "id": "org_01EHZNVPK3SFK441A1RGBFSHRT" + }, + "name": "Production API Key", + "obfuscated_value": "sk_...3456", + "last_used_at": null, + "permissions": [ + "posts:read", + "posts:write" + ], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" +} diff --git a/tests/fixtures/organization_api_key_owner.json b/tests/fixtures/organization_api_key_owner.json new file mode 100644 index 00000000..ddbfe81d --- /dev/null +++ b/tests/fixtures/organization_api_key_owner.json @@ -0,0 +1,4 @@ +{ + "type": "organization", + "id": "org_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/tests/fixtures/organization_api_key_with_value.json b/tests/fixtures/organization_api_key_with_value.json new file mode 100644 index 00000000..9c85f194 --- /dev/null +++ b/tests/fixtures/organization_api_key_with_value.json @@ -0,0 +1,18 @@ +{ + "object": "api_key", + "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", + "owner": { + "type": "organization", + "id": "org_01EHZNVPK3SFK441A1RGBFSHRT" + }, + "name": "Production API Key", + "obfuscated_value": "sk_...3456", + "last_used_at": null, + "permissions": [ + "posts:read", + "posts:write" + ], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "value": "sk_abcdefghijklmnop123456" +} diff --git a/tests/fixtures/organization_api_key_with_value_owner.json b/tests/fixtures/organization_api_key_with_value_owner.json new file mode 100644 index 00000000..ddbfe81d --- /dev/null +++ b/tests/fixtures/organization_api_key_with_value_owner.json @@ -0,0 +1,4 @@ +{ + "type": "organization", + "id": "org_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/tests/fixtures/organization_membership.json b/tests/fixtures/organization_membership.json index afc05d1e..efba3046 100644 --- a/tests/fixtures/organization_membership.json +++ b/tests/fixtures/organization_membership.json @@ -15,5 +15,22 @@ "updated_at": "2026-01-15T12:00:00.000Z", "role": { "slug": "admin" + }, + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": true, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": { + "timezone": "America/New_York" + }, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" } } diff --git a/tests/fixtures/profile.json b/tests/fixtures/profile.json index 485a13ce..09dcaa63 100644 --- a/tests/fixtures/profile.json +++ b/tests/fixtures/profile.json @@ -8,6 +8,7 @@ "email": "todd@example.com", "first_name": "Todd", "last_name": "Rundgren", + "name": "Todd Rundgren", "role": { "slug": "admin" }, diff --git a/tests/fixtures/sso_token_response.json b/tests/fixtures/sso_token_response.json index 757c3fb1..1f627a4a 100644 --- a/tests/fixtures/sso_token_response.json +++ b/tests/fixtures/sso_token_response.json @@ -12,6 +12,7 @@ "email": "todd@example.com", "first_name": "Todd", "last_name": "Rundgren", + "name": "Todd Rundgren", "role": { "slug": "admin" }, diff --git a/tests/fixtures/update_jwt_template.json b/tests/fixtures/update_jwt_template.json index bb613667..160996e7 100644 --- a/tests/fixtures/update_jwt_template.json +++ b/tests/fixtures/update_jwt_template.json @@ -1,3 +1,3 @@ { - "content": "{\"iss\": \"{{environment.id}}\", \"sub\": \"{{user.id}}\"}" + "content": "{\"urn:myapp:full_name\": \"{{user.first_name}} {{user.last_name}}\", \"urn:myapp:email\": \"{{user.email}}\"}" } diff --git a/tests/fixtures/user_api_key.json b/tests/fixtures/user_api_key.json new file mode 100644 index 00000000..0b527caf --- /dev/null +++ b/tests/fixtures/user_api_key.json @@ -0,0 +1,18 @@ +{ + "object": "api_key", + "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", + "owner": { + "type": "user", + "id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" + }, + "name": "Production API Key", + "obfuscated_value": "sk_...3456", + "last_used_at": null, + "permissions": [ + "posts:read", + "posts:write" + ], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" +} diff --git a/tests/fixtures/user_api_key_owner.json b/tests/fixtures/user_api_key_owner.json new file mode 100644 index 00000000..a0fb2ade --- /dev/null +++ b/tests/fixtures/user_api_key_owner.json @@ -0,0 +1,5 @@ +{ + "type": "user", + "id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/tests/fixtures/user_api_key_with_value.json b/tests/fixtures/user_api_key_with_value.json new file mode 100644 index 00000000..99d28c9a --- /dev/null +++ b/tests/fixtures/user_api_key_with_value.json @@ -0,0 +1,19 @@ +{ + "object": "api_key", + "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", + "owner": { + "type": "user", + "id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" + }, + "name": "Production API Key", + "obfuscated_value": "sk_...3456", + "last_used_at": null, + "permissions": [ + "posts:read", + "posts:write" + ], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "value": "sk_abcdefghijklmnop123456" +} diff --git a/tests/fixtures/user_api_key_with_value_owner.json b/tests/fixtures/user_api_key_with_value_owner.json new file mode 100644 index 00000000..a0fb2ade --- /dev/null +++ b/tests/fixtures/user_api_key_with_value_owner.json @@ -0,0 +1,5 @@ +{ + "type": "user", + "id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT" +} diff --git a/tests/fixtures/user_organization_membership.json b/tests/fixtures/user_organization_membership.json index d6acfbe6..184279d9 100644 --- a/tests/fixtures/user_organization_membership.json +++ b/tests/fixtures/user_organization_membership.json @@ -1,7 +1,7 @@ { "object": "organization_membership", "id": "om_01HXYZ123456789ABCDEFGHIJ", - "user_id": "user_01EHQTV6MWP9P1F4ZXGXMC8ABB", + "user_id": "user_01E4ZCR3C56J083X43JQXF3JK5", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", "status": "active", "directory_managed": false, @@ -15,5 +15,22 @@ "updated_at": "2026-01-15T12:00:00.000Z", "role": { "slug": "admin" + }, + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": true, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": { + "timezone": "America/New_York" + }, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" } } diff --git a/tests/fixtures/user_organization_membership_base_list_data.json b/tests/fixtures/user_organization_membership_base_list_data.json index a4e9ebde..f3a20339 100644 --- a/tests/fixtures/user_organization_membership_base_list_data.json +++ b/tests/fixtures/user_organization_membership_base_list_data.json @@ -1,7 +1,7 @@ { "object": "organization_membership", "id": "om_01HXYZ123456789ABCDEFGHIJ", - "user_id": "user_01EHQTV6MWP9P1F4ZXGXMC8ABB", + "user_id": "user_01E4ZCR3C56J083X43JQXF3JK5", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", "status": "active", "directory_managed": false, @@ -12,5 +12,22 @@ "location": "Brooklyn" }, "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" + "updated_at": "2026-01-15T12:00:00.000Z", + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": true, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": { + "timezone": "America/New_York" + }, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" + } } diff --git a/tests/fixtures/vault_byok_key_deleted.json b/tests/fixtures/vault_byok_key_deleted.json new file mode 100644 index 00000000..cd179cb3 --- /dev/null +++ b/tests/fixtures/vault_byok_key_deleted.json @@ -0,0 +1,30 @@ +{ + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "vault.byok_key.deleted", + "data": { + "organization_id": "org_01EHT88Z8J8795GZNQ4ZP1J81T", + "key_provider": "AWS_KMS" + }, + "created_at": "2026-01-15T12:00:00.000Z", + "context": { + "google_analytics_client_id": "GA1.2.1234567890.1234567890", + "google_analytics_sessions": [ + { + "containerId": "GTM-ABCDEF", + "sessionId": "1234567890", + "sessionNumber": "1" + } + ], + "ajs_anonymous_id": "ajs_anon_01EHWNCE74X7JSDV0X3SZ3KJNY", + "client_id": "client_01EHWNCE74X7JSDV0X3SZ3KJNY", + "actor": { + "id": "user_01EHWNCE74X7JSDV0X3SZ3KJNY", + "source": "api", + "name": "Jane Doe" + }, + "previous_attributes": { + "key": {} + } + }, + "object": "event" +} diff --git a/tests/fixtures/vault_byok_key_deleted_data.json b/tests/fixtures/vault_byok_key_deleted_data.json new file mode 100644 index 00000000..ceb94fe7 --- /dev/null +++ b/tests/fixtures/vault_byok_key_deleted_data.json @@ -0,0 +1,4 @@ +{ + "organization_id": "org_01EHT88Z8J8795GZNQ4ZP1J81T", + "key_provider": "AWS_KMS" +} diff --git a/tests/test_api_keys.py b/tests/test_api_keys.py index e2a13dde..934d6acc 100644 --- a/tests/test_api_keys.py +++ b/tests/test_api_keys.py @@ -7,9 +7,9 @@ from tests.generated_helpers import load_fixture from workos.api_keys.models import ( - ApiKey, ApiKeyValidationResponse, - ApiKeyWithValue, + OrganizationApiKey, + OrganizationApiKeyWithValue, OrganizationsApiKeysOrder, ) from workos._pagination import AsyncPage, SyncPage @@ -24,34 +24,14 @@ class TestApiKeys: - def test_create_validation(self, workos, httpx_mock): - httpx_mock.add_response( - json=load_fixture("api_key_validation_response.json"), - ) - result = workos.api_keys.create_validation(value="test_value") - assert isinstance(result, ApiKeyValidationResponse) - request = httpx_mock.get_request() - assert request.method == "POST" - assert request.url.path.endswith("/api_keys/validations") - body = json.loads(request.content) - assert body["value"] == "test_value" - - def test_delete_api_key(self, workos, httpx_mock): - httpx_mock.add_response(status_code=204) - result = workos.api_keys.delete_api_key("test_id") - assert result is None - request = httpx_mock.get_request() - assert request.method == "DELETE" - assert request.url.path.endswith("/api_keys/test_id") - def test_list_organization_api_keys(self, workos, httpx_mock): httpx_mock.add_response( - json=load_fixture("list_api_key.json"), + json=load_fixture("list_organization_api_key.json"), ) page = workos.api_keys.list_organization_api_keys("test_organizationId") assert isinstance(page, SyncPage) assert len(page.data) == 1 - assert isinstance(page.data[0], ApiKey) + assert isinstance(page.data[0], OrganizationApiKey) def test_list_organization_api_keys_empty_page(self, workos, httpx_mock): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) @@ -76,12 +56,12 @@ def test_list_organization_api_keys_encodes_query_params(self, workos, httpx_moc def test_create_organization_api_key(self, workos, httpx_mock): httpx_mock.add_response( - json=load_fixture("api_key_with_value.json"), + json=load_fixture("organization_api_key_with_value.json"), ) result = workos.api_keys.create_organization_api_key( "test_organizationId", name="test_name" ) - assert isinstance(result, ApiKeyWithValue) + assert isinstance(result, OrganizationApiKeyWithValue) assert result.object == "api_key" assert result.id == "api_key_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() @@ -90,34 +70,55 @@ def test_create_organization_api_key(self, workos, httpx_mock): body = json.loads(request.content) assert body["name"] == "test_name" - def test_create_validation_with_request_options(self, workos, httpx_mock): - httpx_mock.add_response(json=load_fixture("api_key_validation_response.json")) - workos.api_keys.create_validation( - value="test_value", request_options={"extra_headers": {"X-Custom": "value"}} + def test_create_validation(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("api_key_validation_response.json"), + ) + result = workos.api_keys.create_validation(value="test_value") + assert isinstance(result, ApiKeyValidationResponse) + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/api_keys/validations") + body = json.loads(request.content) + assert body["value"] == "test_value" + + def test_delete_api_key(self, workos, httpx_mock): + httpx_mock.add_response(status_code=204) + result = workos.api_keys.delete_api_key("test_id") + assert result is None + request = httpx_mock.get_request() + assert request.method == "DELETE" + assert request.url.path.endswith("/api_keys/test_id") + + def test_list_organization_api_keys_with_request_options(self, workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + workos.api_keys.list_organization_api_keys( + "test_organizationId", + request_options={"extra_headers": {"X-Custom": "value"}}, ) request = httpx_mock.get_request() assert request.headers["X-Custom"] == "value" - def test_create_validation_unauthorized(self, workos, httpx_mock): + def test_list_organization_api_keys_unauthorized(self, workos, httpx_mock): httpx_mock.add_response( status_code=401, json={"message": "Unauthorized"}, ) with pytest.raises(AuthenticationError): - workos.api_keys.create_validation(value="test_value") + workos.api_keys.list_organization_api_keys("test_organizationId") - def test_create_validation_not_found(self, httpx_mock): + def test_list_organization_api_keys_not_found(self, httpx_mock): workos = WorkOSClient( api_key="sk_test_123", client_id="client_test", max_retries=0 ) try: httpx_mock.add_response(status_code=404, json={"message": "Not found"}) with pytest.raises(NotFoundError): - workos.api_keys.create_validation(value="test_value") + workos.api_keys.list_organization_api_keys("test_organizationId") finally: workos.close() - def test_create_validation_rate_limited(self, httpx_mock): + def test_list_organization_api_keys_rate_limited(self, httpx_mock): workos = WorkOSClient( api_key="sk_test_123", client_id="client_test", max_retries=0 ) @@ -128,72 +129,54 @@ def test_create_validation_rate_limited(self, httpx_mock): json={"message": "Slow down"}, ) with pytest.raises(RateLimitExceededError): - workos.api_keys.create_validation(value="test_value") + workos.api_keys.list_organization_api_keys("test_organizationId") finally: workos.close() - def test_create_validation_server_error(self, httpx_mock): + def test_list_organization_api_keys_server_error(self, httpx_mock): workos = WorkOSClient( api_key="sk_test_123", client_id="client_test", max_retries=0 ) try: httpx_mock.add_response(status_code=500, json={"message": "Server error"}) with pytest.raises(ServerError): - workos.api_keys.create_validation(value="test_value") + workos.api_keys.list_organization_api_keys("test_organizationId") finally: workos.close() - def test_create_validation_bad_request(self, httpx_mock): + def test_list_organization_api_keys_bad_request(self, httpx_mock): workos = WorkOSClient( api_key="sk_test_123", client_id="client_test", max_retries=0 ) try: httpx_mock.add_response(status_code=400, json={"message": "Bad request"}) with pytest.raises(BadRequestError): - workos.api_keys.create_validation(value="test_value") + workos.api_keys.list_organization_api_keys("test_organizationId") finally: workos.close() - def test_create_validation_unprocessable(self, httpx_mock): + def test_list_organization_api_keys_unprocessable(self, httpx_mock): workos = WorkOSClient( api_key="sk_test_123", client_id="client_test", max_retries=0 ) try: httpx_mock.add_response(status_code=422, json={"message": "Unprocessable"}) with pytest.raises(UnprocessableEntityError): - workos.api_keys.create_validation(value="test_value") + workos.api_keys.list_organization_api_keys("test_organizationId") finally: workos.close() class TestAsyncApiKeys: - @pytest.mark.asyncio - async def test_create_validation(self, async_workos, httpx_mock): - httpx_mock.add_response(json=load_fixture("api_key_validation_response.json")) - result = await async_workos.api_keys.create_validation(value="test_value") - assert isinstance(result, ApiKeyValidationResponse) - request = httpx_mock.get_request() - assert request.method == "POST" - assert request.url.path.endswith("/api_keys/validations") - - @pytest.mark.asyncio - async def test_delete_api_key(self, async_workos, httpx_mock): - httpx_mock.add_response(status_code=204) - result = await async_workos.api_keys.delete_api_key("test_id") - assert result is None - request = httpx_mock.get_request() - assert request.method == "DELETE" - assert request.url.path.endswith("/api_keys/test_id") - @pytest.mark.asyncio async def test_list_organization_api_keys(self, async_workos, httpx_mock): - httpx_mock.add_response(json=load_fixture("list_api_key.json")) + httpx_mock.add_response(json=load_fixture("list_organization_api_key.json")) page = await async_workos.api_keys.list_organization_api_keys( "test_organizationId" ) assert isinstance(page, AsyncPage) assert len(page.data) == 1 - assert isinstance(page.data[0], ApiKey) + assert isinstance(page.data[0], OrganizationApiKey) @pytest.mark.asyncio async def test_list_organization_api_keys_empty_page( @@ -226,11 +209,13 @@ async def test_list_organization_api_keys_encodes_query_params( @pytest.mark.asyncio async def test_create_organization_api_key(self, async_workos, httpx_mock): - httpx_mock.add_response(json=load_fixture("api_key_with_value.json")) + httpx_mock.add_response( + json=load_fixture("organization_api_key_with_value.json") + ) result = await async_workos.api_keys.create_organization_api_key( "test_organizationId", name="test_name" ) - assert isinstance(result, ApiKeyWithValue) + assert isinstance(result, OrganizationApiKeyWithValue) assert result.object == "api_key" assert result.id == "api_key_01EHZNVPK3SFK441A1RGBFSHRT" request = httpx_mock.get_request() @@ -238,36 +223,59 @@ async def test_create_organization_api_key(self, async_workos, httpx_mock): assert request.url.path.endswith("/organizations/test_organizationId/api_keys") @pytest.mark.asyncio - async def test_create_validation_with_request_options( + async def test_create_validation(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("api_key_validation_response.json")) + result = await async_workos.api_keys.create_validation(value="test_value") + assert isinstance(result, ApiKeyValidationResponse) + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/api_keys/validations") + + @pytest.mark.asyncio + async def test_delete_api_key(self, async_workos, httpx_mock): + httpx_mock.add_response(status_code=204) + result = await async_workos.api_keys.delete_api_key("test_id") + assert result is None + request = httpx_mock.get_request() + assert request.method == "DELETE" + assert request.url.path.endswith("/api_keys/test_id") + + @pytest.mark.asyncio + async def test_list_organization_api_keys_with_request_options( self, async_workos, httpx_mock ): - httpx_mock.add_response(json=load_fixture("api_key_validation_response.json")) - await async_workos.api_keys.create_validation( - value="test_value", request_options={"extra_headers": {"X-Custom": "value"}} + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + await async_workos.api_keys.list_organization_api_keys( + "test_organizationId", + request_options={"extra_headers": {"X-Custom": "value"}}, ) request = httpx_mock.get_request() assert request.headers["X-Custom"] == "value" @pytest.mark.asyncio - async def test_create_validation_unauthorized(self, async_workos, httpx_mock): + async def test_list_organization_api_keys_unauthorized( + self, async_workos, httpx_mock + ): httpx_mock.add_response(status_code=401, json={"message": "Unauthorized"}) with pytest.raises(AuthenticationError): - await async_workos.api_keys.create_validation(value="test_value") + await async_workos.api_keys.list_organization_api_keys( + "test_organizationId" + ) @pytest.mark.asyncio - async def test_create_validation_not_found(self, httpx_mock): + async def test_list_organization_api_keys_not_found(self, httpx_mock): workos = AsyncWorkOSClient( api_key="sk_test_123", client_id="client_test", max_retries=0 ) try: httpx_mock.add_response(status_code=404, json={"message": "Not found"}) with pytest.raises(NotFoundError): - await workos.api_keys.create_validation(value="test_value") + await workos.api_keys.list_organization_api_keys("test_organizationId") finally: await workos.close() @pytest.mark.asyncio - async def test_create_validation_rate_limited(self, httpx_mock): + async def test_list_organization_api_keys_rate_limited(self, httpx_mock): workos = AsyncWorkOSClient( api_key="sk_test_123", client_id="client_test", max_retries=0 ) @@ -278,42 +286,42 @@ async def test_create_validation_rate_limited(self, httpx_mock): json={"message": "Slow down"}, ) with pytest.raises(RateLimitExceededError): - await workos.api_keys.create_validation(value="test_value") + await workos.api_keys.list_organization_api_keys("test_organizationId") finally: await workos.close() @pytest.mark.asyncio - async def test_create_validation_server_error(self, httpx_mock): + async def test_list_organization_api_keys_server_error(self, httpx_mock): workos = AsyncWorkOSClient( api_key="sk_test_123", client_id="client_test", max_retries=0 ) try: httpx_mock.add_response(status_code=500, json={"message": "Server error"}) with pytest.raises(ServerError): - await workos.api_keys.create_validation(value="test_value") + await workos.api_keys.list_organization_api_keys("test_organizationId") finally: await workos.close() @pytest.mark.asyncio - async def test_create_validation_bad_request(self, httpx_mock): + async def test_list_organization_api_keys_bad_request(self, httpx_mock): workos = AsyncWorkOSClient( api_key="sk_test_123", client_id="client_test", max_retries=0 ) try: httpx_mock.add_response(status_code=400, json={"message": "Bad request"}) with pytest.raises(BadRequestError): - await workos.api_keys.create_validation(value="test_value") + await workos.api_keys.list_organization_api_keys("test_organizationId") finally: await workos.close() @pytest.mark.asyncio - async def test_create_validation_unprocessable(self, httpx_mock): + async def test_list_organization_api_keys_unprocessable(self, httpx_mock): workos = AsyncWorkOSClient( api_key="sk_test_123", client_id="client_test", max_retries=0 ) try: httpx_mock.add_response(status_code=422, json={"message": "Unprocessable"}) with pytest.raises(UnprocessableEntityError): - await workos.api_keys.create_validation(value="test_value") + await workos.api_keys.list_organization_api_keys("test_organizationId") finally: await workos.close() diff --git a/tests/test_models_round_trip.py b/tests/test_models_round_trip.py index 3925830b..9aca9a12 100644 --- a/tests/test_models_round_trip.py +++ b/tests/test_models_round_trip.py @@ -16,8 +16,10 @@ ApiKey, ApiKeyOwner, ApiKeyValidationResponse, - ApiKeyWithValue, - ApiKeyWithValueOwner, + OrganizationApiKey, + OrganizationApiKeyOwner, + OrganizationApiKeyWithValue, + OrganizationApiKeyWithValueOwner, ) from workos.audit_logs.models import ( AuditLogActionJson, @@ -251,6 +253,8 @@ UserCreated, UserDeleted, UserUpdated, + VaultByokKeyDeleted, + VaultByokKeyDeletedData, VaultByokKeyVerificationCompleted, VaultByokKeyVerificationCompletedData, VaultDataCreated, @@ -364,6 +368,10 @@ ResetPasswordResponse, SendVerificationEmailResponse, User, + UserApiKey, + UserApiKeyOwner, + UserApiKeyWithValue, + UserApiKeyWithValueOwner, UserIdentitiesGetItem, UserInvite, UserOrganizationMembership, @@ -1498,6 +1506,86 @@ def test_role_list_minimal_payload(self): assert serialized["object"] == data["object"] assert serialized["data"] == data["data"] + def test_user_round_trip(self): + data = load_fixture("user.json") + instance = User.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = User.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_user_minimal_payload(self): + data = { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": None, + "last_name": None, + "profile_picture_url": None, + "email": "marcelina.davis@example.com", + "email_verified": True, + "external_id": None, + "last_sign_in_at": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + } + instance = User.from_dict(data) + serialized = instance.to_dict() + assert serialized["object"] == data["object"] + assert serialized["id"] == data["id"] + assert serialized["first_name"] == data["first_name"] + assert serialized["last_name"] == data["last_name"] + assert serialized["profile_picture_url"] == data["profile_picture_url"] + assert serialized["email"] == data["email"] + assert serialized["email_verified"] == data["email_verified"] + assert serialized["external_id"] == data["external_id"] + assert serialized["last_sign_in_at"] == data["last_sign_in_at"] + assert serialized["created_at"] == data["created_at"] + assert serialized["updated_at"] == data["updated_at"] + + def test_user_omits_absent_optional_non_nullable_fields(self): + data = { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": True, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + } + instance = User.from_dict(data) + serialized = instance.to_dict() + assert "metadata" not in serialized + + def test_user_preserves_nullable_fields(self): + data = { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": None, + "last_name": None, + "profile_picture_url": None, + "email": "marcelina.davis@example.com", + "email_verified": True, + "external_id": None, + "metadata": {"timezone": "America/New_York"}, + "last_sign_in_at": None, + "locale": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + } + instance = User.from_dict(data) + serialized = instance.to_dict() + assert serialized["first_name"] is None + assert serialized["last_name"] is None + assert serialized["profile_picture_url"] is None + assert serialized["external_id"] is None + assert serialized["last_sign_in_at"] is None + assert serialized["locale"] is None + def test_connection_round_trip(self): data = load_fixture("connection.json") instance = Connection.from_dict(data) @@ -1780,6 +1868,7 @@ def test_directory_user_with_groups_omits_absent_optional_non_nullable_fields(se "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "job_title": "Software Engineer", "username": "mdavis", "state": "active", @@ -1820,6 +1909,7 @@ def test_directory_user_with_groups_preserves_nullable_fields(self): "email": None, "first_name": None, "last_name": None, + "name": None, "emails": [ { "primary": True, @@ -1858,6 +1948,7 @@ def test_directory_user_with_groups_preserves_nullable_fields(self): assert serialized["email"] is None assert serialized["first_name"] is None assert serialized["last_name"] is None + assert serialized["name"] is None assert serialized["job_title"] is None assert serialized["username"] is None @@ -1871,6 +1962,7 @@ def test_directory_user_with_groups_round_trips_unknown_enum_values(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -2053,6 +2145,7 @@ def test_directory_user_omits_absent_optional_non_nullable_fields(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "job_title": "Software Engineer", "username": "mdavis", "state": "active", @@ -2080,6 +2173,7 @@ def test_directory_user_preserves_nullable_fields(self): "email": None, "first_name": None, "last_name": None, + "name": None, "emails": [ { "primary": True, @@ -2105,6 +2199,7 @@ def test_directory_user_preserves_nullable_fields(self): assert serialized["email"] is None assert serialized["first_name"] is None assert serialized["last_name"] is None + assert serialized["name"] is None assert serialized["job_title"] is None assert serialized["username"] is None @@ -2118,6 +2213,7 @@ def test_directory_user_round_trips_unknown_enum_values(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -2141,86 +2237,6 @@ def test_directory_user_round_trips_unknown_enum_values(self): instance = DirectoryUser.from_dict(data) assert instance.to_dict() == data - def test_user_round_trip(self): - data = load_fixture("user.json") - instance = User.from_dict(data) - serialized = instance.to_dict() - assert serialized == data - restored = User.from_dict(serialized) - assert restored.to_dict() == serialized - - def test_user_minimal_payload(self): - data = { - "object": "user", - "id": "user_01E4ZCR3C56J083X43JQXF3JK5", - "first_name": None, - "last_name": None, - "profile_picture_url": None, - "email": "marcelina.davis@example.com", - "email_verified": True, - "external_id": None, - "last_sign_in_at": None, - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z", - } - instance = User.from_dict(data) - serialized = instance.to_dict() - assert serialized["object"] == data["object"] - assert serialized["id"] == data["id"] - assert serialized["first_name"] == data["first_name"] - assert serialized["last_name"] == data["last_name"] - assert serialized["profile_picture_url"] == data["profile_picture_url"] - assert serialized["email"] == data["email"] - assert serialized["email_verified"] == data["email_verified"] - assert serialized["external_id"] == data["external_id"] - assert serialized["last_sign_in_at"] == data["last_sign_in_at"] - assert serialized["created_at"] == data["created_at"] - assert serialized["updated_at"] == data["updated_at"] - - def test_user_omits_absent_optional_non_nullable_fields(self): - data = { - "object": "user", - "id": "user_01E4ZCR3C56J083X43JQXF3JK5", - "first_name": "Marcelina", - "last_name": "Davis", - "profile_picture_url": "https://workoscdn.com/images/v1/123abc", - "email": "marcelina.davis@example.com", - "email_verified": True, - "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", - "last_sign_in_at": "2025-06-25T19:07:33.155Z", - "locale": "en-US", - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z", - } - instance = User.from_dict(data) - serialized = instance.to_dict() - assert "metadata" not in serialized - - def test_user_preserves_nullable_fields(self): - data = { - "object": "user", - "id": "user_01E4ZCR3C56J083X43JQXF3JK5", - "first_name": None, - "last_name": None, - "profile_picture_url": None, - "email": "marcelina.davis@example.com", - "email_verified": True, - "external_id": None, - "metadata": {"timezone": "America/New_York"}, - "last_sign_in_at": None, - "locale": None, - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z", - } - instance = User.from_dict(data) - serialized = instance.to_dict() - assert serialized["first_name"] is None - assert serialized["last_name"] is None - assert serialized["profile_picture_url"] is None - assert serialized["external_id"] is None - assert serialized["last_sign_in_at"] is None - assert serialized["locale"] is None - def test_waitlist_user_round_trip(self): data = load_fixture("waitlist_user.json") instance = WaitlistUser.from_dict(data) @@ -6270,6 +6286,7 @@ def test_dsync_group_user_added_minimal_payload(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -6328,6 +6345,7 @@ def test_dsync_group_user_added_omits_absent_optional_non_nullable_fields(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -6387,6 +6405,7 @@ def test_dsync_group_user_added_data_minimal_payload(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -6446,6 +6465,7 @@ def test_dsync_user_created_minimal_payload(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -6490,6 +6510,7 @@ def test_dsync_user_created_omits_absent_optional_non_nullable_fields(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -6538,6 +6559,7 @@ def test_dsync_user_deleted_minimal_payload(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -6582,6 +6604,7 @@ def test_dsync_user_deleted_omits_absent_optional_non_nullable_fields(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -6632,6 +6655,7 @@ def test_dsync_group_user_removed_minimal_payload(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -6690,6 +6714,7 @@ def test_dsync_group_user_removed_omits_absent_optional_non_nullable_fields(self "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -6749,6 +6774,7 @@ def test_dsync_group_user_removed_data_minimal_payload(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -6808,6 +6834,7 @@ def test_dsync_user_updated_minimal_payload(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -6853,6 +6880,7 @@ def test_dsync_user_updated_omits_absent_optional_non_nullable_fields(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -6930,6 +6958,7 @@ def test_dsync_user_updated_data_omits_absent_optional_non_nullable_fields(self) "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "job_title": "Software Engineer", "username": "mdavis", "state": "active", @@ -6958,6 +6987,7 @@ def test_dsync_user_updated_data_preserves_nullable_fields(self): "email": None, "first_name": None, "last_name": None, + "name": None, "emails": [ { "primary": True, @@ -6984,6 +7014,7 @@ def test_dsync_user_updated_data_preserves_nullable_fields(self): assert serialized["email"] is None assert serialized["first_name"] is None assert serialized["last_name"] is None + assert serialized["name"] is None assert serialized["job_title"] is None assert serialized["username"] is None @@ -6997,6 +7028,7 @@ def test_dsync_user_updated_data_round_trips_unknown_enum_values(self): "email": "marcelina.davis@example.com", "first_name": "Marcelina", "last_name": "Davis", + "name": "Marcelina Davis", "emails": [ { "primary": True, @@ -12642,6 +12674,74 @@ def test_user_updated_omits_absent_optional_non_nullable_fields(self): serialized = instance.to_dict() assert "context" not in serialized + def test_vault_byok_key_deleted_round_trip(self): + data = load_fixture("vault_byok_key_deleted.json") + instance = VaultByokKeyDeleted.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = VaultByokKeyDeleted.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_vault_byok_key_deleted_minimal_payload(self): + data = { + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "vault.byok_key.deleted", + "data": { + "organization_id": "org_01EHT88Z8J8795GZNQ4ZP1J81T", + "key_provider": "AWS_KMS", + }, + "created_at": "2026-01-15T12:00:00.000Z", + "object": "event", + } + instance = VaultByokKeyDeleted.from_dict(data) + serialized = instance.to_dict() + assert serialized["id"] == data["id"] + assert serialized["event"] == data["event"] + assert serialized["data"] == data["data"] + assert serialized["created_at"] == data["created_at"] + assert serialized["object"] == data["object"] + + def test_vault_byok_key_deleted_omits_absent_optional_non_nullable_fields(self): + data = { + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "vault.byok_key.deleted", + "data": { + "organization_id": "org_01EHT88Z8J8795GZNQ4ZP1J81T", + "key_provider": "AWS_KMS", + }, + "created_at": "2026-01-15T12:00:00.000Z", + "object": "event", + } + instance = VaultByokKeyDeleted.from_dict(data) + serialized = instance.to_dict() + assert "context" not in serialized + + def test_vault_byok_key_deleted_data_round_trip(self): + data = load_fixture("vault_byok_key_deleted_data.json") + instance = VaultByokKeyDeletedData.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = VaultByokKeyDeletedData.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_vault_byok_key_deleted_data_minimal_payload(self): + data = { + "organization_id": "org_01EHT88Z8J8795GZNQ4ZP1J81T", + "key_provider": "AWS_KMS", + } + instance = VaultByokKeyDeletedData.from_dict(data) + serialized = instance.to_dict() + assert serialized["organization_id"] == data["organization_id"] + assert serialized["key_provider"] == data["key_provider"] + + def test_vault_byok_key_deleted_data_round_trips_unknown_enum_values(self): + data = { + "organization_id": "org_01EHT88Z8J8795GZNQ4ZP1J81T", + "key_provider": "unexpected_vault_byok_key_deleted_data_key_provider", + } + instance = VaultByokKeyDeletedData.from_dict(data) + assert instance.to_dict() == data + def test_vault_byok_key_verification_completed_round_trip(self): data = load_fixture("vault_byok_key_verification_completed.json") instance = VaultByokKeyVerificationCompleted.from_dict(data) @@ -13607,28 +13707,6 @@ def test_waitlist_user_denied_omits_absent_optional_non_nullable_fields(self): serialized = instance.to_dict() assert "context" not in serialized - def test_jwt_template_response_round_trip(self): - data = load_fixture("jwt_template_response.json") - instance = JWTTemplateResponse.from_dict(data) - serialized = instance.to_dict() - assert serialized == data - restored = JWTTemplateResponse.from_dict(serialized) - assert restored.to_dict() == serialized - - def test_jwt_template_response_minimal_payload(self): - data = { - "object": "jwt_template", - "content": '{"iss": "{{environment.id}}", "sub": "{{user.id}}"}', - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z", - } - instance = JWTTemplateResponse.from_dict(data) - serialized = instance.to_dict() - assert serialized["object"] == data["object"] - assert serialized["content"] == data["content"] - assert serialized["created_at"] == data["created_at"] - assert serialized["updated_at"] == data["updated_at"] - def test_organization_domain_stand_alone_round_trip(self): data = load_fixture("organization_domain_stand_alone.json") instance = OrganizationDomainStandAlone.from_dict(data) @@ -13744,15 +13822,15 @@ def test_flag_preserves_nullable_fields(self): assert serialized["description"] is None assert serialized["owner"] is None - def test_api_key_with_value_round_trip(self): - data = load_fixture("api_key_with_value.json") - instance = ApiKeyWithValue.from_dict(data) + def test_organization_api_key_round_trip(self): + data = load_fixture("organization_api_key.json") + instance = OrganizationApiKey.from_dict(data) serialized = instance.to_dict() assert serialized == data - restored = ApiKeyWithValue.from_dict(serialized) + restored = OrganizationApiKey.from_dict(serialized) assert restored.to_dict() == serialized - def test_api_key_with_value_minimal_payload(self): + def test_organization_api_key_minimal_payload(self): data = { "object": "api_key", "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", @@ -13763,9 +13841,8 @@ def test_api_key_with_value_minimal_payload(self): "permissions": ["posts:read", "posts:write"], "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", - "value": "sk_abcdefghijklmnop123456", } - instance = ApiKeyWithValue.from_dict(data) + instance = OrganizationApiKey.from_dict(data) serialized = instance.to_dict() assert serialized["object"] == data["object"] assert serialized["id"] == data["id"] @@ -13776,9 +13853,8 @@ def test_api_key_with_value_minimal_payload(self): assert serialized["permissions"] == data["permissions"] assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] - assert serialized["value"] == data["value"] - def test_api_key_with_value_preserves_nullable_fields(self): + def test_organization_api_key_preserves_nullable_fields(self): data = { "object": "api_key", "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", @@ -13789,37 +13865,87 @@ def test_api_key_with_value_preserves_nullable_fields(self): "permissions": ["posts:read", "posts:write"], "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", - "value": "sk_abcdefghijklmnop123456", } - instance = ApiKeyWithValue.from_dict(data) + instance = OrganizationApiKey.from_dict(data) serialized = instance.to_dict() assert serialized["last_used_at"] is None - def test_organization_round_trip(self): - data = load_fixture("organization.json") - instance = Organization.from_dict(data) + def test_organization_api_key_with_value_round_trip(self): + data = load_fixture("organization_api_key_with_value.json") + instance = OrganizationApiKeyWithValue.from_dict(data) serialized = instance.to_dict() assert serialized == data - restored = Organization.from_dict(serialized) + restored = OrganizationApiKeyWithValue.from_dict(serialized) assert restored.to_dict() == serialized - def test_organization_minimal_payload(self): + def test_organization_api_key_with_value_minimal_payload(self): data = { - "object": "organization", - "id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY", - "name": "Acme Inc.", - "domains": [ - { - "object": "organization_domain", - "id": "org_domain_01EHZNVPK2QXHMVWCEDQEKY69A", - "organization_id": "org_01HE8GSH8FQPASKSY27THRKRBP", - "domain": "foo-corp.com", - "state": "pending", - "verification_prefix": "superapp-domain-verification-z3kjny", - "verification_token": "m5Oztg3jdK4NJLgs8uIlIprMw", - "verification_strategy": "dns", - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z", + "object": "api_key", + "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", + "owner": {"type": "organization", "id": "org_01EHZNVPK3SFK441A1RGBFSHRT"}, + "name": "Production API Key", + "obfuscated_value": "sk_...3456", + "last_used_at": None, + "permissions": ["posts:read", "posts:write"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "value": "sk_abcdefghijklmnop123456", + } + instance = OrganizationApiKeyWithValue.from_dict(data) + serialized = instance.to_dict() + assert serialized["object"] == data["object"] + assert serialized["id"] == data["id"] + assert serialized["owner"] == data["owner"] + assert serialized["name"] == data["name"] + assert serialized["obfuscated_value"] == data["obfuscated_value"] + assert serialized["last_used_at"] == data["last_used_at"] + assert serialized["permissions"] == data["permissions"] + assert serialized["created_at"] == data["created_at"] + assert serialized["updated_at"] == data["updated_at"] + assert serialized["value"] == data["value"] + + def test_organization_api_key_with_value_preserves_nullable_fields(self): + data = { + "object": "api_key", + "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", + "owner": {"type": "organization", "id": "org_01EHZNVPK3SFK441A1RGBFSHRT"}, + "name": "Production API Key", + "obfuscated_value": "sk_...3456", + "last_used_at": None, + "permissions": ["posts:read", "posts:write"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "value": "sk_abcdefghijklmnop123456", + } + instance = OrganizationApiKeyWithValue.from_dict(data) + serialized = instance.to_dict() + assert serialized["last_used_at"] is None + + def test_organization_round_trip(self): + data = load_fixture("organization.json") + instance = Organization.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = Organization.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_organization_minimal_payload(self): + data = { + "object": "organization", + "id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY", + "name": "Acme Inc.", + "domains": [ + { + "object": "organization_domain", + "id": "org_domain_01EHZNVPK2QXHMVWCEDQEKY69A", + "organization_id": "org_01HE8GSH8FQPASKSY27THRKRBP", + "domain": "foo-corp.com", + "state": "pending", + "verification_prefix": "superapp-domain-verification-z3kjny", + "verification_token": "m5Oztg3jdK4NJLgs8uIlIprMw", + "verification_strategy": "dns", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", } ], "metadata": {"tier": "diamond"}, @@ -14379,13 +14505,28 @@ def test_user_organization_membership_minimal_payload(self): data = { "object": "organization_membership", "id": "om_01HXYZ123456789ABCDEFGHIJ", - "user_id": "user_01EHQTV6MWP9P1F4ZXGXMC8ABB", + "user_id": "user_01E4ZCR3C56J083X43JQXF3JK5", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", "status": "active", "directory_managed": False, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "role": {"slug": "admin"}, + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": True, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": {"timezone": "America/New_York"}, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, } instance = UserOrganizationMembership.from_dict(data) serialized = instance.to_dict() @@ -14398,6 +14539,7 @@ def test_user_organization_membership_minimal_payload(self): assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] assert serialized["role"] == data["role"] + assert serialized["user"] == data["user"] def test_user_organization_membership_omits_absent_optional_non_nullable_fields( self, @@ -14405,13 +14547,28 @@ def test_user_organization_membership_omits_absent_optional_non_nullable_fields( data = { "object": "organization_membership", "id": "om_01HXYZ123456789ABCDEFGHIJ", - "user_id": "user_01EHQTV6MWP9P1F4ZXGXMC8ABB", + "user_id": "user_01E4ZCR3C56J083X43JQXF3JK5", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", "status": "active", "directory_managed": False, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "role": {"slug": "admin"}, + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": True, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": {"timezone": "America/New_York"}, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, } instance = UserOrganizationMembership.from_dict(data) serialized = instance.to_dict() @@ -14422,7 +14579,7 @@ def test_user_organization_membership_round_trips_unknown_enum_values(self): data = { "object": "organization_membership", "id": "om_01HXYZ123456789ABCDEFGHIJ", - "user_id": "user_01EHQTV6MWP9P1F4ZXGXMC8ABB", + "user_id": "user_01E4ZCR3C56J083X43JQXF3JK5", "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", "status": "unexpected_user_organization_membership_status", "directory_managed": False, @@ -14435,10 +14592,140 @@ def test_user_organization_membership_round_trips_unknown_enum_values(self): "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "role": {"slug": "admin"}, + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": True, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": {"timezone": "America/New_York"}, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, } instance = UserOrganizationMembership.from_dict(data) assert instance.to_dict() == data + def test_user_api_key_round_trip(self): + data = load_fixture("user_api_key.json") + instance = UserApiKey.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = UserApiKey.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_user_api_key_minimal_payload(self): + data = { + "object": "api_key", + "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", + "owner": { + "type": "user", + "id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + }, + "name": "Production API Key", + "obfuscated_value": "sk_...3456", + "last_used_at": None, + "permissions": ["posts:read", "posts:write"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + } + instance = UserApiKey.from_dict(data) + serialized = instance.to_dict() + assert serialized["object"] == data["object"] + assert serialized["id"] == data["id"] + assert serialized["owner"] == data["owner"] + assert serialized["name"] == data["name"] + assert serialized["obfuscated_value"] == data["obfuscated_value"] + assert serialized["last_used_at"] == data["last_used_at"] + assert serialized["permissions"] == data["permissions"] + assert serialized["created_at"] == data["created_at"] + assert serialized["updated_at"] == data["updated_at"] + + def test_user_api_key_preserves_nullable_fields(self): + data = { + "object": "api_key", + "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", + "owner": { + "type": "user", + "id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + }, + "name": "Production API Key", + "obfuscated_value": "sk_...3456", + "last_used_at": None, + "permissions": ["posts:read", "posts:write"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + } + instance = UserApiKey.from_dict(data) + serialized = instance.to_dict() + assert serialized["last_used_at"] is None + + def test_user_api_key_with_value_round_trip(self): + data = load_fixture("user_api_key_with_value.json") + instance = UserApiKeyWithValue.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = UserApiKeyWithValue.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_user_api_key_with_value_minimal_payload(self): + data = { + "object": "api_key", + "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", + "owner": { + "type": "user", + "id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + }, + "name": "Production API Key", + "obfuscated_value": "sk_...3456", + "last_used_at": None, + "permissions": ["posts:read", "posts:write"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "value": "sk_abcdefghijklmnop123456", + } + instance = UserApiKeyWithValue.from_dict(data) + serialized = instance.to_dict() + assert serialized["object"] == data["object"] + assert serialized["id"] == data["id"] + assert serialized["owner"] == data["owner"] + assert serialized["name"] == data["name"] + assert serialized["obfuscated_value"] == data["obfuscated_value"] + assert serialized["last_used_at"] == data["last_used_at"] + assert serialized["permissions"] == data["permissions"] + assert serialized["created_at"] == data["created_at"] + assert serialized["updated_at"] == data["updated_at"] + assert serialized["value"] == data["value"] + + def test_user_api_key_with_value_preserves_nullable_fields(self): + data = { + "object": "api_key", + "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", + "owner": { + "type": "user", + "id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + }, + "name": "Production API Key", + "obfuscated_value": "sk_...3456", + "last_used_at": None, + "permissions": ["posts:read", "posts:write"], + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "value": "sk_abcdefghijklmnop123456", + } + instance = UserApiKeyWithValue.from_dict(data) + serialized = instance.to_dict() + assert serialized["last_used_at"] is None + def test_email_verification_round_trip(self): data = load_fixture("email_verification.json") instance = EmailVerification.from_dict(data) @@ -14855,6 +15142,7 @@ def test_profile_minimal_payload(self): "email": "todd@example.com", "first_name": None, "last_name": None, + "name": None, "raw_attributes": {"key": {}}, } instance = Profile.from_dict(data) @@ -14868,6 +15156,7 @@ def test_profile_minimal_payload(self): assert serialized["email"] == data["email"] assert serialized["first_name"] == data["first_name"] assert serialized["last_name"] == data["last_name"] + assert serialized["name"] == data["name"] assert serialized["raw_attributes"] == data["raw_attributes"] def test_profile_omits_absent_optional_non_nullable_fields(self): @@ -14881,6 +15170,7 @@ def test_profile_omits_absent_optional_non_nullable_fields(self): "email": "todd@example.com", "first_name": "Todd", "last_name": "Rundgren", + "name": "Todd Rundgren", "role": {"slug": "admin"}, "roles": [{"slug": "admin"}], "raw_attributes": {"key": {}}, @@ -14901,6 +15191,7 @@ def test_profile_preserves_nullable_fields(self): "email": "todd@example.com", "first_name": None, "last_name": None, + "name": None, "role": None, "roles": None, "groups": ["Engineering", "Admins"], @@ -14912,6 +15203,7 @@ def test_profile_preserves_nullable_fields(self): assert serialized["organization_id"] is None assert serialized["first_name"] is None assert serialized["last_name"] is None + assert serialized["name"] is None assert serialized["role"] is None assert serialized["roles"] is None @@ -14926,6 +15218,7 @@ def test_profile_round_trips_unknown_enum_values(self): "email": "todd@example.com", "first_name": "Todd", "last_name": "Rundgren", + "name": "Todd Rundgren", "role": {"slug": "admin"}, "roles": [{"slug": "admin"}], "groups": ["Engineering", "Admins"], @@ -14958,6 +15251,7 @@ def test_sso_token_response_minimal_payload(self): "email": "todd@example.com", "first_name": "Todd", "last_name": "Rundgren", + "name": "Todd Rundgren", "role": {"slug": "admin"}, "roles": [{"slug": "admin"}], "groups": ["Engineering", "Admins"], @@ -14987,6 +15281,7 @@ def test_sso_token_response_omits_absent_optional_non_nullable_fields(self): "email": "todd@example.com", "first_name": "Todd", "last_name": "Rundgren", + "name": "Todd Rundgren", "role": {"slug": "admin"}, "roles": [{"slug": "admin"}], "groups": ["Engineering", "Admins"], @@ -15043,6 +15338,28 @@ def test_jwks_response_minimal_payload(self): serialized = instance.to_dict() assert serialized["keys"] == data["keys"] + def test_jwt_template_response_round_trip(self): + data = load_fixture("jwt_template_response.json") + instance = JWTTemplateResponse.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = JWTTemplateResponse.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_jwt_template_response_minimal_payload(self): + data = { + "object": "jwt_template", + "content": '{"urn:myapp:full_name": "{{user.first_name}} {{user.last_name}}", "urn:myapp:email": "{{user.email}}"}', + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + } + instance = JWTTemplateResponse.from_dict(data) + serialized = instance.to_dict() + assert serialized["object"] == data["object"] + assert serialized["content"] == data["content"] + assert serialized["created_at"] == data["created_at"] + assert serialized["updated_at"] == data["updated_at"] + def test_jwks_response_keys_round_trip(self): data = load_fixture("jwks_response_keys.json") instance = JwksResponseKeys.from_dict(data) @@ -15142,6 +15459,46 @@ def test_authenticate_response_oauth_token_minimal_payload(self): assert serialized["expires_at"] == data["expires_at"] assert serialized["scopes"] == data["scopes"] + def test_user_api_key_with_value_owner_round_trip(self): + data = load_fixture("user_api_key_with_value_owner.json") + instance = UserApiKeyWithValueOwner.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = UserApiKeyWithValueOwner.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_user_api_key_with_value_owner_minimal_payload(self): + data = { + "type": "user", + "id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + } + instance = UserApiKeyWithValueOwner.from_dict(data) + serialized = instance.to_dict() + assert serialized["type"] == data["type"] + assert serialized["id"] == data["id"] + assert serialized["organization_id"] == data["organization_id"] + + def test_user_api_key_owner_round_trip(self): + data = load_fixture("user_api_key_owner.json") + instance = UserApiKeyOwner.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = UserApiKeyOwner.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_user_api_key_owner_minimal_payload(self): + data = { + "type": "user", + "id": "user_01EHZNVPK3SFK441A1RGBFSHRT", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + } + instance = UserApiKeyOwner.from_dict(data) + serialized = instance.to_dict() + assert serialized["type"] == data["type"] + assert serialized["id"] == data["id"] + assert serialized["organization_id"] == data["organization_id"] + def test_data_integrations_list_response_data_round_trip(self): data = load_fixture("data_integrations_list_response_data.json") instance = DataIntegrationsListResponseData.from_dict(data) @@ -15372,17 +15729,32 @@ def test_organization_domain_round_trips_unknown_enum_values(self): instance = OrganizationDomain.from_dict(data) assert instance.to_dict() == data - def test_api_key_with_value_owner_round_trip(self): - data = load_fixture("api_key_with_value_owner.json") - instance = ApiKeyWithValueOwner.from_dict(data) + def test_organization_api_key_with_value_owner_round_trip(self): + data = load_fixture("organization_api_key_with_value_owner.json") + instance = OrganizationApiKeyWithValueOwner.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = OrganizationApiKeyWithValueOwner.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_organization_api_key_with_value_owner_minimal_payload(self): + data = {"type": "organization", "id": "org_01EHZNVPK3SFK441A1RGBFSHRT"} + instance = OrganizationApiKeyWithValueOwner.from_dict(data) + serialized = instance.to_dict() + assert serialized["type"] == data["type"] + assert serialized["id"] == data["id"] + + def test_organization_api_key_owner_round_trip(self): + data = load_fixture("organization_api_key_owner.json") + instance = OrganizationApiKeyOwner.from_dict(data) serialized = instance.to_dict() assert serialized == data - restored = ApiKeyWithValueOwner.from_dict(serialized) + restored = OrganizationApiKeyOwner.from_dict(serialized) assert restored.to_dict() == serialized - def test_api_key_with_value_owner_minimal_payload(self): + def test_organization_api_key_owner_minimal_payload(self): data = {"type": "organization", "id": "org_01EHZNVPK3SFK441A1RGBFSHRT"} - instance = ApiKeyWithValueOwner.from_dict(data) + instance = OrganizationApiKeyOwner.from_dict(data) serialized = instance.to_dict() assert serialized["type"] == data["type"] assert serialized["id"] == data["id"] @@ -15479,6 +15851,122 @@ def test_event_context_google_analytics_session_omits_absent_optional_non_nullab assert "sessionId" not in serialized assert "sessionNumber" not in serialized + def test_user_organization_membership_base_list_data_round_trip(self): + data = load_fixture("user_organization_membership_base_list_data.json") + instance = UserOrganizationMembershipBaseListData.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = UserOrganizationMembershipBaseListData.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_user_organization_membership_base_list_data_minimal_payload(self): + data = { + "object": "organization_membership", + "id": "om_01HXYZ123456789ABCDEFGHIJ", + "user_id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "status": "active", + "directory_managed": False, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": True, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": {"timezone": "America/New_York"}, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, + } + instance = UserOrganizationMembershipBaseListData.from_dict(data) + serialized = instance.to_dict() + assert serialized["object"] == data["object"] + assert serialized["id"] == data["id"] + assert serialized["user_id"] == data["user_id"] + assert serialized["organization_id"] == data["organization_id"] + assert serialized["status"] == data["status"] + assert serialized["directory_managed"] == data["directory_managed"] + assert serialized["created_at"] == data["created_at"] + assert serialized["updated_at"] == data["updated_at"] + assert serialized["user"] == data["user"] + + def test_user_organization_membership_base_list_data_omits_absent_optional_non_nullable_fields( + self, + ): + data = { + "object": "organization_membership", + "id": "om_01HXYZ123456789ABCDEFGHIJ", + "user_id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "status": "active", + "directory_managed": False, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": True, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": {"timezone": "America/New_York"}, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, + } + instance = UserOrganizationMembershipBaseListData.from_dict(data) + serialized = instance.to_dict() + assert "organization_name" not in serialized + assert "custom_attributes" not in serialized + + def test_user_organization_membership_base_list_data_round_trips_unknown_enum_values( + self, + ): + data = { + "object": "organization_membership", + "id": "om_01HXYZ123456789ABCDEFGHIJ", + "user_id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", + "status": "unexpected_user_organization_membership_base_list_data_status", + "directory_managed": False, + "organization_name": "Acme Corp", + "custom_attributes": { + "department": "Engineering", + "title": "Developer Experience Engineer", + "location": "Brooklyn", + }, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": True, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": {"timezone": "America/New_York"}, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, + } + instance = UserOrganizationMembershipBaseListData.from_dict(data) + assert instance.to_dict() == data + def test_directory_user_with_groups_email_round_trip(self): data = load_fixture("directory_user_with_groups_email.json") instance = DirectoryUserWithGroupsEmail.from_dict(data) @@ -15562,76 +16050,6 @@ def test_connection_option_preserves_nullable_fields(self): serialized = instance.to_dict() assert serialized["signing_cert"] is None - def test_user_organization_membership_base_list_data_round_trip(self): - data = load_fixture("user_organization_membership_base_list_data.json") - instance = UserOrganizationMembershipBaseListData.from_dict(data) - serialized = instance.to_dict() - assert serialized == data - restored = UserOrganizationMembershipBaseListData.from_dict(serialized) - assert restored.to_dict() == serialized - - def test_user_organization_membership_base_list_data_minimal_payload(self): - data = { - "object": "organization_membership", - "id": "om_01HXYZ123456789ABCDEFGHIJ", - "user_id": "user_01EHQTV6MWP9P1F4ZXGXMC8ABB", - "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", - "status": "active", - "directory_managed": False, - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z", - } - instance = UserOrganizationMembershipBaseListData.from_dict(data) - serialized = instance.to_dict() - assert serialized["object"] == data["object"] - assert serialized["id"] == data["id"] - assert serialized["user_id"] == data["user_id"] - assert serialized["organization_id"] == data["organization_id"] - assert serialized["status"] == data["status"] - assert serialized["directory_managed"] == data["directory_managed"] - assert serialized["created_at"] == data["created_at"] - assert serialized["updated_at"] == data["updated_at"] - - def test_user_organization_membership_base_list_data_omits_absent_optional_non_nullable_fields( - self, - ): - data = { - "object": "organization_membership", - "id": "om_01HXYZ123456789ABCDEFGHIJ", - "user_id": "user_01EHQTV6MWP9P1F4ZXGXMC8ABB", - "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", - "status": "active", - "directory_managed": False, - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z", - } - instance = UserOrganizationMembershipBaseListData.from_dict(data) - serialized = instance.to_dict() - assert "organization_name" not in serialized - assert "custom_attributes" not in serialized - - def test_user_organization_membership_base_list_data_round_trips_unknown_enum_values( - self, - ): - data = { - "object": "organization_membership", - "id": "om_01HXYZ123456789ABCDEFGHIJ", - "user_id": "user_01EHQTV6MWP9P1F4ZXGXMC8ABB", - "organization_id": "org_01EHZNVPK3SFK441A1RGBFSHRT", - "status": "unexpected_user_organization_membership_base_list_data_status", - "directory_managed": False, - "organization_name": "Acme Corp", - "custom_attributes": { - "department": "Engineering", - "title": "Developer Experience Engineer", - "location": "Brooklyn", - }, - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z", - } - instance = UserOrganizationMembershipBaseListData.from_dict(data) - assert instance.to_dict() == data - def test_role_assignment_resource_round_trip(self): data = load_fixture("role_assignment_resource.json") instance = RoleAssignmentResource.from_dict(data) @@ -16123,6 +16541,21 @@ def test_organization_membership_minimal_payload(self): "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "role": {"slug": "admin"}, + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": True, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": {"timezone": "America/New_York"}, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, } instance = OrganizationMembership.from_dict(data) serialized = instance.to_dict() @@ -16135,6 +16568,7 @@ def test_organization_membership_minimal_payload(self): assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] assert serialized["role"] == data["role"] + assert serialized["user"] == data["user"] def test_organization_membership_omits_absent_optional_non_nullable_fields(self): data = { @@ -16147,6 +16581,21 @@ def test_organization_membership_omits_absent_optional_non_nullable_fields(self) "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "role": {"slug": "admin"}, + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": True, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": {"timezone": "America/New_York"}, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, } instance = OrganizationMembership.from_dict(data) serialized = instance.to_dict() @@ -16170,6 +16619,21 @@ def test_organization_membership_round_trips_unknown_enum_values(self): "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "role": {"slug": "admin"}, + "user": { + "object": "user", + "id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "first_name": "Marcelina", + "last_name": "Davis", + "profile_picture_url": "https://workoscdn.com/images/v1/123abc", + "email": "marcelina.davis@example.com", + "email_verified": True, + "external_id": "f1ffa2b2-c20b-4d39-be5c-212726e11222", + "metadata": {"timezone": "America/New_York"}, + "last_sign_in_at": "2025-06-25T19:07:33.155Z", + "locale": "en-US", + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, } instance = OrganizationMembership.from_dict(data) assert instance.to_dict() == data diff --git a/tests/test_user_management.py b/tests/test_user_management.py index beb16bb5..660fa8c6 100644 --- a/tests/test_user_management.py +++ b/tests/test_user_management.py @@ -24,11 +24,14 @@ ResetPasswordResponse, SendVerificationEmailResponse, User, + UserApiKey, + UserApiKeyWithValue, UserIdentitiesGetItem, UserInvite, UserOrganizationMembership, UserSessionsListItem, VerifyEmailResponse, + ApiKeysOrder, UserManagementInvitationsOrder, UserManagementOrganizationMembershipOrder, UserManagementUsersAuthorizedApplicationsOrder, @@ -480,6 +483,18 @@ def test_revoke_invitation(self, workos, httpx_mock): assert request.method == "POST" assert request.url.path.endswith("/user_management/invitations/test_id/revoke") + def test_list_jwt_template(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("jwt_template_response.json"), + ) + result = workos.user_management.list_jwt_template() + assert isinstance(result, JWTTemplateResponse) + assert result.object == "jwt_template" + assert result.created_at == "2026-01-15T12:00:00.000Z" + request = httpx_mock.get_request() + assert request.method == "GET" + assert request.url.path.endswith("/user_management/jwt_template") + def test_update_jwt_template(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("jwt_template_response.json"), @@ -702,6 +717,55 @@ def test_delete_user_authorized_application(self, workos, httpx_mock): "/user_management/users/test_user_id/authorized_applications/test_application_id" ) + def test_list_user_api_keys(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("list_user_api_key.json"), + ) + page = workos.user_management.list_user_api_keys("test_userId") + assert isinstance(page, SyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], UserApiKey) + + def test_list_user_api_keys_empty_page(self, workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = workos.user_management.list_user_api_keys("test_userId") + assert isinstance(page, SyncPage) + assert page.data == [] + + def test_list_user_api_keys_encodes_query_params(self, workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + workos.user_management.list_user_api_keys( + "test_userId", + limit=10, + before="cursor before", + after="cursor/after", + order=ApiKeysOrder("normal"), + organization_id="value organization_id/test", + ) + request = httpx_mock.get_request() + assert request.url.params["limit"] == "10" + assert request.url.params["before"] == "cursor before" + assert request.url.params["after"] == "cursor/after" + assert request.url.params["order"] == "normal" + assert request.url.params["organization_id"] == "value organization_id/test" + + def test_create_user_api_key(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("user_api_key_with_value.json"), + ) + result = workos.user_management.create_user_api_key( + "test_userId", name="test_name", organization_id="test_organization_id" + ) + assert isinstance(result, UserApiKeyWithValue) + assert result.object == "api_key" + assert result.id == "api_key_01EHZNVPK3SFK441A1RGBFSHRT" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/user_management/users/test_userId/api_keys") + body = json.loads(request.content) + assert body["name"] == "test_name" + assert body["organization_id"] == "test_organization_id" + def test_authenticate_with_password(self, workos, httpx_mock): httpx_mock.add_response(json=load_fixture("authenticate_response.json")) result = workos.user_management.authenticate_with_password( @@ -1285,6 +1349,17 @@ async def test_revoke_invitation(self, async_workos, httpx_mock): assert request.method == "POST" assert request.url.path.endswith("/user_management/invitations/test_id/revoke") + @pytest.mark.asyncio + async def test_list_jwt_template(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("jwt_template_response.json")) + result = await async_workos.user_management.list_jwt_template() + assert isinstance(result, JWTTemplateResponse) + assert result.object == "jwt_template" + assert result.created_at == "2026-01-15T12:00:00.000Z" + request = httpx_mock.get_request() + assert request.method == "GET" + assert request.url.path.endswith("/user_management/jwt_template") + @pytest.mark.asyncio async def test_update_jwt_template(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("jwt_template_response.json")) @@ -1517,6 +1592,54 @@ async def test_delete_user_authorized_application(self, async_workos, httpx_mock "/user_management/users/test_user_id/authorized_applications/test_application_id" ) + @pytest.mark.asyncio + async def test_list_user_api_keys(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("list_user_api_key.json")) + page = await async_workos.user_management.list_user_api_keys("test_userId") + assert isinstance(page, AsyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], UserApiKey) + + @pytest.mark.asyncio + async def test_list_user_api_keys_empty_page(self, async_workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = await async_workos.user_management.list_user_api_keys("test_userId") + assert isinstance(page, AsyncPage) + assert page.data == [] + + @pytest.mark.asyncio + async def test_list_user_api_keys_encodes_query_params( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + await async_workos.user_management.list_user_api_keys( + "test_userId", + limit=10, + before="cursor before", + after="cursor/after", + order=ApiKeysOrder("normal"), + organization_id="value organization_id/test", + ) + request = httpx_mock.get_request() + assert request.url.params["limit"] == "10" + assert request.url.params["before"] == "cursor before" + assert request.url.params["after"] == "cursor/after" + assert request.url.params["order"] == "normal" + assert request.url.params["organization_id"] == "value organization_id/test" + + @pytest.mark.asyncio + async def test_create_user_api_key(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("user_api_key_with_value.json")) + result = await async_workos.user_management.create_user_api_key( + "test_userId", name="test_name", organization_id="test_organization_id" + ) + assert isinstance(result, UserApiKeyWithValue) + assert result.object == "api_key" + assert result.id == "api_key_01EHZNVPK3SFK441A1RGBFSHRT" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/user_management/users/test_userId/api_keys") + @pytest.mark.asyncio async def test_authenticate_with_password(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("authenticate_response.json"))