From b64b5b64f21a482105c3e30be58e441eb911096d Mon Sep 17 00:00:00 2001 From: "workos-sdk-automation[bot]" <255426317+workos-sdk-automation[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 21:32:13 +0000 Subject: [PATCH] fix(generated): update generated SDK from spec changes --- .last-synced-sha | 1 + .oagen-manifest.json | 706 +++++++++++++++++- src/workos/_client.py | 36 +- src/workos/admin_portal/_resource.py | 12 +- src/workos/admin_portal/models/__init__.py | 3 + .../domain_verification_intent_options.py | 32 + .../admin_portal/models/generate_link.py | 10 +- .../admin_portal/models/intent_options.py | 22 +- src/workos/authorization/_resource.py | 50 +- src/workos/common/__init__.py | 5 +- src/workos/common/models/__init__.py | 5 +- .../models/create_webhook_endpoint_events.py | 6 + src/workos/common/models/group_created.py | 2 +- src/workos/common/models/group_deleted.py | 2 +- src/workos/common/models/group_updated.py | 2 +- .../common/models/invitation_accepted_data.py | 7 + src/workos/common/models/waitlist_user.py | 66 ++ .../common/models/waitlist_user_approved.py | 58 ++ .../common/models/waitlist_user_created.py | 58 ++ .../common/models/waitlist_user_denied.py | 58 ++ .../common/models/waitlist_user_state.py | 29 + .../models/directory_user_with_groups.py | 19 +- src/workos/events/models/event_schema.py | 9 + src/workos/groups/__init__.py | 4 + src/workos/groups/_resource.py | 688 +++++++++++++++++ src/workos/groups/models/__init__.py | 7 + src/workos/groups/models/create_group.py | 38 + .../groups/models/create_group_membership.py | 31 + src/workos/{common => groups}/models/group.py | 0 src/workos/groups/models/groups_order.py | 7 + src/workos/groups/models/update_group.py | 8 + src/workos/sso/_resource.py | 4 +- src/workos/sso/models/sso_provider.py | 2 - src/workos/types/groups/__init__.py | 3 + .../__init__.py | 3 + .../user_management/models/invitation.py | 7 + ...user_management_authentication_provider.py | 5 +- .../__init__.py | 7 + .../_resource.py | 129 ++++ .../models/__init__.py | 5 + ...nt_organization_membership_groups_order.py | 7 + tests/fixtures/create_group.json | 4 + tests/fixtures/create_group_membership.json | 3 + .../domain_verification_intent_options.json | 3 + tests/fixtures/generate_link.json | 7 +- tests/fixtures/intent_options.json | 3 + tests/fixtures/invitation.json | 1 + tests/fixtures/invitation_accepted.json | 1 + tests/fixtures/invitation_accepted_data.json | 1 + tests/fixtures/invitation_created.json | 1 + tests/fixtures/invitation_created_data.json | 1 + tests/fixtures/invitation_resent.json | 1 + tests/fixtures/invitation_resent_data.json | 1 + tests/fixtures/invitation_revoked.json | 1 + tests/fixtures/invitation_revoked_data.json | 1 + tests/fixtures/list_group.json | 17 + tests/fixtures/list_user_invite.json | 1 + tests/fixtures/update_group.json | 4 + tests/fixtures/user_invite.json | 1 + tests/fixtures/waitlist_user.json | 9 + tests/fixtures/waitlist_user_approved.json | 35 + tests/fixtures/waitlist_user_created.json | 35 + tests/fixtures/waitlist_user_denied.json | 35 + tests/test_authorization.py | 186 +++-- tests/test_groups.py | 503 +++++++++++++ tests/test_models_round_trip.py | 371 +++++++-- ...nagement_organization_membership_groups.py | 283 +++++++ 67 files changed, 3459 insertions(+), 203 deletions(-) create mode 100644 .last-synced-sha create mode 100644 src/workos/admin_portal/models/domain_verification_intent_options.py create mode 100644 src/workos/common/models/waitlist_user.py create mode 100644 src/workos/common/models/waitlist_user_approved.py create mode 100644 src/workos/common/models/waitlist_user_created.py create mode 100644 src/workos/common/models/waitlist_user_denied.py create mode 100644 src/workos/common/models/waitlist_user_state.py create mode 100644 src/workos/groups/__init__.py create mode 100644 src/workos/groups/_resource.py create mode 100644 src/workos/groups/models/__init__.py create mode 100644 src/workos/groups/models/create_group.py create mode 100644 src/workos/groups/models/create_group_membership.py rename src/workos/{common => groups}/models/group.py (100%) create mode 100644 src/workos/groups/models/groups_order.py create mode 100644 src/workos/groups/models/update_group.py create mode 100644 src/workos/types/groups/__init__.py create mode 100644 src/workos/types/user_management_organization_membership_groups/__init__.py create mode 100644 src/workos/user_management_organization_membership_groups/__init__.py create mode 100644 src/workos/user_management_organization_membership_groups/_resource.py create mode 100644 src/workos/user_management_organization_membership_groups/models/__init__.py create mode 100644 src/workos/user_management_organization_membership_groups/models/user_management_organization_membership_groups_order.py create mode 100644 tests/fixtures/create_group.json create mode 100644 tests/fixtures/create_group_membership.json create mode 100644 tests/fixtures/domain_verification_intent_options.json create mode 100644 tests/fixtures/list_group.json create mode 100644 tests/fixtures/update_group.json create mode 100644 tests/fixtures/waitlist_user.json create mode 100644 tests/fixtures/waitlist_user_approved.json create mode 100644 tests/fixtures/waitlist_user_created.json create mode 100644 tests/fixtures/waitlist_user_denied.json create mode 100644 tests/test_groups.py create mode 100644 tests/test_user_management_organization_membership_groups.py diff --git a/.last-synced-sha b/.last-synced-sha new file mode 100644 index 00000000..f72eb0e2 --- /dev/null +++ b/.last-synced-sha @@ -0,0 +1 @@ +92db0495807c86fbbc4d45bd266a6c1f5bcbb59c diff --git a/.oagen-manifest.json b/.oagen-manifest.json index 1b3ee0e7..505e8e2b 100644 --- a/.oagen-manifest.json +++ b/.oagen-manifest.json @@ -1,12 +1,13 @@ { "version": 2, "language": "python", - "generatedAt": "2026-04-23T15:27:42.321Z", + "generatedAt": "2026-04-27T21:32:10.629Z", "files": [ "src/workos/_client.py", "src/workos/admin_portal/__init__.py", "src/workos/admin_portal/_resource.py", "src/workos/admin_portal/models/__init__.py", + "src/workos/admin_portal/models/domain_verification_intent_options.py", "src/workos/admin_portal/models/generate_link.py", "src/workos/admin_portal/models/intent_options.py", "src/workos/admin_portal/models/portal_link_response.py", @@ -251,7 +252,6 @@ "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", @@ -408,6 +408,11 @@ "src/workos/common/models/vault_names_listed.py", "src/workos/common/models/vault_names_listed_data.py", "src/workos/common/models/vault_names_listed_data_actor_source.py", + "src/workos/common/models/waitlist_user.py", + "src/workos/common/models/waitlist_user_approved.py", + "src/workos/common/models/waitlist_user_created.py", + "src/workos/common/models/waitlist_user_denied.py", + "src/workos/common/models/waitlist_user_state.py", "src/workos/common/models/webhook_endpoint_json_status.py", "src/workos/common/models/widget_session_token_scopes.py", "src/workos/connect/__init__.py", @@ -455,6 +460,14 @@ "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", @@ -538,6 +551,7 @@ "src/workos/types/directory_sync/__init__.py", "src/workos/types/events/__init__.py", "src/workos/types/feature_flags/__init__.py", + "src/workos/types/groups/__init__.py", "src/workos/types/multi_factor_auth/__init__.py", "src/workos/types/organization_domains/__init__.py", "src/workos/types/organizations/__init__.py", @@ -545,6 +559,7 @@ "src/workos/types/radar/__init__.py", "src/workos/types/sso/__init__.py", "src/workos/types/user_management/__init__.py", + "src/workos/types/user_management_organization_membership_groups/__init__.py", "src/workos/types/webhooks/__init__.py", "src/workos/types/widgets/__init__.py", "src/workos/user_management/__init__.py", @@ -609,6 +624,10 @@ "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", @@ -749,6 +768,8 @@ "tests/fixtures/create_authorization_permission.json", "tests/fixtures/create_authorization_resource.json", "tests/fixtures/create_cors_origin.json", + "tests/fixtures/create_group.json", + "tests/fixtures/create_group_membership.json", "tests/fixtures/create_m2m_application.json", "tests/fixtures/create_magic_code_and_return.json", "tests/fixtures/create_oauth_application.json", @@ -781,6 +802,7 @@ "tests/fixtures/directory_user_email.json", "tests/fixtures/directory_user_with_groups.json", "tests/fixtures/directory_user_with_groups_email.json", + "tests/fixtures/domain_verification_intent_options.json", "tests/fixtures/dsync_activated.json", "tests/fixtures/dsync_activated_data.json", "tests/fixtures/dsync_activated_data_domain.json", @@ -887,6 +909,7 @@ "tests/fixtures/list_directory_user_with_groups.json", "tests/fixtures/list_event_schema.json", "tests/fixtures/list_flag.json", + "tests/fixtures/list_group.json", "tests/fixtures/list_organization.json", "tests/fixtures/list_role_assignment.json", "tests/fixtures/list_user.json", @@ -999,6 +1022,7 @@ "tests/fixtures/update_audit_logs_retention.json", "tests/fixtures/update_authorization_permission.json", "tests/fixtures/update_authorization_resource.json", + "tests/fixtures/update_group.json", "tests/fixtures/update_jwt_template.json", "tests/fixtures/update_oauth_application.json", "tests/fixtures/update_organization.json", @@ -1045,6 +1069,10 @@ "tests/fixtures/vault_names_listed_data.json", "tests/fixtures/verify_email_address.json", "tests/fixtures/verify_email_response.json", + "tests/fixtures/waitlist_user.json", + "tests/fixtures/waitlist_user_approved.json", + "tests/fixtures/waitlist_user_created.json", + "tests/fixtures/waitlist_user_denied.json", "tests/fixtures/webhook_endpoint_json.json", "tests/fixtures/widget_session_token.json", "tests/fixtures/widget_session_token_response.json", @@ -1056,6 +1084,7 @@ "tests/test_directory_sync.py", "tests/test_events.py", "tests/test_feature_flags.py", + "tests/test_groups.py", "tests/test_models_round_trip.py", "tests/test_multi_factor_auth.py", "tests/test_organization_domains.py", @@ -1064,7 +1093,678 @@ "tests/test_radar.py", "tests/test_sso.py", "tests/test_user_management.py", + "tests/test_user_management_organization_membership_groups.py", "tests/test_webhooks.py", "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" + }, + "POST /auth/factors/enroll": { + "sdkMethod": "enroll_factor", + "service": "multi_factor_auth" + }, + "GET /auth/factors/{id}": { + "sdkMethod": "get_factor", + "service": "multi_factor_auth" + }, + "DELETE /auth/factors/{id}": { + "sdkMethod": "delete_factor", + "service": "multi_factor_auth" + }, + "POST /auth/factors/{id}/challenge": { + "sdkMethod": "challenge_factor", + "service": "multi_factor_auth" + }, + "POST /authkit/oauth2/complete": { + "sdkMethod": "complete_oauth2", + "service": "connect" + }, + "POST /authorization/organization_memberships/{organization_membership_id}/check": { + "sdkMethod": "check", + "service": "authorization" + }, + "GET /authorization/organization_memberships/{organization_membership_id}/resources": { + "sdkMethod": "list_resources_for_membership", + "service": "authorization" + }, + "GET /authorization/organization_memberships/{organization_membership_id}/resources/{resource_id}/permissions": { + "sdkMethod": "list_effective_permissions", + "service": "authorization" + }, + "GET /authorization/organization_memberships/{organization_membership_id}/resources/{resource_type_slug}/{external_id}/permissions": { + "sdkMethod": "list_effective_permissions_by_external_id", + "service": "authorization" + }, + "GET /authorization/organization_memberships/{organization_membership_id}/role_assignments": { + "sdkMethod": "list_role_assignments", + "service": "authorization" + }, + "POST /authorization/organization_memberships/{organization_membership_id}/role_assignments": { + "sdkMethod": "assign_role", + "service": "authorization" + }, + "DELETE /authorization/organization_memberships/{organization_membership_id}/role_assignments": { + "sdkMethod": "remove_role", + "service": "authorization" + }, + "DELETE /authorization/organization_memberships/{organization_membership_id}/role_assignments/{role_assignment_id}": { + "sdkMethod": "remove_role_assignment", + "service": "authorization" + }, + "GET /authorization/organizations/{organizationId}/roles": { + "sdkMethod": "list_organization_roles", + "service": "authorization" + }, + "POST /authorization/organizations/{organizationId}/roles": { + "sdkMethod": "create_organization_role", + "service": "authorization" + }, + "GET /authorization/organizations/{organizationId}/roles/{slug}": { + "sdkMethod": "get_organization_role", + "service": "authorization" + }, + "PATCH /authorization/organizations/{organizationId}/roles/{slug}": { + "sdkMethod": "update_organization_role", + "service": "authorization" + }, + "DELETE /authorization/organizations/{organizationId}/roles/{slug}": { + "sdkMethod": "delete_organization_role", + "service": "authorization" + }, + "POST /authorization/organizations/{organizationId}/roles/{slug}/permissions": { + "sdkMethod": "add_organization_role_permission", + "service": "authorization" + }, + "PUT /authorization/organizations/{organizationId}/roles/{slug}/permissions": { + "sdkMethod": "set_organization_role_permissions", + "service": "authorization" + }, + "DELETE /authorization/organizations/{organizationId}/roles/{slug}/permissions/{permissionSlug}": { + "sdkMethod": "remove_organization_role_permission", + "service": "authorization" + }, + "GET /authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}": { + "sdkMethod": "get_resource_by_external_id", + "service": "authorization" + }, + "PATCH /authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}": { + "sdkMethod": "update_resource_by_external_id", + "service": "authorization" + }, + "DELETE /authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}": { + "sdkMethod": "delete_resource_by_external_id", + "service": "authorization" + }, + "GET /authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}/organization_memberships": { + "sdkMethod": "list_memberships_for_resource_by_external_id", + "service": "authorization" + }, + "GET /authorization/resources": { + "sdkMethod": "list_resources", + "service": "authorization" + }, + "POST /authorization/resources": { + "sdkMethod": "create_resource", + "service": "authorization" + }, + "GET /authorization/resources/{resource_id}": { + "sdkMethod": "get_resource", + "service": "authorization" + }, + "PATCH /authorization/resources/{resource_id}": { + "sdkMethod": "update_resource", + "service": "authorization" + }, + "DELETE /authorization/resources/{resource_id}": { + "sdkMethod": "delete_resource", + "service": "authorization" + }, + "GET /authorization/resources/{resource_id}/organization_memberships": { + "sdkMethod": "list_memberships_for_resource", + "service": "authorization" + }, + "GET /authorization/roles": { + "sdkMethod": "list_environment_roles", + "service": "authorization" + }, + "POST /authorization/roles": { + "sdkMethod": "create_environment_role", + "service": "authorization" + }, + "GET /authorization/roles/{slug}": { + "sdkMethod": "get_environment_role", + "service": "authorization" + }, + "PATCH /authorization/roles/{slug}": { + "sdkMethod": "update_environment_role", + "service": "authorization" + }, + "POST /authorization/roles/{slug}/permissions": { + "sdkMethod": "add_environment_role_permission", + "service": "authorization" + }, + "PUT /authorization/roles/{slug}/permissions": { + "sdkMethod": "set_environment_role_permissions", + "service": "authorization" + }, + "GET /authorization/permissions": { + "sdkMethod": "list_permissions", + "service": "authorization" + }, + "POST /authorization/permissions": { + "sdkMethod": "create_permission", + "service": "authorization" + }, + "GET /authorization/permissions/{slug}": { + "sdkMethod": "get_permission", + "service": "authorization" + }, + "PATCH /authorization/permissions/{slug}": { + "sdkMethod": "update_permission", + "service": "authorization" + }, + "DELETE /authorization/permissions/{slug}": { + "sdkMethod": "delete_permission", + "service": "authorization" + }, + "GET /connect/applications": { + "sdkMethod": "list_applications", + "service": "connect" + }, + "POST /connect/applications": { + "sdkMethod": "create_application", + "service": "connect" + }, + "GET /connect/applications/{id}": { + "sdkMethod": "get_application", + "service": "connect" + }, + "PUT /connect/applications/{id}": { + "sdkMethod": "update_application", + "service": "connect" + }, + "DELETE /connect/applications/{id}": { + "sdkMethod": "delete_application", + "service": "connect" + }, + "GET /connect/applications/{id}/client_secrets": { + "sdkMethod": "list_application_client_secrets", + "service": "connect" + }, + "POST /connect/applications/{id}/client_secrets": { + "sdkMethod": "create_application_client_secret", + "service": "connect" + }, + "DELETE /connect/client_secrets/{id}": { + "sdkMethod": "delete_client_secret", + "service": "connect" + }, + "GET /connections": { + "sdkMethod": "list_connections", + "service": "sso" + }, + "GET /connections/{id}": { + "sdkMethod": "get_connection", + "service": "sso" + }, + "DELETE /connections/{id}": { + "sdkMethod": "delete_connection", + "service": "sso" + }, + "POST /data-integrations/{slug}/authorize": { + "sdkMethod": "authorize_data_integration", + "service": "pipes" + }, + "POST /data-integrations/{slug}/token": { + "sdkMethod": "create_data_integration_token", + "service": "pipes" + }, + "GET /directories": { + "sdkMethod": "list_directories", + "service": "directory_sync" + }, + "GET /directories/{id}": { + "sdkMethod": "get_directory", + "service": "directory_sync" + }, + "DELETE /directories/{id}": { + "sdkMethod": "delete_directory", + "service": "directory_sync" + }, + "GET /directory_groups": { + "sdkMethod": "list_groups", + "service": "directory_sync" + }, + "GET /directory_groups/{id}": { + "sdkMethod": "get_group", + "service": "directory_sync" + }, + "GET /directory_users": { + "sdkMethod": "list_users", + "service": "directory_sync" + }, + "GET /directory_users/{id}": { + "sdkMethod": "get_user", + "service": "directory_sync" + }, + "GET /events": { + "sdkMethod": "list_events", + "service": "events" + }, + "GET /feature-flags": { + "sdkMethod": "list_feature_flags", + "service": "feature_flags" + }, + "GET /feature-flags/{slug}": { + "sdkMethod": "get_feature_flag", + "service": "feature_flags" + }, + "PUT /feature-flags/{slug}/disable": { + "sdkMethod": "disable_feature_flag", + "service": "feature_flags" + }, + "PUT /feature-flags/{slug}/enable": { + "sdkMethod": "enable_feature_flag", + "service": "feature_flags" + }, + "POST /feature-flags/{slug}/targets/{resourceId}": { + "sdkMethod": "add_flag_target", + "service": "feature_flags" + }, + "DELETE /feature-flags/{slug}/targets/{resourceId}": { + "sdkMethod": "remove_flag_target", + "service": "feature_flags" + }, + "POST /organization_domains": { + "sdkMethod": "create_organization_domain", + "service": "organization_domains" + }, + "GET /organization_domains/{id}": { + "sdkMethod": "get_organization_domain", + "service": "organization_domains" + }, + "DELETE /organization_domains/{id}": { + "sdkMethod": "delete_organization_domain", + "service": "organization_domains" + }, + "POST /organization_domains/{id}/verify": { + "sdkMethod": "verify_organization_domain", + "service": "organization_domains" + }, + "GET /organizations": { + "sdkMethod": "list_organizations", + "service": "organizations" + }, + "POST /organizations": { + "sdkMethod": "create_organization", + "service": "organizations" + }, + "GET /organizations/external_id/{external_id}": { + "sdkMethod": "get_organization_by_external_id", + "service": "organizations" + }, + "GET /organizations/{id}": { + "sdkMethod": "get_organization", + "service": "organizations" + }, + "PUT /organizations/{id}": { + "sdkMethod": "update_organization", + "service": "organizations" + }, + "DELETE /organizations/{id}": { + "sdkMethod": "delete_organization", + "service": "organizations" + }, + "GET /organizations/{id}/audit_log_configuration": { + "sdkMethod": "get_audit_log_configuration", + "service": "organizations" + }, + "GET /organizations/{id}/audit_logs_retention": { + "sdkMethod": "get_organization_audit_logs_retention", + "service": "audit_logs" + }, + "PUT /organizations/{id}/audit_logs_retention": { + "sdkMethod": "update_organization_audit_logs_retention", + "service": "audit_logs" + }, + "GET /organizations/{organizationId}/api_keys": { + "sdkMethod": "list_organization_api_keys", + "service": "api_keys" + }, + "POST /organizations/{organizationId}/api_keys": { + "sdkMethod": "create_organization_api_key", + "service": "api_keys" + }, + "GET /organizations/{organizationId}/feature-flags": { + "sdkMethod": "list_organization_feature_flags", + "service": "feature_flags" + }, + "GET /organizations/{organizationId}/groups": { + "sdkMethod": "list_organization_groups", + "service": "groups" + }, + "POST /organizations/{organizationId}/groups": { + "sdkMethod": "create_organization_group", + "service": "groups" + }, + "GET /organizations/{organizationId}/groups/{groupId}": { + "sdkMethod": "get_organization_group", + "service": "groups" + }, + "PATCH /organizations/{organizationId}/groups/{groupId}": { + "sdkMethod": "update_organization_group", + "service": "groups" + }, + "DELETE /organizations/{organizationId}/groups/{groupId}": { + "sdkMethod": "delete_organization_group", + "service": "groups" + }, + "GET /organizations/{organizationId}/groups/{groupId}/organization-memberships": { + "sdkMethod": "list_group_organization_memberships", + "service": "groups" + }, + "POST /organizations/{organizationId}/groups/{groupId}/organization-memberships": { + "sdkMethod": "create_group_organization_membership", + "service": "groups" + }, + "DELETE /organizations/{organizationId}/groups/{groupId}/organization-memberships/{omId}": { + "sdkMethod": "delete_group_organization_membership", + "service": "groups" + }, + "POST /portal/generate_link": { + "sdkMethod": "generate_link", + "service": "admin_portal" + }, + "POST /radar/attempts": { + "sdkMethod": "create_attempt", + "service": "radar" + }, + "PUT /radar/attempts/{id}": { + "sdkMethod": "update_attempt", + "service": "radar" + }, + "POST /radar/lists/{type}/{action}": { + "sdkMethod": "add_list_entry", + "service": "radar" + }, + "DELETE /radar/lists/{type}/{action}": { + "sdkMethod": "remove_list_entry", + "service": "radar" + }, + "GET /sso/authorize": { + "sdkMethod": "get_authorization_url", + "service": "sso" + }, + "GET /sso/logout": { + "sdkMethod": "get_logout_url", + "service": "sso" + }, + "POST /sso/logout/authorize": { + "sdkMethod": "authorize_logout", + "service": "sso" + }, + "GET /sso/profile": { + "sdkMethod": "get_profile", + "service": "sso" + }, + "POST /sso/token": { + "sdkMethod": "get_profile_and_token", + "service": "sso" + }, + "GET /sso/jwks/{clientId}": { + "sdkMethod": "get_jwks", + "service": "user_management" + }, + "POST /user_management/authenticate": { + "sdkMethod": "create_authenticate", + "service": "user_management" + }, + "GET /user_management/authorize": { + "sdkMethod": "get_authorization_url", + "service": "user_management" + }, + "POST /user_management/authorize/device": { + "sdkMethod": "create_device", + "service": "user_management" + }, + "GET /user_management/sessions/logout": { + "sdkMethod": "get_logout_url", + "service": "user_management" + }, + "POST /user_management/sessions/revoke": { + "sdkMethod": "revoke_session", + "service": "user_management" + }, + "POST /user_management/cors_origins": { + "sdkMethod": "create_cors_origin", + "service": "user_management" + }, + "GET /user_management/email_verification/{id}": { + "sdkMethod": "get_email_verification", + "service": "user_management" + }, + "POST /user_management/password_reset": { + "sdkMethod": "reset_password", + "service": "user_management" + }, + "POST /user_management/password_reset/confirm": { + "sdkMethod": "confirm_password_reset", + "service": "user_management" + }, + "GET /user_management/password_reset/{id}": { + "sdkMethod": "get_password_reset", + "service": "user_management" + }, + "GET /user_management/users": { + "sdkMethod": "list_users", + "service": "user_management" + }, + "POST /user_management/users": { + "sdkMethod": "create_user", + "service": "user_management" + }, + "GET /user_management/users/external_id/{external_id}": { + "sdkMethod": "get_user_by_external_id", + "service": "user_management" + }, + "GET /user_management/users/{id}": { + "sdkMethod": "get_user", + "service": "user_management" + }, + "PUT /user_management/users/{id}": { + "sdkMethod": "update_user", + "service": "user_management" + }, + "DELETE /user_management/users/{id}": { + "sdkMethod": "delete_user", + "service": "user_management" + }, + "POST /user_management/users/{id}/email_change/confirm": { + "sdkMethod": "confirm_email_change", + "service": "user_management" + }, + "POST /user_management/users/{id}/email_change/send": { + "sdkMethod": "send_email_change", + "service": "user_management" + }, + "POST /user_management/users/{id}/email_verification/confirm": { + "sdkMethod": "verify_email", + "service": "user_management" + }, + "POST /user_management/users/{id}/email_verification/send": { + "sdkMethod": "send_verification_email", + "service": "user_management" + }, + "GET /user_management/users/{id}/identities": { + "sdkMethod": "get_user_identities", + "service": "user_management" + }, + "GET /user_management/users/{id}/sessions": { + "sdkMethod": "list_sessions", + "service": "user_management" + }, + "GET /user_management/invitations": { + "sdkMethod": "list_invitations", + "service": "user_management" + }, + "POST /user_management/invitations": { + "sdkMethod": "send_invitation", + "service": "user_management" + }, + "GET /user_management/invitations/by_token/{token}": { + "sdkMethod": "find_invitation_by_token", + "service": "user_management" + }, + "GET /user_management/invitations/{id}": { + "sdkMethod": "get_invitation", + "service": "user_management" + }, + "POST /user_management/invitations/{id}/accept": { + "sdkMethod": "accept_invitation", + "service": "user_management" + }, + "POST /user_management/invitations/{id}/resend": { + "sdkMethod": "resend_invitation", + "service": "user_management" + }, + "POST /user_management/invitations/{id}/revoke": { + "sdkMethod": "revoke_invitation", + "service": "user_management" + }, + "PUT /user_management/jwt_template": { + "sdkMethod": "update_jwt_template", + "service": "user_management" + }, + "POST /user_management/magic_auth": { + "sdkMethod": "create_magic_auth", + "service": "user_management" + }, + "GET /user_management/magic_auth/{id}": { + "sdkMethod": "get_magic_auth", + "service": "user_management" + }, + "GET /user_management/organization_memberships": { + "sdkMethod": "list_organization_memberships", + "service": "user_management" + }, + "POST /user_management/organization_memberships": { + "sdkMethod": "create_organization_membership", + "service": "user_management" + }, + "GET /user_management/organization_memberships/{id}": { + "sdkMethod": "get_organization_membership", + "service": "user_management" + }, + "PUT /user_management/organization_memberships/{id}": { + "sdkMethod": "update_organization_membership", + "service": "user_management" + }, + "DELETE /user_management/organization_memberships/{id}": { + "sdkMethod": "delete_organization_membership", + "service": "user_management" + }, + "PUT /user_management/organization_memberships/{id}/deactivate": { + "sdkMethod": "deactivate_organization_membership", + "service": "user_management" + }, + "PUT /user_management/organization_memberships/{id}/reactivate": { + "sdkMethod": "reactivate_organization_membership", + "service": "user_management" + }, + "GET /user_management/organization_memberships/{omId}/groups": { + "sdkMethod": "list_organization_membership_groups", + "service": "user_management_organization_membership_groups" + }, + "POST /user_management/redirect_uris": { + "sdkMethod": "create_redirect_uri", + "service": "user_management" + }, + "GET /user_management/users/{userId}/feature-flags": { + "sdkMethod": "list_user_feature_flags", + "service": "feature_flags" + }, + "GET /user_management/users/{user_id}/authorized_applications": { + "sdkMethod": "list_user_authorized_applications", + "service": "user_management" + }, + "DELETE /user_management/users/{user_id}/authorized_applications/{application_id}": { + "sdkMethod": "delete_user_authorized_application", + "service": "user_management" + }, + "GET /user_management/users/{user_id}/connected_accounts/{slug}": { + "sdkMethod": "get_user_connected_account", + "service": "pipes" + }, + "DELETE /user_management/users/{user_id}/connected_accounts/{slug}": { + "sdkMethod": "delete_user_connected_account", + "service": "pipes" + }, + "GET /user_management/users/{user_id}/data_providers": { + "sdkMethod": "list_user_data_providers", + "service": "pipes" + }, + "GET /user_management/users/{userlandUserId}/auth_factors": { + "sdkMethod": "list_user_auth_factors", + "service": "multi_factor_auth" + }, + "POST /user_management/users/{userlandUserId}/auth_factors": { + "sdkMethod": "create_user_auth_factor", + "service": "multi_factor_auth" + }, + "GET /webhook_endpoints": { + "sdkMethod": "list_webhook_endpoints", + "service": "webhooks" + }, + "POST /webhook_endpoints": { + "sdkMethod": "create_webhook_endpoint", + "service": "webhooks" + }, + "PATCH /webhook_endpoints/{id}": { + "sdkMethod": "update_webhook_endpoint", + "service": "webhooks" + }, + "DELETE /webhook_endpoints/{id}": { + "sdkMethod": "delete_webhook_endpoint", + "service": "webhooks" + }, + "POST /widgets/token": { + "sdkMethod": "create_token", + "service": "widgets" + }, + "GET /audit_logs/actions": { + "sdkMethod": "list_actions", + "service": "audit_logs" + }, + "GET /audit_logs/actions/{actionName}/schemas": { + "sdkMethod": "list_action_schemas", + "service": "audit_logs" + }, + "POST /audit_logs/actions/{actionName}/schemas": { + "sdkMethod": "create_schema", + "service": "audit_logs" + }, + "POST /audit_logs/events": { + "sdkMethod": "create_event", + "service": "audit_logs" + }, + "POST /audit_logs/exports": { + "sdkMethod": "create_export", + "service": "audit_logs" + }, + "GET /audit_logs/exports/{auditLogExportId}": { + "sdkMethod": "get_export", + "service": "audit_logs" + } + } } diff --git a/src/workos/_client.py b/src/workos/_client.py index df199e59..f59743fa 100644 --- a/src/workos/_client.py +++ b/src/workos/_client.py @@ -22,19 +22,21 @@ AsyncOrganizationDomains, ) from .organizations._resource import Organizations, AsyncOrganizations +from .groups._resource import Groups, AsyncGroups from .admin_portal._resource import AdminPortal, AsyncAdminPortal from .radar._resource import Radar, AsyncRadar from .user_management._resource import UserManagement, AsyncUserManagement +from .user_management_organization_membership_groups._resource import ( + UserManagementOrganizationMembershipGroups, + AsyncUserManagementOrganizationMembershipGroups, +) from .webhooks._resource import Webhooks, AsyncWebhooks from .widgets._resource import Widgets, AsyncWidgets from .audit_logs._resource import AuditLogs, AsyncAuditLogs - -# @oagen-ignore-start — non-spec service imports (hand-maintained) from .passwordless import AsyncPasswordless, Passwordless from .vault import AsyncVault, Vault from .actions import Actions, AsyncActions from .pkce import PKCE -# @oagen-ignore-end class WorkOSClient(_SyncBase): @@ -95,6 +97,11 @@ def organizations(self) -> Organizations: """Organizations API resources.""" return Organizations(self) + @functools.cached_property + def groups(self) -> Groups: + """Groups API resources.""" + return Groups(self) + @functools.cached_property def admin_portal(self) -> AdminPortal: """Admin Portal API resources.""" @@ -110,6 +117,13 @@ def user_management(self) -> UserManagement: """User Management API resources.""" return UserManagement(self) + @functools.cached_property + def user_management_organization_membership_groups( + self, + ) -> UserManagementOrganizationMembershipGroups: + """User Management Organization Membership Groups API resources.""" + return UserManagementOrganizationMembershipGroups(self) + @functools.cached_property def webhooks(self) -> Webhooks: """Webhooks API resources.""" @@ -213,6 +227,11 @@ def organizations(self) -> AsyncOrganizations: """Organizations API resources.""" return AsyncOrganizations(self) + @functools.cached_property + def groups(self) -> AsyncGroups: + """Groups API resources.""" + return AsyncGroups(self) + @functools.cached_property def admin_portal(self) -> AsyncAdminPortal: """Admin Portal API resources.""" @@ -228,6 +247,13 @@ def user_management(self) -> AsyncUserManagement: """User Management API resources.""" return AsyncUserManagement(self) + @functools.cached_property + def user_management_organization_membership_groups( + self, + ) -> AsyncUserManagementOrganizationMembershipGroups: + """User Management Organization Membership Groups API resources.""" + return AsyncUserManagementOrganizationMembershipGroups(self) + @functools.cached_property def webhooks(self) -> AsyncWebhooks: """Webhooks API resources.""" @@ -271,3 +297,7 @@ def pkce(self) -> PKCE: return PKCE() # @oagen-ignore-end + + +# @oagen-ignore-start — non-spec service imports (hand-maintained) +# @oagen-ignore-end diff --git a/src/workos/admin_portal/_resource.py b/src/workos/admin_portal/_resource.py index 65250ace..57e4233d 100644 --- a/src/workos/admin_portal/_resource.py +++ b/src/workos/admin_portal/_resource.py @@ -26,7 +26,7 @@ def generate_link( success_url: Optional[str] = None, intent: Optional[Union[GenerateLinkIntent, str]] = None, intent_options: Optional[IntentOptions] = None, - admin_emails: Optional[List[str]] = None, + it_contact_emails: Optional[List[str]] = None, request_options: Optional[RequestOptions] = None, ) -> PortalLinkResponse: """Generate a Portal Link @@ -46,7 +46,7 @@ def generate_link( - `certificate_renewal` - Launch Admin Portal for renewing SAML Certificates - `bring_your_own_key` - Launch Admin Portal for configuring Bring Your Own Key intent_options: Options to configure the Admin Portal based on the intent. - admin_emails: The email addresses of the IT admins to grant access to the Admin Portal for the given organization. Accepts up to 20 emails. + it_contact_emails: The email addresses of the IT contacts to grant access to the Admin Portal for the given organization. Accepts up to 20 emails. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -71,7 +71,7 @@ def generate_link( "intent_options": intent_options.to_dict() if intent_options is not None else None, - "admin_emails": admin_emails, + "it_contact_emails": it_contact_emails, }.items() if v is not None } @@ -98,7 +98,7 @@ async def generate_link( success_url: Optional[str] = None, intent: Optional[Union[GenerateLinkIntent, str]] = None, intent_options: Optional[IntentOptions] = None, - admin_emails: Optional[List[str]] = None, + it_contact_emails: Optional[List[str]] = None, request_options: Optional[RequestOptions] = None, ) -> PortalLinkResponse: """Generate a Portal Link @@ -118,7 +118,7 @@ async def generate_link( - `certificate_renewal` - Launch Admin Portal for renewing SAML Certificates - `bring_your_own_key` - Launch Admin Portal for configuring Bring Your Own Key intent_options: Options to configure the Admin Portal based on the intent. - admin_emails: The email addresses of the IT admins to grant access to the Admin Portal for the given organization. Accepts up to 20 emails. + it_contact_emails: The email addresses of the IT contacts to grant access to the Admin Portal for the given organization. Accepts up to 20 emails. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. Returns: @@ -143,7 +143,7 @@ async def generate_link( "intent_options": intent_options.to_dict() if intent_options is not None else None, - "admin_emails": admin_emails, + "it_contact_emails": it_contact_emails, }.items() if v is not None } diff --git a/src/workos/admin_portal/models/__init__.py b/src/workos/admin_portal/models/__init__.py index 1eaede20..b1b690c9 100644 --- a/src/workos/admin_portal/models/__init__.py +++ b/src/workos/admin_portal/models/__init__.py @@ -1,5 +1,8 @@ # This file is auto-generated by oagen. Do not edit. +from .domain_verification_intent_options import ( + DomainVerificationIntentOptions as DomainVerificationIntentOptions, +) from .generate_link import GenerateLink as GenerateLink from .intent_options import IntentOptions as IntentOptions from .portal_link_response import PortalLinkResponse as PortalLinkResponse diff --git a/src/workos/admin_portal/models/domain_verification_intent_options.py b/src/workos/admin_portal/models/domain_verification_intent_options.py new file mode 100644 index 00000000..440c592b --- /dev/null +++ b/src/workos/admin_portal/models/domain_verification_intent_options.py @@ -0,0 +1,32 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Dict, Optional +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class DomainVerificationIntentOptions: + """Domain Verification Intent Options model.""" + + domain_name: Optional[str] = None + """The domain name to verify. When provided, the domain verification flow will skip the domain entry form and go directly to the verification step.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "DomainVerificationIntentOptions": + """Deserialize from a dictionary.""" + try: + return cls( + domain_name=data.get("domain_name"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("DomainVerificationIntentOptions", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + if self.domain_name is not None: + result["domain_name"] = self.domain_name + return result diff --git a/src/workos/admin_portal/models/generate_link.py b/src/workos/admin_portal/models/generate_link.py index e5e39d67..5786abc2 100644 --- a/src/workos/admin_portal/models/generate_link.py +++ b/src/workos/admin_portal/models/generate_link.py @@ -34,8 +34,8 @@ class GenerateLink: - `bring_your_own_key` - Launch Admin Portal for configuring Bring Your Own Key""" intent_options: Optional["IntentOptions"] = None """Options to configure the Admin Portal based on the intent.""" - admin_emails: Optional[List[str]] = None - """The email addresses of the IT admins to grant access to the Admin Portal for the given organization. Accepts up to 20 emails.""" + it_contact_emails: Optional[List[str]] = None + """The email addresses of the IT contacts to grant access to the Admin Portal for the given organization. Accepts up to 20 emails.""" @classmethod def from_dict(cls, data: Dict[str, Any]) -> "GenerateLink": @@ -53,7 +53,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "GenerateLink": ) if (_v_intent_options := data.get("intent_options")) is not None else None, - admin_emails=data.get("admin_emails"), + it_contact_emails=data.get("it_contact_emails"), ) except (KeyError, ValueError) as e: _raise_deserialize_error("GenerateLink", e) @@ -72,6 +72,6 @@ def to_dict(self) -> Dict[str, Any]: ) if self.intent_options is not None: result["intent_options"] = self.intent_options.to_dict() - if self.admin_emails is not None: - result["admin_emails"] = self.admin_emails + if self.it_contact_emails is not None: + result["it_contact_emails"] = self.it_contact_emails return result diff --git a/src/workos/admin_portal/models/intent_options.py b/src/workos/admin_portal/models/intent_options.py index e38af492..033caf0e 100644 --- a/src/workos/admin_portal/models/intent_options.py +++ b/src/workos/admin_portal/models/intent_options.py @@ -4,9 +4,10 @@ from dataclasses import dataclass from typing import cast -from typing import Any, Dict +from typing import Any, Dict, Optional from workos._types import _raise_deserialize_error +from .domain_verification_intent_options import DomainVerificationIntentOptions from .sso_intent_options import SSOIntentOptions @@ -14,15 +15,25 @@ class IntentOptions: """Intent Options model.""" - sso: "SSOIntentOptions" + sso: Optional["SSOIntentOptions"] = None """SSO-specific options for the Admin Portal.""" + domain_verification: Optional["DomainVerificationIntentOptions"] = None + """Domain verification-specific options for the Admin Portal.""" @classmethod def from_dict(cls, data: Dict[str, Any]) -> "IntentOptions": """Deserialize from a dictionary.""" try: return cls( - sso=SSOIntentOptions.from_dict(cast(Dict[str, Any], data["sso"])), + sso=SSOIntentOptions.from_dict(cast(Dict[str, Any], _v_sso)) + if (_v_sso := data.get("sso")) is not None + else None, + domain_verification=DomainVerificationIntentOptions.from_dict( + cast(Dict[str, Any], _v_domain_verification) + ) + if (_v_domain_verification := data.get("domain_verification")) + is not None + else None, ) except (KeyError, ValueError) as e: _raise_deserialize_error("IntentOptions", e) @@ -30,5 +41,8 @@ def from_dict(cls, data: Dict[str, Any]) -> "IntentOptions": def to_dict(self) -> Dict[str, Any]: """Serialize to a dictionary.""" result: Dict[str, Any] = {} - result["sso"] = self.sso.to_dict() + if self.sso is not None: + result["sso"] = self.sso.to_dict() + if self.domain_verification is not None: + result["domain_verification"] = self.domain_verification.to_dict() return result diff --git a/src/workos/authorization/_resource.py b/src/workos/authorization/_resource.py index 7f4b5a9e..55a229c8 100644 --- a/src/workos/authorization/_resource.py +++ b/src/workos/authorization/_resource.py @@ -119,7 +119,7 @@ def check( request_options=request_options, ) - def list_organization_membership_resources( + def list_resources_for_membership( self, organization_membership_id: str, *, @@ -187,7 +187,7 @@ def list_organization_membership_resources( request_options=request_options, ) - def list_resource_permissions( + def list_effective_permissions( self, organization_membership_id: str, resource_id: str, @@ -295,7 +295,7 @@ def list_effective_permissions_by_external_id( request_options=request_options, ) - def list_organization_membership_role_assignments( + def list_role_assignments( self, organization_membership_id: str, *, @@ -431,7 +431,7 @@ def remove_role( request_options=request_options, ) - def delete_organization_membership_role_assignment( + def remove_role_assignment( self, organization_membership_id: str, role_assignment_id: str, @@ -656,7 +656,7 @@ def delete_organization_role( request_options=request_options, ) - def create_role_permission( + def add_organization_role_permission( self, organization_id: str, slug: str, @@ -697,7 +697,7 @@ def create_role_permission( request_options=request_options, ) - def update_role_permissions( + def set_organization_role_permissions( self, organization_id: str, slug: str, @@ -737,7 +737,7 @@ def update_role_permissions( request_options=request_options, ) - def delete_role_permission( + def remove_organization_role_permission( self, organization_id: str, slug: str, @@ -768,7 +768,7 @@ def delete_role_permission( request_options=request_options, ) - def get_organization_resource( + def get_resource_by_external_id( self, organization_id: str, resource_type_slug: str, @@ -803,7 +803,7 @@ def get_organization_resource( request_options=request_options, ) - def update_organization_resource( + def update_resource_by_external_id( self, organization_id: str, resource_type_slug: str, @@ -868,7 +868,7 @@ def update_organization_resource( request_options=request_options, ) - def delete_organization_resource( + def delete_resource_by_external_id( self, organization_id: str, resource_type_slug: str, @@ -911,7 +911,7 @@ def delete_organization_resource( request_options=request_options, ) - def list_resource_organization_memberships( + def list_memberships_for_resource_by_external_id( self, organization_id: str, resource_type_slug: str, @@ -984,6 +984,7 @@ def list_resources( order: Optional[Union[AuthorizationOrder, str]] = "desc", organization_id: Optional[str] = None, resource_type_slug: Optional[str] = None, + resource_external_id: Optional[str] = None, search: Optional[str] = None, parent: Optional[Union[ParentById, ParentByExternalId]] = None, request_options: Optional[RequestOptions] = None, @@ -999,6 +1000,7 @@ def list_resources( 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`. organization_id: Filter resources by organization ID. resource_type_slug: Filter resources by resource type slug. + resource_external_id: Filter resources by external ID. search: Search resources by name. parent: Identifies the parent. One of: ParentById, ParentByExternalId. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -1022,6 +1024,7 @@ def list_resources( "order": enum_value(order) if order is not None else None, "organization_id": organization_id, "resource_type_slug": resource_type_slug, + "resource_external_id": resource_external_id, "search": search, }.items() if v is not None @@ -1782,7 +1785,7 @@ async def check( request_options=request_options, ) - async def list_organization_membership_resources( + async def list_resources_for_membership( self, organization_membership_id: str, *, @@ -1850,7 +1853,7 @@ async def list_organization_membership_resources( request_options=request_options, ) - async def list_resource_permissions( + async def list_effective_permissions( self, organization_membership_id: str, resource_id: str, @@ -1958,7 +1961,7 @@ async def list_effective_permissions_by_external_id( request_options=request_options, ) - async def list_organization_membership_role_assignments( + async def list_role_assignments( self, organization_membership_id: str, *, @@ -2094,7 +2097,7 @@ async def remove_role( request_options=request_options, ) - async def delete_organization_membership_role_assignment( + async def remove_role_assignment( self, organization_membership_id: str, role_assignment_id: str, @@ -2319,7 +2322,7 @@ async def delete_organization_role( request_options=request_options, ) - async def create_role_permission( + async def add_organization_role_permission( self, organization_id: str, slug: str, @@ -2360,7 +2363,7 @@ async def create_role_permission( request_options=request_options, ) - async def update_role_permissions( + async def set_organization_role_permissions( self, organization_id: str, slug: str, @@ -2400,7 +2403,7 @@ async def update_role_permissions( request_options=request_options, ) - async def delete_role_permission( + async def remove_organization_role_permission( self, organization_id: str, slug: str, @@ -2431,7 +2434,7 @@ async def delete_role_permission( request_options=request_options, ) - async def get_organization_resource( + async def get_resource_by_external_id( self, organization_id: str, resource_type_slug: str, @@ -2466,7 +2469,7 @@ async def get_organization_resource( request_options=request_options, ) - async def update_organization_resource( + async def update_resource_by_external_id( self, organization_id: str, resource_type_slug: str, @@ -2531,7 +2534,7 @@ async def update_organization_resource( request_options=request_options, ) - async def delete_organization_resource( + async def delete_resource_by_external_id( self, organization_id: str, resource_type_slug: str, @@ -2574,7 +2577,7 @@ async def delete_organization_resource( request_options=request_options, ) - async def list_resource_organization_memberships( + async def list_memberships_for_resource_by_external_id( self, organization_id: str, resource_type_slug: str, @@ -2647,6 +2650,7 @@ async def list_resources( order: Optional[Union[AuthorizationOrder, str]] = "desc", organization_id: Optional[str] = None, resource_type_slug: Optional[str] = None, + resource_external_id: Optional[str] = None, search: Optional[str] = None, parent: Optional[Union[ParentById, ParentByExternalId]] = None, request_options: Optional[RequestOptions] = None, @@ -2662,6 +2666,7 @@ async def list_resources( 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`. organization_id: Filter resources by organization ID. resource_type_slug: Filter resources by resource type slug. + resource_external_id: Filter resources by external ID. search: Search resources by name. parent: Identifies the parent. One of: ParentById, ParentByExternalId. request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. @@ -2685,6 +2690,7 @@ async def list_resources( "order": enum_value(order) if order is not None else None, "organization_id": organization_id, "resource_type_slug": resource_type_slug, + "resource_external_id": resource_external_id, "search": search, }.items() if v is not None diff --git a/src/workos/common/__init__.py b/src/workos/common/__init__.py index babfb718..8d5ff562 100644 --- a/src/workos/common/__init__.py +++ b/src/workos/common/__init__.py @@ -237,7 +237,6 @@ 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 @@ -386,5 +385,9 @@ from .models import VaultMetadataReadData as VaultMetadataReadData from .models import VaultNamesListed as VaultNamesListed from .models import VaultNamesListedData as VaultNamesListedData +from .models import WaitlistUser as WaitlistUser +from .models import WaitlistUserApproved as WaitlistUserApproved +from .models import WaitlistUserCreated as WaitlistUserCreated +from .models import WaitlistUserDenied as WaitlistUserDenied from .models import WebhookEndpointJsonStatus as WebhookEndpointJsonStatus from .models import WidgetSessionTokenScopes as WidgetSessionTokenScopes diff --git a/src/workos/common/models/__init__.py b/src/workos/common/models/__init__.py index 0e0e9b4b..f27d1492 100644 --- a/src/workos/common/models/__init__.py +++ b/src/workos/common/models/__init__.py @@ -355,7 +355,6 @@ 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 @@ -580,6 +579,10 @@ from .vault_metadata_read_data import VaultMetadataReadData as VaultMetadataReadData from .vault_names_listed import VaultNamesListed as VaultNamesListed from .vault_names_listed_data import VaultNamesListedData as VaultNamesListedData +from .waitlist_user import WaitlistUser as WaitlistUser +from .waitlist_user_approved import WaitlistUserApproved as WaitlistUserApproved +from .waitlist_user_created import WaitlistUserCreated as WaitlistUserCreated +from .waitlist_user_denied import WaitlistUserDenied as WaitlistUserDenied from .webhook_endpoint_json_status import ( WebhookEndpointJsonStatus as WebhookEndpointJsonStatus, ) diff --git a/src/workos/common/models/create_webhook_endpoint_events.py b/src/workos/common/models/create_webhook_endpoint_events.py index f9fab5e5..eb393978 100644 --- a/src/workos/common/models/create_webhook_endpoint_events.py +++ b/src/workos/common/models/create_webhook_endpoint_events.py @@ -90,6 +90,9 @@ class CreateWebhookEndpointEvents(str, Enum): PERMISSION_UPDATED = "permission.updated" SESSION_CREATED = "session.created" SESSION_REVOKED = "session.revoked" + WAITLIST_USER_APPROVED = "waitlist_user.approved" + WAITLIST_USER_CREATED = "waitlist_user.created" + WAITLIST_USER_DENIED = "waitlist_user.denied" @classmethod def _missing_(cls, value: object) -> Optional["CreateWebhookEndpointEvents"]: @@ -176,4 +179,7 @@ def _missing_(cls, value: object) -> Optional["CreateWebhookEndpointEvents"]: "permission.updated", "session.created", "session.revoked", + "waitlist_user.approved", + "waitlist_user.created", + "waitlist_user.denied", ] diff --git a/src/workos/common/models/group_created.py b/src/workos/common/models/group_created.py index e3ccb528..159d019e 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 .group import Group +from workos.groups.models.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 5c588ccd..13c13b58 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 .group import Group +from workos.groups.models.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 62f4afe4..a81b1908 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 .group import Group +from workos.groups.models.group import Group @dataclass(slots=True) diff --git a/src/workos/common/models/invitation_accepted_data.py b/src/workos/common/models/invitation_accepted_data.py index 8c1faa21..dfdfeb2d 100644 --- a/src/workos/common/models/invitation_accepted_data.py +++ b/src/workos/common/models/invitation_accepted_data.py @@ -35,6 +35,8 @@ class InvitationAcceptedData: """The ID of the user who invited the recipient, if provided.""" accepted_user_id: Optional[str] """The ID of the user who accepted the invitation, once accepted.""" + role_slug: Optional[str] + """Slug of the role the invitee will be assigned on acceptance. Reflects the current role on the invitee's organization membership. null when the invitation has no associated organization.""" created_at: datetime """An ISO 8601 timestamp.""" updated_at: datetime @@ -59,6 +61,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "InvitationAcceptedData": organization_id=data["organization_id"], inviter_user_id=data["inviter_user_id"], accepted_user_id=data["accepted_user_id"], + role_slug=data["role_slug"], created_at=_parse_datetime(data["created_at"]), updated_at=_parse_datetime(data["updated_at"]), ) @@ -95,6 +98,10 @@ def to_dict(self) -> Dict[str, Any]: result["accepted_user_id"] = self.accepted_user_id else: result["accepted_user_id"] = None + if self.role_slug is not None: + result["role_slug"] = self.role_slug + else: + result["role_slug"] = None result["created_at"] = _format_datetime(self.created_at) result["updated_at"] = _format_datetime(self.updated_at) return result diff --git a/src/workos/common/models/waitlist_user.py b/src/workos/common/models/waitlist_user.py new file mode 100644 index 00000000..0ef34da4 --- /dev/null +++ b/src/workos/common/models/waitlist_user.py @@ -0,0 +1,66 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from enum import Enum +from typing import Any, Dict, Literal, Optional +from workos._types import _raise_deserialize_error +from workos._types import _format_datetime, _parse_datetime +from .waitlist_user_state import WaitlistUserState + + +@dataclass(slots=True) +class WaitlistUser: + """Waitlist User model.""" + + object: Literal["waitlist_user"] + """Distinguishes the Waitlist User object.""" + id: str + """The unique ID of the Waitlist User.""" + email: str + """The email address of the Waitlist User.""" + state: "WaitlistUserState" + """The state of the Waitlist User.""" + approved_at: Optional[datetime] + """The timestamp when the Waitlist User was approved, or null if not yet approved.""" + created_at: datetime + """An ISO 8601 timestamp.""" + updated_at: datetime + """An ISO 8601 timestamp.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "WaitlistUser": + """Deserialize from a dictionary.""" + try: + return cls( + object=data["object"], + id=data["id"], + email=data["email"], + state=WaitlistUserState(data["state"]), + approved_at=_parse_datetime(_v_approved_at) + if (_v_approved_at := data["approved_at"]) is not None + else None, + created_at=_parse_datetime(data["created_at"]), + updated_at=_parse_datetime(data["updated_at"]), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("WaitlistUser", 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["email"] = self.email + result["state"] = ( + self.state.value if isinstance(self.state, Enum) else self.state + ) + if self.approved_at is not None: + result["approved_at"] = _format_datetime(self.approved_at) + else: + result["approved_at"] = None + result["created_at"] = _format_datetime(self.created_at) + result["updated_at"] = _format_datetime(self.updated_at) + return result diff --git a/src/workos/common/models/waitlist_user_approved.py b/src/workos/common/models/waitlist_user_approved.py new file mode 100644 index 00000000..7ace227e --- /dev/null +++ b/src/workos/common/models/waitlist_user_approved.py @@ -0,0 +1,58 @@ +# 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 .waitlist_user import WaitlistUser + + +@dataclass(slots=True) +class WaitlistUserApproved: + """Waitlist User Approved model.""" + + id: str + """Unique identifier for the event.""" + event: Literal["waitlist_user.approved"] + data: "WaitlistUser" + """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]) -> "WaitlistUserApproved": + """Deserialize from a dictionary.""" + try: + return cls( + id=data["id"], + event=data["event"], + data=WaitlistUser.from_dict(cast(Dict[str, Any], data["data"])), + created_at=_parse_datetime(data["created_at"]), + object=data["object"], + 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("WaitlistUserApproved", 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/waitlist_user_created.py b/src/workos/common/models/waitlist_user_created.py new file mode 100644 index 00000000..3257a78e --- /dev/null +++ b/src/workos/common/models/waitlist_user_created.py @@ -0,0 +1,58 @@ +# 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 .waitlist_user import WaitlistUser + + +@dataclass(slots=True) +class WaitlistUserCreated: + """Waitlist User Created model.""" + + id: str + """Unique identifier for the event.""" + event: Literal["waitlist_user.created"] + data: "WaitlistUser" + """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]) -> "WaitlistUserCreated": + """Deserialize from a dictionary.""" + try: + return cls( + id=data["id"], + event=data["event"], + data=WaitlistUser.from_dict(cast(Dict[str, Any], data["data"])), + created_at=_parse_datetime(data["created_at"]), + object=data["object"], + 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("WaitlistUserCreated", 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/waitlist_user_denied.py b/src/workos/common/models/waitlist_user_denied.py new file mode 100644 index 00000000..52658648 --- /dev/null +++ b/src/workos/common/models/waitlist_user_denied.py @@ -0,0 +1,58 @@ +# 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 .waitlist_user import WaitlistUser + + +@dataclass(slots=True) +class WaitlistUserDenied: + """Waitlist User Denied model.""" + + id: str + """Unique identifier for the event.""" + event: Literal["waitlist_user.denied"] + data: "WaitlistUser" + """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]) -> "WaitlistUserDenied": + """Deserialize from a dictionary.""" + try: + return cls( + id=data["id"], + event=data["event"], + data=WaitlistUser.from_dict(cast(Dict[str, Any], data["data"])), + created_at=_parse_datetime(data["created_at"]), + object=data["object"], + 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("WaitlistUserDenied", 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/waitlist_user_state.py b/src/workos/common/models/waitlist_user_state.py new file mode 100644 index 00000000..18fa2e15 --- /dev/null +++ b/src/workos/common/models/waitlist_user_state.py @@ -0,0 +1,29 @@ +# This file is auto-generated by oagen. Do not edit. + +"""Enumeration of waitlist user state values.""" + +from __future__ import annotations + +from enum import Enum +from typing import Optional +from typing import Literal, TypeAlias + + +class WaitlistUserState(str, Enum): + """Known values for WaitlistUserState.""" + + PENDING = "pending" + APPROVED = "approved" + DENIED = "denied" + + @classmethod + def _missing_(cls, value: object) -> Optional["WaitlistUserState"]: + if not isinstance(value, str): + return None + unknown = str.__new__(cls, value) + unknown._name_ = value.upper() + unknown._value_ = value + return unknown + + +WaitlistUserStateLiteral: TypeAlias = Literal["pending", "approved", "denied"] 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 2a9970f1..3bafb3c0 100644 --- a/src/workos/directory_sync/models/directory_user_with_groups.py +++ b/src/workos/directory_sync/models/directory_user_with_groups.py @@ -42,8 +42,6 @@ class DirectoryUserWithGroups: """An ISO 8601 timestamp.""" updated_at: datetime """An ISO 8601 timestamp.""" - groups: List["DirectoryGroup"] - """The directory groups the user belongs to.""" first_name: Optional[str] = None """The first name of the user.""" last_name: Optional[str] = None @@ -67,6 +65,10 @@ class DirectoryUserWithGroups: role: Optional["SlimRole"] = None 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. + + .. deprecated:: This field is deprecated.""" @classmethod def from_dict(cls, data: Dict[str, Any]) -> "DirectoryUserWithGroups": @@ -83,10 +85,6 @@ def from_dict(cls, data: Dict[str, Any]) -> "DirectoryUserWithGroups": custom_attributes=data["custom_attributes"], created_at=_parse_datetime(data["created_at"]), updated_at=_parse_datetime(data["updated_at"]), - groups=[ - DirectoryGroup.from_dict(cast(Dict[str, Any], item)) - for item in cast(list[Any], data["groups"]) - ], first_name=data.get("first_name"), last_name=data.get("last_name"), emails=[ @@ -107,6 +105,12 @@ def from_dict(cls, data: Dict[str, Any]) -> "DirectoryUserWithGroups": ] if (_v_roles := data.get("roles")) is not None else None, + groups=[ + DirectoryGroup.from_dict(cast(Dict[str, Any], item)) + for item in cast(list[Any], _v_groups) + ] + if (_v_groups := data.get("groups")) is not None + else None, ) except (KeyError, ValueError) as e: _raise_deserialize_error("DirectoryUserWithGroups", e) @@ -129,7 +133,6 @@ def to_dict(self) -> Dict[str, Any]: result["custom_attributes"] = self.custom_attributes result["created_at"] = _format_datetime(self.created_at) result["updated_at"] = _format_datetime(self.updated_at) - result["groups"] = [item.to_dict() for item in self.groups] if self.first_name is not None: result["first_name"] = self.first_name else: @@ -154,4 +157,6 @@ def to_dict(self) -> Dict[str, Any]: result["role"] = self.role.to_dict() if self.roles is not None: result["roles"] = [item.to_dict() for item in self.roles] + if self.groups is not None: + result["groups"] = [item.to_dict() for item in self.groups] return result diff --git a/src/workos/events/models/event_schema.py b/src/workos/events/models/event_schema.py index 55d806f0..6dabf048 100644 --- a/src/workos/events/models/event_schema.py +++ b/src/workos/events/models/event_schema.py @@ -131,6 +131,9 @@ from workos.common.models.vault_kek_created import VaultKekCreated from workos.common.models.vault_metadata_read import VaultMetadataRead from workos.common.models.vault_names_listed import VaultNamesListed +from workos.common.models.waitlist_user_approved import WaitlistUserApproved +from workos.common.models.waitlist_user_created import WaitlistUserCreated +from workos.common.models.waitlist_user_denied import WaitlistUserDenied @dataclass(slots=True) @@ -240,6 +243,9 @@ def to_dict(self) -> Dict[str, Any]: VaultKekCreated, VaultMetadataRead, VaultNamesListed, + WaitlistUserApproved, + WaitlistUserCreated, + WaitlistUserDenied, EventSchemaUnknown, ] @@ -337,6 +343,9 @@ class EventSchema: "vault.kek.created": VaultKekCreated, "vault.metadata.read": VaultMetadataRead, "vault.names.listed": VaultNamesListed, + "waitlist_user.approved": WaitlistUserApproved, + "waitlist_user.created": WaitlistUserCreated, + "waitlist_user.denied": WaitlistUserDenied, } @classmethod diff --git a/src/workos/groups/__init__.py b/src/workos/groups/__init__.py new file mode 100644 index 00000000..ad3acc85 --- /dev/null +++ b/src/workos/groups/__init__.py @@ -0,0 +1,4 @@ +# This file is auto-generated by oagen. Do not edit. + +from ._resource import Groups, AsyncGroups +from .models import * diff --git a/src/workos/groups/_resource.py b/src/workos/groups/_resource.py new file mode 100644 index 00000000..c7d4dde5 --- /dev/null +++ b/src/workos/groups/_resource.py @@ -0,0 +1,688 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, Optional, Union + +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 ( + UserOrganizationMembershipBaseListData, +) +from .models import GroupsOrder +from .._pagination import AsyncPage, SyncPage + + +class Groups: + """Groups API resources.""" + + def __init__(self, client: "WorkOSClient") -> None: + self._client = client + + def list_organization_groups( + self, + organization_id: str, + *, + limit: Optional[int] = None, + before: Optional[str] = None, + after: Optional[str] = None, + order: Optional[Union[GroupsOrder, str]] = "desc", + request_options: Optional[RequestOptions] = None, + ) -> SyncPage[Group]: + """List groups + + Get a paginated list of groups within an organization. + + Args: + organization_id: The ID of the organization. + 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[Group] + + 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"organizations/{organization_id}/groups", + model=Group, + params=params, + request_options=request_options, + ) + + def create_organization_group( + self, + organization_id: str, + *, + name: str, + description: Optional[str] = None, + request_options: Optional[RequestOptions] = None, + ) -> Group: + """Create a group + + Create a new group within an organization. + + Args: + organization_id: The ID of the organization. + name: The name of the Group. + description: An optional description of the Group. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + Group + + Raises: + BadRequestError: If the request is malformed (400). + AuthorizationError: If the request is forbidden (403). + 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, + "description": description, + }.items() + if v is not None + } + return self._client.request( + method="post", + path=f"organizations/{organization_id}/groups", + body=body, + model=Group, + request_options=request_options, + ) + + def get_organization_group( + self, + organization_id: str, + group_id: str, + *, + request_options: Optional[RequestOptions] = None, + ) -> Group: + """Get a group + + Retrieve a group by its ID within an organization. + + Args: + organization_id: The ID of the organization. + group_id: The ID of the group. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + Group + + 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. + """ + return self._client.request( + method="get", + path=f"organizations/{organization_id}/groups/{group_id}", + model=Group, + request_options=request_options, + ) + + def update_organization_group( + self, + organization_id: str, + group_id: str, + *, + name: Optional[str] = None, + description: Optional[str] = None, + request_options: Optional[RequestOptions] = None, + ) -> Group: + """Update a group + + Update an existing group. Only the fields provided in the request body will be updated. + + Args: + organization_id: The ID of the organization. + group_id: The ID of the group. + name: The name of the Group. + description: An optional description of the Group. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + Group + + Raises: + BadRequestError: If the request is malformed (400). + AuthorizationError: If the request is forbidden (403). + 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, + "description": description, + }.items() + if v is not None + } + return self._client.request( + method="patch", + path=f"organizations/{organization_id}/groups/{group_id}", + body=body, + model=Group, + request_options=request_options, + ) + + def delete_organization_group( + self, + organization_id: str, + group_id: str, + *, + request_options: Optional[RequestOptions] = None, + ) -> None: + """Delete a group + + Delete a group from an organization. + + Args: + organization_id: The ID of the organization. + group_id: The ID of the group. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + 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. + """ + self._client.request( + method="delete", + path=f"organizations/{organization_id}/groups/{group_id}", + request_options=request_options, + ) + + def list_group_organization_memberships( + self, + organization_id: str, + group_id: str, + *, + limit: Optional[int] = None, + before: Optional[str] = None, + after: Optional[str] = None, + order: Optional[Union[GroupsOrder, str]] = "desc", + request_options: Optional[RequestOptions] = None, + ) -> SyncPage[UserOrganizationMembershipBaseListData]: + """List Group members + + Get a list of organization memberships in a group. + + Args: + organization_id: Unique identifier of the Organization. + group_id: Unique identifier of the Group. + 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[UserOrganizationMembershipBaseListData] + + 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"organizations/{organization_id}/groups/{group_id}/organization-memberships", + model=UserOrganizationMembershipBaseListData, + params=params, + request_options=request_options, + ) + + def create_group_organization_membership( + self, + organization_id: str, + group_id: str, + *, + organization_membership_id: str, + request_options: Optional[RequestOptions] = None, + ) -> Group: + """Add a member to a Group + + Add an organization membership to a group. + + Args: + organization_id: Unique identifier of the Organization. + group_id: Unique identifier of the Group. + organization_membership_id: The ID of the Organization Membership to add to the group. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + Group + + Raises: + AuthorizationError: If the request is forbidden (403). + 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] = { + "organization_membership_id": organization_membership_id, + } + return self._client.request( + method="post", + path=f"organizations/{organization_id}/groups/{group_id}/organization-memberships", + body=body, + model=Group, + request_options=request_options, + ) + + def delete_group_organization_membership( + self, + organization_id: str, + group_id: str, + om_id: str, + *, + request_options: Optional[RequestOptions] = None, + ) -> None: + """Remove a member from a Group + + Remove an organization membership from a group. + + Args: + organization_id: Unique identifier of the Organization. + group_id: Unique identifier of the Group. + om_id: Unique identifier of the Organization Membership. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + 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. + """ + self._client.request( + method="delete", + path=f"organizations/{organization_id}/groups/{group_id}/organization-memberships/{om_id}", + request_options=request_options, + ) + + +class AsyncGroups: + """Groups API resources (async).""" + + def __init__(self, client: "AsyncWorkOSClient") -> None: + self._client = client + + async def list_organization_groups( + self, + organization_id: str, + *, + limit: Optional[int] = None, + before: Optional[str] = None, + after: Optional[str] = None, + order: Optional[Union[GroupsOrder, str]] = "desc", + request_options: Optional[RequestOptions] = None, + ) -> AsyncPage[Group]: + """List groups + + Get a paginated list of groups within an organization. + + Args: + organization_id: The ID of the organization. + 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[Group] + + 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"organizations/{organization_id}/groups", + model=Group, + params=params, + request_options=request_options, + ) + + async def create_organization_group( + self, + organization_id: str, + *, + name: str, + description: Optional[str] = None, + request_options: Optional[RequestOptions] = None, + ) -> Group: + """Create a group + + Create a new group within an organization. + + Args: + organization_id: The ID of the organization. + name: The name of the Group. + description: An optional description of the Group. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + Group + + Raises: + BadRequestError: If the request is malformed (400). + AuthorizationError: If the request is forbidden (403). + 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, + "description": description, + }.items() + if v is not None + } + return await self._client.request( + method="post", + path=f"organizations/{organization_id}/groups", + body=body, + model=Group, + request_options=request_options, + ) + + async def get_organization_group( + self, + organization_id: str, + group_id: str, + *, + request_options: Optional[RequestOptions] = None, + ) -> Group: + """Get a group + + Retrieve a group by its ID within an organization. + + Args: + organization_id: The ID of the organization. + group_id: The ID of the group. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + Group + + 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. + """ + return await self._client.request( + method="get", + path=f"organizations/{organization_id}/groups/{group_id}", + model=Group, + request_options=request_options, + ) + + async def update_organization_group( + self, + organization_id: str, + group_id: str, + *, + name: Optional[str] = None, + description: Optional[str] = None, + request_options: Optional[RequestOptions] = None, + ) -> Group: + """Update a group + + Update an existing group. Only the fields provided in the request body will be updated. + + Args: + organization_id: The ID of the organization. + group_id: The ID of the group. + name: The name of the Group. + description: An optional description of the Group. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + Group + + Raises: + BadRequestError: If the request is malformed (400). + AuthorizationError: If the request is forbidden (403). + 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, + "description": description, + }.items() + if v is not None + } + return await self._client.request( + method="patch", + path=f"organizations/{organization_id}/groups/{group_id}", + body=body, + model=Group, + request_options=request_options, + ) + + async def delete_organization_group( + self, + organization_id: str, + group_id: str, + *, + request_options: Optional[RequestOptions] = None, + ) -> None: + """Delete a group + + Delete a group from an organization. + + Args: + organization_id: The ID of the organization. + group_id: The ID of the group. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + 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. + """ + await self._client.request( + method="delete", + path=f"organizations/{organization_id}/groups/{group_id}", + request_options=request_options, + ) + + async def list_group_organization_memberships( + self, + organization_id: str, + group_id: str, + *, + limit: Optional[int] = None, + before: Optional[str] = None, + after: Optional[str] = None, + order: Optional[Union[GroupsOrder, str]] = "desc", + request_options: Optional[RequestOptions] = None, + ) -> AsyncPage[UserOrganizationMembershipBaseListData]: + """List Group members + + Get a list of organization memberships in a group. + + Args: + organization_id: Unique identifier of the Organization. + group_id: Unique identifier of the Group. + 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[UserOrganizationMembershipBaseListData] + + 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"organizations/{organization_id}/groups/{group_id}/organization-memberships", + model=UserOrganizationMembershipBaseListData, + params=params, + request_options=request_options, + ) + + async def create_group_organization_membership( + self, + organization_id: str, + group_id: str, + *, + organization_membership_id: str, + request_options: Optional[RequestOptions] = None, + ) -> Group: + """Add a member to a Group + + Add an organization membership to a group. + + Args: + organization_id: Unique identifier of the Organization. + group_id: Unique identifier of the Group. + organization_membership_id: The ID of the Organization Membership to add to the group. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + Group + + Raises: + AuthorizationError: If the request is forbidden (403). + 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] = { + "organization_membership_id": organization_membership_id, + } + return await self._client.request( + method="post", + path=f"organizations/{organization_id}/groups/{group_id}/organization-memberships", + body=body, + model=Group, + request_options=request_options, + ) + + async def delete_group_organization_membership( + self, + organization_id: str, + group_id: str, + om_id: str, + *, + request_options: Optional[RequestOptions] = None, + ) -> None: + """Remove a member from a Group + + Remove an organization membership from a group. + + Args: + organization_id: Unique identifier of the Organization. + group_id: Unique identifier of the Group. + om_id: Unique identifier of the Organization Membership. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + 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. + """ + await self._client.request( + method="delete", + path=f"organizations/{organization_id}/groups/{group_id}/organization-memberships/{om_id}", + request_options=request_options, + ) diff --git a/src/workos/groups/models/__init__.py b/src/workos/groups/models/__init__.py new file mode 100644 index 00000000..ce775a5e --- /dev/null +++ b/src/workos/groups/models/__init__.py @@ -0,0 +1,7 @@ +# This file is auto-generated by oagen. Do not edit. + +from .create_group import CreateGroup as CreateGroup +from .create_group_membership import CreateGroupMembership as CreateGroupMembership +from .group import Group as Group +from .groups_order import GroupsOrder as GroupsOrder +from .update_group import UpdateGroup as UpdateGroup diff --git a/src/workos/groups/models/create_group.py b/src/workos/groups/models/create_group.py new file mode 100644 index 00000000..da1cf2fa --- /dev/null +++ b/src/workos/groups/models/create_group.py @@ -0,0 +1,38 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Dict, Optional +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class CreateGroup: + """Create Group model.""" + + name: str + """The name of the Group.""" + description: Optional[str] = None + """An optional description of the Group.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "CreateGroup": + """Deserialize from a dictionary.""" + try: + return cls( + name=data["name"], + description=data.get("description"), + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("CreateGroup", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["name"] = self.name + if self.description is not None: + result["description"] = self.description + else: + result["description"] = None + return result diff --git a/src/workos/groups/models/create_group_membership.py b/src/workos/groups/models/create_group_membership.py new file mode 100644 index 00000000..0b3cf0d3 --- /dev/null +++ b/src/workos/groups/models/create_group_membership.py @@ -0,0 +1,31 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Dict +from workos._types import _raise_deserialize_error + + +@dataclass(slots=True) +class CreateGroupMembership: + """Create Group Membership model.""" + + organization_membership_id: str + """The ID of the Organization Membership to add to the group.""" + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "CreateGroupMembership": + """Deserialize from a dictionary.""" + try: + return cls( + organization_membership_id=data["organization_membership_id"], + ) + except (KeyError, ValueError) as e: + _raise_deserialize_error("CreateGroupMembership", e) + + def to_dict(self) -> Dict[str, Any]: + """Serialize to a dictionary.""" + result: Dict[str, Any] = {} + result["organization_membership_id"] = self.organization_membership_id + return result diff --git a/src/workos/common/models/group.py b/src/workos/groups/models/group.py similarity index 100% rename from src/workos/common/models/group.py rename to src/workos/groups/models/group.py diff --git a/src/workos/groups/models/groups_order.py b/src/workos/groups/models/groups_order.py new file mode 100644 index 00000000..6a6b4d04 --- /dev/null +++ b/src/workos/groups/models/groups_order.py @@ -0,0 +1,7 @@ +# 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/groups/models/update_group.py b/src/workos/groups/models/update_group.py new file mode 100644 index 00000000..fb0cd719 --- /dev/null +++ b/src/workos/groups/models/update_group.py @@ -0,0 +1,8 @@ +# This file is auto-generated by oagen. Do not edit. + +from typing import TypeAlias +from workos.authorization.models.update_authorization_permission import ( + UpdateAuthorizationPermission, +) + +UpdateGroup: TypeAlias = UpdateAuthorizationPermission diff --git a/src/workos/sso/_resource.py b/src/workos/sso/_resource.py index 399f3e09..7cfb5015 100644 --- a/src/workos/sso/_resource.py +++ b/src/workos/sso/_resource.py @@ -163,7 +163,7 @@ def get_authorization_url( provider_scopes: Additional scopes to request from the identity provider. Applicable when using OAuth or OpenID Connect connections. provider_query_params: Key/value pairs of query parameters to pass to the OAuth provider. Only applicable when using OAuth connections. domain: (deprecated) Deprecated. Use `connection` or `organization` instead. Used to initiate SSO for a connection by domain. The domain must be associated with a connection in your WorkOS environment. - provider: Used to initiate OAuth authentication with Google, Microsoft, GitHub, or Apple. + provider: Used to initiate OAuth authentication with various providers. redirect_uri: Where to redirect the user after they complete the authentication process. You must use one of the redirect URIs configured via the [Redirects](https://dashboard.workos.com/redirects) page on the dashboard. state: An optional parameter that can be used to encode arbitrary information to help restore application state between redirects. If included, the redirect URI received from WorkOS will contain the exact `state` that was passed. connection: Used to initiate SSO for a connection. The value should be a WorkOS connection ID. @@ -580,7 +580,7 @@ def get_authorization_url( provider_scopes: Additional scopes to request from the identity provider. Applicable when using OAuth or OpenID Connect connections. provider_query_params: Key/value pairs of query parameters to pass to the OAuth provider. Only applicable when using OAuth connections. domain: (deprecated) Deprecated. Use `connection` or `organization` instead. Used to initiate SSO for a connection by domain. The domain must be associated with a connection in your WorkOS environment. - provider: Used to initiate OAuth authentication with Google, Microsoft, GitHub, or Apple. + provider: Used to initiate OAuth authentication with various providers. redirect_uri: Where to redirect the user after they complete the authentication process. You must use one of the redirect URIs configured via the [Redirects](https://dashboard.workos.com/redirects) page on the dashboard. state: An optional parameter that can be used to encode arbitrary information to help restore application state between redirects. If included, the redirect URI received from WorkOS will contain the exact `state` that was passed. connection: Used to initiate SSO for a connection. The value should be a WorkOS connection ID. diff --git a/src/workos/sso/models/sso_provider.py b/src/workos/sso/models/sso_provider.py index 2e836c2e..fc546042 100644 --- a/src/workos/sso/models/sso_provider.py +++ b/src/workos/sso/models/sso_provider.py @@ -14,7 +14,6 @@ class SSOProvider(str, Enum): APPLE_OAUTH = "AppleOAuth" BITBUCKET_OAUTH = "BitbucketOAuth" - DISCORD_OAUTH = "DiscordOAuth" GIT_HUB_OAUTH = "GitHubOAuth" GIT_LAB_OAUTH = "GitLabOAuth" GOOGLE_OAUTH = "GoogleOAuth" @@ -40,7 +39,6 @@ def _missing_(cls, value: object) -> Optional["SSOProvider"]: SSOProviderLiteral: TypeAlias = Literal[ "AppleOAuth", "BitbucketOAuth", - "DiscordOAuth", "GitHubOAuth", "GitLabOAuth", "GoogleOAuth", diff --git a/src/workos/types/groups/__init__.py b/src/workos/types/groups/__init__.py new file mode 100644 index 00000000..2a645cc4 --- /dev/null +++ b/src/workos/types/groups/__init__.py @@ -0,0 +1,3 @@ +# This file is auto-generated by oagen. Do not edit. + +from workos.groups.models import * # noqa: F401,F403 diff --git a/src/workos/types/user_management_organization_membership_groups/__init__.py b/src/workos/types/user_management_organization_membership_groups/__init__.py new file mode 100644 index 00000000..08efae58 --- /dev/null +++ b/src/workos/types/user_management_organization_membership_groups/__init__.py @@ -0,0 +1,3 @@ +# This file is auto-generated by oagen. Do not edit. + +from workos.user_management_organization_membership_groups.models import * # noqa: F401,F403 diff --git a/src/workos/user_management/models/invitation.py b/src/workos/user_management/models/invitation.py index d5592b34..f793e3c2 100644 --- a/src/workos/user_management/models/invitation.py +++ b/src/workos/user_management/models/invitation.py @@ -35,6 +35,8 @@ class Invitation: """The ID of the user who invited the recipient, if provided.""" accepted_user_id: Optional[str] """The ID of the user who accepted the invitation, once accepted.""" + role_slug: Optional[str] + """Slug of the role the invitee will be assigned on acceptance. Reflects the current role on the invitee's organization membership. null when the invitation has no associated organization.""" created_at: datetime """An ISO 8601 timestamp.""" updated_at: datetime @@ -63,6 +65,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "Invitation": organization_id=data["organization_id"], inviter_user_id=data["inviter_user_id"], accepted_user_id=data["accepted_user_id"], + role_slug=data["role_slug"], created_at=_parse_datetime(data["created_at"]), updated_at=_parse_datetime(data["updated_at"]), token=data["token"], @@ -101,6 +104,10 @@ def to_dict(self) -> Dict[str, Any]: result["accepted_user_id"] = self.accepted_user_id else: result["accepted_user_id"] = None + if self.role_slug is not None: + result["role_slug"] = self.role_slug + else: + result["role_slug"] = None result["created_at"] = _format_datetime(self.created_at) result["updated_at"] = _format_datetime(self.updated_at) result["token"] = self.token diff --git a/src/workos/user_management/models/user_management_authentication_provider.py b/src/workos/user_management/models/user_management_authentication_provider.py index 80b1f715..368363eb 100644 --- a/src/workos/user_management/models/user_management_authentication_provider.py +++ b/src/workos/user_management/models/user_management_authentication_provider.py @@ -5,7 +5,8 @@ from __future__ import annotations from enum import Enum -from typing import Literal, Optional, TypeAlias +from typing import Optional +from typing import Literal, TypeAlias class UserManagementAuthenticationProvider(str, Enum): @@ -14,7 +15,6 @@ class UserManagementAuthenticationProvider(str, Enum): AUTHKIT = "authkit" APPLE_OAUTH = "AppleOAuth" BITBUCKET_OAUTH = "BitbucketOAuth" - DISCORD_OAUTH = "DiscordOAuth" GIT_HUB_OAUTH = "GitHubOAuth" GIT_LAB_OAUTH = "GitLabOAuth" GOOGLE_OAUTH = "GoogleOAuth" @@ -43,7 +43,6 @@ def _missing_( "authkit", "AppleOAuth", "BitbucketOAuth", - "DiscordOAuth", "GitHubOAuth", "GitLabOAuth", "GoogleOAuth", diff --git a/src/workos/user_management_organization_membership_groups/__init__.py b/src/workos/user_management_organization_membership_groups/__init__.py new file mode 100644 index 00000000..8ccd1ecf --- /dev/null +++ b/src/workos/user_management_organization_membership_groups/__init__.py @@ -0,0 +1,7 @@ +# This file is auto-generated by oagen. Do not edit. + +from ._resource import ( + UserManagementOrganizationMembershipGroups, + AsyncUserManagementOrganizationMembershipGroups, +) +from .models import * diff --git a/src/workos/user_management_organization_membership_groups/_resource.py b/src/workos/user_management_organization_membership_groups/_resource.py new file mode 100644 index 00000000..c63f2e5f --- /dev/null +++ b/src/workos/user_management_organization_membership_groups/_resource.py @@ -0,0 +1,129 @@ +# This file is auto-generated by oagen. Do not edit. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional, Union + +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 .._pagination import AsyncPage, SyncPage + + +class UserManagementOrganizationMembershipGroups: + """User Management Organization Membership Groups API resources.""" + + def __init__(self, client: "WorkOSClient") -> None: + self._client = client + + def list_organization_membership_groups( + self, + om_id: str, + *, + limit: Optional[int] = None, + before: Optional[str] = None, + after: Optional[str] = None, + order: Optional[ + Union[UserManagementOrganizationMembershipGroupsOrder, str] + ] = "desc", + request_options: Optional[RequestOptions] = None, + ) -> SyncPage[Group]: + """List groups + + Get a list of groups that an organization membership belongs to. + + Args: + om_id: Unique identifier of the Organization Membership. + limit: Upper limit on the number of objects to return, between `1` and `100`. Defaults to `10`. + before: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. + after: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + SyncPage[Group] + + 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, + }.items() + if v is not None + } + return self._client.request_page( + method="get", + path=f"user_management/organization_memberships/{om_id}/groups", + model=Group, + params=params, + request_options=request_options, + ) + + +class AsyncUserManagementOrganizationMembershipGroups: + """User Management Organization Membership Groups API resources (async).""" + + def __init__(self, client: "AsyncWorkOSClient") -> None: + self._client = client + + async def list_organization_membership_groups( + self, + om_id: str, + *, + limit: Optional[int] = None, + before: Optional[str] = None, + after: Optional[str] = None, + order: Optional[ + Union[UserManagementOrganizationMembershipGroupsOrder, str] + ] = "desc", + request_options: Optional[RequestOptions] = None, + ) -> AsyncPage[Group]: + """List groups + + Get a list of groups that an organization membership belongs to. + + Args: + om_id: Unique identifier of the Organization Membership. + limit: Upper limit on the number of objects to return, between `1` and `100`. Defaults to `10`. + before: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `before="obj_123"` to fetch a new batch of objects before `"obj_123"`. + after: An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. For example, if you make a list request and receive 100 objects, ending with `"obj_123"`, your subsequent call can include `after="obj_123"` to fetch a new batch of objects after `"obj_123"`. + order: Order the results by the creation time. Supported values are `"asc"` (ascending), `"desc"` (descending), and `"normal"` (descending with reversed cursor semantics where `before` fetches older records and `after` fetches newer records). Defaults to descending. Defaults to `desc`. + request_options: Per-request options. Supports extra_headers, timeout, max_retries, and base_url override. + + Returns: + AsyncPage[Group] + + 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, + }.items() + if v is not None + } + return await self._client.request_page( + method="get", + path=f"user_management/organization_memberships/{om_id}/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 new file mode 100644 index 00000000..51055ca3 --- /dev/null +++ b/src/workos/user_management_organization_membership_groups/models/__init__.py @@ -0,0 +1,5 @@ +# 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 new file mode 100644 index 00000000..eb8d238e --- /dev/null +++ b/src/workos/user_management_organization_membership_groups/models/user_management_organization_membership_groups_order.py @@ -0,0 +1,7 @@ +# 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/tests/fixtures/create_group.json b/tests/fixtures/create_group.json new file mode 100644 index 00000000..bcb47249 --- /dev/null +++ b/tests/fixtures/create_group.json @@ -0,0 +1,4 @@ +{ + "name": "Engineering", + "description": "The engineering team" +} diff --git a/tests/fixtures/create_group_membership.json b/tests/fixtures/create_group_membership.json new file mode 100644 index 00000000..45ebd1dd --- /dev/null +++ b/tests/fixtures/create_group_membership.json @@ -0,0 +1,3 @@ +{ + "organization_membership_id": "om_01HXYZ123456789ABCDEFGHIJ" +} diff --git a/tests/fixtures/domain_verification_intent_options.json b/tests/fixtures/domain_verification_intent_options.json new file mode 100644 index 00000000..d132a78a --- /dev/null +++ b/tests/fixtures/domain_verification_intent_options.json @@ -0,0 +1,3 @@ +{ + "domain_name": "example.com" +} diff --git a/tests/fixtures/generate_link.json b/tests/fixtures/generate_link.json index 660b36a7..47084d30 100644 --- a/tests/fixtures/generate_link.json +++ b/tests/fixtures/generate_link.json @@ -7,9 +7,12 @@ "sso": { "bookmark_slug": "chatgpt", "provider_type": "GoogleSAML" + }, + "domain_verification": { + "domain_name": "example.com" } }, - "admin_emails": [ - "admin@example.com" + "it_contact_emails": [ + "it-contact@example.com" ] } diff --git a/tests/fixtures/intent_options.json b/tests/fixtures/intent_options.json index 31bad913..894d5998 100644 --- a/tests/fixtures/intent_options.json +++ b/tests/fixtures/intent_options.json @@ -2,5 +2,8 @@ "sso": { "bookmark_slug": "chatgpt", "provider_type": "GoogleSAML" + }, + "domain_verification": { + "domain_name": "example.com" } } diff --git a/tests/fixtures/invitation.json b/tests/fixtures/invitation.json index c2019df7..c97b3187 100644 --- a/tests/fixtures/invitation.json +++ b/tests/fixtures/invitation.json @@ -9,6 +9,7 @@ "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "token": "Z1uX3RbwcIl5fIGJJJCXXisdI", diff --git a/tests/fixtures/invitation_accepted.json b/tests/fixtures/invitation_accepted.json index 61b93ab4..a1460b3c 100644 --- a/tests/fixtures/invitation_accepted.json +++ b/tests/fixtures/invitation_accepted.json @@ -12,6 +12,7 @@ "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" }, diff --git a/tests/fixtures/invitation_accepted_data.json b/tests/fixtures/invitation_accepted_data.json index 66102c6b..845de7b9 100644 --- a/tests/fixtures/invitation_accepted_data.json +++ b/tests/fixtures/invitation_accepted_data.json @@ -9,6 +9,7 @@ "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } diff --git a/tests/fixtures/invitation_created.json b/tests/fixtures/invitation_created.json index c83b104d..19039528 100644 --- a/tests/fixtures/invitation_created.json +++ b/tests/fixtures/invitation_created.json @@ -12,6 +12,7 @@ "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" }, diff --git a/tests/fixtures/invitation_created_data.json b/tests/fixtures/invitation_created_data.json index 66102c6b..845de7b9 100644 --- a/tests/fixtures/invitation_created_data.json +++ b/tests/fixtures/invitation_created_data.json @@ -9,6 +9,7 @@ "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } diff --git a/tests/fixtures/invitation_resent.json b/tests/fixtures/invitation_resent.json index 7ace11e7..39f240fb 100644 --- a/tests/fixtures/invitation_resent.json +++ b/tests/fixtures/invitation_resent.json @@ -12,6 +12,7 @@ "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" }, diff --git a/tests/fixtures/invitation_resent_data.json b/tests/fixtures/invitation_resent_data.json index 66102c6b..845de7b9 100644 --- a/tests/fixtures/invitation_resent_data.json +++ b/tests/fixtures/invitation_resent_data.json @@ -9,6 +9,7 @@ "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } diff --git a/tests/fixtures/invitation_revoked.json b/tests/fixtures/invitation_revoked.json index e8f8ff62..5b2fdfac 100644 --- a/tests/fixtures/invitation_revoked.json +++ b/tests/fixtures/invitation_revoked.json @@ -12,6 +12,7 @@ "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" }, diff --git a/tests/fixtures/invitation_revoked_data.json b/tests/fixtures/invitation_revoked_data.json index 66102c6b..845de7b9 100644 --- a/tests/fixtures/invitation_revoked_data.json +++ b/tests/fixtures/invitation_revoked_data.json @@ -9,6 +9,7 @@ "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z" } diff --git a/tests/fixtures/list_group.json b/tests/fixtures/list_group.json new file mode 100644 index 00000000..9516016b --- /dev/null +++ b/tests/fixtures/list_group.json @@ -0,0 +1,17 @@ +{ + "data": [ + { + "object": "group", + "id": "group_01HXYZ123456789ABCDEFGHIJ", + "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY", + "name": "Engineering", + "description": "The engineering team", + "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_invite.json b/tests/fixtures/list_user_invite.json index 355f3e44..6288c488 100644 --- a/tests/fixtures/list_user_invite.json +++ b/tests/fixtures/list_user_invite.json @@ -11,6 +11,7 @@ "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "token": "Z1uX3RbwcIl5fIGJJJCXXisdI", diff --git a/tests/fixtures/update_group.json b/tests/fixtures/update_group.json new file mode 100644 index 00000000..bcb47249 --- /dev/null +++ b/tests/fixtures/update_group.json @@ -0,0 +1,4 @@ +{ + "name": "Engineering", + "description": "The engineering team" +} diff --git a/tests/fixtures/user_invite.json b/tests/fixtures/user_invite.json index b3a1dbbd..6deb2ab7 100644 --- a/tests/fixtures/user_invite.json +++ b/tests/fixtures/user_invite.json @@ -9,6 +9,7 @@ "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "token": "Z1uX3RbwcIl5fIGJJJCXXisdI", diff --git a/tests/fixtures/waitlist_user.json b/tests/fixtures/waitlist_user.json new file mode 100644 index 00000000..05973598 --- /dev/null +++ b/tests/fixtures/waitlist_user.json @@ -0,0 +1,9 @@ +{ + "object": "waitlist_user", + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": null, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" +} diff --git a/tests/fixtures/waitlist_user_approved.json b/tests/fixtures/waitlist_user_approved.json new file mode 100644 index 00000000..eddf230b --- /dev/null +++ b/tests/fixtures/waitlist_user_approved.json @@ -0,0 +1,35 @@ +{ + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "waitlist_user.approved", + "data": { + "object": "waitlist_user", + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": null, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" + }, + "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/waitlist_user_created.json b/tests/fixtures/waitlist_user_created.json new file mode 100644 index 00000000..c3faaad7 --- /dev/null +++ b/tests/fixtures/waitlist_user_created.json @@ -0,0 +1,35 @@ +{ + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "waitlist_user.created", + "data": { + "object": "waitlist_user", + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": null, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" + }, + "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/waitlist_user_denied.json b/tests/fixtures/waitlist_user_denied.json new file mode 100644 index 00000000..6079919b --- /dev/null +++ b/tests/fixtures/waitlist_user_denied.json @@ -0,0 +1,35 @@ +{ + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "waitlist_user.denied", + "data": { + "object": "waitlist_user", + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": null, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z" + }, + "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/test_authorization.py b/tests/test_authorization.py index 2df6450f..c118f925 100644 --- a/tests/test_authorization.py +++ b/tests/test_authorization.py @@ -55,11 +55,11 @@ def test_check(self, workos, httpx_mock): body = json.loads(request.content) assert body["permission_slug"] == "test_permission_slug" - def test_list_organization_membership_resources(self, workos, httpx_mock): + def test_list_resources_for_membership(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("list_authorization_resource.json"), ) - page = workos.authorization.list_organization_membership_resources( + page = workos.authorization.list_resources_for_membership( "test_organization_membership_id", parent_resource=ParentResourceById(parent_resource_id="test_value"), permission_slug="test_permission_slug", @@ -68,11 +68,9 @@ def test_list_organization_membership_resources(self, workos, httpx_mock): assert len(page.data) == 1 assert isinstance(page.data[0], AuthorizationResource) - def test_list_organization_membership_resources_empty_page( - self, workos, httpx_mock - ): + def test_list_resources_for_membership_empty_page(self, workos, httpx_mock): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - page = workos.authorization.list_organization_membership_resources( + page = workos.authorization.list_resources_for_membership( "test_organization_membership_id", parent_resource=ParentResourceById(parent_resource_id="test_value"), permission_slug="test_permission_slug", @@ -80,11 +78,11 @@ def test_list_organization_membership_resources_empty_page( assert isinstance(page, SyncPage) assert page.data == [] - def test_list_organization_membership_resources_encodes_query_params( + def test_list_resources_for_membership_encodes_query_params( self, workos, httpx_mock ): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - workos.authorization.list_organization_membership_resources( + workos.authorization.list_resources_for_membership( "test_organization_membership_id", parent_resource=ParentResourceById( parent_resource_id="value parent_resource_id/test" @@ -105,28 +103,28 @@ def test_list_organization_membership_resources_encodes_query_params( assert request.url.params["order"] == "normal" assert request.url.params["permission_slug"] == "value permission_slug/test" - def test_list_resource_permissions(self, workos, httpx_mock): + def test_list_effective_permissions(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("list_authorization_permission.json"), ) - page = workos.authorization.list_resource_permissions( + page = workos.authorization.list_effective_permissions( "test_organization_membership_id", "test_resource_id" ) assert isinstance(page, SyncPage) assert len(page.data) == 1 assert isinstance(page.data[0], AuthorizationPermission) - def test_list_resource_permissions_empty_page(self, workos, httpx_mock): + def test_list_effective_permissions_empty_page(self, workos, httpx_mock): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - page = workos.authorization.list_resource_permissions( + page = workos.authorization.list_effective_permissions( "test_organization_membership_id", "test_resource_id" ) assert isinstance(page, SyncPage) assert page.data == [] - def test_list_resource_permissions_encodes_query_params(self, workos, httpx_mock): + def test_list_effective_permissions_encodes_query_params(self, workos, httpx_mock): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - workos.authorization.list_resource_permissions( + workos.authorization.list_effective_permissions( "test_organization_membership_id", "test_resource_id", limit=10, @@ -184,32 +182,28 @@ def test_list_effective_permissions_by_external_id_encodes_query_params( assert request.url.params["after"] == "cursor/after" assert request.url.params["order"] == "normal" - def test_list_organization_membership_role_assignments(self, workos, httpx_mock): + def test_list_role_assignments(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("list_role_assignment.json"), ) - page = workos.authorization.list_organization_membership_role_assignments( + 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) - def test_list_organization_membership_role_assignments_empty_page( - self, workos, httpx_mock - ): + def test_list_role_assignments_empty_page(self, workos, httpx_mock): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - page = workos.authorization.list_organization_membership_role_assignments( + page = workos.authorization.list_role_assignments( "test_organization_membership_id" ) assert isinstance(page, SyncPage) assert page.data == [] - def test_list_organization_membership_role_assignments_encodes_query_params( - self, workos, httpx_mock - ): + def test_list_role_assignments_encodes_query_params(self, workos, httpx_mock): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - workos.authorization.list_organization_membership_role_assignments( + workos.authorization.list_role_assignments( "test_organization_membership_id", limit=10, before="cursor before", @@ -256,9 +250,9 @@ def test_remove_role(self, workos, httpx_mock): "/authorization/organization_memberships/test_organization_membership_id/role_assignments" ) - def test_delete_organization_membership_role_assignment(self, workos, httpx_mock): + def test_remove_role_assignment(self, workos, httpx_mock): httpx_mock.add_response(status_code=204) - result = workos.authorization.delete_organization_membership_role_assignment( + result = workos.authorization.remove_role_assignment( "test_organization_membership_id", "test_role_assignment_id" ) assert result is None @@ -343,11 +337,11 @@ def test_delete_organization_role(self, workos, httpx_mock): "/authorization/organizations/test_organizationId/roles/test_slug" ) - def test_create_role_permission(self, workos, httpx_mock): + def test_add_organization_role_permission(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("role.json"), ) - result = workos.authorization.create_role_permission( + result = workos.authorization.add_organization_role_permission( "test_organizationId", "test_slug", body_slug="test_slug" ) assert isinstance(result, Role) @@ -361,11 +355,11 @@ def test_create_role_permission(self, workos, httpx_mock): body = json.loads(request.content) assert body["slug"] == "test_slug" - def test_update_role_permissions(self, workos, httpx_mock): + def test_set_organization_role_permissions(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("role.json"), ) - result = workos.authorization.update_role_permissions( + result = workos.authorization.set_organization_role_permissions( "test_organizationId", "test_slug", permissions=[] ) assert isinstance(result, Role) @@ -379,9 +373,9 @@ def test_update_role_permissions(self, workos, httpx_mock): body = json.loads(request.content) assert "permissions" in body - def test_delete_role_permission(self, workos, httpx_mock): + def test_remove_organization_role_permission(self, workos, httpx_mock): httpx_mock.add_response(status_code=204) - result = workos.authorization.delete_role_permission( + result = workos.authorization.remove_organization_role_permission( "test_organizationId", "test_slug", "test_permissionSlug" ) assert result is None @@ -391,11 +385,11 @@ def test_delete_role_permission(self, workos, httpx_mock): "/authorization/organizations/test_organizationId/roles/test_slug/permissions/test_permissionSlug" ) - def test_get_organization_resource(self, workos, httpx_mock): + def test_get_resource_by_external_id(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("authorization_resource.json"), ) - result = workos.authorization.get_organization_resource( + result = workos.authorization.get_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id" ) assert isinstance(result, AuthorizationResource) @@ -407,11 +401,11 @@ def test_get_organization_resource(self, workos, httpx_mock): "/authorization/organizations/test_organization_id/resources/test_resource_type_slug/test_external_id" ) - def test_update_organization_resource(self, workos, httpx_mock): + def test_update_resource_by_external_id(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("authorization_resource.json"), ) - result = workos.authorization.update_organization_resource( + result = workos.authorization.update_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id", @@ -426,9 +420,9 @@ def test_update_organization_resource(self, workos, httpx_mock): "/authorization/organizations/test_organization_id/resources/test_resource_type_slug/test_external_id" ) - def test_delete_organization_resource(self, workos, httpx_mock): + def test_delete_resource_by_external_id(self, workos, httpx_mock): httpx_mock.add_response(status_code=204) - result = workos.authorization.delete_organization_resource( + result = workos.authorization.delete_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id" ) assert result is None @@ -438,11 +432,11 @@ def test_delete_organization_resource(self, workos, httpx_mock): "/authorization/organizations/test_organization_id/resources/test_resource_type_slug/test_external_id" ) - def test_delete_organization_resource_encodes_query_params( + def test_delete_resource_by_external_id_encodes_query_params( self, workos, httpx_mock ): httpx_mock.add_response(status_code=204) - workos.authorization.delete_organization_resource( + workos.authorization.delete_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id", @@ -451,11 +445,11 @@ def test_delete_organization_resource_encodes_query_params( request = httpx_mock.get_request() assert request.url.params["cascade_delete"] == "true" - def test_list_resource_organization_memberships(self, workos, httpx_mock): + def test_list_memberships_for_resource_by_external_id(self, workos, httpx_mock): httpx_mock.add_response( json=load_fixture("list_user_organization_membership_base_list_data.json"), ) - page = workos.authorization.list_resource_organization_memberships( + page = workos.authorization.list_memberships_for_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id", @@ -465,11 +459,11 @@ def test_list_resource_organization_memberships(self, workos, httpx_mock): assert len(page.data) == 1 assert isinstance(page.data[0], UserOrganizationMembershipBaseListData) - def test_list_resource_organization_memberships_empty_page( + def test_list_memberships_for_resource_by_external_id_empty_page( self, workos, httpx_mock ): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - page = workos.authorization.list_resource_organization_memberships( + page = workos.authorization.list_memberships_for_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id", @@ -478,11 +472,11 @@ def test_list_resource_organization_memberships_empty_page( assert isinstance(page, SyncPage) assert page.data == [] - def test_list_resource_organization_memberships_encodes_query_params( + def test_list_memberships_for_resource_by_external_id_encodes_query_params( self, workos, httpx_mock ): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - workos.authorization.list_resource_organization_memberships( + workos.authorization.list_memberships_for_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id", @@ -530,6 +524,7 @@ def test_list_resources_encodes_query_params(self, workos, httpx_mock): order=AuthorizationOrder("normal"), organization_id="value organization_id/test", resource_type_slug="value resource_type_slug/test", + resource_external_id="value resource_external_id/test", search="value search/test", ) request = httpx_mock.get_request() @@ -544,6 +539,10 @@ def test_list_resources_encodes_query_params(self, workos, httpx_mock): assert ( request.url.params["resource_type_slug"] == "value resource_type_slug/test" ) + assert ( + request.url.params["resource_external_id"] + == "value resource_external_id/test" + ) assert request.url.params["search"] == "value search/test" def test_create_resource(self, workos, httpx_mock): @@ -933,11 +932,9 @@ async def test_check(self, async_workos, httpx_mock): ) @pytest.mark.asyncio - async def test_list_organization_membership_resources( - self, async_workos, httpx_mock - ): + async def test_list_resources_for_membership(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("list_authorization_resource.json")) - page = await async_workos.authorization.list_organization_membership_resources( + page = await async_workos.authorization.list_resources_for_membership( "test_organization_membership_id", parent_resource=ParentResourceById(parent_resource_id="test_value"), permission_slug="test_permission_slug", @@ -947,11 +944,11 @@ async def test_list_organization_membership_resources( assert isinstance(page.data[0], AuthorizationResource) @pytest.mark.asyncio - async def test_list_organization_membership_resources_empty_page( + async def test_list_resources_for_membership_empty_page( self, async_workos, httpx_mock ): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - page = await async_workos.authorization.list_organization_membership_resources( + page = await async_workos.authorization.list_resources_for_membership( "test_organization_membership_id", parent_resource=ParentResourceById(parent_resource_id="test_value"), permission_slug="test_permission_slug", @@ -960,11 +957,11 @@ async def test_list_organization_membership_resources_empty_page( assert page.data == [] @pytest.mark.asyncio - async def test_list_organization_membership_resources_encodes_query_params( + async def test_list_resources_for_membership_encodes_query_params( self, async_workos, httpx_mock ): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - await async_workos.authorization.list_organization_membership_resources( + await async_workos.authorization.list_resources_for_membership( "test_organization_membership_id", parent_resource=ParentResourceById( parent_resource_id="value parent_resource_id/test" @@ -986,9 +983,9 @@ async def test_list_organization_membership_resources_encodes_query_params( assert request.url.params["permission_slug"] == "value permission_slug/test" @pytest.mark.asyncio - async def test_list_resource_permissions(self, async_workos, httpx_mock): + async def test_list_effective_permissions(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("list_authorization_permission.json")) - page = await async_workos.authorization.list_resource_permissions( + page = await async_workos.authorization.list_effective_permissions( "test_organization_membership_id", "test_resource_id" ) assert isinstance(page, AsyncPage) @@ -996,20 +993,22 @@ async def test_list_resource_permissions(self, async_workos, httpx_mock): assert isinstance(page.data[0], AuthorizationPermission) @pytest.mark.asyncio - async def test_list_resource_permissions_empty_page(self, async_workos, httpx_mock): + async def test_list_effective_permissions_empty_page( + self, async_workos, httpx_mock + ): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - page = await async_workos.authorization.list_resource_permissions( + page = await async_workos.authorization.list_effective_permissions( "test_organization_membership_id", "test_resource_id" ) assert isinstance(page, AsyncPage) assert page.data == [] @pytest.mark.asyncio - async def test_list_resource_permissions_encodes_query_params( + async def test_list_effective_permissions_encodes_query_params( self, async_workos, httpx_mock ): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - await async_workos.authorization.list_resource_permissions( + await async_workos.authorization.list_effective_permissions( "test_organization_membership_id", "test_resource_id", limit=10, @@ -1075,11 +1074,9 @@ async def test_list_effective_permissions_by_external_id_encodes_query_params( assert request.url.params["order"] == "normal" @pytest.mark.asyncio - async def test_list_organization_membership_role_assignments( - self, async_workos, httpx_mock - ): + async def test_list_role_assignments(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("list_role_assignment.json")) - page = await async_workos.authorization.list_organization_membership_role_assignments( + page = await async_workos.authorization.list_role_assignments( "test_organization_membership_id" ) assert isinstance(page, AsyncPage) @@ -1087,22 +1084,20 @@ async def test_list_organization_membership_role_assignments( assert isinstance(page.data[0], RoleAssignment) @pytest.mark.asyncio - async def test_list_organization_membership_role_assignments_empty_page( - self, async_workos, httpx_mock - ): + async def test_list_role_assignments_empty_page(self, async_workos, httpx_mock): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - page = await async_workos.authorization.list_organization_membership_role_assignments( + page = await async_workos.authorization.list_role_assignments( "test_organization_membership_id" ) assert isinstance(page, AsyncPage) assert page.data == [] @pytest.mark.asyncio - async def test_list_organization_membership_role_assignments_encodes_query_params( + async def test_list_role_assignments_encodes_query_params( self, async_workos, httpx_mock ): httpx_mock.add_response(json={"data": [], "list_metadata": {}}) - await async_workos.authorization.list_organization_membership_role_assignments( + await async_workos.authorization.list_role_assignments( "test_organization_membership_id", limit=10, before="cursor before", @@ -1148,11 +1143,9 @@ async def test_remove_role(self, async_workos, httpx_mock): ) @pytest.mark.asyncio - async def test_delete_organization_membership_role_assignment( - self, async_workos, httpx_mock - ): + async def test_remove_role_assignment(self, async_workos, httpx_mock): httpx_mock.add_response(status_code=204) - result = await async_workos.authorization.delete_organization_membership_role_assignment( + result = await async_workos.authorization.remove_role_assignment( "test_organization_membership_id", "test_role_assignment_id" ) assert result is None @@ -1235,9 +1228,9 @@ async def test_delete_organization_role(self, async_workos, httpx_mock): ) @pytest.mark.asyncio - async def test_create_role_permission(self, async_workos, httpx_mock): + async def test_add_organization_role_permission(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("role.json")) - result = await async_workos.authorization.create_role_permission( + result = await async_workos.authorization.add_organization_role_permission( "test_organizationId", "test_slug", body_slug="test_slug" ) assert isinstance(result, Role) @@ -1250,9 +1243,9 @@ async def test_create_role_permission(self, async_workos, httpx_mock): ) @pytest.mark.asyncio - async def test_update_role_permissions(self, async_workos, httpx_mock): + async def test_set_organization_role_permissions(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("role.json")) - result = await async_workos.authorization.update_role_permissions( + result = await async_workos.authorization.set_organization_role_permissions( "test_organizationId", "test_slug", permissions=[] ) assert isinstance(result, Role) @@ -1265,9 +1258,9 @@ async def test_update_role_permissions(self, async_workos, httpx_mock): ) @pytest.mark.asyncio - async def test_delete_role_permission(self, async_workos, httpx_mock): + async def test_remove_organization_role_permission(self, async_workos, httpx_mock): httpx_mock.add_response(status_code=204) - result = await async_workos.authorization.delete_role_permission( + result = await async_workos.authorization.remove_organization_role_permission( "test_organizationId", "test_slug", "test_permissionSlug" ) assert result is None @@ -1278,9 +1271,9 @@ async def test_delete_role_permission(self, async_workos, httpx_mock): ) @pytest.mark.asyncio - async def test_get_organization_resource(self, async_workos, httpx_mock): + async def test_get_resource_by_external_id(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("authorization_resource.json")) - result = await async_workos.authorization.get_organization_resource( + result = await async_workos.authorization.get_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id" ) assert isinstance(result, AuthorizationResource) @@ -1293,9 +1286,9 @@ async def test_get_organization_resource(self, async_workos, httpx_mock): ) @pytest.mark.asyncio - async def test_update_organization_resource(self, async_workos, httpx_mock): + async def test_update_resource_by_external_id(self, async_workos, httpx_mock): httpx_mock.add_response(json=load_fixture("authorization_resource.json")) - result = await async_workos.authorization.update_organization_resource( + result = await async_workos.authorization.update_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id", @@ -1311,9 +1304,9 @@ async def test_update_organization_resource(self, async_workos, httpx_mock): ) @pytest.mark.asyncio - async def test_delete_organization_resource(self, async_workos, httpx_mock): + async def test_delete_resource_by_external_id(self, async_workos, httpx_mock): httpx_mock.add_response(status_code=204) - result = await async_workos.authorization.delete_organization_resource( + result = await async_workos.authorization.delete_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id" ) assert result is None @@ -1324,11 +1317,11 @@ async def test_delete_organization_resource(self, async_workos, httpx_mock): ) @pytest.mark.asyncio - async def test_delete_organization_resource_encodes_query_params( + async def test_delete_resource_by_external_id_encodes_query_params( self, async_workos, httpx_mock ): httpx_mock.add_response(status_code=204) - await async_workos.authorization.delete_organization_resource( + await async_workos.authorization.delete_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id", @@ -1338,13 +1331,13 @@ async def test_delete_organization_resource_encodes_query_params( assert request.url.params["cascade_delete"] == "true" @pytest.mark.asyncio - async def test_list_resource_organization_memberships( + async def test_list_memberships_for_resource_by_external_id( self, async_workos, httpx_mock ): httpx_mock.add_response( json=load_fixture("list_user_organization_membership_base_list_data.json") ) - page = await async_workos.authorization.list_resource_organization_memberships( + page = await async_workos.authorization.list_memberships_for_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id", @@ -1355,11 +1348,11 @@ async def test_list_resource_organization_memberships( assert isinstance(page.data[0], UserOrganizationMembershipBaseListData) @pytest.mark.asyncio - async def test_list_resource_organization_memberships_empty_page( + async def test_list_memberships_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_resource_organization_memberships( + page = await async_workos.authorization.list_memberships_for_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id", @@ -1369,11 +1362,11 @@ async def test_list_resource_organization_memberships_empty_page( assert page.data == [] @pytest.mark.asyncio - async def test_list_resource_organization_memberships_encodes_query_params( + async def test_list_memberships_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_resource_organization_memberships( + await async_workos.authorization.list_memberships_for_resource_by_external_id( "test_organization_id", "test_resource_type_slug", "test_external_id", @@ -1422,6 +1415,7 @@ async def test_list_resources_encodes_query_params(self, async_workos, httpx_moc order=AuthorizationOrder("normal"), organization_id="value organization_id/test", resource_type_slug="value resource_type_slug/test", + resource_external_id="value resource_external_id/test", search="value search/test", ) request = httpx_mock.get_request() @@ -1436,6 +1430,10 @@ async def test_list_resources_encodes_query_params(self, async_workos, httpx_moc assert ( request.url.params["resource_type_slug"] == "value resource_type_slug/test" ) + assert ( + request.url.params["resource_external_id"] + == "value resource_external_id/test" + ) assert request.url.params["search"] == "value search/test" @pytest.mark.asyncio diff --git a/tests/test_groups.py b/tests/test_groups.py new file mode 100644 index 00000000..adcfdae9 --- /dev/null +++ b/tests/test_groups.py @@ -0,0 +1,503 @@ +# This file is auto-generated by oagen. Do not edit. + +import json + +import pytest +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._pagination import AsyncPage, SyncPage +from workos._errors import ( + AuthenticationError, + BadRequestError, + NotFoundError, + RateLimitExceededError, + ServerError, + UnprocessableEntityError, +) + + +class TestGroups: + def test_list_organization_groups(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("list_group.json"), + ) + page = workos.groups.list_organization_groups("test_organizationId") + assert isinstance(page, SyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], Group) + + def test_list_organization_groups_empty_page(self, workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = workos.groups.list_organization_groups("test_organizationId") + assert isinstance(page, SyncPage) + assert page.data == [] + + def test_list_organization_groups_encodes_query_params(self, workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + workos.groups.list_organization_groups( + "test_organizationId", + limit=10, + before="cursor before", + after="cursor/after", + order=GroupsOrder("normal"), + ) + 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" + + def test_create_organization_group(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("group.json"), + ) + result = workos.groups.create_organization_group( + "test_organizationId", name="test_name" + ) + assert isinstance(result, Group) + assert result.object == "group" + assert result.id == "group_01HXYZ123456789ABCDEFGHIJ" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/organizations/test_organizationId/groups") + body = json.loads(request.content) + assert body["name"] == "test_name" + + def test_get_organization_group(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("group.json"), + ) + result = workos.groups.get_organization_group( + "test_organizationId", "test_groupId" + ) + assert isinstance(result, Group) + assert result.object == "group" + assert result.id == "group_01HXYZ123456789ABCDEFGHIJ" + request = httpx_mock.get_request() + assert request.method == "GET" + assert request.url.path.endswith( + "/organizations/test_organizationId/groups/test_groupId" + ) + + def test_update_organization_group(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("group.json"), + ) + result = workos.groups.update_organization_group( + "test_organizationId", "test_groupId" + ) + assert isinstance(result, Group) + assert result.object == "group" + assert result.id == "group_01HXYZ123456789ABCDEFGHIJ" + request = httpx_mock.get_request() + assert request.method == "PATCH" + assert request.url.path.endswith( + "/organizations/test_organizationId/groups/test_groupId" + ) + + def test_delete_organization_group(self, workos, httpx_mock): + httpx_mock.add_response(status_code=204) + result = workos.groups.delete_organization_group( + "test_organizationId", "test_groupId" + ) + assert result is None + request = httpx_mock.get_request() + assert request.method == "DELETE" + assert request.url.path.endswith( + "/organizations/test_organizationId/groups/test_groupId" + ) + + def test_list_group_organization_memberships(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("list_user_organization_membership_base_list_data.json"), + ) + page = workos.groups.list_group_organization_memberships( + "test_organizationId", "test_groupId" + ) + assert isinstance(page, SyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], UserOrganizationMembershipBaseListData) + + def test_list_group_organization_memberships_empty_page(self, workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = workos.groups.list_group_organization_memberships( + "test_organizationId", "test_groupId" + ) + assert isinstance(page, SyncPage) + assert page.data == [] + + def test_list_group_organization_memberships_encodes_query_params( + self, workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + workos.groups.list_group_organization_memberships( + "test_organizationId", + "test_groupId", + limit=10, + before="cursor before", + after="cursor/after", + order=GroupsOrder("normal"), + ) + 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" + + def test_create_group_organization_membership(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("group.json"), + ) + result = workos.groups.create_group_organization_membership( + "test_organizationId", + "test_groupId", + organization_membership_id="test_organization_membership_id", + ) + assert isinstance(result, Group) + assert result.object == "group" + assert result.id == "group_01HXYZ123456789ABCDEFGHIJ" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith( + "/organizations/test_organizationId/groups/test_groupId/organization-memberships" + ) + body = json.loads(request.content) + assert body["organization_membership_id"] == "test_organization_membership_id" + + def test_delete_group_organization_membership(self, workos, httpx_mock): + httpx_mock.add_response(status_code=204) + result = workos.groups.delete_group_organization_membership( + "test_organizationId", "test_groupId", "test_omId" + ) + assert result is None + request = httpx_mock.get_request() + assert request.method == "DELETE" + assert request.url.path.endswith( + "/organizations/test_organizationId/groups/test_groupId/organization-memberships/test_omId" + ) + + def test_list_organization_groups_with_request_options(self, workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + workos.groups.list_organization_groups( + "test_organizationId", + request_options={"extra_headers": {"X-Custom": "value"}}, + ) + request = httpx_mock.get_request() + assert request.headers["X-Custom"] == "value" + + def test_list_organization_groups_unauthorized(self, workos, httpx_mock): + httpx_mock.add_response( + status_code=401, + json={"message": "Unauthorized"}, + ) + with pytest.raises(AuthenticationError): + workos.groups.list_organization_groups("test_organizationId") + + def test_list_organization_groups_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.groups.list_organization_groups("test_organizationId") + finally: + workos.close() + + def test_list_organization_groups_rate_limited(self, httpx_mock): + workos = WorkOSClient( + api_key="sk_test_123", client_id="client_test", max_retries=0 + ) + try: + httpx_mock.add_response( + status_code=429, + headers={"Retry-After": "0"}, + json={"message": "Slow down"}, + ) + with pytest.raises(RateLimitExceededError): + workos.groups.list_organization_groups("test_organizationId") + finally: + workos.close() + + def test_list_organization_groups_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.groups.list_organization_groups("test_organizationId") + finally: + workos.close() + + def test_list_organization_groups_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.groups.list_organization_groups("test_organizationId") + finally: + workos.close() + + def test_list_organization_groups_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.groups.list_organization_groups("test_organizationId") + finally: + workos.close() + + +class TestAsyncGroups: + @pytest.mark.asyncio + async def test_list_organization_groups(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("list_group.json")) + page = await async_workos.groups.list_organization_groups("test_organizationId") + assert isinstance(page, AsyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], Group) + + @pytest.mark.asyncio + async def test_list_organization_groups_empty_page(self, async_workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = await async_workos.groups.list_organization_groups("test_organizationId") + assert isinstance(page, AsyncPage) + assert page.data == [] + + @pytest.mark.asyncio + async def test_list_organization_groups_encodes_query_params( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + await async_workos.groups.list_organization_groups( + "test_organizationId", + limit=10, + before="cursor before", + after="cursor/after", + order=GroupsOrder("normal"), + ) + 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" + + @pytest.mark.asyncio + async def test_create_organization_group(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("group.json")) + result = await async_workos.groups.create_organization_group( + "test_organizationId", name="test_name" + ) + assert isinstance(result, Group) + assert result.object == "group" + assert result.id == "group_01HXYZ123456789ABCDEFGHIJ" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith("/organizations/test_organizationId/groups") + + @pytest.mark.asyncio + async def test_get_organization_group(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("group.json")) + result = await async_workos.groups.get_organization_group( + "test_organizationId", "test_groupId" + ) + assert isinstance(result, Group) + assert result.object == "group" + assert result.id == "group_01HXYZ123456789ABCDEFGHIJ" + request = httpx_mock.get_request() + assert request.method == "GET" + assert request.url.path.endswith( + "/organizations/test_organizationId/groups/test_groupId" + ) + + @pytest.mark.asyncio + async def test_update_organization_group(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("group.json")) + result = await async_workos.groups.update_organization_group( + "test_organizationId", "test_groupId" + ) + assert isinstance(result, Group) + assert result.object == "group" + assert result.id == "group_01HXYZ123456789ABCDEFGHIJ" + request = httpx_mock.get_request() + assert request.method == "PATCH" + assert request.url.path.endswith( + "/organizations/test_organizationId/groups/test_groupId" + ) + + @pytest.mark.asyncio + async def test_delete_organization_group(self, async_workos, httpx_mock): + httpx_mock.add_response(status_code=204) + result = await async_workos.groups.delete_organization_group( + "test_organizationId", "test_groupId" + ) + assert result is None + request = httpx_mock.get_request() + assert request.method == "DELETE" + assert request.url.path.endswith( + "/organizations/test_organizationId/groups/test_groupId" + ) + + @pytest.mark.asyncio + async def test_list_group_organization_memberships(self, async_workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("list_user_organization_membership_base_list_data.json") + ) + page = await async_workos.groups.list_group_organization_memberships( + "test_organizationId", "test_groupId" + ) + assert isinstance(page, AsyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], UserOrganizationMembershipBaseListData) + + @pytest.mark.asyncio + async def test_list_group_organization_memberships_empty_page( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = await async_workos.groups.list_group_organization_memberships( + "test_organizationId", "test_groupId" + ) + assert isinstance(page, AsyncPage) + assert page.data == [] + + @pytest.mark.asyncio + async def test_list_group_organization_memberships_encodes_query_params( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + await async_workos.groups.list_group_organization_memberships( + "test_organizationId", + "test_groupId", + limit=10, + before="cursor before", + after="cursor/after", + order=GroupsOrder("normal"), + ) + 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" + + @pytest.mark.asyncio + async def test_create_group_organization_membership(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("group.json")) + result = await async_workos.groups.create_group_organization_membership( + "test_organizationId", + "test_groupId", + organization_membership_id="test_organization_membership_id", + ) + assert isinstance(result, Group) + assert result.object == "group" + assert result.id == "group_01HXYZ123456789ABCDEFGHIJ" + request = httpx_mock.get_request() + assert request.method == "POST" + assert request.url.path.endswith( + "/organizations/test_organizationId/groups/test_groupId/organization-memberships" + ) + + @pytest.mark.asyncio + async def test_delete_group_organization_membership(self, async_workos, httpx_mock): + httpx_mock.add_response(status_code=204) + result = await async_workos.groups.delete_group_organization_membership( + "test_organizationId", "test_groupId", "test_omId" + ) + assert result is None + request = httpx_mock.get_request() + assert request.method == "DELETE" + assert request.url.path.endswith( + "/organizations/test_organizationId/groups/test_groupId/organization-memberships/test_omId" + ) + + @pytest.mark.asyncio + async def test_list_organization_groups_with_request_options( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + await async_workos.groups.list_organization_groups( + "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_list_organization_groups_unauthorized( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(status_code=401, json={"message": "Unauthorized"}) + with pytest.raises(AuthenticationError): + await async_workos.groups.list_organization_groups("test_organizationId") + + @pytest.mark.asyncio + async def test_list_organization_groups_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.groups.list_organization_groups("test_organizationId") + finally: + await workos.close() + + @pytest.mark.asyncio + async def test_list_organization_groups_rate_limited(self, httpx_mock): + workos = AsyncWorkOSClient( + api_key="sk_test_123", client_id="client_test", max_retries=0 + ) + try: + httpx_mock.add_response( + status_code=429, + headers={"Retry-After": "0"}, + json={"message": "Slow down"}, + ) + with pytest.raises(RateLimitExceededError): + await workos.groups.list_organization_groups("test_organizationId") + finally: + await workos.close() + + @pytest.mark.asyncio + async def test_list_organization_groups_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.groups.list_organization_groups("test_organizationId") + finally: + await workos.close() + + @pytest.mark.asyncio + async def test_list_organization_groups_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.groups.list_organization_groups("test_organizationId") + finally: + await workos.close() + + @pytest.mark.asyncio + async def test_list_organization_groups_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.groups.list_organization_groups("test_organizationId") + finally: + await workos.close() diff --git a/tests/test_models_round_trip.py b/tests/test_models_round_trip.py index 8ee7ba69..3925830b 100644 --- a/tests/test_models_round_trip.py +++ b/tests/test_models_round_trip.py @@ -7,6 +7,7 @@ from tests.generated_helpers import load_fixture from workos.admin_portal.models import ( + DomainVerificationIntentOptions, IntentOptions, PortalLinkResponse, SSOIntentOptions, @@ -176,7 +177,6 @@ FlagUpdatedContextPreviousAttributeData, FlagUpdatedData, FlagUpdatedDataOwner, - Group, GroupCreated, GroupDeleted, GroupMemberAdded, @@ -271,6 +271,10 @@ VaultMetadataReadData, VaultNamesListed, VaultNamesListedData, + WaitlistUser, + WaitlistUserApproved, + WaitlistUserCreated, + WaitlistUserDenied, ) from workos.connect.models import ( ApplicationCredentialsListItem, @@ -292,6 +296,7 @@ ) from workos.events.models import EventListListMetadata, EventSchema, EventSchemaUnknown from workos.feature_flags.models import FeatureFlag, FeatureFlagOwner, Flag, FlagOwner +from workos.groups.models import Group from workos.multi_factor_auth.models import ( AuthenticationChallenge, AuthenticationChallengeVerifyResponse, @@ -648,6 +653,27 @@ def test_sso_intent_options_omits_absent_optional_non_nullable_fields(self): assert "bookmark_slug" not in serialized assert "provider_type" not in serialized + def test_domain_verification_intent_options_round_trip(self): + data = load_fixture("domain_verification_intent_options.json") + instance = DomainVerificationIntentOptions.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = DomainVerificationIntentOptions.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_domain_verification_intent_options_minimal_payload(self): + data = {} + instance = DomainVerificationIntentOptions.from_dict(data) + assert instance.to_dict() is not None + + def test_domain_verification_intent_options_omits_absent_optional_non_nullable_fields( + self, + ): + data = {} + instance = DomainVerificationIntentOptions.from_dict(data) + serialized = instance.to_dict() + assert "domain_name" not in serialized + def test_intent_options_round_trip(self): data = load_fixture("intent_options.json") instance = IntentOptions.from_dict(data) @@ -657,10 +683,16 @@ def test_intent_options_round_trip(self): assert restored.to_dict() == serialized def test_intent_options_minimal_payload(self): - data = {"sso": {"bookmark_slug": "chatgpt", "provider_type": "GoogleSAML"}} + data = {} + instance = IntentOptions.from_dict(data) + assert instance.to_dict() is not None + + def test_intent_options_omits_absent_optional_non_nullable_fields(self): + data = {} instance = IntentOptions.from_dict(data) serialized = instance.to_dict() - assert serialized["sso"] == data["sso"] + assert "sso" not in serialized + assert "domain_verification" not in serialized def test_external_auth_complete_response_round_trip(self): data = load_fixture("external_auth_complete_response.json") @@ -1875,6 +1907,48 @@ def test_directory_user_with_groups_round_trips_unknown_enum_values(self): instance = DirectoryUserWithGroups.from_dict(data) assert instance.to_dict() == data + def test_group_round_trip(self): + data = load_fixture("group.json") + instance = Group.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = Group.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_group_minimal_payload(self): + data = { + "object": "group", + "id": "group_01HXYZ123456789ABCDEFGHIJ", + "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY", + "name": "Engineering", + "description": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + } + instance = Group.from_dict(data) + serialized = instance.to_dict() + assert serialized["object"] == data["object"] + assert serialized["id"] == data["id"] + assert serialized["organization_id"] == data["organization_id"] + assert serialized["name"] == data["name"] + assert serialized["description"] == data["description"] + assert serialized["created_at"] == data["created_at"] + assert serialized["updated_at"] == data["updated_at"] + + def test_group_preserves_nullable_fields(self): + data = { + "object": "group", + "id": "group_01HXYZ123456789ABCDEFGHIJ", + "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY", + "name": "Engineering", + "description": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + } + instance = Group.from_dict(data) + serialized = instance.to_dict() + assert serialized["description"] is None + def test_event_context_actor_round_trip(self): data = load_fixture("event_context_actor.json") instance = EventContextActor.from_dict(data) @@ -2067,48 +2141,6 @@ def test_directory_user_round_trips_unknown_enum_values(self): instance = DirectoryUser.from_dict(data) assert instance.to_dict() == data - def test_group_round_trip(self): - data = load_fixture("group.json") - instance = Group.from_dict(data) - serialized = instance.to_dict() - assert serialized == data - restored = Group.from_dict(serialized) - assert restored.to_dict() == serialized - - def test_group_minimal_payload(self): - data = { - "object": "group", - "id": "group_01HXYZ123456789ABCDEFGHIJ", - "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY", - "name": "Engineering", - "description": None, - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z", - } - instance = Group.from_dict(data) - serialized = instance.to_dict() - assert serialized["object"] == data["object"] - assert serialized["id"] == data["id"] - assert serialized["organization_id"] == data["organization_id"] - assert serialized["name"] == data["name"] - assert serialized["description"] == data["description"] - assert serialized["created_at"] == data["created_at"] - assert serialized["updated_at"] == data["updated_at"] - - def test_group_preserves_nullable_fields(self): - data = { - "object": "group", - "id": "group_01HXYZ123456789ABCDEFGHIJ", - "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY", - "name": "Engineering", - "description": None, - "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z", - } - instance = Group.from_dict(data) - serialized = instance.to_dict() - assert serialized["description"] is None - def test_user_round_trip(self): data = load_fixture("user.json") instance = User.from_dict(data) @@ -2189,6 +2221,61 @@ def test_user_preserves_nullable_fields(self): 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) + serialized = instance.to_dict() + assert serialized == data + restored = WaitlistUser.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_waitlist_user_minimal_payload(self): + data = { + "object": "waitlist_user", + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + } + instance = WaitlistUser.from_dict(data) + serialized = instance.to_dict() + assert serialized["object"] == data["object"] + assert serialized["id"] == data["id"] + assert serialized["email"] == data["email"] + assert serialized["state"] == data["state"] + assert serialized["approved_at"] == data["approved_at"] + assert serialized["created_at"] == data["created_at"] + assert serialized["updated_at"] == data["updated_at"] + + def test_waitlist_user_preserves_nullable_fields(self): + data = { + "object": "waitlist_user", + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + } + instance = WaitlistUser.from_dict(data) + serialized = instance.to_dict() + assert serialized["approved_at"] is None + + def test_waitlist_user_round_trips_unknown_enum_values(self): + data = { + "object": "waitlist_user", + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "unexpected_waitlist_user_state", + "approved_at": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + } + instance = WaitlistUser.from_dict(data) + assert instance.to_dict() == data + def test_action_authentication_denied_round_trip(self): data = load_fixture("action_authentication_denied.json") instance = ActionAuthenticationDenied.from_dict(data) @@ -8513,6 +8600,7 @@ def test_invitation_accepted_minimal_payload(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": None, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -8542,6 +8630,7 @@ def test_invitation_accepted_omits_absent_optional_non_nullable_fields(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": None, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -8572,6 +8661,7 @@ def test_invitation_accepted_data_minimal_payload(self): "organization_id": None, "inviter_user_id": None, "accepted_user_id": None, + "role_slug": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -8587,6 +8677,7 @@ def test_invitation_accepted_data_minimal_payload(self): assert serialized["organization_id"] == data["organization_id"] assert serialized["inviter_user_id"] == data["inviter_user_id"] assert serialized["accepted_user_id"] == data["accepted_user_id"] + assert serialized["role_slug"] == data["role_slug"] assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] @@ -8602,6 +8693,7 @@ def test_invitation_accepted_data_preserves_nullable_fields(self): "organization_id": None, "inviter_user_id": None, "accepted_user_id": None, + "role_slug": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -8612,6 +8704,7 @@ def test_invitation_accepted_data_preserves_nullable_fields(self): assert serialized["organization_id"] is None assert serialized["inviter_user_id"] is None assert serialized["accepted_user_id"] is None + assert serialized["role_slug"] is None def test_invitation_accepted_data_round_trips_unknown_enum_values(self): data = { @@ -8625,6 +8718,7 @@ def test_invitation_accepted_data_round_trips_unknown_enum_values(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": None, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -8654,6 +8748,7 @@ def test_invitation_created_minimal_payload(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": None, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -8683,6 +8778,7 @@ def test_invitation_created_omits_absent_optional_non_nullable_fields(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": None, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -8713,6 +8809,7 @@ def test_invitation_created_data_minimal_payload(self): "organization_id": None, "inviter_user_id": None, "accepted_user_id": None, + "role_slug": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -8728,6 +8825,7 @@ def test_invitation_created_data_minimal_payload(self): assert serialized["organization_id"] == data["organization_id"] assert serialized["inviter_user_id"] == data["inviter_user_id"] assert serialized["accepted_user_id"] == data["accepted_user_id"] + assert serialized["role_slug"] == data["role_slug"] assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] @@ -8743,6 +8841,7 @@ def test_invitation_created_data_preserves_nullable_fields(self): "organization_id": None, "inviter_user_id": None, "accepted_user_id": None, + "role_slug": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -8753,6 +8852,7 @@ def test_invitation_created_data_preserves_nullable_fields(self): assert serialized["organization_id"] is None assert serialized["inviter_user_id"] is None assert serialized["accepted_user_id"] is None + assert serialized["role_slug"] is None def test_invitation_created_data_round_trips_unknown_enum_values(self): data = { @@ -8766,6 +8866,7 @@ def test_invitation_created_data_round_trips_unknown_enum_values(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": None, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -8795,6 +8896,7 @@ def test_invitation_resent_minimal_payload(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": None, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -8824,6 +8926,7 @@ def test_invitation_resent_omits_absent_optional_non_nullable_fields(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": None, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -8854,6 +8957,7 @@ def test_invitation_resent_data_minimal_payload(self): "organization_id": None, "inviter_user_id": None, "accepted_user_id": None, + "role_slug": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -8869,6 +8973,7 @@ def test_invitation_resent_data_minimal_payload(self): assert serialized["organization_id"] == data["organization_id"] assert serialized["inviter_user_id"] == data["inviter_user_id"] assert serialized["accepted_user_id"] == data["accepted_user_id"] + assert serialized["role_slug"] == data["role_slug"] assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] @@ -8884,6 +8989,7 @@ def test_invitation_resent_data_preserves_nullable_fields(self): "organization_id": None, "inviter_user_id": None, "accepted_user_id": None, + "role_slug": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -8894,6 +9000,7 @@ def test_invitation_resent_data_preserves_nullable_fields(self): assert serialized["organization_id"] is None assert serialized["inviter_user_id"] is None assert serialized["accepted_user_id"] is None + assert serialized["role_slug"] is None def test_invitation_resent_data_round_trips_unknown_enum_values(self): data = { @@ -8907,6 +9014,7 @@ def test_invitation_resent_data_round_trips_unknown_enum_values(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": None, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -8936,6 +9044,7 @@ def test_invitation_revoked_minimal_payload(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": None, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -8965,6 +9074,7 @@ def test_invitation_revoked_omits_absent_optional_non_nullable_fields(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": None, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", }, @@ -8995,6 +9105,7 @@ def test_invitation_revoked_data_minimal_payload(self): "organization_id": None, "inviter_user_id": None, "accepted_user_id": None, + "role_slug": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -9010,6 +9121,7 @@ def test_invitation_revoked_data_minimal_payload(self): assert serialized["organization_id"] == data["organization_id"] assert serialized["inviter_user_id"] == data["inviter_user_id"] assert serialized["accepted_user_id"] == data["accepted_user_id"] + assert serialized["role_slug"] == data["role_slug"] assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] @@ -9025,6 +9137,7 @@ def test_invitation_revoked_data_preserves_nullable_fields(self): "organization_id": None, "inviter_user_id": None, "accepted_user_id": None, + "role_slug": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -9035,6 +9148,7 @@ def test_invitation_revoked_data_preserves_nullable_fields(self): assert serialized["organization_id"] is None assert serialized["inviter_user_id"] is None assert serialized["accepted_user_id"] is None + assert serialized["role_slug"] is None def test_invitation_revoked_data_round_trips_unknown_enum_values(self): data = { @@ -9048,6 +9162,7 @@ def test_invitation_revoked_data_round_trips_unknown_enum_values(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": None, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", } @@ -13336,6 +13451,162 @@ def test_vault_names_listed_data_round_trips_unknown_enum_values(self): instance = VaultNamesListedData.from_dict(data) assert instance.to_dict() == data + def test_waitlist_user_approved_round_trip(self): + data = load_fixture("waitlist_user_approved.json") + instance = WaitlistUserApproved.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = WaitlistUserApproved.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_waitlist_user_approved_minimal_payload(self): + data = { + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "waitlist_user.approved", + "data": { + "object": "waitlist_user", + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, + "created_at": "2026-01-15T12:00:00.000Z", + "object": "event", + } + instance = WaitlistUserApproved.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_waitlist_user_approved_omits_absent_optional_non_nullable_fields(self): + data = { + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "waitlist_user.approved", + "data": { + "object": "waitlist_user", + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, + "created_at": "2026-01-15T12:00:00.000Z", + "object": "event", + } + instance = WaitlistUserApproved.from_dict(data) + serialized = instance.to_dict() + assert "context" not in serialized + + def test_waitlist_user_created_round_trip(self): + data = load_fixture("waitlist_user_created.json") + instance = WaitlistUserCreated.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = WaitlistUserCreated.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_waitlist_user_created_minimal_payload(self): + data = { + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "waitlist_user.created", + "data": { + "object": "waitlist_user", + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, + "created_at": "2026-01-15T12:00:00.000Z", + "object": "event", + } + instance = WaitlistUserCreated.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_waitlist_user_created_omits_absent_optional_non_nullable_fields(self): + data = { + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "waitlist_user.created", + "data": { + "object": "waitlist_user", + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, + "created_at": "2026-01-15T12:00:00.000Z", + "object": "event", + } + instance = WaitlistUserCreated.from_dict(data) + serialized = instance.to_dict() + assert "context" not in serialized + + def test_waitlist_user_denied_round_trip(self): + data = load_fixture("waitlist_user_denied.json") + instance = WaitlistUserDenied.from_dict(data) + serialized = instance.to_dict() + assert serialized == data + restored = WaitlistUserDenied.from_dict(serialized) + assert restored.to_dict() == serialized + + def test_waitlist_user_denied_minimal_payload(self): + data = { + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "waitlist_user.denied", + "data": { + "object": "waitlist_user", + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, + "created_at": "2026-01-15T12:00:00.000Z", + "object": "event", + } + instance = WaitlistUserDenied.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_waitlist_user_denied_omits_absent_optional_non_nullable_fields(self): + data = { + "id": "event_01EHZNVPK3SFK441A1RGBFSHRT", + "event": "waitlist_user.denied", + "data": { + "object": "waitlist_user", + "id": "wl_user_01E4ZCR3C56J083X43JQXF3JK5", + "email": "marcelina.davis@example.com", + "state": "pending", + "approved_at": None, + "created_at": "2026-01-15T12:00:00.000Z", + "updated_at": "2026-01-15T12:00:00.000Z", + }, + "created_at": "2026-01-15T12:00:00.000Z", + "object": "event", + } + instance = WaitlistUserDenied.from_dict(data) + 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) @@ -14024,6 +14295,7 @@ def test_user_invite_minimal_payload(self): "organization_id": None, "inviter_user_id": None, "accepted_user_id": None, + "role_slug": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "token": "Z1uX3RbwcIl5fIGJJJCXXisdI", @@ -14041,6 +14313,7 @@ def test_user_invite_minimal_payload(self): assert serialized["organization_id"] == data["organization_id"] assert serialized["inviter_user_id"] == data["inviter_user_id"] assert serialized["accepted_user_id"] == data["accepted_user_id"] + assert serialized["role_slug"] == data["role_slug"] assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] assert serialized["token"] == data["token"] @@ -14058,6 +14331,7 @@ def test_user_invite_preserves_nullable_fields(self): "organization_id": None, "inviter_user_id": None, "accepted_user_id": None, + "role_slug": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "token": "Z1uX3RbwcIl5fIGJJJCXXisdI", @@ -14070,6 +14344,7 @@ def test_user_invite_preserves_nullable_fields(self): assert serialized["organization_id"] is None assert serialized["inviter_user_id"] is None assert serialized["accepted_user_id"] is None + assert serialized["role_slug"] is None def test_user_invite_round_trips_unknown_enum_values(self): data = { @@ -14083,6 +14358,7 @@ def test_user_invite_round_trips_unknown_enum_values(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": None, + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "token": "Z1uX3RbwcIl5fIGJJJCXXisdI", @@ -15756,6 +16032,7 @@ def test_invitation_minimal_payload(self): "organization_id": None, "inviter_user_id": None, "accepted_user_id": None, + "role_slug": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "token": "Z1uX3RbwcIl5fIGJJJCXXisdI", @@ -15773,6 +16050,7 @@ def test_invitation_minimal_payload(self): assert serialized["organization_id"] == data["organization_id"] assert serialized["inviter_user_id"] == data["inviter_user_id"] assert serialized["accepted_user_id"] == data["accepted_user_id"] + assert serialized["role_slug"] == data["role_slug"] assert serialized["created_at"] == data["created_at"] assert serialized["updated_at"] == data["updated_at"] assert serialized["token"] == data["token"] @@ -15790,6 +16068,7 @@ def test_invitation_preserves_nullable_fields(self): "organization_id": None, "inviter_user_id": None, "accepted_user_id": None, + "role_slug": None, "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "token": "Z1uX3RbwcIl5fIGJJJCXXisdI", @@ -15802,6 +16081,7 @@ def test_invitation_preserves_nullable_fields(self): assert serialized["organization_id"] is None assert serialized["inviter_user_id"] is None assert serialized["accepted_user_id"] is None + assert serialized["role_slug"] is None def test_invitation_round_trips_unknown_enum_values(self): data = { @@ -15815,6 +16095,7 @@ def test_invitation_round_trips_unknown_enum_values(self): "organization_id": "org_01E4ZCR3C56J083X43JQXF3JK5", "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": "user_01E4ZCR3C56J083X43JQXF3JK5", + "role_slug": "admin", "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "token": "Z1uX3RbwcIl5fIGJJJCXXisdI", diff --git a/tests/test_user_management_organization_membership_groups.py b/tests/test_user_management_organization_membership_groups.py new file mode 100644 index 00000000..1fec904b --- /dev/null +++ b/tests/test_user_management_organization_membership_groups.py @@ -0,0 +1,283 @@ +# This file is auto-generated by oagen. Do not edit. + + +import pytest +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._pagination import AsyncPage, SyncPage +from workos._errors import ( + AuthenticationError, + BadRequestError, + NotFoundError, + RateLimitExceededError, + ServerError, + UnprocessableEntityError, +) + + +class TestUserManagementOrganizationMembershipGroups: + def test_list_organization_membership_groups(self, workos, httpx_mock): + httpx_mock.add_response( + json=load_fixture("list_group.json"), + ) + page = workos.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + assert isinstance(page, SyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], Group) + + def test_list_organization_membership_groups_empty_page(self, workos, httpx_mock): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = workos.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + assert isinstance(page, SyncPage) + assert page.data == [] + + def test_list_organization_membership_groups_encodes_query_params( + self, workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + workos.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId", + limit=10, + before="cursor before", + after="cursor/after", + order=UserManagementOrganizationMembershipGroupsOrder("normal"), + ) + 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" + + def test_list_organization_membership_groups_with_request_options( + self, workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + workos.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId", request_options={"extra_headers": {"X-Custom": "value"}} + ) + request = httpx_mock.get_request() + assert request.headers["X-Custom"] == "value" + + def test_list_organization_membership_groups_unauthorized(self, workos, httpx_mock): + httpx_mock.add_response( + status_code=401, + json={"message": "Unauthorized"}, + ) + with pytest.raises(AuthenticationError): + workos.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + + def test_list_organization_membership_groups_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.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + finally: + workos.close() + + def test_list_organization_membership_groups_rate_limited(self, httpx_mock): + workos = WorkOSClient( + api_key="sk_test_123", client_id="client_test", max_retries=0 + ) + try: + httpx_mock.add_response( + status_code=429, + headers={"Retry-After": "0"}, + json={"message": "Slow down"}, + ) + with pytest.raises(RateLimitExceededError): + workos.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + finally: + workos.close() + + def test_list_organization_membership_groups_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.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + finally: + workos.close() + + def test_list_organization_membership_groups_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.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + finally: + workos.close() + + def test_list_organization_membership_groups_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.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + finally: + workos.close() + + +class TestAsyncUserManagementOrganizationMembershipGroups: + @pytest.mark.asyncio + async def test_list_organization_membership_groups(self, async_workos, httpx_mock): + httpx_mock.add_response(json=load_fixture("list_group.json")) + page = await async_workos.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + assert isinstance(page, AsyncPage) + assert len(page.data) == 1 + assert isinstance(page.data[0], Group) + + @pytest.mark.asyncio + async def test_list_organization_membership_groups_empty_page( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + page = await async_workos.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + assert isinstance(page, AsyncPage) + assert page.data == [] + + @pytest.mark.asyncio + async def test_list_organization_membership_groups_encodes_query_params( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + await async_workos.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId", + limit=10, + before="cursor before", + after="cursor/after", + order=UserManagementOrganizationMembershipGroupsOrder("normal"), + ) + 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" + + @pytest.mark.asyncio + async def test_list_organization_membership_groups_with_request_options( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(json={"data": [], "list_metadata": {}}) + await async_workos.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId", request_options={"extra_headers": {"X-Custom": "value"}} + ) + request = httpx_mock.get_request() + assert request.headers["X-Custom"] == "value" + + @pytest.mark.asyncio + async def test_list_organization_membership_groups_unauthorized( + self, async_workos, httpx_mock + ): + httpx_mock.add_response(status_code=401, json={"message": "Unauthorized"}) + with pytest.raises(AuthenticationError): + await async_workos.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + + @pytest.mark.asyncio + async def test_list_organization_membership_groups_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.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + finally: + await workos.close() + + @pytest.mark.asyncio + async def test_list_organization_membership_groups_rate_limited(self, httpx_mock): + workos = AsyncWorkOSClient( + api_key="sk_test_123", client_id="client_test", max_retries=0 + ) + try: + httpx_mock.add_response( + status_code=429, + headers={"Retry-After": "0"}, + json={"message": "Slow down"}, + ) + with pytest.raises(RateLimitExceededError): + await workos.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + finally: + await workos.close() + + @pytest.mark.asyncio + async def test_list_organization_membership_groups_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.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + finally: + await workos.close() + + @pytest.mark.asyncio + async def test_list_organization_membership_groups_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.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + finally: + await workos.close() + + @pytest.mark.asyncio + async def test_list_organization_membership_groups_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.user_management_organization_membership_groups.list_organization_membership_groups( + "test_omId" + ) + finally: + await workos.close()