diff --git a/.last-synced-sha b/.last-synced-sha index f72eb0e2..047fc7ee 100644 --- a/.last-synced-sha +++ b/.last-synced-sha @@ -1 +1 @@ -92db0495807c86fbbc4d45bd266a6c1f5bcbb59c +a10d9ecb766d2dd996aecb19aa9c801d78bb7c26 diff --git a/.oagen-manifest.json b/.oagen-manifest.json index 8f408c22..0dd0fe80 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-06T01:54:21.659Z", "files": [ "src/workos/_client.py", "src/workos/admin_portal/__init__.py", @@ -18,10 +18,12 @@ "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/organizations_api_keys_order.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/user_api_key_owner.py", "src/workos/api_keys/models/validate_api_key.py", "src/workos/audit_logs/__init__.py", "src/workos/audit_logs/_resource.py", @@ -41,7 +43,6 @@ "src/workos/audit_logs/models/audit_log_schema_json_actor.py", "src/workos/audit_logs/models/audit_log_schema_json_target.py", "src/workos/audit_logs/models/audit_log_schema_target.py", - "src/workos/audit_logs/models/audit_logs_order.py", "src/workos/authorization/__init__.py", "src/workos/authorization/_resource.py", "src/workos/authorization/models/__init__.py", @@ -49,7 +50,6 @@ "src/workos/authorization/models/assign_role.py", "src/workos/authorization/models/authorization_assignment.py", "src/workos/authorization/models/authorization_check.py", - "src/workos/authorization/models/authorization_order.py", "src/workos/authorization/models/authorization_permission.py", "src/workos/authorization/models/authorization_resource.py", "src/workos/authorization/models/check_authorization.py", @@ -57,12 +57,10 @@ "src/workos/authorization/models/create_authorization_resource.py", "src/workos/authorization/models/create_organization_role.py", "src/workos/authorization/models/create_role.py", + "src/workos/authorization/models/pagination_order.py", "src/workos/authorization/models/permission.py", - "src/workos/authorization/models/permissions_order.py", "src/workos/authorization/models/remove_role.py", "src/workos/authorization/models/role.py", - "src/workos/authorization/models/role_assignment.py", - "src/workos/authorization/models/role_assignment_resource.py", "src/workos/authorization/models/role_list.py", "src/workos/authorization/models/set_role_permissions.py", "src/workos/authorization/models/slim_role.py", @@ -71,6 +69,8 @@ "src/workos/authorization/models/update_organization_role.py", "src/workos/authorization/models/update_role.py", "src/workos/authorization/models/user_organization_membership_base_list_data.py", + "src/workos/authorization/models/user_role_assignment.py", + "src/workos/authorization/models/user_role_assignment_resource.py", "src/workos/common/__init__.py", "src/workos/common/models/__init__.py", "src/workos/common/models/action_authentication_denied.py", @@ -369,6 +369,8 @@ "src/workos/common/models/update_user_password_hash_type.py", "src/workos/common/models/update_webhook_endpoint_events.py", "src/workos/common/models/update_webhook_endpoint_status.py", + "src/workos/common/models/user_api_key_created_data_owner.py", + "src/workos/common/models/user_api_key_revoked_data_owner.py", "src/workos/common/models/user_created.py", "src/workos/common/models/user_deleted.py", "src/workos/common/models/user_identities_get_item_provider.py", @@ -378,9 +380,11 @@ "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_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", "src/workos/common/models/vault_data_created.py", "src/workos/common/models/vault_data_created_data.py", "src/workos/common/models/vault_data_created_data_actor_source.py", @@ -419,7 +423,6 @@ "src/workos/connect/_resource.py", "src/workos/connect/models/__init__.py", "src/workos/connect/models/application_credentials_list_item.py", - "src/workos/connect/models/applications_order.py", "src/workos/connect/models/connect_application.py", "src/workos/connect/models/create_application_secret.py", "src/workos/connect/models/create_m2m_application.py", @@ -435,38 +438,30 @@ "src/workos/directory_sync/__init__.py", "src/workos/directory_sync/_resource.py", "src/workos/directory_sync/models/__init__.py", - "src/workos/directory_sync/models/directories_order.py", "src/workos/directory_sync/models/directory.py", "src/workos/directory_sync/models/directory_group.py", - "src/workos/directory_sync/models/directory_groups_order.py", "src/workos/directory_sync/models/directory_metadata.py", "src/workos/directory_sync/models/directory_metadata_user.py", "src/workos/directory_sync/models/directory_user_with_groups.py", "src/workos/directory_sync/models/directory_user_with_groups_email.py", - "src/workos/directory_sync/models/directory_users_order.py", "src/workos/events/__init__.py", "src/workos/events/_resource.py", "src/workos/events/models/__init__.py", "src/workos/events/models/event_list_list_metadata.py", "src/workos/events/models/event_schema.py", - "src/workos/events/models/events_order.py", "src/workos/feature_flags/__init__.py", "src/workos/feature_flags/_resource.py", "src/workos/feature_flags/models/__init__.py", "src/workos/feature_flags/models/feature_flag.py", "src/workos/feature_flags/models/feature_flag_owner.py", - "src/workos/feature_flags/models/feature_flags_order.py", "src/workos/feature_flags/models/flag.py", "src/workos/feature_flags/models/flag_owner.py", - "src/workos/feature_flags/models/organizations_feature_flags_order.py", - "src/workos/feature_flags/models/user_management_users_feature_flags_order.py", "src/workos/groups/__init__.py", "src/workos/groups/_resource.py", "src/workos/groups/models/__init__.py", "src/workos/groups/models/create_group.py", "src/workos/groups/models/create_group_membership.py", "src/workos/groups/models/group.py", - "src/workos/groups/models/groups_order.py", "src/workos/groups/models/update_group.py", "src/workos/multi_factor_auth/__init__.py", "src/workos/multi_factor_auth/_resource.py", @@ -484,7 +479,6 @@ "src/workos/multi_factor_auth/models/challenge_authentication_factor.py", "src/workos/multi_factor_auth/models/enroll_user_authentication_factor.py", "src/workos/multi_factor_auth/models/user_authentication_factor_enroll_response.py", - "src/workos/multi_factor_auth/models/user_management_multi_factor_authentication_order.py", "src/workos/organization_domains/__init__.py", "src/workos/organization_domains/_resource.py", "src/workos/organization_domains/models/__init__.py", @@ -500,7 +494,6 @@ "src/workos/organizations/models/organization.py", "src/workos/organizations/models/organization_domain_data.py", "src/workos/organizations/models/organization_input.py", - "src/workos/organizations/models/organizations_order.py", "src/workos/organizations/models/update_audit_logs_retention.py", "src/workos/organizations/models/update_organization.py", "src/workos/pipes/__init__.py", @@ -533,7 +526,6 @@ "src/workos/sso/models/connection_domain.py", "src/workos/sso/models/connection_option.py", "src/workos/sso/models/connections_connection_type.py", - "src/workos/sso/models/connections_order.py", "src/workos/sso/models/profile.py", "src/workos/sso/models/sso_authorize_url_response.py", "src/workos/sso/models/sso_logout_authorize_request.py", @@ -578,6 +570,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,15 +603,14 @@ "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_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", "src/workos/user_management/models/user_management_authentication_screen_hint.py", - "src/workos/user_management/models/user_management_invitations_order.py", - "src/workos/user_management/models/user_management_organization_membership_order.py", "src/workos/user_management/models/user_management_organization_membership_statuses.py", - "src/workos/user_management/models/user_management_users_authorized_applications_order.py", - "src/workos/user_management/models/user_management_users_order.py", "src/workos/user_management/models/user_organization_membership.py", "src/workos/user_management/models/user_sessions_impersonator.py", "src/workos/user_management/models/user_sessions_list_item.py", @@ -627,14 +619,12 @@ "src/workos/user_management_organization_membership_groups/__init__.py", "src/workos/user_management_organization_membership_groups/_resource.py", "src/workos/user_management_organization_membership_groups/models/__init__.py", - "src/workos/user_management_organization_membership_groups/models/user_management_organization_membership_groups_order.py", "src/workos/webhooks/__init__.py", "src/workos/webhooks/_resource.py", "src/workos/webhooks/models/__init__.py", "src/workos/webhooks/models/create_webhook_endpoint.py", "src/workos/webhooks/models/update_webhook_endpoint.py", "src/workos/webhooks/models/webhook_endpoint_json.py", - "src/workos/webhooks/models/webhooks_order.py", "src/workos/widgets/__init__.py", "src/workos/widgets/_resource.py", "src/workos/widgets/models/__init__.py", @@ -654,8 +644,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 +769,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 +884,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,11 +899,13 @@ "tests/fixtures/list_flag.json", "tests/fixtures/list_group.json", "tests/fixtures/list_organization.json", - "tests/fixtures/list_role_assignment.json", + "tests/fixtures/list_organization_api_key.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", + "tests/fixtures/list_user_role_assignment.json", "tests/fixtures/list_user_sessions_list_item.json", "tests/fixtures/list_webhook_endpoint_json.json", "tests/fixtures/magic_auth.json", @@ -925,6 +915,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", @@ -992,8 +986,6 @@ "tests/fixtures/reset_password_response.json", "tests/fixtures/revoke_session.json", "tests/fixtures/role.json", - "tests/fixtures/role_assignment.json", - "tests/fixtures/role_assignment_resource.json", "tests/fixtures/role_created.json", "tests/fixtures/role_created_data.json", "tests/fixtures/role_deleted.json", @@ -1032,6 +1024,12 @@ "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_created_data_owner.json", + "tests/fixtures/user_api_key_owner.json", + "tests/fixtures/user_api_key_revoked_data_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", @@ -1043,10 +1041,14 @@ "tests/fixtures/user_object.json", "tests/fixtures/user_organization_membership.json", "tests/fixtures/user_organization_membership_base_list_data.json", + "tests/fixtures/user_role_assignment.json", + "tests/fixtures/user_role_assignment_resource.json", "tests/fixtures/user_sessions_impersonator.json", "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 +1100,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" @@ -1210,6 +1204,10 @@ "sdkMethod": "list_memberships_for_resource_by_external_id", "service": "authorization" }, + "GET /authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}/role_assignments": { + "sdkMethod": "list_role_assignments_for_resource_by_external_id", + "service": "authorization" + }, "GET /authorization/resources": { "sdkMethod": "list_resources", "service": "authorization" @@ -1234,6 +1232,10 @@ "sdkMethod": "list_memberships_for_resource", "service": "authorization" }, + "GET /authorization/resources/{resource_id}/role_assignments": { + "sdkMethod": "list_role_assignments_for_resource", + "service": "authorization" + }, "GET /authorization/roles": { "sdkMethod": "list_environment_roles", "service": "authorization" @@ -1642,6 +1644,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 +1748,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..de42677a 100644 --- a/src/workos/api_keys/_resource.py +++ b/src/workos/api_keys/_resource.py @@ -3,13 +3,18 @@ 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 OrganizationsApiKeysOrder +from .models import ( + ApiKeyValidationResponse, + OrganizationApiKey, + OrganizationApiKeyWithValue, +) +from workos.authorization.models.pagination_order import PaginationOrder 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, @@ -86,9 +31,9 @@ def list_organization_api_keys( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[OrganizationsApiKeysOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, 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. @@ -98,11 +43,11 @@ def list_organization_api_keys( 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`. + order: Order the results by the creation time. 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, @@ -246,9 +191,9 @@ async def list_organization_api_keys( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[OrganizationsApiKeysOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, 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. @@ -258,11 +203,11 @@ async def list_organization_api_keys( 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`. + order: Order the results by the creation time. 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..13298c77 100644 --- a/src/workos/api_keys/models/__init__.py +++ b/src/workos/api_keys/models/__init__.py @@ -5,12 +5,18 @@ 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 .organizations_api_keys_order import ( - OrganizationsApiKeysOrder as OrganizationsApiKeysOrder, +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 .user_api_key_owner import UserApiKeyOwner as UserApiKeyOwner from .validate_api_key import ValidateApiKey as ValidateApiKey diff --git a/src/workos/api_keys/models/api_key.py b/src/workos/api_keys/models/api_key.py index c59bbf72..f067c54c 100644 --- a/src/workos/api_keys/models/api_key.py +++ b/src/workos/api_keys/models/api_key.py @@ -5,22 +5,23 @@ from dataclasses import dataclass from datetime import datetime from typing import cast -from typing import Any, Dict, List, Literal, Optional +from typing import Any, Dict, List, Literal, Optional, Union from workos._types import _raise_deserialize_error from workos._types import _format_datetime, _parse_datetime from .api_key_owner import ApiKeyOwner +from .user_api_key_owner import UserApiKeyOwner @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.""" id: str """Unique identifier of the API Key.""" - owner: "ApiKeyOwner" + owner: Union["ApiKeyOwner", "UserApiKeyOwner"] """The entity that owns the API Key.""" name: str """A descriptive name for the API Key.""" @@ -42,7 +43,19 @@ def from_dict(cls, data: Dict[str, Any]) -> "ApiKey": return cls( object=data.get("object", "api_key"), id=data["id"], - owner=ApiKeyOwner.from_dict(cast(Dict[str, Any], data["owner"])), + owner=( + _disc.from_dict(cast(Dict[str, Any], data["owner"])) + if ( + _disc := { + "organization": ApiKeyOwner, + "user": UserApiKeyOwner, + }.get( + cast(str, cast(Dict[str, Any], data["owner"]).get("type")) + ) + ) + is not None + else data["owner"] + ), name=data["name"], obfuscated_value=data["obfuscated_value"], last_used_at=_parse_datetime(_v_last_used_at) @@ -60,7 +73,9 @@ def to_dict(self) -> Dict[str, Any]: result: Dict[str, Any] = {} result["object"] = self.object result["id"] = self.id - result["owner"] = self.owner.to_dict() + result["owner"] = ( + self.owner.to_dict() if hasattr(self.owner, "to_dict") else self.owner + ) result["name"] = self.name result["obfuscated_value"] = self.obfuscated_value if self.last_used_at is not None: 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 deleted file mode 100644 index 1ee9eb61..00000000 --- a/src/workos/api_keys/models/organizations_api_keys_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -OrganizationsApiKeysOrder: TypeAlias = ApplicationsOrder -__all__ = ["OrganizationsApiKeysOrder"] diff --git a/src/workos/api_keys/models/user_api_key_owner.py b/src/workos/api_keys/models/user_api_key_owner.py new file mode 100644 index 00000000..9b7b4af6 --- /dev/null +++ b/src/workos/api_keys/models/user_api_key_owner.py @@ -0,0 +1,8 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias +from workos.common.models.user_api_key_created_data_owner import ( + UserApiKeyCreatedDataOwner, +) + +UserApiKeyOwner: TypeAlias = UserApiKeyCreatedDataOwner diff --git a/src/workos/audit_logs/_resource.py b/src/workos/audit_logs/_resource.py index e70ccd18..0e82ea9f 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 @@ -18,7 +19,7 @@ AuditLogSchemaTarget, ) from workos.organizations.models.audit_logs_retention_json import AuditLogsRetentionJson -from .models import AuditLogsOrder +from workos.authorization.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage @@ -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, @@ -101,7 +102,7 @@ def list_actions( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuditLogsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[AuditLogActionJson]: """List Actions @@ -112,7 +113,7 @@ def list_actions( 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`. + order: Order the results by the creation time. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -150,7 +151,7 @@ def list_action_schemas( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuditLogsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[AuditLogSchemaJson]: """List Schemas @@ -162,7 +163,7 @@ def list_action_schemas( 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`. + order: Order the results by the creation time. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -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, @@ -456,7 +457,7 @@ async def list_actions( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuditLogsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[AuditLogActionJson]: """List Actions @@ -467,7 +468,7 @@ async def list_actions( 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`. + order: Order the results by the creation time. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -505,7 +506,7 @@ async def list_action_schemas( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuditLogsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[AuditLogSchemaJson]: """List Schemas @@ -517,7 +518,7 @@ async def list_action_schemas( 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`. + order: Order the results by the creation time. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -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/__init__.py b/src/workos/audit_logs/models/__init__.py index 77cfcb41..81483482 100644 --- a/src/workos/audit_logs/models/__init__.py +++ b/src/workos/audit_logs/models/__init__.py @@ -21,4 +21,3 @@ AuditLogSchemaJsonTarget as AuditLogSchemaJsonTarget, ) from .audit_log_schema_target import AuditLogSchemaTarget as AuditLogSchemaTarget -from .audit_logs_order import AuditLogsOrder as AuditLogsOrder diff --git a/src/workos/audit_logs/models/audit_logs_order.py b/src/workos/audit_logs/models/audit_logs_order.py deleted file mode 100644 index e1193053..00000000 --- a/src/workos/audit_logs/models/audit_logs_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -AuditLogsOrder: TypeAlias = ApplicationsOrder -__all__ = ["AuditLogsOrder"] diff --git a/src/workos/authorization/_resource.py b/src/workos/authorization/_resource.py index 55a229c8..c9ac680e 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 @@ -14,11 +15,11 @@ AuthorizationResource, Permission, Role, - RoleAssignment, RoleList, UserOrganizationMembershipBaseListData, + UserRoleAssignment, ) -from .models import AuthorizationAssignment, AuthorizationOrder, PermissionsOrder +from .models import AuthorizationAssignment, PaginationOrder from .._pagination import AsyncPage, SyncPage from dataclasses import dataclass @@ -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, @@ -126,7 +127,7 @@ def list_resources_for_membership( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", permission_slug: str, parent_resource: Union[ParentResourceById, ParentResourceByExternalId], request_options: Optional[RequestOptions] = None, @@ -142,7 +143,7 @@ def list_resources_for_membership( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. permission_slug: The permission slug to filter by. Only child resources where the organization membership has this permission are returned. parent_resource: Identifies the parent resource. One of: ParentResourceById, ParentResourceByExternalId. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -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, @@ -195,7 +196,7 @@ def list_effective_permissions( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[AuthorizationPermission]: """List effective permissions for an organization membership on a resource @@ -208,7 +209,7 @@ def list_effective_permissions( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -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, @@ -249,7 +250,7 @@ def list_effective_permissions_by_external_id( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[AuthorizationPermission]: """List effective permissions for an organization membership on a resource by external ID @@ -263,7 +264,7 @@ def list_effective_permissions_by_external_id( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -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, @@ -302,9 +303,9 @@ def list_role_assignments( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, - ) -> SyncPage[RoleAssignment]: + ) -> SyncPage[UserRoleAssignment]: """List role assignments List all role assignments for an organization membership. This returns all roles that have been assigned to the user on resources, including organization-level and sub-resource roles. @@ -314,11 +315,11 @@ def list_role_assignments( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - SyncPage[RoleAssignment] + SyncPage[UserRoleAssignment] Raises: AuthorizationError: If the request is forbidden (403). @@ -339,8 +340,8 @@ def list_role_assignments( } return self._client.request_page( method="get", - path=f"authorization/organization_memberships/{organization_membership_id}/role_assignments", - model=RoleAssignment, + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/role_assignments", + model=UserRoleAssignment, params=params, request_options=request_options, ) @@ -352,7 +353,7 @@ def assign_role( role_slug: str, resource_target: Union[ResourceTargetById, ResourceTargetByExternalId], request_options: Optional[RequestOptions] = None, - ) -> RoleAssignment: + ) -> UserRoleAssignment: """Assign a role Assign a role to an organization membership on a specific resource. @@ -364,7 +365,7 @@ def assign_role( request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - RoleAssignment + UserRoleAssignment Raises: AuthorizationError: If the request is forbidden (403). @@ -384,9 +385,9 @@ 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, + model=UserRoleAssignment, 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, ) @@ -920,7 +921,7 @@ def list_memberships_for_resource_by_external_id( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", permission_slug: str, assignment: Optional[Union[AuthorizationAssignment, str]] = None, request_options: Optional[RequestOptions] = None, @@ -936,7 +937,7 @@ def list_memberships_for_resource_by_external_id( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. permission_slug: The permission slug to filter by. Only users with this permission on the resource are returned. assignment: Filter by assignment type. Use "direct" for direct assignments only, or "indirect" to include inherited assignments. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -969,19 +970,73 @@ 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, ) + def list_role_assignments_for_resource_by_external_id( + self, + organization_id: str, + resource_type_slug: str, + external_id: str, + *, + limit: Optional[int] = None, + before: Optional[str] = None, + after: Optional[str] = None, + order: Optional[Union[PaginationOrder, str]] = "desc", + request_options: Optional[RequestOptions] = None, + ) -> SyncPage[UserRoleAssignment]: + """List role assignments for a resource by external ID + + List all role assignments granted on a resource, identified by its external ID. Each assignment includes the organization membership it was granted to. + + Args: + organization_id: The ID of the organization that owns the resource. + resource_type_slug: The slug of the resource type. + external_id: An identifier you provide to reference the resource in your system. + 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. + 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + SyncPage[UserRoleAssignment] + + Raises: + AuthorizationError: If the request is forbidden (403). + 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, + }.items() + if v is not None + } + return self._client.request_page( + method="get", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/resources/{quote(str(resource_type_slug), safe='')}/{quote(str(external_id), safe='')}/role_assignments", + model=UserRoleAssignment, + params=params, + request_options=request_options, + ) + def list_resources( self, *, limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", organization_id: Optional[str] = None, resource_type_slug: Optional[str] = None, resource_external_id: Optional[str] = None, @@ -997,7 +1052,7 @@ def list_resources( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. organization_id: Filter resources by organization ID. resource_type_slug: Filter resources by resource type slug. resource_external_id: Filter resources by external ID. @@ -1138,7 +1193,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 +1253,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 +1293,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, ) @@ -1250,7 +1305,7 @@ def list_memberships_for_resource( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", permission_slug: str, assignment: Optional[Union[AuthorizationAssignment, str]] = None, request_options: Optional[RequestOptions] = None, @@ -1264,7 +1319,7 @@ def list_memberships_for_resource( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. permission_slug: The permission slug to filter by. Only users with this permission on the resource are returned. assignment: Filter by assignment type. Use `direct` for direct assignments only, or `indirect` to include inherited assignments. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -1297,12 +1352,62 @@ 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, ) + def list_role_assignments_for_resource( + self, + resource_id: str, + *, + limit: Optional[int] = None, + before: Optional[str] = None, + after: Optional[str] = None, + order: Optional[Union[PaginationOrder, str]] = "desc", + request_options: Optional[RequestOptions] = None, + ) -> SyncPage[UserRoleAssignment]: + """List role assignments for a resource + + List all role assignments granted on a specific resource instance. Each assignment includes the organization membership it was granted to. + + Args: + resource_id: The ID of the authorization resource. + 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. + 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + SyncPage[UserRoleAssignment] + + Raises: + AuthorizationError: If the request is forbidden (403). + 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, + }.items() + if v is not None + } + return self._client.request_page( + method="get", + path=f"authorization/resources/{quote(str(resource_id), safe='')}/role_assignments", + model=UserRoleAssignment, + params=params, + request_options=request_options, + ) + def list_environment_roles( self, *, @@ -1405,7 +1510,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 +1555,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 +1594,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 +1633,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, @@ -1540,7 +1645,7 @@ def list_permissions( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[PermissionsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[AuthorizationPermission]: """List permissions @@ -1551,7 +1656,7 @@ def list_permissions( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -1656,7 +1761,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 +1805,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 +1834,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 +1884,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, @@ -1792,7 +1897,7 @@ async def list_resources_for_membership( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", permission_slug: str, parent_resource: Union[ParentResourceById, ParentResourceByExternalId], request_options: Optional[RequestOptions] = None, @@ -1808,7 +1913,7 @@ async def list_resources_for_membership( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. permission_slug: The permission slug to filter by. Only child resources where the organization membership has this permission are returned. parent_resource: Identifies the parent resource. One of: ParentResourceById, ParentResourceByExternalId. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -1847,7 +1952,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, @@ -1861,7 +1966,7 @@ async def list_effective_permissions( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[AuthorizationPermission]: """List effective permissions for an organization membership on a resource @@ -1874,7 +1979,7 @@ async def list_effective_permissions( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -1900,7 +2005,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, @@ -1915,7 +2020,7 @@ async def list_effective_permissions_by_external_id( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[AuthorizationPermission]: """List effective permissions for an organization membership on a resource by external ID @@ -1929,7 +2034,7 @@ async def list_effective_permissions_by_external_id( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -1955,7 +2060,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, @@ -1968,9 +2073,9 @@ async def list_role_assignments( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, - ) -> AsyncPage[RoleAssignment]: + ) -> AsyncPage[UserRoleAssignment]: """List role assignments List all role assignments for an organization membership. This returns all roles that have been assigned to the user on resources, including organization-level and sub-resource roles. @@ -1980,11 +2085,11 @@ async def list_role_assignments( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - AsyncPage[RoleAssignment] + AsyncPage[UserRoleAssignment] Raises: AuthorizationError: If the request is forbidden (403). @@ -2005,8 +2110,8 @@ async def list_role_assignments( } return await self._client.request_page( method="get", - path=f"authorization/organization_memberships/{organization_membership_id}/role_assignments", - model=RoleAssignment, + path=f"authorization/organization_memberships/{quote(str(organization_membership_id), safe='')}/role_assignments", + model=UserRoleAssignment, params=params, request_options=request_options, ) @@ -2018,7 +2123,7 @@ async def assign_role( role_slug: str, resource_target: Union[ResourceTargetById, ResourceTargetByExternalId], request_options: Optional[RequestOptions] = None, - ) -> RoleAssignment: + ) -> UserRoleAssignment: """Assign a role Assign a role to an organization membership on a specific resource. @@ -2030,7 +2135,7 @@ async def assign_role( request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - RoleAssignment + UserRoleAssignment Raises: AuthorizationError: If the request is forbidden (403). @@ -2050,9 +2155,9 @@ 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, + model=UserRoleAssignment, request_options=request_options, ) @@ -2092,7 +2197,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 +2227,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 +2257,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 +2309,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 +2343,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 +2390,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 +2423,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 +2462,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 +2502,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 +2535,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 +2569,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 +2633,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 +2677,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, ) @@ -2586,7 +2691,7 @@ async def list_memberships_for_resource_by_external_id( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", permission_slug: str, assignment: Optional[Union[AuthorizationAssignment, str]] = None, request_options: Optional[RequestOptions] = None, @@ -2602,7 +2707,7 @@ async def list_memberships_for_resource_by_external_id( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. permission_slug: The permission slug to filter by. Only users with this permission on the resource are returned. assignment: Filter by assignment type. Use "direct" for direct assignments only, or "indirect" to include inherited assignments. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -2635,19 +2740,73 @@ 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, ) + async def list_role_assignments_for_resource_by_external_id( + self, + organization_id: str, + resource_type_slug: str, + external_id: str, + *, + limit: Optional[int] = None, + before: Optional[str] = None, + after: Optional[str] = None, + order: Optional[Union[PaginationOrder, str]] = "desc", + request_options: Optional[RequestOptions] = None, + ) -> AsyncPage[UserRoleAssignment]: + """List role assignments for a resource by external ID + + List all role assignments granted on a resource, identified by its external ID. Each assignment includes the organization membership it was granted to. + + Args: + organization_id: The ID of the organization that owns the resource. + resource_type_slug: The slug of the resource type. + external_id: An identifier you provide to reference the resource in your system. + 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. + 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + AsyncPage[UserRoleAssignment] + + Raises: + AuthorizationError: If the request is forbidden (403). + 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, + }.items() + if v is not None + } + return await self._client.request_page( + method="get", + path=f"authorization/organizations/{quote(str(organization_id), safe='')}/resources/{quote(str(resource_type_slug), safe='')}/{quote(str(external_id), safe='')}/role_assignments", + model=UserRoleAssignment, + params=params, + request_options=request_options, + ) + async def list_resources( self, *, limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", organization_id: Optional[str] = None, resource_type_slug: Optional[str] = None, resource_external_id: Optional[str] = None, @@ -2663,7 +2822,7 @@ async def list_resources( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. organization_id: Filter resources by organization ID. resource_type_slug: Filter resources by resource type slug. resource_external_id: Filter resources by external ID. @@ -2804,7 +2963,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 +3023,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 +3063,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, ) @@ -2916,7 +3075,7 @@ async def list_memberships_for_resource( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[AuthorizationOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", permission_slug: str, assignment: Optional[Union[AuthorizationAssignment, str]] = None, request_options: Optional[RequestOptions] = None, @@ -2930,7 +3089,7 @@ async def list_memberships_for_resource( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. permission_slug: The permission slug to filter by. Only users with this permission on the resource are returned. assignment: Filter by assignment type. Use `direct` for direct assignments only, or `indirect` to include inherited assignments. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -2963,12 +3122,62 @@ 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, ) + async def list_role_assignments_for_resource( + self, + resource_id: str, + *, + limit: Optional[int] = None, + before: Optional[str] = None, + after: Optional[str] = None, + order: Optional[Union[PaginationOrder, str]] = "desc", + request_options: Optional[RequestOptions] = None, + ) -> AsyncPage[UserRoleAssignment]: + """List role assignments for a resource + + List all role assignments granted on a specific resource instance. Each assignment includes the organization membership it was granted to. + + Args: + resource_id: The ID of the authorization resource. + 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. + 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + AsyncPage[UserRoleAssignment] + + Raises: + AuthorizationError: If the request is forbidden (403). + 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, + }.items() + if v is not None + } + return await self._client.request_page( + method="get", + path=f"authorization/resources/{quote(str(resource_id), safe='')}/role_assignments", + model=UserRoleAssignment, + params=params, + request_options=request_options, + ) + async def list_environment_roles( self, *, @@ -3071,7 +3280,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 +3325,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 +3364,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 +3403,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, @@ -3206,7 +3415,7 @@ async def list_permissions( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[PermissionsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[AuthorizationPermission]: """List permissions @@ -3217,7 +3426,7 @@ async def list_permissions( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -3322,7 +3531,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 +3575,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 +3604,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/__init__.py b/src/workos/authorization/models/__init__.py index 6e303bff..11e13736 100644 --- a/src/workos/authorization/models/__init__.py +++ b/src/workos/authorization/models/__init__.py @@ -4,7 +4,6 @@ from .assign_role import AssignRole as AssignRole from .authorization_assignment import AuthorizationAssignment as AuthorizationAssignment from .authorization_check import AuthorizationCheck as AuthorizationCheck -from .authorization_order import AuthorizationOrder as AuthorizationOrder from .authorization_permission import AuthorizationPermission as AuthorizationPermission from .authorization_resource import AuthorizationResource as AuthorizationResource from .check_authorization import CheckAuthorization as CheckAuthorization @@ -16,12 +15,10 @@ ) from .create_organization_role import CreateOrganizationRole as CreateOrganizationRole from .create_role import CreateRole as CreateRole +from .pagination_order import PaginationOrder as PaginationOrder from .permission import Permission as Permission -from .permissions_order import PermissionsOrder as PermissionsOrder from .remove_role import RemoveRole as RemoveRole from .role import Role as Role -from .role_assignment import RoleAssignment as RoleAssignment -from .role_assignment_resource import RoleAssignmentResource as RoleAssignmentResource from .role_list import RoleList as RoleList from .set_role_permissions import SetRolePermissions as SetRolePermissions from .slim_role import SlimRole as SlimRole @@ -36,3 +33,7 @@ from .user_organization_membership_base_list_data import ( UserOrganizationMembershipBaseListData as UserOrganizationMembershipBaseListData, ) +from .user_role_assignment import UserRoleAssignment as UserRoleAssignment +from .user_role_assignment_resource import ( + UserRoleAssignmentResource as UserRoleAssignmentResource, +) diff --git a/src/workos/authorization/models/authorization_order.py b/src/workos/authorization/models/authorization_order.py deleted file mode 100644 index 9920b5c9..00000000 --- a/src/workos/authorization/models/authorization_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -AuthorizationOrder: TypeAlias = ApplicationsOrder -__all__ = ["AuthorizationOrder"] diff --git a/src/workos/connect/models/applications_order.py b/src/workos/authorization/models/pagination_order.py similarity index 62% rename from src/workos/connect/models/applications_order.py rename to src/workos/authorization/models/pagination_order.py index 15ae48f3..29099d54 100644 --- a/src/workos/connect/models/applications_order.py +++ b/src/workos/authorization/models/pagination_order.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. -"""Enumeration of applications order values.""" +"""Enumeration of pagination order values.""" from __future__ import annotations @@ -9,15 +9,15 @@ from typing import Literal, TypeAlias -class ApplicationsOrder(str, Enum): - """Known values for ApplicationsOrder.""" +class PaginationOrder(str, Enum): + """Known values for PaginationOrder.""" NORMAL = "normal" DESC = "desc" ASC = "asc" @classmethod - def _missing_(cls, value: object) -> Optional["ApplicationsOrder"]: + def _missing_(cls, value: object) -> Optional["PaginationOrder"]: if not isinstance(value, str): return None unknown = str.__new__(cls, value) @@ -26,4 +26,4 @@ def _missing_(cls, value: object) -> Optional["ApplicationsOrder"]: return unknown -ApplicationsOrderLiteral: TypeAlias = Literal["normal", "desc", "asc"] +PaginationOrderLiteral: TypeAlias = Literal["normal", "desc", "asc"] diff --git a/src/workos/authorization/models/permissions_order.py b/src/workos/authorization/models/permissions_order.py deleted file mode 100644 index cc601f83..00000000 --- a/src/workos/authorization/models/permissions_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -PermissionsOrder: TypeAlias = ApplicationsOrder -__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/authorization/models/role_assignment.py b/src/workos/authorization/models/user_role_assignment.py similarity index 71% rename from src/workos/authorization/models/role_assignment.py rename to src/workos/authorization/models/user_role_assignment.py index 89ab276d..702b726e 100644 --- a/src/workos/authorization/models/role_assignment.py +++ b/src/workos/authorization/models/user_role_assignment.py @@ -9,49 +9,53 @@ from workos._types import _raise_deserialize_error from workos._types import _format_datetime, _parse_datetime -from .role_assignment_resource import RoleAssignmentResource from .slim_role import SlimRole +from .user_role_assignment_resource import UserRoleAssignmentResource @dataclass(slots=True) -class RoleAssignment: - """Role Assignment model.""" +class UserRoleAssignment: + """User Role Assignment model.""" object: Literal["role_assignment"] """Distinguishes the role assignment object.""" id: str """Unique identifier of the role assignment.""" + organization_membership_id: str + """The ID of the organization membership the role is assigned to.""" role: "SlimRole" """The role included in the assignment.""" - resource: "RoleAssignmentResource" - """The resource to which the role is assigned.""" + resource: "UserRoleAssignmentResource" + """The resource the role is assigned on.""" created_at: datetime """An ISO 8601 timestamp.""" updated_at: datetime """An ISO 8601 timestamp.""" @classmethod - def from_dict(cls, data: Dict[str, Any]) -> "RoleAssignment": + def from_dict(cls, data: Dict[str, Any]) -> "UserRoleAssignment": """Deserialize from a dictionary.""" try: return cls( object=data.get("object", "role_assignment"), id=data["id"], + organization_membership_id=data["organization_membership_id"], role=SlimRole.from_dict(cast(Dict[str, Any], data["role"])), - resource=RoleAssignmentResource.from_dict( + resource=UserRoleAssignmentResource.from_dict( cast(Dict[str, Any], data["resource"]) ), created_at=_parse_datetime(data["created_at"]), updated_at=_parse_datetime(data["updated_at"]), ) except (KeyError, ValueError) as e: - _raise_deserialize_error("RoleAssignment", e) + _raise_deserialize_error("UserRoleAssignment", 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["organization_membership_id"] = self.organization_membership_id result["role"] = self.role.to_dict() result["resource"] = self.resource.to_dict() result["created_at"] = _format_datetime(self.created_at) diff --git a/src/workos/authorization/models/role_assignment_resource.py b/src/workos/authorization/models/user_role_assignment_resource.py similarity index 82% rename from src/workos/authorization/models/role_assignment_resource.py rename to src/workos/authorization/models/user_role_assignment_resource.py index 3d57016c..9fe36c7d 100644 --- a/src/workos/authorization/models/role_assignment_resource.py +++ b/src/workos/authorization/models/user_role_assignment_resource.py @@ -8,8 +8,8 @@ @dataclass(slots=True) -class RoleAssignmentResource: - """The resource to which the role is assigned.""" +class UserRoleAssignmentResource: + """The resource the role is assigned on.""" id: str """The unique ID of the Resource.""" @@ -19,7 +19,7 @@ class RoleAssignmentResource: """The slug of the resource type this resource belongs to.""" @classmethod - def from_dict(cls, data: Dict[str, Any]) -> "RoleAssignmentResource": + def from_dict(cls, data: Dict[str, Any]) -> "UserRoleAssignmentResource": """Deserialize from a dictionary.""" try: return cls( @@ -28,7 +28,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "RoleAssignmentResource": resource_type_slug=data["resource_type_slug"], ) except (KeyError, ValueError) as e: - _raise_deserialize_error("RoleAssignmentResource", e) + _raise_deserialize_error("UserRoleAssignmentResource", e) def to_dict(self) -> Dict[str, Any]: """Serialize to a dictionary.""" diff --git a/src/workos/common/__init__.py b/src/workos/common/__init__.py index 8d5ff562..2994b27a 100644 --- a/src/workos/common/__init__.py +++ b/src/workos/common/__init__.py @@ -350,6 +350,8 @@ from .models import UpdateUserPasswordHashType as UpdateUserPasswordHashType from .models import UpdateWebhookEndpointEvents as UpdateWebhookEndpointEvents from .models import UpdateWebhookEndpointStatus as UpdateWebhookEndpointStatus +from .models import UserApiKeyCreatedDataOwner as UserApiKeyCreatedDataOwner +from .models import UserApiKeyRevokedDataOwner as UserApiKeyRevokedDataOwner from .models import UserCreated as UserCreated from .models import UserDeleted as UserDeleted from .models import UserIdentitiesGetItemProvider as UserIdentitiesGetItemProvider @@ -361,6 +363,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..635d2663 100644 --- a/src/workos/common/models/__init__.py +++ b/src/workos/common/models/__init__.py @@ -540,6 +540,12 @@ from .update_webhook_endpoint_status import ( UpdateWebhookEndpointStatus as UpdateWebhookEndpointStatus, ) +from .user_api_key_created_data_owner import ( + UserApiKeyCreatedDataOwner as UserApiKeyCreatedDataOwner, +) +from .user_api_key_revoked_data_owner import ( + UserApiKeyRevokedDataOwner as UserApiKeyRevokedDataOwner, +) from .user_created import UserCreated as UserCreated from .user_deleted import UserDeleted as UserDeleted from .user_identities_get_item_provider import ( @@ -555,6 +561,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.py b/src/workos/common/models/api_key_created_data.py index bfa07413..641fd076 100644 --- a/src/workos/common/models/api_key_created_data.py +++ b/src/workos/common/models/api_key_created_data.py @@ -4,10 +4,11 @@ from dataclasses import dataclass from typing import cast -from typing import Any, Dict, List, Literal, Optional +from typing import Any, Dict, List, Literal, Optional, Union from workos._types import _raise_deserialize_error from .api_key_created_data_owner import ApiKeyCreatedDataOwner +from .user_api_key_created_data_owner import UserApiKeyCreatedDataOwner @dataclass(slots=True) @@ -18,7 +19,7 @@ class ApiKeyCreatedData: """Distinguishes the API key object.""" id: str """Unique identifier of the API key.""" - owner: "ApiKeyCreatedDataOwner" + owner: Union["ApiKeyCreatedDataOwner", "UserApiKeyCreatedDataOwner"] """The owner of the API key.""" name: str """The name of the API key.""" @@ -40,8 +41,18 @@ def from_dict(cls, data: Dict[str, Any]) -> "ApiKeyCreatedData": return cls( object=data.get("object", "api_key"), id=data["id"], - owner=ApiKeyCreatedDataOwner.from_dict( - cast(Dict[str, Any], data["owner"]) + owner=( + _disc.from_dict(cast(Dict[str, Any], data["owner"])) + if ( + _disc := { + "organization": ApiKeyCreatedDataOwner, + "user": UserApiKeyCreatedDataOwner, + }.get( + cast(str, cast(Dict[str, Any], data["owner"]).get("type")) + ) + ) + is not None + else data["owner"] ), name=data["name"], obfuscated_value=data["obfuscated_value"], @@ -58,7 +69,9 @@ def to_dict(self) -> Dict[str, Any]: result: Dict[str, Any] = {} result["object"] = self.object result["id"] = self.id - result["owner"] = self.owner.to_dict() + result["owner"] = ( + self.owner.to_dict() if hasattr(self.owner, "to_dict") else self.owner + ) result["name"] = self.name result["obfuscated_value"] = self.obfuscated_value if self.last_used_at is not None: 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/user_api_key_created_data_owner.py b/src/workos/common/models/user_api_key_created_data_owner.py new file mode 100644 index 00000000..fc8a89f6 --- /dev/null +++ b/src/workos/common/models/user_api_key_created_data_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 UserApiKeyCreatedDataOwner: + """User Api Key Created Data Owner model.""" + + type: Literal["user"] + """The type of the API key owner.""" + id: str + """The unique identifier of the user who owns the API key.""" + organization_id: str + """The unique identifier of the organization the API key belongs to.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "UserApiKeyCreatedDataOwner": + """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("UserApiKeyCreatedDataOwner", 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/common/models/user_api_key_revoked_data_owner.py b/src/workos/common/models/user_api_key_revoked_data_owner.py new file mode 100644 index 00000000..01e0a8a2 --- /dev/null +++ b/src/workos/common/models/user_api_key_revoked_data_owner.py @@ -0,0 +1,6 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias +from .user_api_key_created_data_owner import UserApiKeyCreatedDataOwner + +UserApiKeyRevokedDataOwner: TypeAlias = UserApiKeyCreatedDataOwner 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..8a5f74a5 --- /dev/null +++ b/src/workos/common/models/vault_byok_key_deleted_data.py @@ -0,0 +1,40 @@ +# 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_provider import VaultByokKeyProvider + + +@dataclass(slots=True) +class VaultByokKeyDeletedData: + """The event payload.""" + + organization_id: str + """The unique identifier of the organization.""" + key_provider: "VaultByokKeyProvider" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "VaultByokKeyDeletedData": + """Deserialize from a dictionary.""" + try: + return cls( + organization_id=data["organization_id"], + key_provider=VaultByokKeyProvider(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_verification_completed_data_key_provider.py b/src/workos/common/models/vault_byok_key_provider.py similarity index 55% rename from src/workos/common/models/vault_byok_key_verification_completed_data_key_provider.py rename to src/workos/common/models/vault_byok_key_provider.py index c63ba21d..968f9b85 100644 --- a/src/workos/common/models/vault_byok_key_verification_completed_data_key_provider.py +++ b/src/workos/common/models/vault_byok_key_provider.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. -"""Enumeration of vault byok key verification completed data key provider values.""" +"""Enumeration of vault byok key provider values.""" from __future__ import annotations @@ -9,17 +9,15 @@ from typing import Literal, TypeAlias -class VaultByokKeyVerificationCompletedDataKeyProvider(str, Enum): - """Known values for VaultByokKeyVerificationCompletedDataKeyProvider.""" +class VaultByokKeyProvider(str, Enum): + """Known values for VaultByokKeyProvider.""" AWS_KMS = "AWS_KMS" GCP_KMS = "GCP_KMS" AZURE_KEY_VAULT = "AZURE_KEY_VAULT" @classmethod - def _missing_( - cls, value: object - ) -> Optional["VaultByokKeyVerificationCompletedDataKeyProvider"]: + def _missing_(cls, value: object) -> Optional["VaultByokKeyProvider"]: if not isinstance(value, str): return None unknown = str.__new__(cls, value) @@ -28,6 +26,6 @@ def _missing_( return unknown -VaultByokKeyVerificationCompletedDataKeyProviderLiteral: TypeAlias = Literal[ +VaultByokKeyProviderLiteral: TypeAlias = Literal[ "AWS_KMS", "GCP_KMS", "AZURE_KEY_VAULT" ] diff --git a/src/workos/common/models/vault_byok_key_verification_completed_data.py b/src/workos/common/models/vault_byok_key_verification_completed_data.py index 0d9528d7..1268509b 100644 --- a/src/workos/common/models/vault_byok_key_verification_completed_data.py +++ b/src/workos/common/models/vault_byok_key_verification_completed_data.py @@ -6,9 +6,7 @@ from enum import Enum from typing import Any, Dict from workos._types import _raise_deserialize_error -from .vault_byok_key_verification_completed_data_key_provider import ( - VaultByokKeyVerificationCompletedDataKeyProvider, -) +from .vault_byok_key_provider import VaultByokKeyProvider @dataclass(slots=True) @@ -17,8 +15,7 @@ class VaultByokKeyVerificationCompletedData: organization_id: str """The unique identifier of the organization.""" - key_provider: "VaultByokKeyVerificationCompletedDataKeyProvider" - """The external key provider used for BYOK.""" + key_provider: "VaultByokKeyProvider" verified: bool """Whether the BYOK key verification completed successfully.""" @@ -28,9 +25,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "VaultByokKeyVerificationCompletedDa try: return cls( organization_id=data["organization_id"], - key_provider=VaultByokKeyVerificationCompletedDataKeyProvider( - data["key_provider"] - ), + key_provider=VaultByokKeyProvider(data["key_provider"]), verified=data["verified"], ) except (KeyError, ValueError) as e: diff --git a/src/workos/connect/_resource.py b/src/workos/connect/_resource.py index e7b474de..7724497c 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 @@ -19,7 +20,7 @@ UserConsentOption, UserObject, ) -from .models import ApplicationsOrder +from workos.authorization.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage @@ -94,7 +95,7 @@ def list_applications( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[ApplicationsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", organization_id: Optional[str] = None, request_options: Optional[RequestOptions] = None, ) -> SyncPage[ConnectApplication]: @@ -106,7 +107,7 @@ def list_applications( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. organization_id: Filter Connect Applications by organization ID. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -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, ) @@ -508,7 +509,7 @@ async def list_applications( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[ApplicationsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", organization_id: Optional[str] = None, request_options: Optional[RequestOptions] = None, ) -> AsyncPage[ConnectApplication]: @@ -520,7 +521,7 @@ async def list_applications( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. organization_id: Filter Connect Applications by organization ID. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -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/__init__.py b/src/workos/connect/models/__init__.py index 47c3afac..a5db4c7d 100644 --- a/src/workos/connect/models/__init__.py +++ b/src/workos/connect/models/__init__.py @@ -3,7 +3,6 @@ from .application_credentials_list_item import ( ApplicationCredentialsListItem as ApplicationCredentialsListItem, ) -from .applications_order import ApplicationsOrder as ApplicationsOrder from .connect_application import ConnectApplication as ConnectApplication from .create_application_secret import ( CreateApplicationSecret as CreateApplicationSecret, diff --git a/src/workos/directory_sync/_resource.py b/src/workos/directory_sync/_resource.py index d183730e..71f2b54d 100644 --- a/src/workos/directory_sync/_resource.py +++ b/src/workos/directory_sync/_resource.py @@ -3,13 +3,14 @@ from __future__ import annotations from typing import TYPE_CHECKING, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient from .._types import RequestOptions, enum_value from .models import Directory, DirectoryGroup, DirectoryUserWithGroups -from .models import DirectoriesOrder, DirectoryGroupsOrder, DirectoryUsersOrder +from workos.authorization.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage @@ -25,7 +26,7 @@ def list_directories( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[DirectoriesOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", organization_id: Optional[str] = None, search: Optional[str] = None, domain: Optional[str] = None, @@ -39,7 +40,7 @@ def list_directories( 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`. + order: Order the results by the creation time. organization_id: Filter Directories by their associated organization. search: Searchable text to match against Directory names. domain: (deprecated) Filter Directories by their associated domain. @@ -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, ) @@ -139,7 +140,7 @@ def list_groups( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[DirectoryGroupsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", directory: Optional[str] = None, user: Optional[str] = None, request_options: Optional[RequestOptions] = None, @@ -152,7 +153,7 @@ def list_groups( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. directory: Unique identifier of the WorkOS Directory. This value can be obtained from the WorkOS dashboard or from the WorkOS API. user: Unique identifier of the WorkOS Directory User. This value can be obtained from the WorkOS API. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -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, ) @@ -225,7 +226,7 @@ def list_users( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[DirectoryUsersOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", directory: Optional[str] = None, group: Optional[str] = None, request_options: Optional[RequestOptions] = None, @@ -238,7 +239,7 @@ def list_users( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. directory: Unique identifier of the WorkOS Directory. This value can be obtained from the WorkOS dashboard or from the WorkOS API. group: Unique identifier of the WorkOS Directory Group. This value can be obtained from the WorkOS API. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -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, ) @@ -318,7 +319,7 @@ async def list_directories( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[DirectoriesOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", organization_id: Optional[str] = None, search: Optional[str] = None, domain: Optional[str] = None, @@ -332,7 +333,7 @@ async def list_directories( 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`. + order: Order the results by the creation time. organization_id: Filter Directories by their associated organization. search: Searchable text to match against Directory names. domain: (deprecated) Filter Directories by their associated domain. @@ -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, ) @@ -432,7 +433,7 @@ async def list_groups( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[DirectoryGroupsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", directory: Optional[str] = None, user: Optional[str] = None, request_options: Optional[RequestOptions] = None, @@ -445,7 +446,7 @@ async def list_groups( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. directory: Unique identifier of the WorkOS Directory. This value can be obtained from the WorkOS dashboard or from the WorkOS API. user: Unique identifier of the WorkOS Directory User. This value can be obtained from the WorkOS API. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -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, ) @@ -518,7 +519,7 @@ async def list_users( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[DirectoryUsersOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", directory: Optional[str] = None, group: Optional[str] = None, request_options: Optional[RequestOptions] = None, @@ -531,7 +532,7 @@ async def list_users( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. directory: Unique identifier of the WorkOS Directory. This value can be obtained from the WorkOS dashboard or from the WorkOS API. group: Unique identifier of the WorkOS Directory Group. This value can be obtained from the WorkOS API. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -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/__init__.py b/src/workos/directory_sync/models/__init__.py index 7857cdb8..89091483 100644 --- a/src/workos/directory_sync/models/__init__.py +++ b/src/workos/directory_sync/models/__init__.py @@ -1,9 +1,7 @@ # This file is auto-generated by oagen. Do not edit. -from .directories_order import DirectoriesOrder as DirectoriesOrder from .directory import Directory as Directory from .directory_group import DirectoryGroup as DirectoryGroup -from .directory_groups_order import DirectoryGroupsOrder as DirectoryGroupsOrder from .directory_metadata import DirectoryMetadata as DirectoryMetadata from .directory_metadata_user import DirectoryMetadataUser as DirectoryMetadataUser from .directory_user_with_groups import ( @@ -12,4 +10,3 @@ from .directory_user_with_groups_email import ( DirectoryUserWithGroupsEmail as DirectoryUserWithGroupsEmail, ) -from .directory_users_order import DirectoryUsersOrder as DirectoryUsersOrder diff --git a/src/workos/directory_sync/models/directories_order.py b/src/workos/directory_sync/models/directories_order.py deleted file mode 100644 index 9b78ed24..00000000 --- a/src/workos/directory_sync/models/directories_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -DirectoriesOrder: TypeAlias = ApplicationsOrder -__all__ = ["DirectoriesOrder"] diff --git a/src/workos/directory_sync/models/directory_groups_order.py b/src/workos/directory_sync/models/directory_groups_order.py deleted file mode 100644 index 35c06d3f..00000000 --- a/src/workos/directory_sync/models/directory_groups_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -DirectoryGroupsOrder: TypeAlias = ApplicationsOrder -__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 deleted file mode 100644 index 5409eaca..00000000 --- a/src/workos/directory_sync/models/directory_users_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -DirectoryUsersOrder: TypeAlias = ApplicationsOrder -__all__ = ["DirectoryUsersOrder"] diff --git a/src/workos/events/_resource.py b/src/workos/events/_resource.py index 4271433b..85b39ada 100644 --- a/src/workos/events/_resource.py +++ b/src/workos/events/_resource.py @@ -9,7 +9,7 @@ from .._types import RequestOptions, enum_value from .models import EventSchema, EventSchemaVariant -from .models import EventsOrder +from workos.authorization.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage @@ -25,7 +25,7 @@ def list_events( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[EventsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", events: Optional[List[str]] = None, range_start: Optional[str] = None, range_end: Optional[str] = None, @@ -40,7 +40,7 @@ def list_events( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. events: Filter events by one or more event types (e.g. `dsync.user.created`). range_start: ISO-8601 date string to filter events created after this date. range_end: ISO-8601 date string to filter events created before this date. @@ -97,7 +97,7 @@ async def list_events( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[EventsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", events: Optional[List[str]] = None, range_start: Optional[str] = None, range_end: Optional[str] = None, @@ -112,7 +112,7 @@ async def list_events( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. events: Filter events by one or more event types (e.g. `dsync.user.created`). range_start: ISO-8601 date string to filter events created after this date. range_end: ISO-8601 date string to filter events created before this date. diff --git a/src/workos/events/models/__init__.py b/src/workos/events/models/__init__.py index 0f13cad7..e067666c 100644 --- a/src/workos/events/models/__init__.py +++ b/src/workos/events/models/__init__.py @@ -4,4 +4,3 @@ from .event_schema import EventSchema as EventSchema from .event_schema import EventSchemaUnknown as EventSchemaUnknown from .event_schema import EventSchemaVariant as EventSchemaVariant -from .events_order import EventsOrder as EventsOrder 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 deleted file mode 100644 index b272b239..00000000 --- a/src/workos/events/models/events_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -EventsOrder: TypeAlias = ApplicationsOrder -__all__ = ["EventsOrder"] diff --git a/src/workos/feature_flags/_resource.py b/src/workos/feature_flags/_resource.py index b1da5d77..da9adff2 100644 --- a/src/workos/feature_flags/_resource.py +++ b/src/workos/feature_flags/_resource.py @@ -3,17 +3,14 @@ from __future__ import annotations from typing import TYPE_CHECKING, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient from .._types import RequestOptions, enum_value from .models import FeatureFlag, Flag -from .models import ( - FeatureFlagsOrder, - OrganizationsFeatureFlagsOrder, - UserManagementUsersFeatureFlagsOrder, -) +from workos.authorization.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage @@ -29,7 +26,7 @@ def list_feature_flags( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[FeatureFlagsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[Flag]: """List feature flags @@ -40,7 +37,7 @@ def list_feature_flags( 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`. + order: Order the results by the creation time. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -97,7 +94,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 +124,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 +154,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 +185,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 +215,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, ) @@ -229,7 +226,7 @@ def list_organization_feature_flags( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[OrganizationsFeatureFlagsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[Flag]: """List enabled feature flags for an organization @@ -241,7 +238,7 @@ def list_organization_feature_flags( 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`. + order: Order the results by the creation time. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -265,7 +262,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, @@ -278,7 +275,7 @@ def list_user_feature_flags( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[UserManagementUsersFeatureFlagsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[Flag]: """List enabled feature flags for a user @@ -290,7 +287,7 @@ def list_user_feature_flags( 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`. + order: Order the results by the creation time. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -314,7 +311,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, @@ -333,7 +330,7 @@ async def list_feature_flags( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[FeatureFlagsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[Flag]: """List feature flags @@ -344,7 +341,7 @@ async def list_feature_flags( 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`. + order: Order the results by the creation time. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -401,7 +398,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 +428,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 +458,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 +489,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 +519,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, ) @@ -533,7 +530,7 @@ async def list_organization_feature_flags( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[OrganizationsFeatureFlagsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[Flag]: """List enabled feature flags for an organization @@ -545,7 +542,7 @@ async def list_organization_feature_flags( 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`. + order: Order the results by the creation time. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -569,7 +566,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, @@ -582,7 +579,7 @@ async def list_user_feature_flags( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[UserManagementUsersFeatureFlagsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[Flag]: """List enabled feature flags for a user @@ -594,7 +591,7 @@ async def list_user_feature_flags( 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`. + order: Order the results by the creation time. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -618,7 +615,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/__init__.py b/src/workos/feature_flags/models/__init__.py index 27979c7c..169652f3 100644 --- a/src/workos/feature_flags/models/__init__.py +++ b/src/workos/feature_flags/models/__init__.py @@ -2,12 +2,5 @@ from .feature_flag import FeatureFlag as FeatureFlag from .feature_flag_owner import FeatureFlagOwner as FeatureFlagOwner -from .feature_flags_order import FeatureFlagsOrder as FeatureFlagsOrder from .flag import Flag as Flag from .flag_owner import FlagOwner as FlagOwner -from .organizations_feature_flags_order import ( - OrganizationsFeatureFlagsOrder as OrganizationsFeatureFlagsOrder, -) -from .user_management_users_feature_flags_order import ( - UserManagementUsersFeatureFlagsOrder as UserManagementUsersFeatureFlagsOrder, -) diff --git a/src/workos/feature_flags/models/feature_flags_order.py b/src/workos/feature_flags/models/feature_flags_order.py deleted file mode 100644 index 52e0eb14..00000000 --- a/src/workos/feature_flags/models/feature_flags_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -FeatureFlagsOrder: TypeAlias = ApplicationsOrder -__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 deleted file mode 100644 index cfe72a19..00000000 --- a/src/workos/feature_flags/models/organizations_feature_flags_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -OrganizationsFeatureFlagsOrder: TypeAlias = ApplicationsOrder -__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 deleted file mode 100644 index 8ff0e415..00000000 --- a/src/workos/feature_flags/models/user_management_users_feature_flags_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -UserManagementUsersFeatureFlagsOrder: TypeAlias = ApplicationsOrder -__all__ = ["UserManagementUsersFeatureFlagsOrder"] diff --git a/src/workos/groups/_resource.py b/src/workos/groups/_resource.py index c7d4dde5..66a2cd05 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 @@ -12,7 +13,7 @@ from workos.authorization.models.user_organization_membership_base_list_data import ( UserOrganizationMembershipBaseListData, ) -from .models import GroupsOrder +from workos.authorization.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage @@ -29,7 +30,7 @@ def list_organization_groups( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[GroupsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[Group]: """List groups @@ -41,7 +42,7 @@ def list_organization_groups( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -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, ) @@ -236,7 +237,7 @@ def list_group_organization_memberships( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[GroupsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[UserOrganizationMembershipBaseListData]: """List Group members @@ -249,7 +250,7 @@ def list_group_organization_memberships( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -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, ) @@ -365,7 +366,7 @@ async def list_organization_groups( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[GroupsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[Group]: """List groups @@ -377,7 +378,7 @@ async def list_organization_groups( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -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, ) @@ -572,7 +573,7 @@ async def list_group_organization_memberships( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[GroupsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[UserOrganizationMembershipBaseListData]: """List Group members @@ -585,7 +586,7 @@ async def list_group_organization_memberships( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -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/__init__.py b/src/workos/groups/models/__init__.py index ce775a5e..89737c62 100644 --- a/src/workos/groups/models/__init__.py +++ b/src/workos/groups/models/__init__.py @@ -3,5 +3,4 @@ from .create_group import CreateGroup as CreateGroup from .create_group_membership import CreateGroupMembership as CreateGroupMembership from .group import Group as Group -from .groups_order import GroupsOrder as GroupsOrder from .update_group import UpdateGroup as UpdateGroup diff --git a/src/workos/groups/models/groups_order.py b/src/workos/groups/models/groups_order.py deleted file mode 100644 index 6a6b4d04..00000000 --- a/src/workos/groups/models/groups_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -GroupsOrder: TypeAlias = ApplicationsOrder -__all__ = ["GroupsOrder"] diff --git a/src/workos/multi_factor_auth/_resource.py b/src/workos/multi_factor_auth/_resource.py index d89a5e09..11ef582d 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 @@ -15,7 +16,7 @@ AuthenticationFactorEnrolled, UserAuthenticationFactorEnrollResponse, ) -from .models import UserManagementMultiFactorAuthenticationOrder +from workos.authorization.models.pagination_order import PaginationOrder from workos.common.models.authentication_factors_create_request_type import ( AuthenticationFactorsCreateRequestType, ) @@ -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, @@ -220,9 +221,7 @@ def list_user_auth_factors( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[ - Union[UserManagementMultiFactorAuthenticationOrder, str] - ] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[AuthenticationFactor]: """List authentication factors @@ -234,7 +233,7 @@ def list_user_auth_factors( 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`. + order: Order the results by the creation time. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -258,7 +257,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 +306,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 +351,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 +432,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 +459,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 +498,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, @@ -512,9 +511,7 @@ async def list_user_auth_factors( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[ - Union[UserManagementMultiFactorAuthenticationOrder, str] - ] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[AuthenticationFactor]: """List authentication factors @@ -526,7 +523,7 @@ async def list_user_auth_factors( 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`. + order: Order the results by the creation time. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -550,7 +547,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 +596,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/__init__.py b/src/workos/multi_factor_auth/models/__init__.py index 4c0e060b..dad412d7 100644 --- a/src/workos/multi_factor_auth/models/__init__.py +++ b/src/workos/multi_factor_auth/models/__init__.py @@ -35,6 +35,3 @@ from .user_authentication_factor_enroll_response import ( UserAuthenticationFactorEnrollResponse as UserAuthenticationFactorEnrollResponse, ) -from .user_management_multi_factor_authentication_order import ( - UserManagementMultiFactorAuthenticationOrder as UserManagementMultiFactorAuthenticationOrder, -) 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 deleted file mode 100644 index 6113602c..00000000 --- a/src/workos/multi_factor_auth/models/user_management_multi_factor_authentication_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -UserManagementMultiFactorAuthenticationOrder: TypeAlias = ApplicationsOrder -__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..97d59184 100644 --- a/src/workos/organizations/_resource.py +++ b/src/workos/organizations/_resource.py @@ -3,13 +3,14 @@ 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 AuditLogConfiguration, Organization, OrganizationDomainData -from .models import OrganizationsOrder +from workos.authorization.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage @@ -25,7 +26,7 @@ def list_organizations( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[OrganizationsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", domains: Optional[List[str]] = None, search: Optional[str] = None, request_options: Optional[RequestOptions] = None, @@ -38,7 +39,7 @@ def list_organizations( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. domains: The domains of an Organization. Any Organization with a matching domain will be returned. search: Searchable text for an Organization. Matches against the organization name. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -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, ) @@ -324,7 +325,7 @@ async def list_organizations( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[OrganizationsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", domains: Optional[List[str]] = None, search: Optional[str] = None, request_options: Optional[RequestOptions] = None, @@ -337,7 +338,7 @@ async def list_organizations( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. domains: The domains of an Organization. Any Organization with a matching domain will be returned. search: Searchable text for an Organization. Matches against the organization name. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -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/__init__.py b/src/workos/organizations/models/__init__.py index d64209ec..e9809cbd 100644 --- a/src/workos/organizations/models/__init__.py +++ b/src/workos/organizations/models/__init__.py @@ -8,7 +8,6 @@ from .organization import Organization as Organization from .organization_domain_data import OrganizationDomainData as OrganizationDomainData from .organization_input import OrganizationInput as OrganizationInput -from .organizations_order import OrganizationsOrder as OrganizationsOrder from .update_audit_logs_retention import ( UpdateAuditLogsRetention as UpdateAuditLogsRetention, ) diff --git a/src/workos/organizations/models/organizations_order.py b/src/workos/organizations/models/organizations_order.py deleted file mode 100644 index 0fc8338e..00000000 --- a/src/workos/organizations/models/organizations_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -OrganizationsOrder: TypeAlias = ApplicationsOrder -__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..9ab960d1 100644 --- a/src/workos/sso/_resource.py +++ b/src/workos/sso/_resource.py @@ -3,13 +3,15 @@ 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 Connection, Profile, SSOLogoutAuthorizeResponse, SSOTokenResponse -from .models import ConnectionsConnectionType, ConnectionsOrder, SSOProvider +from .models import ConnectionsConnectionType, SSOProvider +from workos.authorization.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage @@ -25,7 +27,7 @@ def list_connections( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[ConnectionsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", connection_type: Optional[Union[ConnectionsConnectionType, str]] = None, domain: Optional[str] = None, organization_id: Optional[str] = None, @@ -40,7 +42,7 @@ def list_connections( 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`. + order: Order the results by the creation time. connection_type: Filter Connections by their type. domain: Filter Connections by their associated domain. organization_id: Filter Connections by their associated organization. @@ -107,7 +109,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 +137,7 @@ def delete_connection( """ self._client.request( method="delete", - path=f"connections/{id}", + path=f"connections/{quote(str(id), safe='')}", request_options=request_options, ) @@ -442,7 +444,7 @@ async def list_connections( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[ConnectionsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", connection_type: Optional[Union[ConnectionsConnectionType, str]] = None, domain: Optional[str] = None, organization_id: Optional[str] = None, @@ -457,7 +459,7 @@ async def list_connections( 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`. + order: Order the results by the creation time. connection_type: Filter Connections by their type. domain: Filter Connections by their associated domain. organization_id: Filter Connections by their associated organization. @@ -524,7 +526,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 +554,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/__init__.py b/src/workos/sso/models/__init__.py index cd9ff9f0..9c7d63e1 100644 --- a/src/workos/sso/models/__init__.py +++ b/src/workos/sso/models/__init__.py @@ -6,7 +6,6 @@ from .connections_connection_type import ( ConnectionsConnectionType as ConnectionsConnectionType, ) -from .connections_order import ConnectionsOrder as ConnectionsOrder from .profile import Profile as Profile from .sso_authorize_url_response import ( SSOAuthorizeUrlResponse as SSOAuthorizeUrlResponse, diff --git a/src/workos/sso/models/connections_order.py b/src/workos/sso/models/connections_order.py deleted file mode 100644 index d7c89f0d..00000000 --- a/src/workos/sso/models/connections_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -ConnectionsOrder: TypeAlias = ApplicationsOrder -__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..997e7c0e 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, @@ -43,12 +46,9 @@ from .models import ( UserManagementAuthenticationProvider, UserManagementAuthenticationScreenHint, - UserManagementInvitationsOrder, - UserManagementOrganizationMembershipOrder, UserManagementOrganizationMembershipStatuses, - UserManagementUsersAuthorizedApplicationsOrder, - UserManagementUsersOrder, ) +from workos.authorization.models.pagination_order import PaginationOrder from workos.common.models.create_user_invite_options_locale import ( CreateUserInviteOptionsLocale, ) @@ -128,7 +128,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 +713,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 +819,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, ) @@ -830,7 +830,7 @@ def list_users( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[UserManagementUsersOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", organization: Optional[str] = None, organization_id: Optional[str] = None, email: Optional[str] = None, @@ -844,7 +844,7 @@ def list_users( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. organization: (deprecated) Filter users by the organization they are a member of. Deprecated in favor of `organization_id`. organization_id: Filter users by the organization they are a member of. email: Filter users by their email address. @@ -968,7 +968,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 +998,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 +1064,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 +1092,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 +1129,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 +1167,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 +1205,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 +1237,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 +1267,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 [ @@ -1281,7 +1281,7 @@ def list_sessions( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[UserManagementUsersOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[UserSessionsListItem]: """List sessions @@ -1293,7 +1293,7 @@ def list_sessions( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -1318,7 +1318,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, @@ -1330,7 +1330,7 @@ def list_invitations( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[UserManagementInvitationsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", organization_id: Optional[str] = None, email: Optional[str] = None, request_options: Optional[RequestOptions] = None, @@ -1343,7 +1343,7 @@ def list_invitations( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. organization_id: The ID of the [organization](https://workos.com/docs/reference/organization) that the recipient will join. email: The email address of the recipient. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -1457,7 +1457,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 +1487,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 +1518,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 +1559,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 +1590,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 +1721,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, ) @@ -1707,7 +1732,7 @@ def list_organization_memberships( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[UserManagementOrganizationMembershipOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", organization_id: Optional[str] = None, statuses: Optional[ List[Union[UserManagementOrganizationMembershipStatuses, str]] @@ -1723,7 +1748,7 @@ def list_organization_memberships( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. organization_id: The ID of the [organization](https://workos.com/docs/reference/organization) which the user belongs to. statuses: Filter by the status of the organization membership. Array including any of `active`, `inactive`, or `pending`. user_id: The ID of the [user](https://workos.com/docs/reference/authkit/user). @@ -1836,7 +1861,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 +1900,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 +1928,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 +1964,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 +2001,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, ) @@ -2023,9 +2048,7 @@ def list_user_authorized_applications( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[ - Union[UserManagementUsersAuthorizedApplicationsOrder, str] - ] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[AuthorizedConnectApplicationListData]: """List authorized applications @@ -2037,7 +2060,7 @@ def list_user_authorized_applications( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -2062,7 +2085,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 +2115,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[PaginationOrder, 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. + 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 +2436,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 +3021,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 +3127,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, ) @@ -3015,7 +3138,7 @@ async def list_users( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[UserManagementUsersOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", organization: Optional[str] = None, organization_id: Optional[str] = None, email: Optional[str] = None, @@ -3029,7 +3152,7 @@ async def list_users( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. organization: (deprecated) Filter users by the organization they are a member of. Deprecated in favor of `organization_id`. organization_id: Filter users by the organization they are a member of. email: Filter users by their email address. @@ -3153,7 +3276,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 +3306,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 +3372,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 +3400,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 +3437,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 +3475,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 +3513,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 +3545,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 +3575,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 [ @@ -3466,7 +3589,7 @@ async def list_sessions( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[UserManagementUsersOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[UserSessionsListItem]: """List sessions @@ -3478,7 +3601,7 @@ async def list_sessions( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -3503,7 +3626,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, @@ -3515,7 +3638,7 @@ async def list_invitations( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[UserManagementInvitationsOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", organization_id: Optional[str] = None, email: Optional[str] = None, request_options: Optional[RequestOptions] = None, @@ -3528,7 +3651,7 @@ async def list_invitations( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. organization_id: The ID of the [organization](https://workos.com/docs/reference/organization) that the recipient will join. email: The email address of the recipient. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -3642,7 +3765,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 +3795,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 +3826,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 +3867,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 +3898,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 +4029,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, ) @@ -3892,7 +4040,7 @@ async def list_organization_memberships( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[UserManagementOrganizationMembershipOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", organization_id: Optional[str] = None, statuses: Optional[ List[Union[UserManagementOrganizationMembershipStatuses, str]] @@ -3908,7 +4056,7 @@ async def list_organization_memberships( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. organization_id: The ID of the [organization](https://workos.com/docs/reference/organization) which the user belongs to. statuses: Filter by the status of the organization membership. Array including any of `active`, `inactive`, or `pending`. user_id: The ID of the [user](https://workos.com/docs/reference/authkit/user). @@ -4021,7 +4169,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 +4208,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 +4236,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 +4272,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 +4309,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, ) @@ -4208,9 +4356,7 @@ async def list_user_authorized_applications( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[ - Union[UserManagementUsersAuthorizedApplicationsOrder, str] - ] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[AuthorizedConnectApplicationListData]: """List authorized applications @@ -4222,7 +4368,7 @@ async def list_user_authorized_applications( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -4247,7 +4393,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 +4423,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[PaginationOrder, 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. + 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..b569ff2b 100644 --- a/src/workos/user_management/models/__init__.py +++ b/src/workos/user_management/models/__init__.py @@ -25,6 +25,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 +90,11 @@ OrganizationSelectionSessionAuthenticateRequest as OrganizationSelectionSessionAuthenticateRequest, ) from .user import User as User +from .user_api_key import UserApiKey as UserApiKey +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 ( @@ -97,21 +103,9 @@ from .user_management_authentication_screen_hint import ( UserManagementAuthenticationScreenHint as UserManagementAuthenticationScreenHint, ) -from .user_management_invitations_order import ( - UserManagementInvitationsOrder as UserManagementInvitationsOrder, -) -from .user_management_organization_membership_order import ( - UserManagementOrganizationMembershipOrder as UserManagementOrganizationMembershipOrder, -) from .user_management_organization_membership_statuses import ( UserManagementOrganizationMembershipStatuses as UserManagementOrganizationMembershipStatuses, ) -from .user_management_users_authorized_applications_order import ( - UserManagementUsersAuthorizedApplicationsOrder as UserManagementUsersAuthorizedApplicationsOrder, -) -from .user_management_users_order import ( - UserManagementUsersOrder as UserManagementUsersOrder, -) from .user_organization_membership import ( UserOrganizationMembership as UserOrganizationMembership, ) 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..85f53ab8 --- /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 workos.api_keys.models.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/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..4178ebcb --- /dev/null +++ b/src/workos/user_management/models/user_api_key_with_value_owner.py @@ -0,0 +1,8 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias +from workos.common.models.user_api_key_created_data_owner import ( + UserApiKeyCreatedDataOwner, +) + +UserApiKeyWithValueOwner: TypeAlias = UserApiKeyCreatedDataOwner diff --git a/src/workos/user_management/models/user_management_invitations_order.py b/src/workos/user_management/models/user_management_invitations_order.py deleted file mode 100644 index a33850b2..00000000 --- a/src/workos/user_management/models/user_management_invitations_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -UserManagementInvitationsOrder: TypeAlias = ApplicationsOrder -__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 deleted file mode 100644 index 7f54ab09..00000000 --- a/src/workos/user_management/models/user_management_organization_membership_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -UserManagementOrganizationMembershipOrder: TypeAlias = ApplicationsOrder -__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 deleted file mode 100644 index a4c0ef90..00000000 --- a/src/workos/user_management/models/user_management_users_authorized_applications_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -UserManagementUsersAuthorizedApplicationsOrder: TypeAlias = ApplicationsOrder -__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 deleted file mode 100644 index 0e7922fa..00000000 --- a/src/workos/user_management/models/user_management_users_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -UserManagementUsersOrder: TypeAlias = ApplicationsOrder -__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..52cb67f2 100644 --- a/src/workos/user_management_organization_membership_groups/_resource.py +++ b/src/workos/user_management_organization_membership_groups/_resource.py @@ -3,13 +3,14 @@ from __future__ import annotations from typing import TYPE_CHECKING, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient from .._types import RequestOptions, enum_value from workos.groups.models.group import Group -from .models import UserManagementOrganizationMembershipGroupsOrder +from workos.authorization.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage @@ -26,9 +27,7 @@ def list_organization_membership_groups( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[ - Union[UserManagementOrganizationMembershipGroupsOrder, str] - ] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[Group]: """List groups @@ -40,7 +39,7 @@ def list_organization_membership_groups( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -64,7 +63,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, @@ -84,9 +83,7 @@ async def list_organization_membership_groups( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[ - Union[UserManagementOrganizationMembershipGroupsOrder, str] - ] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[Group]: """List groups @@ -98,7 +95,7 @@ async def list_organization_membership_groups( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -122,7 +119,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/__init__.py b/src/workos/user_management_organization_membership_groups/models/__init__.py index 51055ca3..33e9c7b7 100644 --- a/src/workos/user_management_organization_membership_groups/models/__init__.py +++ b/src/workos/user_management_organization_membership_groups/models/__init__.py @@ -1,5 +1,2 @@ # This file is auto-generated by oagen. Do not edit. -from .user_management_organization_membership_groups_order import ( - UserManagementOrganizationMembershipGroupsOrder as UserManagementOrganizationMembershipGroupsOrder, -) 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 deleted file mode 100644 index eb8d238e..00000000 --- a/src/workos/user_management_organization_membership_groups/models/user_management_organization_membership_groups_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -UserManagementOrganizationMembershipGroupsOrder: TypeAlias = ApplicationsOrder -__all__ = ["UserManagementOrganizationMembershipGroupsOrder"] diff --git a/src/workos/webhooks/_resource.py b/src/workos/webhooks/_resource.py index 7f0a27f2..6d493335 100644 --- a/src/workos/webhooks/_resource.py +++ b/src/workos/webhooks/_resource.py @@ -3,13 +3,14 @@ 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 WebhookEndpointJson -from .models import WebhooksOrder +from workos.authorization.models.pagination_order import PaginationOrder from workos.common.models.create_webhook_endpoint_events import ( CreateWebhookEndpointEvents, ) @@ -38,7 +39,7 @@ def list_webhook_endpoints( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[WebhooksOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> SyncPage[WebhookEndpointJson]: """List Webhook Endpoints @@ -49,7 +50,7 @@ def list_webhook_endpoints( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -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, ) @@ -295,7 +296,7 @@ async def list_webhook_endpoints( limit: Optional[int] = None, before: Optional[str] = None, after: Optional[str] = None, - order: Optional[Union[WebhooksOrder, str]] = "desc", + order: Optional[Union[PaginationOrder, str]] = "desc", request_options: Optional[RequestOptions] = None, ) -> AsyncPage[WebhookEndpointJson]: """List Webhook Endpoints @@ -306,7 +307,7 @@ async def list_webhook_endpoints( 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. 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. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. - order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -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/__init__.py b/src/workos/webhooks/models/__init__.py index 298f1958..71fea740 100644 --- a/src/workos/webhooks/models/__init__.py +++ b/src/workos/webhooks/models/__init__.py @@ -3,4 +3,3 @@ from .create_webhook_endpoint import CreateWebhookEndpoint as CreateWebhookEndpoint from .update_webhook_endpoint import UpdateWebhookEndpoint as UpdateWebhookEndpoint from .webhook_endpoint_json import WebhookEndpointJson as WebhookEndpointJson -from .webhooks_order import WebhooksOrder as WebhooksOrder diff --git a/src/workos/webhooks/models/webhooks_order.py b/src/workos/webhooks/models/webhooks_order.py deleted file mode 100644 index 41161321..00000000 --- a/src/workos/webhooks/models/webhooks_order.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is auto-generated by oagen. Do not edit. - -from typing import TypeAlias -from workos.connect.models.applications_order import ApplicationsOrder - -WebhooksOrder: TypeAlias = ApplicationsOrder -__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/list_user_role_assignment.json b/tests/fixtures/list_user_role_assignment.json new file mode 100644 index 00000000..e909d9ac --- /dev/null +++ b/tests/fixtures/list_user_role_assignment.json @@ -0,0 +1,23 @@ +{ + "data": [ + { + "object": "role_assignment", + "id": "role_assignment_01HXYZ123456789ABCDEFGH", + "organization_membership_id": "om_01HXYZ123456789ABCDEFGHIJ", + "role": { + "slug": "admin" + }, + "resource": { + "id": "authz_resource_01HXYZ123456789ABCDEFGH", + "external_id": "proj-456", + "resource_type_slug": "project" + }, + "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/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_created_data_owner.json b/tests/fixtures/user_api_key_created_data_owner.json new file mode 100644 index 00000000..125ba4bc --- /dev/null +++ b/tests/fixtures/user_api_key_created_data_owner.json @@ -0,0 +1,5 @@ +{ + "type": "user", + "id": "user_01EHWNCE74X7JSDV0X3SZ3KJNY", + "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY" +} 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_revoked_data_owner.json b/tests/fixtures/user_api_key_revoked_data_owner.json new file mode 100644 index 00000000..125ba4bc --- /dev/null +++ b/tests/fixtures/user_api_key_revoked_data_owner.json @@ -0,0 +1,5 @@ +{ + "type": "user", + "id": "user_01EHWNCE74X7JSDV0X3SZ3KJNY", + "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY" +} 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/user_role_assignment.json b/tests/fixtures/user_role_assignment.json new file mode 100644 index 00000000..48ca7237 --- /dev/null +++ b/tests/fixtures/user_role_assignment.json @@ -0,0 +1,15 @@ +{ + "object": "role_assignment", + "id": "role_assignment_01HXYZ123456789ABCDEFGH", + "organization_membership_id": "om_01HXYZ123456789ABCDEFGHIJ", + "role": { + "slug": "admin" + }, + "resource": { + "id": "authz_resource_01HXYZ123456789ABCDEFGH", + "external_id": "proj-456", + "resource_type_slug": "project" + }, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" +} diff --git a/tests/fixtures/user_role_assignment_resource.json b/tests/fixtures/user_role_assignment_resource.json new file mode 100644 index 00000000..53f9d508 --- /dev/null +++ b/tests/fixtures/user_role_assignment_resource.json @@ -0,0 +1,5 @@ +{ + "id": "authz_resource_01HXYZ123456789ABCDEFGH", + "external_id": "proj-456", + "resource_type_slug": "project" +} 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..c902c77a 100644 --- a/tests/test_api_keys.py +++ b/tests/test_api_keys.py @@ -7,11 +7,11 @@ from tests.generated_helpers import load_fixture from workos.api_keys.models import ( - ApiKey, ApiKeyValidationResponse, - ApiKeyWithValue, - OrganizationsApiKeysOrder, + OrganizationApiKey, + OrganizationApiKeyWithValue, ) +from workos.authorization.models import PaginationOrder from workos._pagination import AsyncPage, SyncPage from workos._errors import ( AuthenticationError, @@ -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": {}}) @@ -66,22 +46,22 @@ def test_list_organization_api_keys_encodes_query_params(self, workos, httpx_moc limit=10, before="cursor before", after="cursor/after", - order=OrganizationsApiKeysOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" 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( @@ -216,21 +199,23 @@ async def test_list_organization_api_keys_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=OrganizationsApiKeysOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @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_audit_logs.py b/tests/test_audit_logs.py index 5bd6d3bc..77e771e6 100644 --- a/tests/test_audit_logs.py +++ b/tests/test_audit_logs.py @@ -12,8 +12,8 @@ AuditLogEventCreateResponse, AuditLogExportJson, AuditLogSchemaJson, - AuditLogsOrder, ) +from workos.authorization.models import PaginationOrder from workos.organizations.models import AuditLogsRetentionJson from workos._pagination import AsyncPage, SyncPage from workos._errors import ( @@ -74,13 +74,13 @@ def test_list_actions_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=AuditLogsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_list_action_schemas(self, workos, httpx_mock): httpx_mock.add_response( @@ -104,13 +104,13 @@ def test_list_action_schemas_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=AuditLogsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_create_schema(self, workos, httpx_mock): httpx_mock.add_response( @@ -306,13 +306,13 @@ async def test_list_actions_encodes_query_params(self, async_workos, httpx_mock) limit=10, before="cursor before", after="cursor/after", - order=AuditLogsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_list_action_schemas(self, async_workos, httpx_mock): @@ -339,13 +339,13 @@ async def test_list_action_schemas_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=AuditLogsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_create_schema(self, async_workos, httpx_mock): diff --git a/tests/test_authorization.py b/tests/test_authorization.py index c118f925..18459f44 100644 --- a/tests/test_authorization.py +++ b/tests/test_authorization.py @@ -12,12 +12,11 @@ AuthorizationResource, Permission, Role, - RoleAssignment, RoleList, UserOrganizationMembershipBaseListData, + UserRoleAssignment, AuthorizationAssignment, - AuthorizationOrder, - PermissionsOrder, + PaginationOrder, ) from workos._pagination import AsyncPage, SyncPage from workos._errors import ( @@ -90,7 +89,7 @@ def test_list_resources_for_membership_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), permission_slug="value permission_slug/test", ) request = httpx_mock.get_request() @@ -100,7 +99,7 @@ def test_list_resources_for_membership_encodes_query_params( 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["order"] == "value_order" assert request.url.params["permission_slug"] == "value permission_slug/test" def test_list_effective_permissions(self, workos, httpx_mock): @@ -130,13 +129,13 @@ def test_list_effective_permissions_encodes_query_params(self, workos, httpx_moc limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_list_effective_permissions_by_external_id(self, workos, httpx_mock): httpx_mock.add_response( @@ -174,24 +173,24 @@ def test_list_effective_permissions_by_external_id_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_list_role_assignments(self, workos, httpx_mock): httpx_mock.add_response( - json=load_fixture("list_role_assignment.json"), + json=load_fixture("list_user_role_assignment.json"), ) page = workos.authorization.list_role_assignments( "test_organization_membership_id" ) assert isinstance(page, SyncPage) assert len(page.data) == 1 - assert isinstance(page.data[0], RoleAssignment) + assert isinstance(page.data[0], UserRoleAssignment) def test_list_role_assignments_empty_page(self, workos, httpx_mock): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) @@ -208,24 +207,24 @@ def test_list_role_assignments_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_assign_role(self, workos, httpx_mock): httpx_mock.add_response( - json=load_fixture("role_assignment.json"), + json=load_fixture("user_role_assignment.json"), ) result = workos.authorization.assign_role( "test_organization_membership_id", role_slug="test_role_slug", resource_target=ResourceTargetById(resource_id="test_value"), ) - assert isinstance(result, RoleAssignment) + assert isinstance(result, UserRoleAssignment) assert result.object == "role_assignment" assert result.id == "role_assignment_01HXYZ123456789ABCDEFGH" request = httpx_mock.get_request() @@ -483,7 +482,7 @@ def test_list_memberships_for_resource_by_external_id_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), permission_slug="value permission_slug/test", assignment=AuthorizationAssignment("direct"), ) @@ -491,10 +490,52 @@ def test_list_memberships_for_resource_by_external_id_encodes_query_params( 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["order"] == "value_order" assert request.url.params["permission_slug"] == "value permission_slug/test" assert request.url.params["assignment"] == "direct" + def test_list_role_assignments_for_resource_by_external_id( + self, workos, httpx_mock + ): + httpx_mock.add_response( + json=load_fixture("list_user_role_assignment.json"), + ) + page = workos.authorization.list_role_assignments_for_resource_by_external_id( + "test_organization_id", "test_resource_type_slug", "test_external_id" + ) + assert isinstance(page, SyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], UserRoleAssignment) + + def test_list_role_assignments_for_resource_by_external_id_empty_page( + self, workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = workos.authorization.list_role_assignments_for_resource_by_external_id( + "test_organization_id", "test_resource_type_slug", "test_external_id" + ) + assert isinstance(page, SyncPage) + assert page.data == [] + + def test_list_role_assignments_for_resource_by_external_id_encodes_query_params( + self, workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + workos.authorization.list_role_assignments_for_resource_by_external_id( + "test_organization_id", + "test_resource_type_slug", + "test_external_id", + limit=10, + before="cursor before", + after="cursor/after", + order=PaginationOrder("value_order"), + ) + 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"] == "value_order" + def test_list_resources(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("list_authorization_resource.json"), @@ -521,7 +562,7 @@ def test_list_resources_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), organization_id="value organization_id/test", resource_type_slug="value resource_type_slug/test", resource_external_id="value resource_external_id/test", @@ -534,7 +575,7 @@ def test_list_resources_encodes_query_params(self, workos, httpx_mock): 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["order"] == "value_order" assert request.url.params["organization_id"] == "value organization_id/test" assert ( request.url.params["resource_type_slug"] == "value resource_type_slug/test" @@ -637,7 +678,7 @@ def test_list_memberships_for_resource_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), permission_slug="value permission_slug/test", assignment=AuthorizationAssignment("direct"), ) @@ -645,10 +686,46 @@ def test_list_memberships_for_resource_encodes_query_params( 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["order"] == "value_order" assert request.url.params["permission_slug"] == "value permission_slug/test" assert request.url.params["assignment"] == "direct" + def test_list_role_assignments_for_resource(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("list_user_role_assignment.json"), + ) + page = workos.authorization.list_role_assignments_for_resource( + "test_resource_id" + ) + assert isinstance(page, SyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], UserRoleAssignment) + + def test_list_role_assignments_for_resource_empty_page(self, workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = workos.authorization.list_role_assignments_for_resource( + "test_resource_id" + ) + assert isinstance(page, SyncPage) + assert page.data == [] + + def test_list_role_assignments_for_resource_encodes_query_params( + self, workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + workos.authorization.list_role_assignments_for_resource( + "test_resource_id", + limit=10, + before="cursor before", + after="cursor/after", + order=PaginationOrder("value_order"), + ) + 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"] == "value_order" + def test_list_environment_roles(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("role_list.json"), @@ -754,13 +831,13 @@ def test_list_permissions_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=PermissionsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_create_permission(self, workos, httpx_mock): httpx_mock.add_response( @@ -969,7 +1046,7 @@ async def test_list_resources_for_membership_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), permission_slug="value permission_slug/test", ) request = httpx_mock.get_request() @@ -979,7 +1056,7 @@ async def test_list_resources_for_membership_encodes_query_params( 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["order"] == "value_order" assert request.url.params["permission_slug"] == "value permission_slug/test" @pytest.mark.asyncio @@ -1014,13 +1091,13 @@ async def test_list_effective_permissions_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_list_effective_permissions_by_external_id( @@ -1065,23 +1142,23 @@ async def test_list_effective_permissions_by_external_id_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_list_role_assignments(self, async_workos, httpx_mock): - httpx_mock.add_response(json=load_fixture("list_role_assignment.json")) + httpx_mock.add_response(json=load_fixture("list_user_role_assignment.json")) page = await async_workos.authorization.list_role_assignments( "test_organization_membership_id" ) assert isinstance(page, AsyncPage) assert len(page.data) == 1 - assert isinstance(page.data[0], RoleAssignment) + assert isinstance(page.data[0], UserRoleAssignment) @pytest.mark.asyncio async def test_list_role_assignments_empty_page(self, async_workos, httpx_mock): @@ -1102,23 +1179,23 @@ async def test_list_role_assignments_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_assign_role(self, async_workos, httpx_mock): - httpx_mock.add_response(json=load_fixture("role_assignment.json")) + httpx_mock.add_response(json=load_fixture("user_role_assignment.json")) result = await async_workos.authorization.assign_role( "test_organization_membership_id", role_slug="test_role_slug", resource_target=ResourceTargetById(resource_id="test_value"), ) - assert isinstance(result, RoleAssignment) + assert isinstance(result, UserRoleAssignment) assert result.object == "role_assignment" assert result.id == "role_assignment_01HXYZ123456789ABCDEFGH" request = httpx_mock.get_request() @@ -1373,7 +1450,7 @@ async def test_list_memberships_for_resource_by_external_id_encodes_query_params limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), permission_slug="value permission_slug/test", assignment=AuthorizationAssignment("direct"), ) @@ -1381,10 +1458,53 @@ async def test_list_memberships_for_resource_by_external_id_encodes_query_params 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["order"] == "value_order" assert request.url.params["permission_slug"] == "value permission_slug/test" assert request.url.params["assignment"] == "direct" + @pytest.mark.asyncio + async def test_list_role_assignments_for_resource_by_external_id( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json=load_fixture("list_user_role_assignment.json")) + page = await async_workos.authorization.list_role_assignments_for_resource_by_external_id( + "test_organization_id", "test_resource_type_slug", "test_external_id" + ) + assert isinstance(page, AsyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], UserRoleAssignment) + + @pytest.mark.asyncio + async def test_list_role_assignments_for_resource_by_external_id_empty_page( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = await async_workos.authorization.list_role_assignments_for_resource_by_external_id( + "test_organization_id", "test_resource_type_slug", "test_external_id" + ) + assert isinstance(page, AsyncPage) + assert page.data == [] + + @pytest.mark.asyncio + async def test_list_role_assignments_for_resource_by_external_id_encodes_query_params( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + await async_workos.authorization.list_role_assignments_for_resource_by_external_id( + "test_organization_id", + "test_resource_type_slug", + "test_external_id", + limit=10, + before="cursor before", + after="cursor/after", + order=PaginationOrder("value_order"), + ) + 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"] == "value_order" + @pytest.mark.asyncio async def test_list_resources(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("list_authorization_resource.json")) @@ -1412,7 +1532,7 @@ async def test_list_resources_encodes_query_params(self, async_workos, httpx_moc limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), organization_id="value organization_id/test", resource_type_slug="value resource_type_slug/test", resource_external_id="value resource_external_id/test", @@ -1425,7 +1545,7 @@ async def test_list_resources_encodes_query_params(self, async_workos, httpx_moc 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["order"] == "value_order" assert request.url.params["organization_id"] == "value organization_id/test" assert ( request.url.params["resource_type_slug"] == "value resource_type_slug/test" @@ -1529,7 +1649,7 @@ async def test_list_memberships_for_resource_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=AuthorizationOrder("normal"), + order=PaginationOrder("value_order"), permission_slug="value permission_slug/test", assignment=AuthorizationAssignment("direct"), ) @@ -1537,10 +1657,49 @@ async def test_list_memberships_for_resource_encodes_query_params( 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["order"] == "value_order" assert request.url.params["permission_slug"] == "value permission_slug/test" assert request.url.params["assignment"] == "direct" + @pytest.mark.asyncio + async def test_list_role_assignments_for_resource(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("list_user_role_assignment.json")) + page = await async_workos.authorization.list_role_assignments_for_resource( + "test_resource_id" + ) + assert isinstance(page, AsyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], UserRoleAssignment) + + @pytest.mark.asyncio + async def test_list_role_assignments_for_resource_empty_page( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = await async_workos.authorization.list_role_assignments_for_resource( + "test_resource_id" + ) + assert isinstance(page, AsyncPage) + assert page.data == [] + + @pytest.mark.asyncio + async def test_list_role_assignments_for_resource_encodes_query_params( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + await async_workos.authorization.list_role_assignments_for_resource( + "test_resource_id", + limit=10, + before="cursor before", + after="cursor/after", + order=PaginationOrder("value_order"), + ) + 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"] == "value_order" + @pytest.mark.asyncio async def test_list_environment_roles(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("role_list.json")) @@ -1636,13 +1795,13 @@ async def test_list_permissions_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=PermissionsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_create_permission(self, async_workos, httpx_mock): diff --git a/tests/test_connect.py b/tests/test_connect.py index 64dbebd9..65fead16 100644 --- a/tests/test_connect.py +++ b/tests/test_connect.py @@ -6,13 +6,13 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture +from workos.authorization.models import PaginationOrder from workos.connect.models import ( ApplicationCredentialsListItem, ConnectApplication, ExternalAuthCompleteResponse, NewConnectApplicationSecret, UserObject, - ApplicationsOrder, ) from workos._pagination import AsyncPage, SyncPage from workos._errors import ( @@ -67,14 +67,14 @@ def test_list_applications_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=ApplicationsOrder("normal"), + order=PaginationOrder("value_order"), 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["order"] == "value_order" assert request.url.params["organization_id"] == "value organization_id/test" def test_create_application(self, workos, httpx_mock): @@ -315,14 +315,14 @@ async def test_list_applications_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=ApplicationsOrder("normal"), + order=PaginationOrder("value_order"), 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["order"] == "value_order" assert request.url.params["organization_id"] == "value organization_id/test" @pytest.mark.asyncio diff --git a/tests/test_directory_sync.py b/tests/test_directory_sync.py index 7dae6f98..0cccf681 100644 --- a/tests/test_directory_sync.py +++ b/tests/test_directory_sync.py @@ -5,13 +5,11 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture +from workos.authorization.models import PaginationOrder from workos.directory_sync.models import ( Directory, DirectoryGroup, DirectoryUserWithGroups, - DirectoriesOrder, - DirectoryGroupsOrder, - DirectoryUsersOrder, ) from workos._pagination import AsyncPage, SyncPage from workos._errors import ( @@ -46,7 +44,7 @@ def test_list_directories_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=DirectoriesOrder("normal"), + order=PaginationOrder("value_order"), organization_id="value organization_id/test", search="value search/test", domain="value domain/test", @@ -55,7 +53,7 @@ def test_list_directories_encodes_query_params(self, workos, httpx_mock): 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["order"] == "value_order" assert request.url.params["organization_id"] == "value organization_id/test" assert request.url.params["search"] == "value search/test" assert request.url.params["domain"] == "value domain/test" @@ -101,7 +99,7 @@ def test_list_groups_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=DirectoryGroupsOrder("normal"), + order=PaginationOrder("value_order"), directory="value directory/test", user="value user/test", ) @@ -109,7 +107,7 @@ def test_list_groups_encodes_query_params(self, workos, httpx_mock): 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["order"] == "value_order" assert request.url.params["directory"] == "value directory/test" assert request.url.params["user"] == "value user/test" @@ -146,7 +144,7 @@ def test_list_users_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=DirectoryUsersOrder("normal"), + order=PaginationOrder("value_order"), directory="value directory/test", group="value group/test", ) @@ -154,7 +152,7 @@ def test_list_users_encodes_query_params(self, workos, httpx_mock): 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["order"] == "value_order" assert request.url.params["directory"] == "value directory/test" assert request.url.params["group"] == "value group/test" @@ -271,7 +269,7 @@ async def test_list_directories_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=DirectoriesOrder("normal"), + order=PaginationOrder("value_order"), organization_id="value organization_id/test", search="value search/test", domain="value domain/test", @@ -280,7 +278,7 @@ async def test_list_directories_encodes_query_params( 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["order"] == "value_order" assert request.url.params["organization_id"] == "value organization_id/test" assert request.url.params["search"] == "value search/test" assert request.url.params["domain"] == "value domain/test" @@ -327,7 +325,7 @@ async def test_list_groups_encodes_query_params(self, async_workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=DirectoryGroupsOrder("normal"), + order=PaginationOrder("value_order"), directory="value directory/test", user="value user/test", ) @@ -335,7 +333,7 @@ async def test_list_groups_encodes_query_params(self, async_workos, httpx_mock): 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["order"] == "value_order" assert request.url.params["directory"] == "value directory/test" assert request.url.params["user"] == "value user/test" @@ -374,7 +372,7 @@ async def test_list_users_encodes_query_params(self, async_workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=DirectoryUsersOrder("normal"), + order=PaginationOrder("value_order"), directory="value directory/test", group="value group/test", ) @@ -382,7 +380,7 @@ async def test_list_users_encodes_query_params(self, async_workos, httpx_mock): 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["order"] == "value_order" assert request.url.params["directory"] == "value directory/test" assert request.url.params["group"] == "value group/test" diff --git a/tests/test_events.py b/tests/test_events.py index 34b5be84..edf633d7 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -5,8 +5,8 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture +from workos.authorization.models import PaginationOrder from workos.common.models import DsyncUserCreated -from workos.events.models import EventsOrder from workos._pagination import AsyncPage, SyncPage from workos._errors import ( AuthenticationError, @@ -40,7 +40,7 @@ def test_list_events_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=EventsOrder("normal"), + order=PaginationOrder("value_order"), events=["val1", "val2"], range_start="value range_start/test", range_end="value range_end/test", @@ -50,7 +50,7 @@ def test_list_events_encodes_query_params(self, workos, httpx_mock): 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["order"] == "value_order" assert request.url.params["events"] == "val1,val2" assert request.url.params["range_start"] == "value range_start/test" assert request.url.params["range_end"] == "value range_end/test" @@ -155,7 +155,7 @@ async def test_list_events_encodes_query_params(self, async_workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=EventsOrder("normal"), + order=PaginationOrder("value_order"), events=["val1", "val2"], range_start="value range_start/test", range_end="value range_end/test", @@ -165,7 +165,7 @@ async def test_list_events_encodes_query_params(self, async_workos, httpx_mock): 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["order"] == "value_order" assert request.url.params["events"] == "val1,val2" assert request.url.params["range_start"] == "value range_start/test" assert request.url.params["range_end"] == "value range_end/test" diff --git a/tests/test_feature_flags.py b/tests/test_feature_flags.py index 6f8ef66b..97a89e56 100644 --- a/tests/test_feature_flags.py +++ b/tests/test_feature_flags.py @@ -5,13 +5,8 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture -from workos.feature_flags.models import ( - FeatureFlag, - Flag, - FeatureFlagsOrder, - OrganizationsFeatureFlagsOrder, - UserManagementUsersFeatureFlagsOrder, -) +from workos.authorization.models import PaginationOrder +from workos.feature_flags.models import FeatureFlag, Flag from workos._pagination import AsyncPage, SyncPage from workos._errors import ( AuthenticationError, @@ -45,13 +40,13 @@ def test_list_feature_flags_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=FeatureFlagsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_get_feature_flag(self, workos, httpx_mock): httpx_mock.add_response( @@ -136,13 +131,13 @@ def test_list_organization_feature_flags_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=OrganizationsFeatureFlagsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_list_user_feature_flags(self, workos, httpx_mock): httpx_mock.add_response( @@ -166,13 +161,13 @@ def test_list_user_feature_flags_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=UserManagementUsersFeatureFlagsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_list_feature_flags_with_request_options(self, workos, httpx_mock): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) @@ -275,13 +270,13 @@ async def test_list_feature_flags_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=FeatureFlagsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_get_feature_flag(self, async_workos, httpx_mock): @@ -370,13 +365,13 @@ async def test_list_organization_feature_flags_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=OrganizationsFeatureFlagsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_list_user_feature_flags(self, async_workos, httpx_mock): @@ -403,13 +398,13 @@ async def test_list_user_feature_flags_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=UserManagementUsersFeatureFlagsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_list_feature_flags_with_request_options( diff --git a/tests/test_groups.py b/tests/test_groups.py index adcfdae9..be888b00 100644 --- a/tests/test_groups.py +++ b/tests/test_groups.py @@ -6,8 +6,11 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture -from workos.authorization.models import UserOrganizationMembershipBaseListData -from workos.groups.models import Group, GroupsOrder +from workos.authorization.models import ( + UserOrganizationMembershipBaseListData, + PaginationOrder, +) +from workos.groups.models import Group from workos._pagination import AsyncPage, SyncPage from workos._errors import ( AuthenticationError, @@ -42,13 +45,13 @@ def test_list_organization_groups_encodes_query_params(self, workos, httpx_mock) limit=10, before="cursor before", after="cursor/after", - order=GroupsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_create_organization_group(self, workos, httpx_mock): httpx_mock.add_response( @@ -139,13 +142,13 @@ def test_list_group_organization_memberships_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=GroupsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_create_group_organization_membership(self, workos, httpx_mock): httpx_mock.add_response( @@ -282,13 +285,13 @@ async def test_list_organization_groups_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=GroupsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_create_organization_group(self, async_workos, httpx_mock): @@ -380,13 +383,13 @@ async def test_list_group_organization_memberships_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=GroupsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_create_group_organization_membership(self, async_workos, httpx_mock): diff --git a/tests/test_models_round_trip.py b/tests/test_models_round_trip.py index 3925830b..2511629f 100644 --- a/tests/test_models_round_trip.py +++ b/tests/test_models_round_trip.py @@ -16,8 +16,11 @@ ApiKey, ApiKeyOwner, ApiKeyValidationResponse, - ApiKeyWithValue, - ApiKeyWithValueOwner, + OrganizationApiKey, + OrganizationApiKeyOwner, + OrganizationApiKeyWithValue, + OrganizationApiKeyWithValueOwner, + UserApiKeyOwner, ) from workos.audit_logs.models import ( AuditLogActionJson, @@ -39,11 +42,11 @@ AuthorizationResource, Permission, Role, - RoleAssignment, - RoleAssignmentResource, RoleList, SlimRole, UserOrganizationMembershipBaseListData, + UserRoleAssignment, + UserRoleAssignmentResource, ) from workos.common.models import ( ActionAuthenticationDenied, @@ -248,9 +251,13 @@ SessionRevoked, SessionRevokedData, SessionRevokedDataImpersonator, + UserApiKeyCreatedDataOwner, + UserApiKeyRevokedDataOwner, UserCreated, UserDeleted, UserUpdated, + VaultByokKeyDeleted, + VaultByokKeyDeletedData, VaultByokKeyVerificationCompleted, VaultByokKeyVerificationCompletedData, VaultDataCreated, @@ -364,6 +371,9 @@ ResetPasswordResponse, SendVerificationEmailResponse, User, + UserApiKey, + UserApiKeyWithValue, + UserApiKeyWithValueOwner, UserIdentitiesGetItem, UserInvite, UserOrganizationMembership, @@ -1370,18 +1380,19 @@ def test_slim_role_minimal_payload(self): serialized = instance.to_dict() assert serialized["slug"] == data["slug"] - def test_role_assignment_round_trip(self): - data = load_fixture("role_assignment.json") - instance = RoleAssignment.from_dict(data) + def test_user_role_assignment_round_trip(self): + data = load_fixture("user_role_assignment.json") + instance = UserRoleAssignment.from_dict(data) serialized = instance.to_dict() assert serialized == data - restored = RoleAssignment.from_dict(serialized) + restored = UserRoleAssignment.from_dict(serialized) assert restored.to_dict() == serialized - def test_role_assignment_minimal_payload(self): + def test_user_role_assignment_minimal_payload(self): data = { "object": "role_assignment", "id": "role_assignment_01HXYZ123456789ABCDEFGH", + "organization_membership_id": "om_01HXYZ123456789ABCDEFGHIJ", "role": {"slug": "admin"}, "resource": { "id": "authz_resource_01HXYZ123456789ABCDEFGH", @@ -1391,10 +1402,14 @@ def test_role_assignment_minimal_payload(self): "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } - instance = RoleAssignment.from_dict(data) + instance = UserRoleAssignment.from_dict(data) serialized = instance.to_dict() assert serialized["object"] == data["object"] assert serialized["id"] == data["id"] + assert ( + serialized["organization_membership_id"] + == data["organization_membership_id"] + ) assert serialized["role"] == data["role"] assert serialized["resource"] == data["resource"] assert serialized["created_at"] == data["created_at"] @@ -1498,6 +1513,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 +1875,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 +1916,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 +1955,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 +1969,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 +2152,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 +2180,7 @@ def test_directory_user_preserves_nullable_fields(self): "email": None, "first_name": None, "last_name": None, + "name": None, "emails": [ { "primary": True, @@ -2105,6 +2206,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 +2220,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 +2244,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) @@ -2612,6 +2635,26 @@ def test_api_key_created_data_owner_minimal_payload(self): assert serialized["type"] == data["type"] assert serialized["id"] == data["id"] + def test_user_api_key_created_data_owner_round_trip(self): + data = load_fixture("user_api_key_created_data_owner.json") + instance = UserApiKeyCreatedDataOwner.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = UserApiKeyCreatedDataOwner.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_user_api_key_created_data_owner_minimal_payload(self): + data = { + "type": "user", + "id": "user_01EHWNCE74X7JSDV0X3SZ3KJNY", + "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY", + } + instance = UserApiKeyCreatedDataOwner.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_api_key_revoked_round_trip(self): data = load_fixture("api_key_revoked.json") instance = ApiKeyRevoked.from_dict(data) @@ -2737,6 +2780,26 @@ def test_api_key_revoked_data_owner_minimal_payload(self): assert serialized["type"] == data["type"] assert serialized["id"] == data["id"] + def test_user_api_key_revoked_data_owner_round_trip(self): + data = load_fixture("user_api_key_revoked_data_owner.json") + instance = UserApiKeyRevokedDataOwner.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = UserApiKeyRevokedDataOwner.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_user_api_key_revoked_data_owner_minimal_payload(self): + data = { + "type": "user", + "id": "user_01EHWNCE74X7JSDV0X3SZ3KJNY", + "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY", + } + instance = UserApiKeyRevokedDataOwner.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_authentication_email_verification_failed_round_trip(self): data = load_fixture("authentication_email_verification_failed.json") instance = AuthenticationEmailVerificationFailed.from_dict(data) @@ -6270,6 +6333,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 +6392,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 +6452,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 +6512,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 +6557,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 +6606,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 +6651,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 +6702,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 +6761,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 +6821,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 +6881,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 +6927,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 +7005,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 +7034,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 +7061,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 +7075,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 +12721,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,37 +13754,15 @@ 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) + def test_organization_domain_stand_alone_round_trip(self): + data = load_fixture("organization_domain_stand_alone.json") + instance = OrganizationDomainStandAlone.from_dict(data) serialized = instance.to_dict() assert serialized == data - restored = JWTTemplateResponse.from_dict(serialized) + restored = OrganizationDomainStandAlone.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) - serialized = instance.to_dict() - assert serialized == data - restored = OrganizationDomainStandAlone.from_dict(serialized) - assert restored.to_dict() == serialized - - def test_organization_domain_stand_alone_minimal_payload(self): + def test_organization_domain_stand_alone_minimal_payload(self): data = { "object": "organization_domain", "id": "org_domain_01EHZNVPK2QXHMVWCEDQEKY69A", @@ -13744,15 +13869,63 @@ 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", + "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", + } + instance = OrganizationApiKey.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_organization_api_key_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", + } + instance = OrganizationApiKey.from_dict(data) + serialized = instance.to_dict() + assert serialized["last_used_at"] is None + + 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 = OrganizationApiKeyWithValue.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_organization_api_key_with_value_minimal_payload(self): data = { "object": "api_key", "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", @@ -13765,7 +13938,7 @@ def test_api_key_with_value_minimal_payload(self): "updated_at": "2026-01-15T12:00:00.000Z", "value": "sk_abcdefghijklmnop123456", } - instance = ApiKeyWithValue.from_dict(data) + instance = OrganizationApiKeyWithValue.from_dict(data) serialized = instance.to_dict() assert serialized["object"] == data["object"] assert serialized["id"] == data["id"] @@ -13778,7 +13951,7 @@ def test_api_key_with_value_minimal_payload(self): 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_with_value_preserves_nullable_fields(self): data = { "object": "api_key", "id": "api_key_01EHZNVPK3SFK441A1RGBFSHRT", @@ -13791,7 +13964,7 @@ def test_api_key_with_value_preserves_nullable_fields(self): "updated_at": "2026-01-15T12:00:00.000Z", "value": "sk_abcdefghijklmnop123456", } - instance = ApiKeyWithValue.from_dict(data) + instance = OrganizationApiKeyWithValue.from_dict(data) serialized = instance.to_dict() assert serialized["last_used_at"] is None @@ -14379,13 +14552,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 +14586,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 +14594,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 +14626,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 +14639,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 +15189,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 +15203,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 +15217,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 +15238,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 +15250,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 +15265,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 +15298,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 +15328,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 +15385,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 +15506,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 +15776,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 +15898,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,91 +16097,21 @@ 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) + def test_user_role_assignment_resource_round_trip(self): + data = load_fixture("user_role_assignment_resource.json") + instance = UserRoleAssignmentResource.from_dict(data) serialized = instance.to_dict() assert serialized == data - restored = RoleAssignmentResource.from_dict(serialized) + restored = UserRoleAssignmentResource.from_dict(serialized) assert restored.to_dict() == serialized - def test_role_assignment_resource_minimal_payload(self): + def test_user_role_assignment_resource_minimal_payload(self): data = { "id": "authz_resource_01HXYZ123456789ABCDEFGH", "external_id": "proj-456", "resource_type_slug": "project", } - instance = RoleAssignmentResource.from_dict(data) + instance = UserRoleAssignmentResource.from_dict(data) serialized = instance.to_dict() assert serialized["id"] == data["id"] assert serialized["external_id"] == data["external_id"] @@ -16123,6 +16588,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 +16615,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 +16628,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 +16666,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_multi_factor_auth.py b/tests/test_multi_factor_auth.py index 146bfb41..ab7c34dd 100644 --- a/tests/test_multi_factor_auth.py +++ b/tests/test_multi_factor_auth.py @@ -6,6 +6,7 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture +from workos.authorization.models import PaginationOrder from workos.common.models import AuthenticationFactorsCreateRequestType from workos.multi_factor_auth.models import ( AuthenticationChallenge, @@ -13,7 +14,6 @@ AuthenticationFactor, AuthenticationFactorEnrolled, UserAuthenticationFactorEnrollResponse, - UserManagementMultiFactorAuthenticationOrder, ) from workos._pagination import AsyncPage, SyncPage from workos._errors import ( @@ -110,13 +110,13 @@ def test_list_user_auth_factors_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=UserManagementMultiFactorAuthenticationOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_create_user_auth_factor(self, workos, httpx_mock): httpx_mock.add_response( @@ -304,13 +304,13 @@ async def test_list_user_auth_factors_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=UserManagementMultiFactorAuthenticationOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_create_user_auth_factor(self, async_workos, httpx_mock): diff --git a/tests/test_organizations.py b/tests/test_organizations.py index 9e300cbe..eb87c59e 100644 --- a/tests/test_organizations.py +++ b/tests/test_organizations.py @@ -6,11 +6,8 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture -from workos.organizations.models import ( - AuditLogConfiguration, - Organization, - OrganizationsOrder, -) +from workos.authorization.models import PaginationOrder +from workos.organizations.models import AuditLogConfiguration, Organization from workos._pagination import AsyncPage, SyncPage from workos._errors import ( AuthenticationError, @@ -44,7 +41,7 @@ def test_list_organizations_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=OrganizationsOrder("normal"), + order=PaginationOrder("value_order"), domains=["val1", "val2"], search="value search/test", ) @@ -52,7 +49,7 @@ def test_list_organizations_encodes_query_params(self, workos, httpx_mock): 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["order"] == "value_order" assert request.url.params["domains"] == "val1,val2" assert request.url.params["search"] == "value search/test" @@ -231,7 +228,7 @@ async def test_list_organizations_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=OrganizationsOrder("normal"), + order=PaginationOrder("value_order"), domains=["val1", "val2"], search="value search/test", ) @@ -239,7 +236,7 @@ async def test_list_organizations_encodes_query_params( 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["order"] == "value_order" assert request.url.params["domains"] == "val1,val2" assert request.url.params["search"] == "value search/test" diff --git a/tests/test_sso.py b/tests/test_sso.py index 4f6903d7..15b50490 100644 --- a/tests/test_sso.py +++ b/tests/test_sso.py @@ -6,13 +6,13 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture +from workos.authorization.models import PaginationOrder from workos.sso.models import ( Connection, Profile, SSOLogoutAuthorizeResponse, SSOTokenResponse, ConnectionsConnectionType, - ConnectionsOrder, ) from workos._pagination import AsyncPage, SyncPage from workos._errors import ( @@ -47,7 +47,7 @@ def test_list_connections_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=ConnectionsOrder("normal"), + order=PaginationOrder("value_order"), connection_type=ConnectionsConnectionType("ADFSSAML"), domain="value domain/test", organization_id="value organization_id/test", @@ -57,7 +57,7 @@ def test_list_connections_encodes_query_params(self, workos, httpx_mock): 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["order"] == "value_order" assert request.url.params["connection_type"] == "ADFSSAML" assert request.url.params["domain"] == "value domain/test" assert request.url.params["organization_id"] == "value organization_id/test" @@ -240,7 +240,7 @@ async def test_list_connections_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=ConnectionsOrder("normal"), + order=PaginationOrder("value_order"), connection_type=ConnectionsConnectionType("ADFSSAML"), domain="value domain/test", organization_id="value organization_id/test", @@ -250,7 +250,7 @@ async def test_list_connections_encodes_query_params( 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["order"] == "value_order" assert request.url.params["connection_type"] == "ADFSSAML" assert request.url.params["domain"] == "value domain/test" assert request.url.params["organization_id"] == "value organization_id/test" diff --git a/tests/test_user_management.py b/tests/test_user_management.py index beb16bb5..bdcffd8e 100644 --- a/tests/test_user_management.py +++ b/tests/test_user_management.py @@ -6,6 +6,7 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture +from workos.authorization.models import PaginationOrder from workos.user_management.models import ( AuthenticateResponse, AuthorizedConnectApplicationListData, @@ -24,15 +25,13 @@ ResetPasswordResponse, SendVerificationEmailResponse, User, + UserApiKey, + UserApiKeyWithValue, UserIdentitiesGetItem, UserInvite, UserOrganizationMembership, UserSessionsListItem, VerifyEmailResponse, - UserManagementInvitationsOrder, - UserManagementOrganizationMembershipOrder, - UserManagementUsersAuthorizedApplicationsOrder, - UserManagementUsersOrder, ) from workos._pagination import AsyncPage, SyncPage from workos._errors import ( @@ -195,7 +194,7 @@ def test_list_users_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=UserManagementUsersOrder("normal"), + order=PaginationOrder("value_order"), organization="value organization/test", organization_id="value organization_id/test", email="value email/test", @@ -204,7 +203,7 @@ def test_list_users_encodes_query_params(self, workos, httpx_mock): 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["order"] == "value_order" assert request.url.params["organization"] == "value organization/test" assert request.url.params["organization_id"] == "value organization_id/test" assert request.url.params["email"] == "value email/test" @@ -363,13 +362,13 @@ def test_list_sessions_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=UserManagementUsersOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_list_invitations(self, workos, httpx_mock): httpx_mock.add_response( @@ -392,7 +391,7 @@ def test_list_invitations_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=UserManagementInvitationsOrder("normal"), + order=PaginationOrder("value_order"), organization_id="value organization_id/test", email="value email/test", ) @@ -400,7 +399,7 @@ def test_list_invitations_encodes_query_params(self, workos, httpx_mock): 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["order"] == "value_order" assert request.url.params["organization_id"] == "value organization_id/test" assert request.url.params["email"] == "value email/test" @@ -480,6 +479,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"), @@ -543,7 +554,7 @@ def test_list_organization_memberships_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=UserManagementOrganizationMembershipOrder("normal"), + order=PaginationOrder("value_order"), organization_id="value organization_id/test", statuses=["val1", "val2"], user_id="value user_id/test", @@ -552,7 +563,7 @@ def test_list_organization_memberships_encodes_query_params( 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["order"] == "value_order" assert request.url.params["organization_id"] == "value organization_id/test" assert request.url.params["statuses"] == "val1,val2" assert request.url.params["user_id"] == "value user_id/test" @@ -682,13 +693,13 @@ def test_list_user_authorized_applications_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=UserManagementUsersAuthorizedApplicationsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_delete_user_authorized_application(self, workos, httpx_mock): httpx_mock.add_response(status_code=204) @@ -702,6 +713,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=PaginationOrder("value_order"), + 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"] == "value_order" + 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( @@ -1010,7 +1070,7 @@ async def test_list_users_encodes_query_params(self, async_workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=UserManagementUsersOrder("normal"), + order=PaginationOrder("value_order"), organization="value organization/test", organization_id="value organization_id/test", email="value email/test", @@ -1019,7 +1079,7 @@ async def test_list_users_encodes_query_params(self, async_workos, httpx_mock): 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["order"] == "value_order" assert request.url.params["organization"] == "value organization/test" assert request.url.params["organization_id"] == "value organization_id/test" assert request.url.params["email"] == "value email/test" @@ -1171,13 +1231,13 @@ async def test_list_sessions_encodes_query_params(self, async_workos, httpx_mock limit=10, before="cursor before", after="cursor/after", - order=UserManagementUsersOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_list_invitations(self, async_workos, httpx_mock): @@ -1203,7 +1263,7 @@ async def test_list_invitations_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=UserManagementInvitationsOrder("normal"), + order=PaginationOrder("value_order"), organization_id="value organization_id/test", email="value email/test", ) @@ -1211,7 +1271,7 @@ async def test_list_invitations_encodes_query_params( 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["order"] == "value_order" assert request.url.params["organization_id"] == "value organization_id/test" assert request.url.params["email"] == "value email/test" @@ -1285,6 +1345,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")) @@ -1350,7 +1421,7 @@ async def test_list_organization_memberships_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=UserManagementOrganizationMembershipOrder("normal"), + order=PaginationOrder("value_order"), organization_id="value organization_id/test", statuses=["val1", "val2"], user_id="value user_id/test", @@ -1359,7 +1430,7 @@ async def test_list_organization_memberships_encodes_query_params( 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["order"] == "value_order" assert request.url.params["organization_id"] == "value organization_id/test" assert request.url.params["statuses"] == "val1,val2" assert request.url.params["user_id"] == "value user_id/test" @@ -1496,13 +1567,13 @@ async def test_list_user_authorized_applications_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=UserManagementUsersAuthorizedApplicationsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_delete_user_authorized_application(self, async_workos, httpx_mock): @@ -1517,6 +1588,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=PaginationOrder("value_order"), + 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"] == "value_order" + 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")) diff --git a/tests/test_user_management_organization_membership_groups.py b/tests/test_user_management_organization_membership_groups.py index 1fec904b..4aa3c6d1 100644 --- a/tests/test_user_management_organization_membership_groups.py +++ b/tests/test_user_management_organization_membership_groups.py @@ -5,10 +5,8 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture +from workos.authorization.models import PaginationOrder from workos.groups.models import Group -from workos.user_management_organization_membership_groups.models import ( - UserManagementOrganizationMembershipGroupsOrder, -) from workos._pagination import AsyncPage, SyncPage from workos._errors import ( AuthenticationError, @@ -49,13 +47,13 @@ def test_list_organization_membership_groups_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=UserManagementOrganizationMembershipGroupsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_list_organization_membership_groups_with_request_options( self, workos, httpx_mock @@ -179,13 +177,13 @@ async def test_list_organization_membership_groups_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=UserManagementOrganizationMembershipGroupsOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_list_organization_membership_groups_with_request_options( diff --git a/tests/test_webhooks.py b/tests/test_webhooks.py index 8ebfee9a..f28bd184 100644 --- a/tests/test_webhooks.py +++ b/tests/test_webhooks.py @@ -6,7 +6,8 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture -from workos.webhooks.models import WebhookEndpointJson, WebhooksOrder +from workos.authorization.models import PaginationOrder +from workos.webhooks.models import WebhookEndpointJson from workos._pagination import AsyncPage, SyncPage from workos._errors import ( AuthenticationError, @@ -40,13 +41,13 @@ def test_list_webhook_endpoints_encodes_query_params(self, workos, httpx_mock): limit=10, before="cursor before", after="cursor/after", - order=WebhooksOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" def test_create_webhook_endpoint(self, workos, httpx_mock): httpx_mock.add_response( @@ -186,13 +187,13 @@ async def test_list_webhook_endpoints_encodes_query_params( limit=10, before="cursor before", after="cursor/after", - order=WebhooksOrder("normal"), + order=PaginationOrder("value_order"), ) 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["order"] == "value_order" @pytest.mark.asyncio async def test_create_webhook_endpoint(self, async_workos, httpx_mock):