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..6dc3b99f 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-06T23:30:58.090Z", "files": [ "src/workos/_client.py", "src/workos/admin_portal/__init__.py", @@ -18,10 +18,11 @@ "src/workos/api_keys/models/api_key.py", "src/workos/api_keys/models/api_key_owner.py", "src/workos/api_keys/models/api_key_validation_response.py", - "src/workos/api_keys/models/api_key_with_value.py", - "src/workos/api_keys/models/api_key_with_value_owner.py", "src/workos/api_keys/models/create_organization_api_key.py", - "src/workos/api_keys/models/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/validate_api_key.py", "src/workos/audit_logs/__init__.py", "src/workos/audit_logs/_resource.py", @@ -41,16 +42,12 @@ "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", - "src/workos/authorization/models/add_role_permission.py", "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", "src/workos/authorization/models/create_authorization_permission.py", @@ -58,25 +55,23 @@ "src/workos/authorization/models/create_organization_role.py", "src/workos/authorization/models/create_role.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", "src/workos/authorization/models/update_authorization_permission.py", "src/workos/authorization/models/update_authorization_resource.py", "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", "src/workos/common/models/action_authentication_denied_data.py", "src/workos/common/models/action_user_registration_denied.py", "src/workos/common/models/action_user_registration_denied_data.py", + "src/workos/common/models/add_role_permission.py", "src/workos/common/models/api_key_created.py", "src/workos/common/models/api_key_created_data.py", "src/workos/common/models/api_key_created_data_owner.py", @@ -88,12 +83,20 @@ "src/workos/common/models/audit_log_configuration_state.py", "src/workos/common/models/audit_log_export_json_state.py", "src/workos/common/models/authenticate_response_authentication_method.py", + "src/workos/common/models/authenticate_response_impersonator.py", + "src/workos/common/models/authentication_challenge.py", "src/workos/common/models/authentication_email_verification_failed.py", "src/workos/common/models/authentication_email_verification_failed_data.py", "src/workos/common/models/authentication_email_verification_failed_data_error.py", "src/workos/common/models/authentication_email_verification_succeeded.py", "src/workos/common/models/authentication_email_verification_succeeded_data.py", + "src/workos/common/models/authentication_factor.py", + "src/workos/common/models/authentication_factor_enrolled.py", + "src/workos/common/models/authentication_factor_enrolled_sms.py", + "src/workos/common/models/authentication_factor_enrolled_totp.py", "src/workos/common/models/authentication_factor_enrolled_type.py", + "src/workos/common/models/authentication_factor_sms.py", + "src/workos/common/models/authentication_factor_totp.py", "src/workos/common/models/authentication_factor_type.py", "src/workos/common/models/authentication_factors_create_request_type.py", "src/workos/common/models/authentication_magic_auth_failed.py", @@ -138,6 +141,8 @@ "src/workos/common/models/authentication_sso_timed_out_data.py", "src/workos/common/models/authentication_sso_timed_out_data_error.py", "src/workos/common/models/authentication_sso_timed_out_data_sso.py", + "src/workos/common/models/authorization_permission.py", + "src/workos/common/models/connect_application.py", "src/workos/common/models/connected_account_state.py", "src/workos/common/models/connection_activated.py", "src/workos/common/models/connection_activated_data.py", @@ -174,6 +179,7 @@ "src/workos/common/models/data_integration_access_token_response_error.py", "src/workos/common/models/data_integrations_list_response_data_connected_account_state.py", "src/workos/common/models/data_integrations_list_response_data_ownership.py", + "src/workos/common/models/directory_group.py", "src/workos/common/models/directory_state.py", "src/workos/common/models/directory_type.py", "src/workos/common/models/directory_user.py", @@ -214,6 +220,9 @@ "src/workos/common/models/event_context_actor.py", "src/workos/common/models/event_context_actor_source.py", "src/workos/common/models/event_context_google_analytics_session.py", + "src/workos/common/models/feature_flag.py", + "src/workos/common/models/feature_flag_owner.py", + "src/workos/common/models/flag.py", "src/workos/common/models/flag_created.py", "src/workos/common/models/flag_created_context.py", "src/workos/common/models/flag_created_context_actor.py", @@ -226,6 +235,7 @@ "src/workos/common/models/flag_deleted_context_actor_source.py", "src/workos/common/models/flag_deleted_data.py", "src/workos/common/models/flag_deleted_data_owner.py", + "src/workos/common/models/flag_owner.py", "src/workos/common/models/flag_rule_updated.py", "src/workos/common/models/flag_rule_updated_context.py", "src/workos/common/models/flag_rule_updated_context_access_type.py", @@ -252,6 +262,7 @@ "src/workos/common/models/flag_updated_data.py", "src/workos/common/models/flag_updated_data_owner.py", "src/workos/common/models/generate_link_intent.py", + "src/workos/common/models/group.py", "src/workos/common/models/group_created.py", "src/workos/common/models/group_deleted.py", "src/workos/common/models/group_member_added.py", @@ -284,6 +295,7 @@ "src/workos/common/models/organization_deleted_data_domain.py", "src/workos/common/models/organization_deleted_data_domain_state.py", "src/workos/common/models/organization_deleted_data_domain_verification_strategy.py", + "src/workos/common/models/organization_domain.py", "src/workos/common/models/organization_domain_created.py", "src/workos/common/models/organization_domain_created_data.py", "src/workos/common/models/organization_domain_created_data_state.py", @@ -332,6 +344,7 @@ "src/workos/common/models/organization_updated_data_domain.py", "src/workos/common/models/organization_updated_data_domain_state.py", "src/workos/common/models/organization_updated_data_domain_verification_strategy.py", + "src/workos/common/models/pagination_order.py", "src/workos/common/models/password_reset_created.py", "src/workos/common/models/password_reset_created_data.py", "src/workos/common/models/password_reset_succeeded.py", @@ -366,21 +379,30 @@ "src/workos/common/models/session_revoked_data_auth_method.py", "src/workos/common/models/session_revoked_data_impersonator.py", "src/workos/common/models/session_revoked_data_status.py", + "src/workos/common/models/slim_role.py", "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.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", "src/workos/common/models/user_invite_state.py", + "src/workos/common/models/user_organization_membership_base_list_data.py", "src/workos/common/models/user_organization_membership_base_list_data_status.py", "src/workos/common/models/user_organization_membership_status.py", "src/workos/common/models/user_sessions_auth_method.py", + "src/workos/common/models/user_sessions_impersonator.py", + "src/workos/common/models/user_sessions_list_item.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,8 +441,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", "src/workos/connect/models/create_oauth_application.py", @@ -435,61 +455,38 @@ "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", "src/workos/multi_factor_auth/models/__init__.py", - "src/workos/multi_factor_auth/models/authentication_challenge.py", "src/workos/multi_factor_auth/models/authentication_challenge_verify_response.py", "src/workos/multi_factor_auth/models/authentication_challenges_verify_request.py", - "src/workos/multi_factor_auth/models/authentication_factor.py", - "src/workos/multi_factor_auth/models/authentication_factor_enrolled.py", - "src/workos/multi_factor_auth/models/authentication_factor_enrolled_sms.py", - "src/workos/multi_factor_auth/models/authentication_factor_enrolled_totp.py", - "src/workos/multi_factor_auth/models/authentication_factor_sms.py", - "src/workos/multi_factor_auth/models/authentication_factor_totp.py", "src/workos/multi_factor_auth/models/authentication_factors_create_request.py", "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", "src/workos/organization_domains/models/create_organization_domain.py", - "src/workos/organization_domains/models/organization_domain.py", "src/workos/organization_domains/models/organization_domain_stand_alone.py", "src/workos/organizations/__init__.py", "src/workos/organizations/_resource.py", @@ -500,7 +497,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 +529,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", @@ -566,7 +561,6 @@ "src/workos/user_management/_resource.py", "src/workos/user_management/models/__init__.py", "src/workos/user_management/models/authenticate_response.py", - "src/workos/user_management/models/authenticate_response_impersonator.py", "src/workos/user_management/models/authenticate_response_oauth_token.py", "src/workos/user_management/models/authorization_code_session_authenticate_request.py", "src/workos/user_management/models/authorized_connect_application_list_data.py", @@ -578,6 +572,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", @@ -609,32 +604,27 @@ "src/workos/user_management/models/update_jwt_template.py", "src/workos/user_management/models/update_user.py", "src/workos/user_management/models/update_user_organization_membership.py", - "src/workos/user_management/models/user.py", + "src/workos/user_management/models/user_api_key.py", + "src/workos/user_management/models/user_api_key_owner.py", + "src/workos/user_management/models/user_api_key_with_value.py", + "src/workos/user_management/models/user_api_key_with_value_owner.py", "src/workos/user_management/models/user_identities_get_item.py", "src/workos/user_management/models/user_invite.py", "src/workos/user_management/models/user_management_authentication_provider.py", "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", "src/workos/user_management/models/verify_email_address.py", "src/workos/user_management/models/verify_email_response.py", "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/pyproject.toml b/pyproject.toml index 1504c082..8663995c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ test = [ lint = ["ruff~=0.15"] type_check = ["pyright~=1.1"] nox = ["nox~=2026.2", "nox-uv~=0.7"] -docs = ["pdoc>=14", "pydoc-markdown>=4"] +docs = ["pdoc>=14", "pydoc-markdown>=4", "black~=26.3"] [tool.pytest.ini_options] 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..e72adfb6 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.common.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. @@ -102,7 +47,7 @@ def list_organization_api_keys( request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - SyncPage[ApiKey] + SyncPage[OrganizationApiKey] Raises: NotFoundError: If the resource is not found (404). @@ -122,8 +67,8 @@ def list_organization_api_keys( } return self._client.request_page( method="get", - path=f"organizations/{organization_id}/api_keys", - model=ApiKey, + path=f"organizations/{quote(str(organization_id), safe='')}/api_keys", + model=OrganizationApiKey, params=params, request_options=request_options, ) @@ -135,7 +80,7 @@ def create_organization_api_key( name: str, permissions: Optional[List[str]] = None, request_options: Optional[RequestOptions] = None, - ) -> ApiKeyWithValue: + ) -> OrganizationApiKeyWithValue: """Create an API key for an organization Create a new API key for an organization. @@ -147,7 +92,7 @@ def create_organization_api_key( request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ApiKeyWithValue + OrganizationApiKeyWithValue Raises: NotFoundError: If the resource is not found (404). @@ -166,20 +111,13 @@ def create_organization_api_key( } return self._client.request( method="post", - path=f"organizations/{organization_id}/api_keys", + path=f"organizations/{quote(str(organization_id), safe='')}/api_keys", body=body, - model=ApiKeyWithValue, + model=OrganizationApiKeyWithValue, request_options=request_options, ) - -class AsyncApiKeys: - """Api Keys API resources (async).""" - - def __init__(self, client: "AsyncWorkOSClient") -> None: - self._client = client - - async def create_validation( + def create_validation( self, *, value: str, @@ -205,7 +143,7 @@ async def create_validation( body: Dict[str, Any] = { "value": value, } - return await self._client.request( + return self._client.request( method="post", path="api_keys/validations", body=body, @@ -213,7 +151,7 @@ async def create_validation( request_options=request_options, ) - async def delete_api_key( + def delete_api_key( self, id: str, *, @@ -233,12 +171,19 @@ async def delete_api_key( RateLimitExceededError: If rate limited (429). ServerError: If the server returns a 5xx error. """ - await self._client.request( + self._client.request( method="delete", - path=f"api_keys/{id}", + path=f"api_keys/{quote(str(id), safe='')}", request_options=request_options, ) + +class AsyncApiKeys: + """Api Keys API resources (async).""" + + def __init__(self, client: "AsyncWorkOSClient") -> None: + self._client = client + async def list_organization_api_keys( self, organization_id: str, @@ -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. @@ -262,7 +207,7 @@ async def list_organization_api_keys( request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - AsyncPage[ApiKey] + AsyncPage[OrganizationApiKey] Raises: NotFoundError: If the resource is not found (404). @@ -282,8 +227,8 @@ async def list_organization_api_keys( } return await self._client.request_page( method="get", - path=f"organizations/{organization_id}/api_keys", - model=ApiKey, + path=f"organizations/{quote(str(organization_id), safe='')}/api_keys", + model=OrganizationApiKey, params=params, request_options=request_options, ) @@ -295,7 +240,7 @@ async def create_organization_api_key( name: str, permissions: Optional[List[str]] = None, request_options: Optional[RequestOptions] = None, - ) -> ApiKeyWithValue: + ) -> OrganizationApiKeyWithValue: """Create an API key for an organization Create a new API key for an organization. @@ -307,7 +252,7 @@ async def create_organization_api_key( request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: - ApiKeyWithValue + OrganizationApiKeyWithValue Raises: NotFoundError: If the resource is not found (404). @@ -326,8 +271,68 @@ async def create_organization_api_key( } return await self._client.request( method="post", - path=f"organizations/{organization_id}/api_keys", + path=f"organizations/{quote(str(organization_id), safe='')}/api_keys", + body=body, + model=OrganizationApiKeyWithValue, + request_options=request_options, + ) + + async def create_validation( + self, + *, + value: str, + request_options: Optional[RequestOptions] = None, + ) -> ApiKeyValidationResponse: + """Validate API key + + Validate an API key value and return the API key object if valid. + + Args: + value: The value for an API key. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + ApiKeyValidationResponse + + Raises: + AuthenticationError: If the API key is invalid (401). + UnprocessableEntityError: If the request data is unprocessable (422). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + body: Dict[str, Any] = { + "value": value, + } + return await self._client.request( + method="post", + path="api_keys/validations", body=body, - model=ApiKeyWithValue, + model=ApiKeyValidationResponse, + request_options=request_options, + ) + + async def delete_api_key( + self, + id: str, + *, + request_options: Optional[RequestOptions] = None, + ) -> None: + """Delete an API key + + Permanently deletes an API key. This action cannot be undone. Once deleted, any requests using this API key will fail authentication. + + Args: + id: The unique ID of the API key. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Raises: + NotFoundError: If the resource is not found (404). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + await self._client.request( + method="delete", + path=f"api_keys/{quote(str(id), safe='')}", request_options=request_options, ) diff --git a/src/workos/api_keys/models/__init__.py b/src/workos/api_keys/models/__init__.py index 86ecd07b..340528a7 100644 --- a/src/workos/api_keys/models/__init__.py +++ b/src/workos/api_keys/models/__init__.py @@ -5,12 +5,17 @@ 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 .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..dcc59ed6 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 workos.user_management.models.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.""" @@ -39,10 +40,23 @@ class ApiKey: def from_dict(cls, data: Dict[str, Any]) -> "ApiKey": """Deserialize from a dictionary.""" try: + _owner_raw = data["owner"] + _owner_data = cast(Dict[str, Any], _owner_raw) + _owner_disc = cast(str, _owner_data.get("type")) + _owner_disc_map: Dict[str, Any] = { + "organization": ApiKeyOwner, + "user": UserApiKeyOwner, + } + _owner_cls = _owner_disc_map.get(_owner_disc) + if _owner_cls is None: + raise ValueError( + f"Unknown discriminator 'type' for ApiKey.owner: {_owner_disc!r}. " + f"Expected one of {sorted(_owner_disc_map)}." + ) return cls( object=data.get("object", "api_key"), id=data["id"], - owner=ApiKeyOwner.from_dict(cast(Dict[str, Any], data["owner"])), + owner=_owner_cls.from_dict(_owner_data), name=data["name"], obfuscated_value=data["obfuscated_value"], last_used_at=_parse_datetime(_v_last_used_at) 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/audit_logs/_resource.py b/src/workos/audit_logs/_resource.py index e70ccd18..9c7dc15b 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.common.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 @@ -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 @@ -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 @@ -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 @@ -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..b55c3acc 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 @@ -10,15 +11,18 @@ from .._types import RequestOptions, enum_value from .models import ( AuthorizationCheck, - AuthorizationPermission, AuthorizationResource, Permission, Role, - RoleAssignment, RoleList, + UserRoleAssignment, +) +from workos.common.models.authorization_permission import AuthorizationPermission +from workos.common.models.user_organization_membership_base_list_data import ( UserOrganizationMembershipBaseListData, ) -from .models import AuthorizationAssignment, AuthorizationOrder, PermissionsOrder +from .models import AuthorizationAssignment +from workos.common.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage from dataclasses import dataclass @@ -113,7 +117,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 +130,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, @@ -181,7 +185,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 +199,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 @@ -234,7 +238,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 +253,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 @@ -289,7 +293,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 +306,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. @@ -318,7 +322,7 @@ def list_role_assignments( 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 +343,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 +356,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 +368,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 +388,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 +430,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 +460,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 +490,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 +542,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 +576,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 +623,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 +656,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 +695,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 +735,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 +768,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 +802,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 +866,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 +910,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 +924,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, @@ -969,19 +973,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. Defaults to `desc`. + 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, @@ -1138,7 +1196,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 +1256,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 +1296,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 +1308,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, @@ -1297,12 +1355,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. Defaults to `desc`. + 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 +1513,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 +1558,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 +1597,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 +1636,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 +1648,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 @@ -1656,7 +1764,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 +1808,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 +1837,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 +1887,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 +1900,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, @@ -1847,7 +1955,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 +1969,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 @@ -1900,7 +2008,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 +2023,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 @@ -1955,7 +2063,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 +2076,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. @@ -1984,7 +2092,7 @@ async def list_role_assignments( 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 +2113,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 +2126,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 +2138,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 +2158,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 +2200,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 +2230,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 +2260,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 +2312,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 +2346,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 +2393,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 +2426,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 +2465,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 +2505,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 +2538,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 +2572,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 +2636,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 +2680,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 +2694,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, @@ -2635,19 +2743,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. Defaults to `desc`. + 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, @@ -2804,7 +2966,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 +3026,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 +3066,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 +3078,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, @@ -2963,12 +3125,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. Defaults to `desc`. + 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 +3283,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 +3328,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 +3367,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 +3406,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 +3418,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 @@ -3322,7 +3534,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 +3578,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 +3607,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..cc94713f 100644 --- a/src/workos/authorization/models/__init__.py +++ b/src/workos/authorization/models/__init__.py @@ -1,11 +1,14 @@ # This file is auto-generated by oagen. Do not edit. -from .add_role_permission import AddRolePermission as AddRolePermission +from workos.common.models.add_role_permission import ( + AddRolePermission as AddRolePermission, +) 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 workos.common.models.authorization_permission import ( + AuthorizationPermission as AuthorizationPermission, +) from .authorization_resource import AuthorizationResource as AuthorizationResource from .check_authorization import CheckAuthorization as CheckAuthorization from .create_authorization_permission import ( @@ -16,15 +19,13 @@ ) from .create_organization_role import CreateOrganizationRole as CreateOrganizationRole from .create_role import CreateRole as CreateRole +from workos.common.models.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 +from workos.common.models.slim_role import SlimRole as SlimRole from .update_authorization_permission import ( UpdateAuthorizationPermission as UpdateAuthorizationPermission, ) @@ -33,6 +34,10 @@ ) from .update_organization_role import UpdateOrganizationRole as UpdateOrganizationRole from .update_role import UpdateRole as UpdateRole -from .user_organization_membership_base_list_data import ( +from workos.common.models.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/authorization/models/permission.py b/src/workos/authorization/models/permission.py index 4957be8a..2d4dccd2 100644 --- a/src/workos/authorization/models/permission.py +++ b/src/workos/authorization/models/permission.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from .authorization_permission import AuthorizationPermission +from workos.common.models.authorization_permission import AuthorizationPermission Permission: TypeAlias = AuthorizationPermission 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/role_assignment.py b/src/workos/authorization/models/user_role_assignment.py similarity index 69% rename from src/workos/authorization/models/role_assignment.py rename to src/workos/authorization/models/user_role_assignment.py index 89ab276d..a2b9b45b 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 workos.common.models.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..de763f73 100644 --- a/src/workos/common/__init__.py +++ b/src/workos/common/__init__.py @@ -4,6 +4,7 @@ from .models import ActionAuthenticationDeniedData as ActionAuthenticationDeniedData from .models import ActionUserRegistrationDenied as ActionUserRegistrationDenied from .models import ActionUserRegistrationDeniedData as ActionUserRegistrationDeniedData +from .models import AddRolePermission as AddRolePermission from .models import ApiKeyCreated as ApiKeyCreated from .models import ApiKeyCreatedData as ApiKeyCreatedData from .models import ApiKeyCreatedDataOwner as ApiKeyCreatedDataOwner @@ -21,6 +22,8 @@ from .models import ( AuthenticateResponseAuthenticationMethod as AuthenticateResponseAuthenticationMethod, ) +from .models import AuthenticateResponseImpersonator as AuthenticateResponseImpersonator +from .models import AuthenticationChallenge as AuthenticationChallenge from .models import ( AuthenticationEmailVerificationFailed as AuthenticationEmailVerificationFailed, ) @@ -36,11 +39,17 @@ from .models import ( AuthenticationEmailVerificationSucceededData as AuthenticationEmailVerificationSucceededData, ) +from .models import AuthenticationFactor as AuthenticationFactor +from .models import AuthenticationFactorEnrolled as AuthenticationFactorEnrolled +from .models import AuthenticationFactorEnrolledSms as AuthenticationFactorEnrolledSms +from .models import AuthenticationFactorEnrolledTotp as AuthenticationFactorEnrolledTotp from .models import AuthenticationFactorEnrolledType as AuthenticationFactorEnrolledType -from .models import AuthenticationFactorType as AuthenticationFactorType from .models import ( AuthenticationFactorsCreateRequestType as AuthenticationFactorsCreateRequestType, ) +from .models import AuthenticationFactorSms as AuthenticationFactorSms +from .models import AuthenticationFactorTotp as AuthenticationFactorTotp +from .models import AuthenticationFactorType as AuthenticationFactorType from .models import AuthenticationMagicAuthFailed as AuthenticationMagicAuthFailed from .models import ( AuthenticationMagicAuthFailedData as AuthenticationMagicAuthFailedData, @@ -104,6 +113,8 @@ AuthenticationSSOTimedOutDataError as AuthenticationSSOTimedOutDataError, ) from .models import AuthenticationSSOTimedOutDataSSO as AuthenticationSSOTimedOutDataSSO +from .models import AuthorizationPermission as AuthorizationPermission +from .models import ConnectApplication as ConnectApplication from .models import ConnectedAccountState as ConnectedAccountState from .models import ConnectionActivated as ConnectionActivated from .models import ConnectionActivatedData as ConnectionActivatedData @@ -150,6 +161,7 @@ from .models import ( DataIntegrationsListResponseDataOwnership as DataIntegrationsListResponseDataOwnership, ) +from .models import DirectoryGroup as DirectoryGroup from .models import DirectoryState as DirectoryState from .models import DirectoryType as DirectoryType from .models import DirectoryUser as DirectoryUser @@ -183,6 +195,9 @@ from .models import ( EventContextGoogleAnalyticsSession as EventContextGoogleAnalyticsSession, ) +from .models import FeatureFlag as FeatureFlag +from .models import FeatureFlagOwner as FeatureFlagOwner +from .models import Flag as Flag from .models import FlagCreated as FlagCreated from .models import FlagCreatedContext as FlagCreatedContext from .models import FlagCreatedContextActor as FlagCreatedContextActor @@ -193,6 +208,7 @@ from .models import FlagDeletedContextActor as FlagDeletedContextActor from .models import FlagDeletedData as FlagDeletedData from .models import FlagDeletedDataOwner as FlagDeletedDataOwner +from .models import FlagOwner as FlagOwner from .models import FlagRuleUpdated as FlagRuleUpdated from .models import FlagRuleUpdatedContext as FlagRuleUpdatedContext from .models import FlagRuleUpdatedContextActor as FlagRuleUpdatedContextActor @@ -237,6 +253,7 @@ from .models import FlagUpdatedData as FlagUpdatedData from .models import FlagUpdatedDataOwner as FlagUpdatedDataOwner from .models import GenerateLinkIntent as GenerateLinkIntent +from .models import Group as Group from .models import GroupCreated as GroupCreated from .models import GroupDeleted as GroupDeleted from .models import GroupMemberAdded as GroupMemberAdded @@ -261,6 +278,7 @@ from .models import OrganizationDeleted as OrganizationDeleted from .models import OrganizationDeletedData as OrganizationDeletedData from .models import OrganizationDeletedDataDomain as OrganizationDeletedDataDomain +from .models import OrganizationDomain as OrganizationDomain from .models import OrganizationDomainCreated as OrganizationDomainCreated from .models import OrganizationDomainCreatedData as OrganizationDomainCreatedData from .models import OrganizationDomainDataState as OrganizationDomainDataState @@ -311,6 +329,7 @@ from .models import OrganizationUpdated as OrganizationUpdated from .models import OrganizationUpdatedData as OrganizationUpdatedData from .models import OrganizationUpdatedDataDomain as OrganizationUpdatedDataDomain +from .models import PaginationOrder as PaginationOrder from .models import PasswordResetCreated as PasswordResetCreated from .models import PasswordResetCreatedData as PasswordResetCreatedData from .models import PasswordResetSucceeded as PasswordResetSucceeded @@ -347,20 +366,31 @@ from .models import SessionRevoked as SessionRevoked from .models import SessionRevokedData as SessionRevokedData from .models import SessionRevokedDataImpersonator as SessionRevokedDataImpersonator +from .models import SlimRole as SlimRole from .models import UpdateUserPasswordHashType as UpdateUserPasswordHashType from .models import UpdateWebhookEndpointEvents as UpdateWebhookEndpointEvents from .models import UpdateWebhookEndpointStatus as UpdateWebhookEndpointStatus +from .models import User as User +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 from .models import UserInviteState as UserInviteState +from .models import ( + UserOrganizationMembershipBaseListData as UserOrganizationMembershipBaseListData, +) from .models import ( UserOrganizationMembershipBaseListDataStatus as UserOrganizationMembershipBaseListDataStatus, ) from .models import UserOrganizationMembershipStatus as UserOrganizationMembershipStatus from .models import UserSessionsAuthMethod as UserSessionsAuthMethod +from .models import UserSessionsImpersonator as UserSessionsImpersonator +from .models import UserSessionsListItem as UserSessionsListItem 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..b21bb204 100644 --- a/src/workos/common/models/__init__.py +++ b/src/workos/common/models/__init__.py @@ -12,6 +12,7 @@ from .action_user_registration_denied_data import ( ActionUserRegistrationDeniedData as ActionUserRegistrationDeniedData, ) +from .add_role_permission import AddRolePermission as AddRolePermission from .api_key_created import ApiKeyCreated as ApiKeyCreated from .api_key_created_data import ApiKeyCreatedData as ApiKeyCreatedData from .api_key_created_data_owner import ApiKeyCreatedDataOwner as ApiKeyCreatedDataOwner @@ -33,6 +34,10 @@ from .authenticate_response_authentication_method import ( AuthenticateResponseAuthenticationMethod as AuthenticateResponseAuthenticationMethod, ) +from .authenticate_response_impersonator import ( + AuthenticateResponseImpersonator as AuthenticateResponseImpersonator, +) +from .authentication_challenge import AuthenticationChallenge as AuthenticationChallenge from .authentication_email_verification_failed import ( AuthenticationEmailVerificationFailed as AuthenticationEmailVerificationFailed, ) @@ -48,15 +53,31 @@ from .authentication_email_verification_succeeded_data import ( AuthenticationEmailVerificationSucceededData as AuthenticationEmailVerificationSucceededData, ) +from .authentication_factor import AuthenticationFactor as AuthenticationFactor +from .authentication_factor_enrolled import ( + AuthenticationFactorEnrolled as AuthenticationFactorEnrolled, +) +from .authentication_factor_enrolled_sms import ( + AuthenticationFactorEnrolledSms as AuthenticationFactorEnrolledSms, +) +from .authentication_factor_enrolled_totp import ( + AuthenticationFactorEnrolledTotp as AuthenticationFactorEnrolledTotp, +) from .authentication_factor_enrolled_type import ( AuthenticationFactorEnrolledType as AuthenticationFactorEnrolledType, ) -from .authentication_factor_type import ( - AuthenticationFactorType as AuthenticationFactorType, -) from .authentication_factors_create_request_type import ( AuthenticationFactorsCreateRequestType as AuthenticationFactorsCreateRequestType, ) +from .authentication_factor_sms import ( + AuthenticationFactorSms as AuthenticationFactorSms, +) +from .authentication_factor_totp import ( + AuthenticationFactorTotp as AuthenticationFactorTotp, +) +from .authentication_factor_type import ( + AuthenticationFactorType as AuthenticationFactorType, +) from .authentication_magic_auth_failed import ( AuthenticationMagicAuthFailed as AuthenticationMagicAuthFailed, ) @@ -180,6 +201,8 @@ from .authentication_sso_timed_out_data_sso import ( AuthenticationSSOTimedOutDataSSO as AuthenticationSSOTimedOutDataSSO, ) +from .authorization_permission import AuthorizationPermission as AuthorizationPermission +from .connect_application import ConnectApplication as ConnectApplication from .connected_account_state import ConnectedAccountState as ConnectedAccountState from .connection_activated import ConnectionActivated as ConnectionActivated from .connection_activated_data import ( @@ -242,6 +265,7 @@ from .data_integrations_list_response_data_ownership import ( DataIntegrationsListResponseDataOwnership as DataIntegrationsListResponseDataOwnership, ) +from .directory_group import DirectoryGroup as DirectoryGroup from .directory_state import DirectoryState as DirectoryState from .directory_type import DirectoryType as DirectoryType from .directory_user import DirectoryUser as DirectoryUser @@ -291,6 +315,9 @@ from .event_context_google_analytics_session import ( EventContextGoogleAnalyticsSession as EventContextGoogleAnalyticsSession, ) +from .feature_flag import FeatureFlag as FeatureFlag +from .feature_flag_owner import FeatureFlagOwner as FeatureFlagOwner +from .flag import Flag as Flag from .flag_created import FlagCreated as FlagCreated from .flag_created_context import FlagCreatedContext as FlagCreatedContext from .flag_created_context_actor import ( @@ -305,6 +332,7 @@ ) from .flag_deleted_data import FlagDeletedData as FlagDeletedData from .flag_deleted_data_owner import FlagDeletedDataOwner as FlagDeletedDataOwner +from .flag_owner import FlagOwner as FlagOwner from .flag_rule_updated import FlagRuleUpdated as FlagRuleUpdated from .flag_rule_updated_context import FlagRuleUpdatedContext as FlagRuleUpdatedContext from .flag_rule_updated_context_actor import ( @@ -355,6 +383,7 @@ from .flag_updated_data import FlagUpdatedData as FlagUpdatedData from .flag_updated_data_owner import FlagUpdatedDataOwner as FlagUpdatedDataOwner from .generate_link_intent import GenerateLinkIntent as GenerateLinkIntent +from .group import Group as Group from .group_created import GroupCreated as GroupCreated from .group_deleted import GroupDeleted as GroupDeleted from .group_member_added import GroupMemberAdded as GroupMemberAdded @@ -387,6 +416,7 @@ from .organization_deleted_data_domain import ( OrganizationDeletedDataDomain as OrganizationDeletedDataDomain, ) +from .organization_domain import OrganizationDomain as OrganizationDomain from .organization_domain_created import ( OrganizationDomainCreated as OrganizationDomainCreated, ) @@ -481,6 +511,7 @@ from .organization_updated_data_domain import ( OrganizationUpdatedDataDomain as OrganizationUpdatedDataDomain, ) +from .pagination_order import PaginationOrder as PaginationOrder from .password_reset_created import PasswordResetCreated as PasswordResetCreated from .password_reset_created_data import ( PasswordResetCreatedData as PasswordResetCreatedData, @@ -531,6 +562,7 @@ from .session_revoked_data_impersonator import ( SessionRevokedDataImpersonator as SessionRevokedDataImpersonator, ) +from .slim_role import SlimRole as SlimRole from .update_user_password_hash_type import ( UpdateUserPasswordHashType as UpdateUserPasswordHashType, ) @@ -540,12 +572,22 @@ from .update_webhook_endpoint_status import ( UpdateWebhookEndpointStatus as UpdateWebhookEndpointStatus, ) +from .user import User as User +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 ( UserIdentitiesGetItemProvider as UserIdentitiesGetItemProvider, ) from .user_invite_state import UserInviteState as UserInviteState +from .user_organization_membership_base_list_data import ( + UserOrganizationMembershipBaseListData as UserOrganizationMembershipBaseListData, +) from .user_organization_membership_base_list_data_status import ( UserOrganizationMembershipBaseListDataStatus as UserOrganizationMembershipBaseListDataStatus, ) @@ -553,8 +595,16 @@ UserOrganizationMembershipStatus as UserOrganizationMembershipStatus, ) from .user_sessions_auth_method import UserSessionsAuthMethod as UserSessionsAuthMethod +from .user_sessions_impersonator import ( + UserSessionsImpersonator as UserSessionsImpersonator, +) +from .user_sessions_list_item import UserSessionsListItem as UserSessionsListItem 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/authorization/models/add_role_permission.py b/src/workos/common/models/add_role_permission.py similarity index 100% rename from src/workos/authorization/models/add_role_permission.py rename to src/workos/common/models/add_role_permission.py diff --git a/src/workos/common/models/api_key_created_data.py b/src/workos/common/models/api_key_created_data.py index bfa07413..e075c098 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.""" @@ -37,12 +38,23 @@ class ApiKeyCreatedData: def from_dict(cls, data: Dict[str, Any]) -> "ApiKeyCreatedData": """Deserialize from a dictionary.""" try: + _owner_raw = data["owner"] + _owner_data = cast(Dict[str, Any], _owner_raw) + _owner_disc = cast(str, _owner_data.get("type")) + _owner_disc_map: Dict[str, Any] = { + "organization": ApiKeyCreatedDataOwner, + "user": UserApiKeyCreatedDataOwner, + } + _owner_cls = _owner_disc_map.get(_owner_disc) + if _owner_cls is None: + raise ValueError( + f"Unknown discriminator 'type' for ApiKeyCreatedData.owner: {_owner_disc!r}. " + f"Expected one of {sorted(_owner_disc_map)}." + ) return cls( object=data.get("object", "api_key"), id=data["id"], - owner=ApiKeyCreatedDataOwner.from_dict( - cast(Dict[str, Any], data["owner"]) - ), + owner=_owner_cls.from_dict(_owner_data), name=data["name"], obfuscated_value=data["obfuscated_value"], last_used_at=data["last_used_at"], 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/user_management/models/authenticate_response_impersonator.py b/src/workos/common/models/authenticate_response_impersonator.py similarity index 100% rename from src/workos/user_management/models/authenticate_response_impersonator.py rename to src/workos/common/models/authenticate_response_impersonator.py diff --git a/src/workos/multi_factor_auth/models/authentication_challenge.py b/src/workos/common/models/authentication_challenge.py similarity index 100% rename from src/workos/multi_factor_auth/models/authentication_challenge.py rename to src/workos/common/models/authentication_challenge.py diff --git a/src/workos/multi_factor_auth/models/authentication_factor.py b/src/workos/common/models/authentication_factor.py similarity index 97% rename from src/workos/multi_factor_auth/models/authentication_factor.py rename to src/workos/common/models/authentication_factor.py index 39773c18..55d5a852 100644 --- a/src/workos/multi_factor_auth/models/authentication_factor.py +++ b/src/workos/common/models/authentication_factor.py @@ -12,7 +12,7 @@ from .authentication_factor_sms import AuthenticationFactorSms from .authentication_factor_totp import AuthenticationFactorTotp -from workos.common.models.authentication_factor_type import AuthenticationFactorType +from .authentication_factor_type import AuthenticationFactorType @dataclass(slots=True) diff --git a/src/workos/multi_factor_auth/models/authentication_factor_enrolled.py b/src/workos/common/models/authentication_factor_enrolled.py similarity index 96% rename from src/workos/multi_factor_auth/models/authentication_factor_enrolled.py rename to src/workos/common/models/authentication_factor_enrolled.py index 61a52dbb..96ab3e0e 100644 --- a/src/workos/multi_factor_auth/models/authentication_factor_enrolled.py +++ b/src/workos/common/models/authentication_factor_enrolled.py @@ -12,9 +12,7 @@ from .authentication_factor_enrolled_sms import AuthenticationFactorEnrolledSms from .authentication_factor_enrolled_totp import AuthenticationFactorEnrolledTotp -from workos.common.models.authentication_factor_enrolled_type import ( - AuthenticationFactorEnrolledType, -) +from .authentication_factor_enrolled_type import AuthenticationFactorEnrolledType @dataclass(slots=True) diff --git a/src/workos/multi_factor_auth/models/authentication_factor_enrolled_sms.py b/src/workos/common/models/authentication_factor_enrolled_sms.py similarity index 100% rename from src/workos/multi_factor_auth/models/authentication_factor_enrolled_sms.py rename to src/workos/common/models/authentication_factor_enrolled_sms.py diff --git a/src/workos/multi_factor_auth/models/authentication_factor_enrolled_totp.py b/src/workos/common/models/authentication_factor_enrolled_totp.py similarity index 100% rename from src/workos/multi_factor_auth/models/authentication_factor_enrolled_totp.py rename to src/workos/common/models/authentication_factor_enrolled_totp.py diff --git a/src/workos/multi_factor_auth/models/authentication_factor_sms.py b/src/workos/common/models/authentication_factor_sms.py similarity index 100% rename from src/workos/multi_factor_auth/models/authentication_factor_sms.py rename to src/workos/common/models/authentication_factor_sms.py diff --git a/src/workos/multi_factor_auth/models/authentication_factor_totp.py b/src/workos/common/models/authentication_factor_totp.py similarity index 100% rename from src/workos/multi_factor_auth/models/authentication_factor_totp.py rename to src/workos/common/models/authentication_factor_totp.py diff --git a/src/workos/authorization/models/authorization_permission.py b/src/workos/common/models/authorization_permission.py similarity index 100% rename from src/workos/authorization/models/authorization_permission.py rename to src/workos/common/models/authorization_permission.py diff --git a/src/workos/connect/models/connect_application.py b/src/workos/common/models/connect_application.py similarity index 100% rename from src/workos/connect/models/connect_application.py rename to src/workos/common/models/connect_application.py diff --git a/src/workos/directory_sync/models/directory_group.py b/src/workos/common/models/directory_group.py similarity index 100% rename from src/workos/directory_sync/models/directory_group.py rename to src/workos/common/models/directory_group.py diff --git a/src/workos/common/models/directory_user.py b/src/workos/common/models/directory_user.py index 37772c9f..fdbdb527 100644 --- a/src/workos/common/models/directory_user.py +++ b/src/workos/common/models/directory_user.py @@ -11,7 +11,7 @@ from workos._types import _format_datetime, _parse_datetime from .directory_user_email import DirectoryUserEmail -from workos.authorization.models.slim_role import SlimRole +from .slim_role import SlimRole from .directory_user_state import DirectoryUserState @@ -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_group_created.py b/src/workos/common/models/dsync_group_created.py index 5e70cdb6..e6d91496 100644 --- a/src/workos/common/models/dsync_group_created.py +++ b/src/workos/common/models/dsync_group_created.py @@ -9,7 +9,7 @@ from workos._types import _raise_deserialize_error from workos._types import _format_datetime, _parse_datetime -from workos.directory_sync.models.directory_group import DirectoryGroup +from .directory_group import DirectoryGroup from .event_context import EventContext diff --git a/src/workos/common/models/dsync_group_deleted.py b/src/workos/common/models/dsync_group_deleted.py index 7107a0ee..fb835f01 100644 --- a/src/workos/common/models/dsync_group_deleted.py +++ b/src/workos/common/models/dsync_group_deleted.py @@ -9,7 +9,7 @@ from workos._types import _raise_deserialize_error from workos._types import _format_datetime, _parse_datetime -from workos.directory_sync.models.directory_group import DirectoryGroup +from .directory_group import DirectoryGroup from .event_context import EventContext diff --git a/src/workos/common/models/dsync_group_user_added_data.py b/src/workos/common/models/dsync_group_user_added_data.py index a329349f..e06eadd7 100644 --- a/src/workos/common/models/dsync_group_user_added_data.py +++ b/src/workos/common/models/dsync_group_user_added_data.py @@ -7,7 +7,7 @@ from typing import Any, Dict from workos._types import _raise_deserialize_error -from workos.directory_sync.models.directory_group import DirectoryGroup +from .directory_group import DirectoryGroup from .directory_user import DirectoryUser diff --git a/src/workos/common/models/dsync_user_updated_data.py b/src/workos/common/models/dsync_user_updated_data.py index 90956d01..82715e5c 100644 --- a/src/workos/common/models/dsync_user_updated_data.py +++ b/src/workos/common/models/dsync_user_updated_data.py @@ -11,7 +11,7 @@ from workos._types import _format_datetime, _parse_datetime from .dsync_user_updated_data_email import DsyncUserUpdatedDataEmail -from workos.authorization.models.slim_role import SlimRole +from .slim_role import SlimRole from .dsync_user_updated_data_state import DsyncUserUpdatedDataState @@ -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/feature_flags/models/feature_flag.py b/src/workos/common/models/feature_flag.py similarity index 100% rename from src/workos/feature_flags/models/feature_flag.py rename to src/workos/common/models/feature_flag.py diff --git a/src/workos/feature_flags/models/feature_flag_owner.py b/src/workos/common/models/feature_flag_owner.py similarity index 100% rename from src/workos/feature_flags/models/feature_flag_owner.py rename to src/workos/common/models/feature_flag_owner.py diff --git a/src/workos/feature_flags/models/flag.py b/src/workos/common/models/flag.py similarity index 100% rename from src/workos/feature_flags/models/flag.py rename to src/workos/common/models/flag.py diff --git a/src/workos/common/models/flag_created_data_owner.py b/src/workos/common/models/flag_created_data_owner.py index 0130feeb..470b237d 100644 --- a/src/workos/common/models/flag_created_data_owner.py +++ b/src/workos/common/models/flag_created_data_owner.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.feature_flags.models.feature_flag_owner import FeatureFlagOwner +from .feature_flag_owner import FeatureFlagOwner FlagCreatedDataOwner: TypeAlias = FeatureFlagOwner diff --git a/src/workos/common/models/flag_deleted_data_owner.py b/src/workos/common/models/flag_deleted_data_owner.py index 39872ec7..cce2779d 100644 --- a/src/workos/common/models/flag_deleted_data_owner.py +++ b/src/workos/common/models/flag_deleted_data_owner.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.feature_flags.models.feature_flag_owner import FeatureFlagOwner +from .feature_flag_owner import FeatureFlagOwner FlagDeletedDataOwner: TypeAlias = FeatureFlagOwner diff --git a/src/workos/feature_flags/models/flag_owner.py b/src/workos/common/models/flag_owner.py similarity index 100% rename from src/workos/feature_flags/models/flag_owner.py rename to src/workos/common/models/flag_owner.py diff --git a/src/workos/common/models/flag_rule_updated_data_owner.py b/src/workos/common/models/flag_rule_updated_data_owner.py index 8ef7f1f2..609d433f 100644 --- a/src/workos/common/models/flag_rule_updated_data_owner.py +++ b/src/workos/common/models/flag_rule_updated_data_owner.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.feature_flags.models.feature_flag_owner import FeatureFlagOwner +from .feature_flag_owner import FeatureFlagOwner FlagRuleUpdatedDataOwner: TypeAlias = FeatureFlagOwner diff --git a/src/workos/common/models/flag_updated_data_owner.py b/src/workos/common/models/flag_updated_data_owner.py index 6185e0ca..fd8bbf4b 100644 --- a/src/workos/common/models/flag_updated_data_owner.py +++ b/src/workos/common/models/flag_updated_data_owner.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.feature_flags.models.feature_flag_owner import FeatureFlagOwner +from .feature_flag_owner import FeatureFlagOwner FlagUpdatedDataOwner: TypeAlias = FeatureFlagOwner diff --git a/src/workos/groups/models/group.py b/src/workos/common/models/group.py similarity index 100% rename from src/workos/groups/models/group.py rename to src/workos/common/models/group.py diff --git a/src/workos/common/models/group_created.py b/src/workos/common/models/group_created.py index a530afaa..86771890 100644 --- a/src/workos/common/models/group_created.py +++ b/src/workos/common/models/group_created.py @@ -10,7 +10,7 @@ from workos._types import _format_datetime, _parse_datetime from .event_context import EventContext -from workos.groups.models.group import Group +from .group import Group @dataclass(slots=True) diff --git a/src/workos/common/models/group_deleted.py b/src/workos/common/models/group_deleted.py index 436f27fa..51ded16a 100644 --- a/src/workos/common/models/group_deleted.py +++ b/src/workos/common/models/group_deleted.py @@ -10,7 +10,7 @@ from workos._types import _format_datetime, _parse_datetime from .event_context import EventContext -from workos.groups.models.group import Group +from .group import Group @dataclass(slots=True) diff --git a/src/workos/common/models/group_updated.py b/src/workos/common/models/group_updated.py index 894e0def..966ac61d 100644 --- a/src/workos/common/models/group_updated.py +++ b/src/workos/common/models/group_updated.py @@ -10,7 +10,7 @@ from workos._types import _format_datetime, _parse_datetime from .event_context import EventContext -from workos.groups.models.group import Group +from .group import Group @dataclass(slots=True) diff --git a/src/workos/common/models/organization_created_data_domain.py b/src/workos/common/models/organization_created_data_domain.py index 2578b80c..82e4b663 100644 --- a/src/workos/common/models/organization_created_data_domain.py +++ b/src/workos/common/models/organization_created_data_domain.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.organization_domains.models.organization_domain import OrganizationDomain +from .organization_domain import OrganizationDomain OrganizationCreatedDataDomain: TypeAlias = OrganizationDomain diff --git a/src/workos/common/models/organization_deleted_data_domain.py b/src/workos/common/models/organization_deleted_data_domain.py index abb23be0..afd1613c 100644 --- a/src/workos/common/models/organization_deleted_data_domain.py +++ b/src/workos/common/models/organization_deleted_data_domain.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.organization_domains.models.organization_domain import OrganizationDomain +from .organization_domain import OrganizationDomain OrganizationDeletedDataDomain: TypeAlias = OrganizationDomain diff --git a/src/workos/organization_domains/models/organization_domain.py b/src/workos/common/models/organization_domain.py similarity index 95% rename from src/workos/organization_domains/models/organization_domain.py rename to src/workos/common/models/organization_domain.py index 2620dcf0..58dcb5f7 100644 --- a/src/workos/organization_domains/models/organization_domain.py +++ b/src/workos/common/models/organization_domain.py @@ -8,8 +8,8 @@ from typing import Any, Dict, Literal, Optional from workos._types import _raise_deserialize_error from workos._types import _format_datetime, _parse_datetime -from workos.common.models.organization_domain_state import OrganizationDomainState -from workos.common.models.organization_domain_verification_strategy import ( +from .organization_domain_state import OrganizationDomainState +from .organization_domain_verification_strategy import ( OrganizationDomainVerificationStrategy, ) diff --git a/src/workos/common/models/organization_domain_created_data.py b/src/workos/common/models/organization_domain_created_data.py index 70f8cc8c..f28fb9fb 100644 --- a/src/workos/common/models/organization_domain_created_data.py +++ b/src/workos/common/models/organization_domain_created_data.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.organization_domains.models.organization_domain import OrganizationDomain +from .organization_domain import OrganizationDomain OrganizationDomainCreatedData: TypeAlias = OrganizationDomain diff --git a/src/workos/common/models/organization_domain_deleted_data.py b/src/workos/common/models/organization_domain_deleted_data.py index 31c1f3a2..67ca9fe0 100644 --- a/src/workos/common/models/organization_domain_deleted_data.py +++ b/src/workos/common/models/organization_domain_deleted_data.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.organization_domains.models.organization_domain import OrganizationDomain +from .organization_domain import OrganizationDomain OrganizationDomainDeletedData: TypeAlias = OrganizationDomain diff --git a/src/workos/common/models/organization_domain_updated_data.py b/src/workos/common/models/organization_domain_updated_data.py index 79f8eba2..85b0793f 100644 --- a/src/workos/common/models/organization_domain_updated_data.py +++ b/src/workos/common/models/organization_domain_updated_data.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.organization_domains.models.organization_domain import OrganizationDomain +from .organization_domain import OrganizationDomain OrganizationDomainUpdatedData: TypeAlias = OrganizationDomain diff --git a/src/workos/common/models/organization_domain_verification_failed_data_organization_domain.py b/src/workos/common/models/organization_domain_verification_failed_data_organization_domain.py index ec057f65..8435c17b 100644 --- a/src/workos/common/models/organization_domain_verification_failed_data_organization_domain.py +++ b/src/workos/common/models/organization_domain_verification_failed_data_organization_domain.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.organization_domains.models.organization_domain import OrganizationDomain +from .organization_domain import OrganizationDomain OrganizationDomainVerificationFailedDataOrganizationDomain: TypeAlias = ( OrganizationDomain diff --git a/src/workos/common/models/organization_domain_verified_data.py b/src/workos/common/models/organization_domain_verified_data.py index f881f1f3..86f019cc 100644 --- a/src/workos/common/models/organization_domain_verified_data.py +++ b/src/workos/common/models/organization_domain_verified_data.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.organization_domains.models.organization_domain import OrganizationDomain +from .organization_domain import OrganizationDomain OrganizationDomainVerifiedData: TypeAlias = OrganizationDomain diff --git a/src/workos/common/models/organization_membership_created_data.py b/src/workos/common/models/organization_membership_created_data.py index 53606c09..bb742f77 100644 --- a/src/workos/common/models/organization_membership_created_data.py +++ b/src/workos/common/models/organization_membership_created_data.py @@ -10,7 +10,7 @@ from workos._types import _raise_deserialize_error from workos._types import _format_datetime, _parse_datetime -from workos.authorization.models.slim_role import SlimRole +from .slim_role import SlimRole from .organization_membership_created_data_status import ( OrganizationMembershipCreatedDataStatus, ) diff --git a/src/workos/common/models/organization_updated_data_domain.py b/src/workos/common/models/organization_updated_data_domain.py index 56c47833..1867807d 100644 --- a/src/workos/common/models/organization_updated_data_domain.py +++ b/src/workos/common/models/organization_updated_data_domain.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.organization_domains.models.organization_domain import OrganizationDomain +from .organization_domain import OrganizationDomain OrganizationUpdatedDataDomain: TypeAlias = OrganizationDomain diff --git a/src/workos/connect/models/applications_order.py b/src/workos/common/models/pagination_order.py similarity index 62% rename from src/workos/connect/models/applications_order.py rename to src/workos/common/models/pagination_order.py index 15ae48f3..29099d54 100644 --- a/src/workos/connect/models/applications_order.py +++ b/src/workos/common/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/common/models/session_created_data.py b/src/workos/common/models/session_created_data.py index 32af27ac..5467db81 100644 --- a/src/workos/common/models/session_created_data.py +++ b/src/workos/common/models/session_created_data.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.user_management.models.user_sessions_list_item import UserSessionsListItem +from .user_sessions_list_item import UserSessionsListItem SessionCreatedData: TypeAlias = UserSessionsListItem diff --git a/src/workos/common/models/session_created_data_impersonator.py b/src/workos/common/models/session_created_data_impersonator.py index 4114d18b..03bea8af 100644 --- a/src/workos/common/models/session_created_data_impersonator.py +++ b/src/workos/common/models/session_created_data_impersonator.py @@ -1,8 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.user_management.models.authenticate_response_impersonator import ( - AuthenticateResponseImpersonator, -) +from .authenticate_response_impersonator import AuthenticateResponseImpersonator SessionCreatedDataImpersonator: TypeAlias = AuthenticateResponseImpersonator diff --git a/src/workos/common/models/session_revoked_data.py b/src/workos/common/models/session_revoked_data.py index d336515a..f5979011 100644 --- a/src/workos/common/models/session_revoked_data.py +++ b/src/workos/common/models/session_revoked_data.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.user_management.models.user_sessions_list_item import UserSessionsListItem +from .user_sessions_list_item import UserSessionsListItem SessionRevokedData: TypeAlias = UserSessionsListItem diff --git a/src/workos/common/models/session_revoked_data_impersonator.py b/src/workos/common/models/session_revoked_data_impersonator.py index e254b8f0..abc95915 100644 --- a/src/workos/common/models/session_revoked_data_impersonator.py +++ b/src/workos/common/models/session_revoked_data_impersonator.py @@ -1,8 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from workos.user_management.models.authenticate_response_impersonator import ( - AuthenticateResponseImpersonator, -) +from .authenticate_response_impersonator import AuthenticateResponseImpersonator SessionRevokedDataImpersonator: TypeAlias = AuthenticateResponseImpersonator diff --git a/src/workos/authorization/models/slim_role.py b/src/workos/common/models/slim_role.py similarity index 100% rename from src/workos/authorization/models/slim_role.py rename to src/workos/common/models/slim_role.py diff --git a/src/workos/user_management/models/user.py b/src/workos/common/models/user.py similarity index 100% rename from src/workos/user_management/models/user.py rename to src/workos/common/models/user.py 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/user_created.py b/src/workos/common/models/user_created.py index e384b439..9024de22 100644 --- a/src/workos/common/models/user_created.py +++ b/src/workos/common/models/user_created.py @@ -10,7 +10,7 @@ from workos._types import _format_datetime, _parse_datetime from .event_context import EventContext -from workos.user_management.models.user import User +from .user import User @dataclass(slots=True) diff --git a/src/workos/common/models/user_deleted.py b/src/workos/common/models/user_deleted.py index 31316cc8..8b1538e1 100644 --- a/src/workos/common/models/user_deleted.py +++ b/src/workos/common/models/user_deleted.py @@ -10,7 +10,7 @@ from workos._types import _format_datetime, _parse_datetime from .event_context import EventContext -from workos.user_management.models.user import User +from .user import User @dataclass(slots=True) diff --git a/src/workos/authorization/models/user_organization_membership_base_list_data.py b/src/workos/common/models/user_organization_membership_base_list_data.py similarity index 91% rename from src/workos/authorization/models/user_organization_membership_base_list_data.py rename to src/workos/common/models/user_organization_membership_base_list_data.py index 0cc63eaa..6de974f2 100644 --- a/src/workos/authorization/models/user_organization_membership_base_list_data.py +++ b/src/workos/common/models/user_organization_membership_base_list_data.py @@ -5,10 +5,13 @@ 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.common.models.user_organization_membership_base_list_data_status import ( + +from .user import User +from .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/user_management/models/user_sessions_impersonator.py b/src/workos/common/models/user_sessions_impersonator.py similarity index 100% rename from src/workos/user_management/models/user_sessions_impersonator.py rename to src/workos/common/models/user_sessions_impersonator.py diff --git a/src/workos/user_management/models/user_sessions_list_item.py b/src/workos/common/models/user_sessions_list_item.py similarity index 96% rename from src/workos/user_management/models/user_sessions_list_item.py rename to src/workos/common/models/user_sessions_list_item.py index b7874f60..c9d90f6a 100644 --- a/src/workos/user_management/models/user_sessions_list_item.py +++ b/src/workos/common/models/user_sessions_list_item.py @@ -11,8 +11,8 @@ from workos._types import _format_datetime, _parse_datetime from .user_sessions_impersonator import UserSessionsImpersonator -from workos.common.models.user_sessions_auth_method import UserSessionsAuthMethod -from workos.common.models.user_sessions_status import UserSessionsStatus +from .user_sessions_auth_method import UserSessionsAuthMethod +from .user_sessions_status import UserSessionsStatus @dataclass(slots=True) diff --git a/src/workos/common/models/user_updated.py b/src/workos/common/models/user_updated.py index 6704ed0d..b4a18398 100644 --- a/src/workos/common/models/user_updated.py +++ b/src/workos/common/models/user_updated.py @@ -10,7 +10,7 @@ from workos._types import _format_datetime, _parse_datetime from .event_context import EventContext -from workos.user_management.models.user import User +from .user import User @dataclass(slots=True) 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..be9036cd 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 @@ -10,7 +11,6 @@ from .._types import RequestOptions, enum_value from .models import ( ApplicationCredentialsListItem, - ConnectApplication, CreateM2MApplication, CreateOAuthApplication, ExternalAuthCompleteResponse, @@ -19,7 +19,8 @@ UserConsentOption, UserObject, ) -from .models import ApplicationsOrder +from workos.common.models.connect_application import ConnectApplication +from workos.common.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]: @@ -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]: @@ -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..5c4d76ec 100644 --- a/src/workos/connect/models/__init__.py +++ b/src/workos/connect/models/__init__.py @@ -3,8 +3,9 @@ from .application_credentials_list_item import ( ApplicationCredentialsListItem as ApplicationCredentialsListItem, ) -from .applications_order import ApplicationsOrder as ApplicationsOrder -from .connect_application import ConnectApplication as ConnectApplication +from workos.common.models.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..0ab19ca1 100644 --- a/src/workos/directory_sync/_resource.py +++ b/src/workos/directory_sync/_resource.py @@ -3,13 +3,15 @@ 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 .models import Directory, DirectoryUserWithGroups +from workos.common.models.directory_group import DirectoryGroup +from workos.common.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage @@ -25,7 +27,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, @@ -102,7 +104,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 +131,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 +141,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, @@ -214,7 +216,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 +227,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, @@ -300,7 +302,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 +320,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, @@ -395,7 +397,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 +424,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 +434,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, @@ -507,7 +509,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 +520,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, @@ -593,7 +595,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..3677f9a9 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 workos.common.models.directory_group import DirectoryGroup as DirectoryGroup 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..348a73eb 100644 --- a/src/workos/directory_sync/models/directory_user_with_groups.py +++ b/src/workos/directory_sync/models/directory_user_with_groups.py @@ -10,9 +10,9 @@ from workos._types import _raise_deserialize_error from workos._types import _format_datetime, _parse_datetime -from .directory_group import DirectoryGroup +from workos.common.models.directory_group import DirectoryGroup from .directory_user_with_groups_email import DirectoryUserWithGroupsEmail -from workos.authorization.models.slim_role import SlimRole +from workos.common.models.slim_role import SlimRole from workos.common.models.directory_user_with_groups_state import ( DirectoryUserWithGroupsState, ) @@ -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..d2ef7ffd 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.common.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, @@ -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, 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..44354d8d 100644 --- a/src/workos/feature_flags/_resource.py +++ b/src/workos/feature_flags/_resource.py @@ -3,17 +3,15 @@ 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.common.models.feature_flag import FeatureFlag +from workos.common.models.flag import Flag +from workos.common.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage @@ -29,7 +27,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 @@ -97,7 +95,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 +125,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 +155,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 +186,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 +216,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 +227,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 @@ -265,7 +263,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 +276,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 @@ -314,7 +312,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 +331,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 @@ -401,7 +399,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 +429,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 +459,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 +490,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 +520,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 +531,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 @@ -569,7 +567,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 +580,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 @@ -618,7 +616,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..95ca0499 100644 --- a/src/workos/feature_flags/models/__init__.py +++ b/src/workos/feature_flags/models/__init__.py @@ -1,13 +1,6 @@ # This file is auto-generated by oagen. Do not edit. -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, -) +from workos.common.models.feature_flag import FeatureFlag as FeatureFlag +from workos.common.models.feature_flag_owner import FeatureFlagOwner as FeatureFlagOwner +from workos.common.models.flag import Flag as Flag +from workos.common.models.flag_owner import FlagOwner as FlagOwner 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..51c48067 100644 --- a/src/workos/groups/_resource.py +++ b/src/workos/groups/_resource.py @@ -3,16 +3,17 @@ 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 from .._types import RequestOptions, enum_value -from .models import Group -from workos.authorization.models.user_organization_membership_base_list_data import ( +from workos.common.models.group import Group +from workos.common.models.user_organization_membership_base_list_data import ( UserOrganizationMembershipBaseListData, ) -from .models import GroupsOrder +from workos.common.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 @@ -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 @@ -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 @@ -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 @@ -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..70e41b0d 100644 --- a/src/workos/groups/models/__init__.py +++ b/src/workos/groups/models/__init__.py @@ -2,6 +2,5 @@ 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 workos.common.models.group import Group as Group 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..6ea2f582 100644 --- a/src/workos/multi_factor_auth/_resource.py +++ b/src/workos/multi_factor_auth/_resource.py @@ -3,22 +3,25 @@ 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 from .._types import RequestOptions, enum_value from .models import ( - AuthenticationChallenge, AuthenticationChallengeVerifyResponse, - AuthenticationFactor, - AuthenticationFactorEnrolled, UserAuthenticationFactorEnrollResponse, ) -from .models import UserManagementMultiFactorAuthenticationOrder +from workos.common.models.authentication_challenge import AuthenticationChallenge +from workos.common.models.authentication_factor import AuthenticationFactor +from workos.common.models.authentication_factor_enrolled import ( + AuthenticationFactorEnrolled, +) from workos.common.models.authentication_factors_create_request_type import ( AuthenticationFactorsCreateRequestType, ) +from workos.common.models.pagination_order import PaginationOrder from .._pagination import AsyncPage, SyncPage @@ -60,7 +63,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 +144,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 +171,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 +210,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 +223,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 @@ -258,7 +259,7 @@ def list_user_auth_factors( } return self._client.request_page( method="get", - path=f"user_management/users/{userland_user_id}/auth_factors", + path=f"user_management/users/{quote(str(userland_user_id), safe='')}/auth_factors", model=AuthenticationFactor, params=params, request_options=request_options, @@ -307,7 +308,7 @@ def create_user_auth_factor( } return self._client.request( method="post", - path=f"user_management/users/{userland_user_id}/auth_factors", + path=f"user_management/users/{quote(str(userland_user_id), safe='')}/auth_factors", body=body, model=UserAuthenticationFactorEnrollResponse, request_options=request_options, @@ -352,7 +353,7 @@ async def verify_challenge( } return await self._client.request( method="post", - path=f"auth/challenges/{id}/verify", + path=f"auth/challenges/{quote(str(id), safe='')}/verify", body=body, model=AuthenticationChallengeVerifyResponse, request_options=request_options, @@ -433,7 +434,7 @@ async def get_factor( """ return await self._client.request( method="get", - path=f"auth/factors/{id}", + path=f"auth/factors/{quote(str(id), safe='')}", model=AuthenticationFactor, request_options=request_options, ) @@ -460,7 +461,7 @@ async def delete_factor( """ await self._client.request( method="delete", - path=f"auth/factors/{id}", + path=f"auth/factors/{quote(str(id), safe='')}", request_options=request_options, ) @@ -499,7 +500,7 @@ async def challenge_factor( } return await self._client.request( method="post", - path=f"auth/factors/{id}/challenge", + path=f"auth/factors/{quote(str(id), safe='')}/challenge", body=body, model=AuthenticationChallenge, request_options=request_options, @@ -512,9 +513,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 @@ -550,7 +549,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 +598,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..a6d1f9bf 100644 --- a/src/workos/multi_factor_auth/models/__init__.py +++ b/src/workos/multi_factor_auth/models/__init__.py @@ -1,31 +1,35 @@ # This file is auto-generated by oagen. Do not edit. -from .authentication_challenge import AuthenticationChallenge as AuthenticationChallenge -from .authentication_challenge_verify_response import ( - AuthenticationChallengeVerifyResponse as AuthenticationChallengeVerifyResponse, +from workos.common.models.authentication_challenge import ( + AuthenticationChallenge as AuthenticationChallenge, ) from .authentication_challenges_verify_request import ( AuthenticationChallengesVerifyRequest as AuthenticationChallengesVerifyRequest, ) -from .authentication_factor import AuthenticationFactor as AuthenticationFactor -from .authentication_factor_enrolled import ( +from .authentication_challenge_verify_response import ( + AuthenticationChallengeVerifyResponse as AuthenticationChallengeVerifyResponse, +) +from workos.common.models.authentication_factor import ( + AuthenticationFactor as AuthenticationFactor, +) +from workos.common.models.authentication_factor_enrolled import ( AuthenticationFactorEnrolled as AuthenticationFactorEnrolled, ) -from .authentication_factor_enrolled_sms import ( +from workos.common.models.authentication_factor_enrolled_sms import ( AuthenticationFactorEnrolledSms as AuthenticationFactorEnrolledSms, ) -from .authentication_factor_enrolled_totp import ( +from workos.common.models.authentication_factor_enrolled_totp import ( AuthenticationFactorEnrolledTotp as AuthenticationFactorEnrolledTotp, ) -from .authentication_factor_sms import ( +from .authentication_factors_create_request import ( + AuthenticationFactorsCreateRequest as AuthenticationFactorsCreateRequest, +) +from workos.common.models.authentication_factor_sms import ( AuthenticationFactorSms as AuthenticationFactorSms, ) -from .authentication_factor_totp import ( +from workos.common.models.authentication_factor_totp import ( AuthenticationFactorTotp as AuthenticationFactorTotp, ) -from .authentication_factors_create_request import ( - AuthenticationFactorsCreateRequest as AuthenticationFactorsCreateRequest, -) from .challenge_authentication_factor import ( ChallengeAuthenticationFactor as ChallengeAuthenticationFactor, ) @@ -35,6 +39,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/authentication_challenge_verify_response.py b/src/workos/multi_factor_auth/models/authentication_challenge_verify_response.py index 96e6686a..da48dfa4 100644 --- a/src/workos/multi_factor_auth/models/authentication_challenge_verify_response.py +++ b/src/workos/multi_factor_auth/models/authentication_challenge_verify_response.py @@ -7,7 +7,7 @@ from typing import Any, Dict from workos._types import _raise_deserialize_error -from .authentication_challenge import AuthenticationChallenge +from workos.common.models.authentication_challenge import AuthenticationChallenge @dataclass(slots=True) diff --git a/src/workos/multi_factor_auth/models/user_authentication_factor_enroll_response.py b/src/workos/multi_factor_auth/models/user_authentication_factor_enroll_response.py index 1ba6421a..5c76cc81 100644 --- a/src/workos/multi_factor_auth/models/user_authentication_factor_enroll_response.py +++ b/src/workos/multi_factor_auth/models/user_authentication_factor_enroll_response.py @@ -7,8 +7,10 @@ from typing import Any, Dict from workos._types import _raise_deserialize_error -from .authentication_challenge import AuthenticationChallenge -from .authentication_factor_enrolled import AuthenticationFactorEnrolled +from workos.common.models.authentication_challenge import AuthenticationChallenge +from workos.common.models.authentication_factor_enrolled import ( + AuthenticationFactorEnrolled, +) @dataclass(slots=True) 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..9c7e5d7a 100644 --- a/src/workos/organization_domains/_resource.py +++ b/src/workos/organization_domains/_resource.py @@ -3,12 +3,14 @@ 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 from .._types import RequestOptions -from .models import OrganizationDomain, OrganizationDomainStandAlone +from .models import OrganizationDomainStandAlone +from workos.common.models.organization_domain import OrganizationDomain class OrganizationDomains: @@ -79,7 +81,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 +108,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 +137,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 +211,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 +238,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 +267,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/organization_domains/models/__init__.py b/src/workos/organization_domains/models/__init__.py index 5b4f6869..a8435a78 100644 --- a/src/workos/organization_domains/models/__init__.py +++ b/src/workos/organization_domains/models/__init__.py @@ -3,7 +3,9 @@ from .create_organization_domain import ( CreateOrganizationDomain as CreateOrganizationDomain, ) -from .organization_domain import OrganizationDomain as OrganizationDomain +from workos.common.models.organization_domain import ( + OrganizationDomain as OrganizationDomain, +) from .organization_domain_stand_alone import ( OrganizationDomainStandAlone as OrganizationDomainStandAlone, ) diff --git a/src/workos/organization_domains/models/organization_domain_stand_alone.py b/src/workos/organization_domains/models/organization_domain_stand_alone.py index e8c74715..d2a22aa8 100644 --- a/src/workos/organization_domains/models/organization_domain_stand_alone.py +++ b/src/workos/organization_domains/models/organization_domain_stand_alone.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from .organization_domain import OrganizationDomain +from workos.common.models.organization_domain import OrganizationDomain OrganizationDomainStandAlone: TypeAlias = OrganizationDomain diff --git a/src/workos/organizations/_resource.py b/src/workos/organizations/_resource.py index e0065743..c108ad5a 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.common.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, @@ -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, @@ -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/organization.py b/src/workos/organizations/models/organization.py index 4ab993a1..991ca031 100644 --- a/src/workos/organizations/models/organization.py +++ b/src/workos/organizations/models/organization.py @@ -9,7 +9,7 @@ from workos._types import _raise_deserialize_error from workos._types import _format_datetime, _parse_datetime -from workos.organization_domains.models.organization_domain import OrganizationDomain +from workos.common.models.organization_domain import OrganizationDomain @dataclass(slots=True) 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..d4e0a9ee 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.common.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, @@ -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, @@ -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..f6e58f0f 100644 --- a/src/workos/sso/models/profile.py +++ b/src/workos/sso/models/profile.py @@ -8,7 +8,7 @@ from typing import Any, Dict, List, Literal, Optional from workos._types import _raise_deserialize_error -from workos.authorization.models.slim_role import SlimRole +from workos.common.models.slim_role import SlimRole from workos.common.models.profile_connection_type import ProfileConnectionType @@ -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..12e34b52 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 @@ -33,25 +34,24 @@ MagicAuthCodeSessionAuthenticateRequest, MFATotpSessionAuthenticateRequest, OrganizationSelectionSessionAuthenticateRequest, - User, + UserApiKey, + UserApiKeyWithValue, UserIdentitiesGetItem, UserInvite, UserOrganizationMembership, - UserSessionsListItem, VerifyEmailResponse, ) +from workos.common.models.user import User +from workos.common.models.user_sessions_list_item import UserSessionsListItem from .models import ( UserManagementAuthenticationProvider, UserManagementAuthenticationScreenHint, - UserManagementInvitationsOrder, - UserManagementOrganizationMembershipOrder, UserManagementOrganizationMembershipStatuses, - UserManagementUsersAuthorizedApplicationsOrder, - UserManagementUsersOrder, ) from workos.common.models.create_user_invite_options_locale import ( CreateUserInviteOptionsLocale, ) +from workos.common.models.pagination_order import PaginationOrder from workos.common.models.resend_user_invite_options_locale import ( ResendUserInviteOptionsLocale, ) @@ -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, @@ -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 @@ -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, @@ -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]] @@ -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 @@ -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. Defaults to `desc`. + organization_id: The ID of the organization to filter user API keys by. When provided, only API keys created against that organization membership are returned. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + SyncPage[UserApiKey] + + Raises: + NotFoundError: If the resource is not found (404). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + params = { + k: v + for k, v in { + "limit": limit, + "before": before, + "after": after, + "order": enum_value(order) if order is not None else None, + "organization_id": organization_id, + }.items() + if v is not None + } + return self._client.request_page( + method="get", + path=f"user_management/users/{quote(str(user_id), safe='')}/api_keys", + model=UserApiKey, + params=params, + request_options=request_options, + ) + + def create_user_api_key( + self, + user_id: str, + *, + name: str, + organization_id: str, + permissions: Optional[List[str]] = None, + request_options: Optional[RequestOptions] = None, + ) -> UserApiKeyWithValue: + """Create an API key for a user + + Create a new API key owned by a user. The user must have an active membership in the specified organization. + + Args: + user_id: Unique identifier of the user. + name: A descriptive name for the API key. + organization_id: The ID of the organization the user API key is associated with. The user must have an active membership in this organization. + permissions: The permission slugs to assign to the API key. Each permission must be enabled for user API keys. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + UserApiKeyWithValue + + Raises: + BadRequestError: If the request is malformed (400). + NotFoundError: If the resource is not found (404). + UnprocessableEntityError: If the request data is unprocessable (422). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + body: Dict[str, Any] = { + k: v + for k, v in { + "name": name, + "organization_id": organization_id, + "permissions": permissions, + }.items() + if v is not None + } + return self._client.request( + method="post", + path=f"user_management/users/{quote(str(user_id), safe='')}/api_keys", + body=body, + model=UserApiKeyWithValue, request_options=request_options, ) @@ -2313,7 +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, @@ -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 @@ -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, @@ -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]] @@ -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 @@ -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. Defaults to `desc`. + organization_id: The ID of the organization to filter user API keys by. When provided, only API keys created against that organization membership are returned. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + AsyncPage[UserApiKey] + + Raises: + NotFoundError: If the resource is not found (404). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + params = { + k: v + for k, v in { + "limit": limit, + "before": before, + "after": after, + "order": enum_value(order) if order is not None else None, + "organization_id": organization_id, + }.items() + if v is not None + } + return await self._client.request_page( + method="get", + path=f"user_management/users/{quote(str(user_id), safe='')}/api_keys", + model=UserApiKey, + params=params, + request_options=request_options, + ) + + async def create_user_api_key( + self, + user_id: str, + *, + name: str, + organization_id: str, + permissions: Optional[List[str]] = None, + request_options: Optional[RequestOptions] = None, + ) -> UserApiKeyWithValue: + """Create an API key for a user + + Create a new API key owned by a user. The user must have an active membership in the specified organization. + + Args: + user_id: Unique identifier of the user. + name: A descriptive name for the API key. + organization_id: The ID of the organization the user API key is associated with. The user must have an active membership in this organization. + permissions: The permission slugs to assign to the API key. Each permission must be enabled for user API keys. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + UserApiKeyWithValue + + Raises: + BadRequestError: If the request is malformed (400). + NotFoundError: If the resource is not found (404). + UnprocessableEntityError: If the request data is unprocessable (422). + AuthenticationError: If the API key is invalid (401). + RateLimitExceededError: If rate limited (429). + ServerError: If the server returns a 5xx error. + """ + body: Dict[str, Any] = { + k: v + for k, v in { + "name": name, + "organization_id": organization_id, + "permissions": permissions, + }.items() + if v is not None + } + return await self._client.request( + method="post", + path=f"user_management/users/{quote(str(user_id), safe='')}/api_keys", + body=body, + model=UserApiKeyWithValue, request_options=request_options, ) diff --git a/src/workos/user_management/models/__init__.py b/src/workos/user_management/models/__init__.py index 12ba3b65..d36fa02d 100644 --- a/src/workos/user_management/models/__init__.py +++ b/src/workos/user_management/models/__init__.py @@ -1,7 +1,7 @@ # This file is auto-generated by oagen. Do not edit. from .authenticate_response import AuthenticateResponse as AuthenticateResponse -from .authenticate_response_impersonator import ( +from workos.common.models.authenticate_response_impersonator import ( AuthenticateResponseImpersonator as AuthenticateResponseImpersonator, ) from .authenticate_response_oauth_token import ( @@ -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, ) @@ -43,9 +44,9 @@ ) from .email_verification import EmailVerification as EmailVerification from .invitation import Invitation as Invitation -from .jwt_template_response import JWTTemplateResponse as JWTTemplateResponse from .jwks_response import JwksResponse as JwksResponse from .jwks_response_keys import JwksResponseKeys as JwksResponseKeys +from .jwt_template_response import JWTTemplateResponse as JWTTemplateResponse from .magic_auth import MagicAuth as MagicAuth from .organization_membership import OrganizationMembership as OrganizationMembership from .password_reset import PasswordReset as PasswordReset @@ -61,13 +62,13 @@ ) from .reset_password_response import ResetPasswordResponse as ResetPasswordResponse from .revoke_session import RevokeSession as RevokeSession -from .sso_device_authorization_request import ( - SSODeviceAuthorizationRequest as SSODeviceAuthorizationRequest, -) from .send_email_change import SendEmailChange as SendEmailChange from .send_verification_email_response import ( SendVerificationEmailResponse as SendVerificationEmailResponse, ) +from .sso_device_authorization_request import ( + SSODeviceAuthorizationRequest as SSODeviceAuthorizationRequest, +) from .update_jwt_template import UpdateJWTTemplate as UpdateJWTTemplate from .update_user import UpdateUser as UpdateUser from .update_user_organization_membership import ( @@ -88,7 +89,13 @@ from .organization_selection_session_authenticate_request import ( OrganizationSelectionSessionAuthenticateRequest as OrganizationSelectionSessionAuthenticateRequest, ) -from .user import User as User +from workos.common.models.user import User as User +from .user_api_key import UserApiKey as UserApiKey +from .user_api_key_owner import UserApiKeyOwner as UserApiKeyOwner +from .user_api_key_with_value import UserApiKeyWithValue as UserApiKeyWithValue +from .user_api_key_with_value_owner import ( + UserApiKeyWithValueOwner as UserApiKeyWithValueOwner, +) from .user_identities_get_item import UserIdentitiesGetItem as UserIdentitiesGetItem from .user_invite import UserInvite as UserInvite from .user_management_authentication_provider import ( @@ -97,27 +104,17 @@ 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, ) -from .user_sessions_impersonator import ( +from workos.common.models.user_sessions_impersonator import ( UserSessionsImpersonator as UserSessionsImpersonator, ) -from .user_sessions_list_item import UserSessionsListItem as UserSessionsListItem +from workos.common.models.user_sessions_list_item import ( + UserSessionsListItem as UserSessionsListItem, +) from .verify_email_address import VerifyEmailAddress as VerifyEmailAddress from .verify_email_response import VerifyEmailResponse as VerifyEmailResponse diff --git a/src/workos/user_management/models/authenticate_response.py b/src/workos/user_management/models/authenticate_response.py index d9557b15..5f2b6284 100644 --- a/src/workos/user_management/models/authenticate_response.py +++ b/src/workos/user_management/models/authenticate_response.py @@ -8,9 +8,11 @@ from typing import Any, Dict, Optional from workos._types import _raise_deserialize_error -from .authenticate_response_impersonator import AuthenticateResponseImpersonator +from workos.common.models.authenticate_response_impersonator import ( + AuthenticateResponseImpersonator, +) from .authenticate_response_oauth_token import AuthenticateResponseOAuthToken -from .user import User +from workos.common.models.user import User from workos.common.models.authenticate_response_authentication_method import ( AuthenticateResponseAuthenticationMethod, ) diff --git a/src/workos/user_management/models/authorized_connect_application_list_data.py b/src/workos/user_management/models/authorized_connect_application_list_data.py index 15b91590..e6c4e35c 100644 --- a/src/workos/user_management/models/authorized_connect_application_list_data.py +++ b/src/workos/user_management/models/authorized_connect_application_list_data.py @@ -7,7 +7,7 @@ from typing import Any, Dict, List, Literal, Optional from workos._types import _raise_deserialize_error -from workos.connect.models.connect_application import ConnectApplication +from workos.common.models.connect_application import ConnectApplication @dataclass(slots=True) 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/email_change.py b/src/workos/user_management/models/email_change.py index df9d95a8..42552bb1 100644 --- a/src/workos/user_management/models/email_change.py +++ b/src/workos/user_management/models/email_change.py @@ -9,7 +9,7 @@ from workos._types import _raise_deserialize_error from workos._types import _format_datetime, _parse_datetime -from .user import User +from workos.common.models.user import User @dataclass(slots=True) diff --git a/src/workos/user_management/models/email_change_confirmation_user.py b/src/workos/user_management/models/email_change_confirmation_user.py index a05f3ffd..71648c15 100644 --- a/src/workos/user_management/models/email_change_confirmation_user.py +++ b/src/workos/user_management/models/email_change_confirmation_user.py @@ -1,6 +1,6 @@ # This file is auto-generated by oagen. Do not edit. from typing import TypeAlias -from .user import User +from workos.common.models.user import User EmailChangeConfirmationUser: TypeAlias = User diff --git a/src/workos/user_management/models/organization_membership.py b/src/workos/user_management/models/organization_membership.py index 5375c21a..53008d7f 100644 --- a/src/workos/user_management/models/organization_membership.py +++ b/src/workos/user_management/models/organization_membership.py @@ -10,7 +10,8 @@ from workos._types import _raise_deserialize_error from workos._types import _format_datetime, _parse_datetime -from workos.authorization.models.slim_role import SlimRole +from workos.common.models.slim_role import SlimRole +from workos.common.models.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/reset_password_response.py b/src/workos/user_management/models/reset_password_response.py index 2865b517..350116b3 100644 --- a/src/workos/user_management/models/reset_password_response.py +++ b/src/workos/user_management/models/reset_password_response.py @@ -7,7 +7,7 @@ from typing import Any, Dict from workos._types import _raise_deserialize_error -from .user import User +from workos.common.models.user import User @dataclass(slots=True) diff --git a/src/workos/user_management/models/user_api_key.py b/src/workos/user_management/models/user_api_key.py new file mode 100644 index 00000000..a3fd779a --- /dev/null +++ b/src/workos/user_management/models/user_api_key.py @@ -0,0 +1,73 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from typing import cast +from typing import Any, Dict, List, Literal, Optional +from workos._types import _raise_deserialize_error +from workos._types import _format_datetime, _parse_datetime + +from .user_api_key_owner import UserApiKeyOwner + + +@dataclass(slots=True) +class UserApiKey: + """User Api Key model.""" + + object: Literal["api_key"] + """Distinguishes the API Key object.""" + id: str + """Unique identifier of the API Key.""" + owner: "UserApiKeyOwner" + """The entity that owns the API Key.""" + name: str + """A descriptive name for the API Key.""" + obfuscated_value: str + """An obfuscated representation of the API Key value.""" + last_used_at: Optional[datetime] + """Timestamp of when the API Key was last used.""" + permissions: List[str] + """The permission slugs assigned to the API Key.""" + created_at: datetime + """An ISO 8601 timestamp.""" + updated_at: datetime + """An ISO 8601 timestamp.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "UserApiKey": + """Deserialize from a dictionary.""" + try: + return cls( + object=data.get("object", "api_key"), + id=data["id"], + owner=UserApiKeyOwner.from_dict(cast(Dict[str, Any], data["owner"])), + name=data["name"], + obfuscated_value=data["obfuscated_value"], + last_used_at=_parse_datetime(_v_last_used_at) + if (_v_last_used_at := data["last_used_at"]) is not None + else None, + permissions=data["permissions"], + created_at=_parse_datetime(data["created_at"]), + updated_at=_parse_datetime(data["updated_at"]), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("UserApiKey", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["object"] = self.object + result["id"] = self.id + result["owner"] = self.owner.to_dict() + result["name"] = self.name + result["obfuscated_value"] = self.obfuscated_value + if self.last_used_at is not None: + result["last_used_at"] = _format_datetime(self.last_used_at) + else: + result["last_used_at"] = None + result["permissions"] = self.permissions + result["created_at"] = _format_datetime(self.created_at) + result["updated_at"] = _format_datetime(self.updated_at) + return result diff --git a/src/workos/user_management/models/user_api_key_owner.py b/src/workos/user_management/models/user_api_key_owner.py new file mode 100644 index 00000000..9b7b4af6 --- /dev/null +++ b/src/workos/user_management/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/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..5b04421e 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.common.models.group import Group +from workos.common.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 @@ -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 @@ -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..0b0d11fb 100644 --- a/src/workos/webhooks/_resource.py +++ b/src/workos/webhooks/_resource.py @@ -3,16 +3,17 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from urllib.parse import quote if TYPE_CHECKING: from .._client import AsyncWorkOSClient, WorkOSClient from .._types import RequestOptions, enum_value from .models import WebhookEndpointJson -from .models import WebhooksOrder from workos.common.models.create_webhook_endpoint_events import ( CreateWebhookEndpointEvents, ) +from workos.common.models.pagination_order import PaginationOrder from workos.common.models.update_webhook_endpoint_events import ( UpdateWebhookEndpointEvents, ) @@ -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 @@ -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 @@ -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..bb9fc3f1 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.common.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..2608de20 100644 --- a/tests/test_audit_logs.py +++ b/tests/test_audit_logs.py @@ -12,8 +12,8 @@ AuditLogEventCreateResponse, AuditLogExportJson, AuditLogSchemaJson, - AuditLogsOrder, ) +from workos.common.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..dbaae3cf 100644 --- a/tests/test_authorization.py +++ b/tests/test_authorization.py @@ -8,16 +8,17 @@ from workos.authorization.models import ( AuthorizationCheck, - AuthorizationPermission, AuthorizationResource, Permission, Role, - RoleAssignment, RoleList, - UserOrganizationMembershipBaseListData, + UserRoleAssignment, AuthorizationAssignment, - AuthorizationOrder, - PermissionsOrder, +) +from workos.common.models import ( + AuthorizationPermission, + UserOrganizationMembershipBaseListData, + PaginationOrder, ) from workos._pagination import AsyncPage, SyncPage from workos._errors import ( @@ -90,7 +91,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 +101,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 +131,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 +175,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 +209,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 +484,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 +492,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 +564,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 +577,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 +680,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 +688,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 +833,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 +1048,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 +1058,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 +1093,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 +1144,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 +1181,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 +1452,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 +1460,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 +1534,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 +1547,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 +1651,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 +1659,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 +1797,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..e031957b 100644 --- a/tests/test_connect.py +++ b/tests/test_connect.py @@ -6,13 +6,12 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture +from workos.common.models import ConnectApplication, PaginationOrder from workos.connect.models import ( ApplicationCredentialsListItem, - ConnectApplication, ExternalAuthCompleteResponse, NewConnectApplicationSecret, UserObject, - ApplicationsOrder, ) from workos._pagination import AsyncPage, SyncPage from workos._errors import ( @@ -67,14 +66,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 +314,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..5cd3cc83 100644 --- a/tests/test_directory_sync.py +++ b/tests/test_directory_sync.py @@ -5,14 +5,8 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture -from workos.directory_sync.models import ( - Directory, - DirectoryGroup, - DirectoryUserWithGroups, - DirectoriesOrder, - DirectoryGroupsOrder, - DirectoryUsersOrder, -) +from workos.common.models import DirectoryGroup, PaginationOrder +from workos.directory_sync.models import Directory, DirectoryUserWithGroups from workos._pagination import AsyncPage, SyncPage from workos._errors import ( AuthenticationError, @@ -46,7 +40,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 +49,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 +95,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 +103,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 +140,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 +148,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 +265,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 +274,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 +321,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 +329,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 +368,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 +376,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..5b4e9686 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -5,8 +5,7 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture -from workos.common.models import DsyncUserCreated -from workos.events.models import EventsOrder +from workos.common.models import DsyncUserCreated, PaginationOrder from workos._pagination import AsyncPage, SyncPage from workos._errors import ( AuthenticationError, @@ -40,7 +39,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 +49,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 +154,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 +164,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..beacf742 100644 --- a/tests/test_feature_flags.py +++ b/tests/test_feature_flags.py @@ -5,13 +5,7 @@ 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.common.models import FeatureFlag, Flag, PaginationOrder from workos._pagination import AsyncPage, SyncPage from workos._errors import ( AuthenticationError, @@ -45,13 +39,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 +130,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 +160,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 +269,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 +364,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 +397,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..1a260c92 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.common.models import ( + Group, + UserOrganizationMembershipBaseListData, + PaginationOrder, +) 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..cc3ba292 100644 --- a/tests/test_models_round_trip.py +++ b/tests/test_models_round_trip.py @@ -16,8 +16,10 @@ ApiKey, ApiKeyOwner, ApiKeyValidationResponse, - ApiKeyWithValue, - ApiKeyWithValueOwner, + OrganizationApiKey, + OrganizationApiKeyOwner, + OrganizationApiKeyWithValue, + OrganizationApiKeyWithValueOwner, ) from workos.audit_logs.models import ( AuditLogActionJson, @@ -39,11 +41,11 @@ AuthorizationResource, Permission, Role, - RoleAssignment, - RoleAssignmentResource, RoleList, SlimRole, UserOrganizationMembershipBaseListData, + UserRoleAssignment, + UserRoleAssignmentResource, ) from workos.common.models import ( ActionAuthenticationDenied, @@ -248,9 +250,13 @@ SessionRevoked, SessionRevokedData, SessionRevokedDataImpersonator, + UserApiKeyCreatedDataOwner, + UserApiKeyRevokedDataOwner, UserCreated, UserDeleted, UserUpdated, + VaultByokKeyDeleted, + VaultByokKeyDeletedData, VaultByokKeyVerificationCompleted, VaultByokKeyVerificationCompletedData, VaultDataCreated, @@ -364,6 +370,10 @@ ResetPasswordResponse, SendVerificationEmailResponse, User, + UserApiKey, + UserApiKeyOwner, + 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..e3e5f864 100644 --- a/tests/test_multi_factor_auth.py +++ b/tests/test_multi_factor_auth.py @@ -6,14 +6,16 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture -from workos.common.models import AuthenticationFactorsCreateRequestType -from workos.multi_factor_auth.models import ( +from workos.common.models import ( AuthenticationChallenge, - AuthenticationChallengeVerifyResponse, AuthenticationFactor, AuthenticationFactorEnrolled, + AuthenticationFactorsCreateRequestType, + PaginationOrder, +) +from workos.multi_factor_auth.models import ( + AuthenticationChallengeVerifyResponse, UserAuthenticationFactorEnrollResponse, - UserManagementMultiFactorAuthenticationOrder, ) from workos._pagination import AsyncPage, SyncPage from workos._errors import ( @@ -110,13 +112,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 +306,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_organization_domains.py b/tests/test_organization_domains.py index 7ecbbbfd..0b2c52a0 100644 --- a/tests/test_organization_domains.py +++ b/tests/test_organization_domains.py @@ -6,10 +6,8 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture -from workos.organization_domains.models import ( - OrganizationDomain, - OrganizationDomainStandAlone, -) +from workos.common.models import OrganizationDomain +from workos.organization_domains.models import OrganizationDomainStandAlone from workos._errors import ( AuthenticationError, BadRequestError, diff --git a/tests/test_organizations.py b/tests/test_organizations.py index 9e300cbe..d480b254 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.common.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..a4d92208 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.common.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..52da6242 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.common.models import User, UserSessionsListItem, PaginationOrder from workos.user_management.models import ( AuthenticateResponse, AuthorizedConnectApplicationListData, @@ -23,16 +24,12 @@ RedirectUri, 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 +192,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 +201,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 +360,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 +389,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 +397,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 +477,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 +552,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 +561,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 +691,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 +711,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 +1068,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 +1077,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 +1229,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 +1261,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 +1269,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 +1343,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 +1419,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 +1428,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 +1565,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 +1586,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..0ab43f00 100644 --- a/tests/test_user_management_organization_membership_groups.py +++ b/tests/test_user_management_organization_membership_groups.py @@ -5,10 +5,7 @@ from workos import WorkOSClient, AsyncWorkOSClient from tests.generated_helpers import load_fixture -from workos.groups.models import Group -from workos.user_management_organization_membership_groups.models import ( - UserManagementOrganizationMembershipGroupsOrder, -) +from workos.common.models import Group, PaginationOrder from workos._pagination import AsyncPage, SyncPage from workos._errors import ( AuthenticationError, @@ -49,13 +46,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 +176,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..0f15e748 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.common.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): diff --git a/uv.lock b/uv.lock index 2a3c2c6c..d3d0b223 100644 --- a/uv.lock +++ b/uv.lock @@ -46,7 +46,7 @@ wheels = [ [[package]] name = "black" -version = "23.12.1" +version = "26.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -54,24 +54,38 @@ dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "platformdirs" }, + { name = "pytokens" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fd/f4/a57cde4b60da0e249073009f4a9087e9e0a955deae78d3c2a493208d0c5c/black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5", size = 620809, upload-time = "2023-12-22T23:06:17.382Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/58/677da52d845b59505a8a787ff22eff9cfd9046b5789aa2bd387b236db5c5/black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2", size = 1560531, upload-time = "2023-12-22T23:18:20.555Z" }, - { url = "https://files.pythonhosted.org/packages/11/92/522a4f1e4b2b8da62e4ec0cb8acf2d257e6d39b31f4214f0fd94d2eeb5bd/black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba", size = 1404644, upload-time = "2023-12-22T23:17:46.425Z" }, - { url = "https://files.pythonhosted.org/packages/a4/dc/af67d8281e9a24f73d24b060f3f03f6d9ad6be259b3c6acef2845e17d09c/black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0", size = 1711153, upload-time = "2023-12-22T23:08:34.4Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0f/94d7c36b421ea187359c413be7b9fc66dc105620c3a30b1c94310265830a/black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3", size = 1332918, upload-time = "2023-12-22T23:10:28.188Z" }, - { url = "https://files.pythonhosted.org/packages/ed/2c/d9b1a77101e6e5f294f6553d76c39322122bfea2a438aeea4eb6d4b22749/black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba", size = 1541926, upload-time = "2023-12-22T23:23:17.72Z" }, - { url = "https://files.pythonhosted.org/packages/72/e2/d981a3ff05ba9abe3cfa33e70c986facb0614fd57c4f802ef435f4dd1697/black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b", size = 1388465, upload-time = "2023-12-22T23:19:00.611Z" }, - { url = "https://files.pythonhosted.org/packages/eb/59/1f5c8eb7bba8a8b1bb5c87f097d16410c93a48a6655be3773db5d2783deb/black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59", size = 1691993, upload-time = "2023-12-22T23:08:32.018Z" }, - { url = "https://files.pythonhosted.org/packages/37/bf/a80abc6fcdb00f0d4d3d74184b172adbf2197f6b002913fa0fb6af4dc6db/black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50", size = 1340929, upload-time = "2023-12-22T23:09:37.088Z" }, - { url = "https://files.pythonhosted.org/packages/66/16/8726cedc83be841dfa854bbeef1288ee82272282a71048d7935292182b0b/black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e", size = 1569989, upload-time = "2023-12-22T23:20:22.158Z" }, - { url = "https://files.pythonhosted.org/packages/d2/1e/30f5eafcc41b8378890ba39b693fa111f7dca8a2620ba5162075d95ffe46/black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec", size = 1398647, upload-time = "2023-12-22T23:19:57.225Z" }, - { url = "https://files.pythonhosted.org/packages/99/de/ddb45cc044256431d96d846ce03164d149d81ca606b5172224d1872e0b58/black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e", size = 1720450, upload-time = "2023-12-22T23:08:52.675Z" }, - { url = "https://files.pythonhosted.org/packages/98/2b/54e5dbe9be5a10cbea2259517206ff7b6a452bb34e07508c7e1395950833/black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9", size = 1351070, upload-time = "2023-12-22T23:09:32.762Z" }, - { url = "https://files.pythonhosted.org/packages/7b/14/4da7b12a9abc43a601c215cb5a3d176734578da109f0dbf0a832ed78be09/black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e", size = 194363, upload-time = "2023-12-22T23:06:14.278Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/e1/c5/61175d618685d42b005847464b8fb4743a67b1b8fdb75e50e5a96c31a27a/black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07", size = 666155, upload-time = "2026-03-12T03:36:03.593Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/a8/11170031095655d36ebc6664fe0897866f6023892396900eec0e8fdc4299/black-26.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2", size = 1866562, upload-time = "2026-03-12T03:39:58.639Z" }, + { url = "https://files.pythonhosted.org/packages/69/ce/9e7548d719c3248c6c2abfd555d11169457cbd584d98d179111338423790/black-26.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b", size = 1703623, upload-time = "2026-03-12T03:40:00.347Z" }, + { url = "https://files.pythonhosted.org/packages/7f/0a/8d17d1a9c06f88d3d030d0b1d4373c1551146e252afe4547ed601c0e697f/black-26.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac", size = 1768388, upload-time = "2026-03-12T03:40:01.765Z" }, + { url = "https://files.pythonhosted.org/packages/52/79/c1ee726e221c863cde5164f925bacf183dfdf0397d4e3f94889439b947b4/black-26.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a", size = 1412969, upload-time = "2026-03-12T03:40:03.252Z" }, + { url = "https://files.pythonhosted.org/packages/73/a5/15c01d613f5756f68ed8f6d4ec0a1e24b82b18889fa71affd3d1f7fad058/black-26.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a", size = 1220345, upload-time = "2026-03-12T03:40:04.892Z" }, + { url = "https://files.pythonhosted.org/packages/17/57/5f11c92861f9c92eb9dddf515530bc2d06db843e44bdcf1c83c1427824bc/black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff", size = 1851987, upload-time = "2026-03-12T03:40:06.248Z" }, + { url = "https://files.pythonhosted.org/packages/54/aa/340a1463660bf6831f9e39646bf774086dbd8ca7fc3cded9d59bbdf4ad0a/black-26.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c", size = 1689499, upload-time = "2026-03-12T03:40:07.642Z" }, + { url = "https://files.pythonhosted.org/packages/f3/01/b726c93d717d72733da031d2de10b92c9fa4c8d0c67e8a8a372076579279/black-26.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5", size = 1754369, upload-time = "2026-03-12T03:40:09.279Z" }, + { url = "https://files.pythonhosted.org/packages/e3/09/61e91881ca291f150cfc9eb7ba19473c2e59df28859a11a88248b5cbbc4d/black-26.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e", size = 1413613, upload-time = "2026-03-12T03:40:10.943Z" }, + { url = "https://files.pythonhosted.org/packages/16/73/544f23891b22e7efe4d8f812371ab85b57f6a01b2fc45e3ba2e52ba985b8/black-26.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5", size = 1219719, upload-time = "2026-03-12T03:40:12.597Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f8/da5eae4fc75e78e6dceb60624e1b9662ab00d6b452996046dfa9b8a6025b/black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1", size = 1895920, upload-time = "2026-03-12T03:40:13.921Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9f/04e6f26534da2e1629b2b48255c264cabf5eedc5141d04516d9d68a24111/black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f", size = 1718499, upload-time = "2026-03-12T03:40:15.239Z" }, + { url = "https://files.pythonhosted.org/packages/04/91/a5935b2a63e31b331060c4a9fdb5a6c725840858c599032a6f3aac94055f/black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7", size = 1794994, upload-time = "2026-03-12T03:40:17.124Z" }, + { url = "https://files.pythonhosted.org/packages/e7/0a/86e462cdd311a3c2a8ece708d22aba17d0b2a0d5348ca34b40cdcbea512e/black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983", size = 1420867, upload-time = "2026-03-12T03:40:18.83Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e5/22515a19cb7eaee3440325a6b0d95d2c0e88dd180cb011b12ae488e031d1/black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb", size = 1230124, upload-time = "2026-03-12T03:40:20.425Z" }, + { url = "https://files.pythonhosted.org/packages/f5/77/5728052a3c0450c53d9bb3945c4c46b91baa62b2cafab6801411b6271e45/black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54", size = 1895034, upload-time = "2026-03-12T03:40:21.813Z" }, + { url = "https://files.pythonhosted.org/packages/52/73/7cae55fdfdfbe9d19e9a8d25d145018965fe2079fa908101c3733b0c55a0/black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f", size = 1718503, upload-time = "2026-03-12T03:40:23.666Z" }, + { url = "https://files.pythonhosted.org/packages/e1/87/af89ad449e8254fdbc74654e6467e3c9381b61472cc532ee350d28cfdafb/black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56", size = 1793557, upload-time = "2026-03-12T03:40:25.497Z" }, + { url = "https://files.pythonhosted.org/packages/43/10/d6c06a791d8124b843bf325ab4ac7d2f5b98731dff84d6064eafd687ded1/black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839", size = 1422766, upload-time = "2026-03-12T03:40:27.14Z" }, + { url = "https://files.pythonhosted.org/packages/59/4f/40a582c015f2d841ac24fed6390bd68f0fc896069ff3a886317959c9daf8/black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2", size = 1232140, upload-time = "2026-03-12T03:40:28.882Z" }, + { url = "https://files.pythonhosted.org/packages/d5/da/e36e27c9cebc1311b7579210df6f1c86e50f2d7143ae4fcf8a5017dc8809/black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78", size = 1889234, upload-time = "2026-03-12T03:40:30.964Z" }, + { url = "https://files.pythonhosted.org/packages/0e/7b/9871acf393f64a5fa33668c19350ca87177b181f44bb3d0c33b2d534f22c/black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568", size = 1720522, upload-time = "2026-03-12T03:40:32.346Z" }, + { url = "https://files.pythonhosted.org/packages/03/87/e766c7f2e90c07fb7586cc787c9ae6462b1eedab390191f2b7fc7f6170a9/black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f", size = 1787824, upload-time = "2026-03-12T03:40:33.636Z" }, + { url = "https://files.pythonhosted.org/packages/ac/94/2424338fb2d1875e9e83eed4c8e9c67f6905ec25afd826a911aea2b02535/black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c", size = 1445855, upload-time = "2026-03-12T03:40:35.442Z" }, + { url = "https://files.pythonhosted.org/packages/86/43/0c3338bd928afb8ee7471f1a4eec3bdbe2245ccb4a646092a222e8669840/black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1", size = 1258109, upload-time = "2026-03-12T03:40:36.832Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0d/52d98722666d6fc6c3dd4c76df339501d6efd40e0ff95e6186a7b7f0befd/black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b", size = 207542, upload-time = "2026-03-12T03:36:01.668Z" }, ] [[package]] @@ -543,30 +557,30 @@ wheels = [ [[package]] name = "docspec" -version = "2.2.2" +version = "2.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "databind-core" }, { name = "databind-json" }, { name = "deprecated" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/fe/1ad244d0ca186b5386050ec30dfd59bd3dbeea5baec33ca861dd43b922e6/docspec-2.2.2.tar.gz", hash = "sha256:c772c6facfce839176b647701082c7a22b3d22d872d392552cf5d65e0348c919", size = 14086, upload-time = "2025-05-06T12:39:59.466Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3c/39/7a71382107445b2cd50c67c6194e3e584f19748a817c3b29e8be8a14f00f/docspec-2.2.1.tar.gz", hash = "sha256:4854e77edc0e2de40e785e57e95880f7095a05fe978f8b54cef7a269586e15ff", size = 8646, upload-time = "2023-05-28T11:24:18.68Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/57/1011f2e88743a818cced9a95d54200ba6a05decaf43fd91d8c6ed9f6470d/docspec-2.2.2-py3-none-any.whl", hash = "sha256:854d25401e7ec2d155b0c1e001e25819d16b6df3a7575212a7f340ae8b00122e", size = 9726, upload-time = "2025-05-06T12:39:58.047Z" }, + { url = "https://files.pythonhosted.org/packages/33/aa/0c9d71cc9d450afd3993d09835e2910810a45b0703f585e1aee1d9b78969/docspec-2.2.1-py3-none-any.whl", hash = "sha256:7538f750095a9688c6980ff9a4e029a823a500f64bd00b6b4bdb27951feb31cb", size = 9844, upload-time = "2023-05-28T11:24:15.419Z" }, ] [[package]] name = "docspec-python" -version = "2.2.1" +version = "2.2.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "black" }, { name = "docspec" }, { name = "nr-util" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/88/99c5e27a894f01290364563c84838cf68f1a8629474b5bbfc3bf35a8d923/docspec_python-2.2.1.tar.gz", hash = "sha256:c41b850b4d6f4de30999ea6f82c9cdb9183d9bcba45559ee9173d3dab7281559", size = 13838, upload-time = "2023-05-28T11:24:19.846Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/ea/e6d9d9c2f805c6ac8072d0e3ee5b1da2dd61886c662327df937dec9f282c/docspec_python-2.2.2.tar.gz", hash = "sha256:429be834d09549461b95bf45eb53c16859f3dfb3e9220408b3bfb12812ccb3fb", size = 22154, upload-time = "2025-05-06T12:40:33.286Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/49/b8d1a2fa01b6f7a1a9daa1d485efc7684489028d6a356fc2bc5b40131061/docspec_python-2.2.1-py3-none-any.whl", hash = "sha256:76ac41d35a8face35b2d766c2e8a416fb8832359785d396f0d53bcb00f178e54", size = 16093, upload-time = "2023-05-28T11:24:17.261Z" }, + { url = "https://files.pythonhosted.org/packages/03/c2/b3226746fb6b91893da270a60e77bb420d59cf33a7b9a4e719a236955971/docspec_python-2.2.2-py3-none-any.whl", hash = "sha256:caa32dc1e8c470af8a5ecad67cca614e68c1563ac01dab0c0486c4d7f709d6b1", size = 15988, upload-time = "2025-05-06T12:40:31.554Z" }, ] [[package]] @@ -1024,6 +1038,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e2/d2/1eb1ea9c84f0d2033eb0b49675afdc71aa4ea801b74615f00f3c33b725e3/pytest_httpx-0.36.0-py3-none-any.whl", hash = "sha256:bd4c120bb80e142df856e825ec9f17981effb84d159f9fa29ed97e2357c3a9c8", size = 20229, upload-time = "2025-12-02T16:34:56.45Z" }, ] +[[package]] +name = "pytokens" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/24/f206113e05cb8ef51b3850e7ef88f20da6f4bf932190ceb48bd3da103e10/pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5", size = 161522, upload-time = "2026-01-30T01:02:50.393Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e9/06a6bf1b90c2ed81a9c7d2544232fe5d2891d1cd480e8a1809ca354a8eb2/pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe", size = 246945, upload-time = "2026-01-30T01:02:52.399Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/f6fb1007a4c3d8b682d5d65b7c1fb33257587a5f782647091e3408abe0b8/pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c", size = 259525, upload-time = "2026-01-30T01:02:53.737Z" }, + { url = "https://files.pythonhosted.org/packages/04/92/086f89b4d622a18418bac74ab5db7f68cf0c21cf7cc92de6c7b919d76c88/pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7", size = 262693, upload-time = "2026-01-30T01:02:54.871Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7b/8b31c347cf94a3f900bdde750b2e9131575a61fdb620d3d3c75832262137/pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2", size = 103567, upload-time = "2026-01-30T01:02:56.414Z" }, + { url = "https://files.pythonhosted.org/packages/3d/92/790ebe03f07b57e53b10884c329b9a1a308648fc083a6d4a39a10a28c8fc/pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440", size = 160864, upload-time = "2026-01-30T01:02:57.882Z" }, + { url = "https://files.pythonhosted.org/packages/13/25/a4f555281d975bfdd1eba731450e2fe3a95870274da73fb12c40aeae7625/pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc", size = 248565, upload-time = "2026-01-30T01:02:59.912Z" }, + { url = "https://files.pythonhosted.org/packages/17/50/bc0394b4ad5b1601be22fa43652173d47e4c9efbf0044c62e9a59b747c56/pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d", size = 260824, upload-time = "2026-01-30T01:03:01.471Z" }, + { url = "https://files.pythonhosted.org/packages/4e/54/3e04f9d92a4be4fc6c80016bc396b923d2a6933ae94b5f557c939c460ee0/pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16", size = 264075, upload-time = "2026-01-30T01:03:04.143Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1b/44b0326cb5470a4375f37988aea5d61b5cc52407143303015ebee94abfd6/pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6", size = 103323, upload-time = "2026-01-30T01:03:05.412Z" }, + { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, + { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, + { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, + { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, + { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, + { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, + { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, + { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, + { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, + { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, + { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -1295,6 +1348,7 @@ dev = [ { name = "ruff" }, ] docs = [ + { name = "black" }, { name = "pdoc" }, { name = "pydoc-markdown" }, ] @@ -1335,6 +1389,7 @@ dev = [ { name = "ruff", specifier = "~=0.15" }, ] docs = [ + { name = "black", specifier = "~=26.3" }, { name = "pdoc", specifier = ">=14" }, { name = "pydoc-markdown", specifier = ">=4" }, ]