From dfa9a90d964c0a9eee5111a33e1b98ed5a841880 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 20:06:35 +0000 Subject: [PATCH] fix(generated): No changes detected in API specification --- .last-synced-sha | 1 + .oagen-manifest.json | 698 +++++++++++++++++- lib/Resource/CreateGroup.php | 36 + lib/Resource/CreateGroupMembership.php | 32 + lib/Resource/CreateWebhookEndpointEvents.php | 3 + lib/Resource/DirectoryUserWithGroups.php | 3 +- .../DomainVerificationIntentOptions.php | 32 + lib/Resource/GenerateLink.php | 8 +- lib/Resource/IntentOptions.php | 10 +- lib/Resource/Invitation.php | 4 + lib/Resource/InvitationAcceptedData.php | 4 + lib/Resource/InvitationCreatedData.php | 4 + lib/Resource/InvitationResentData.php | 4 + lib/Resource/InvitationRevokedData.php | 4 + lib/Resource/SSOProvider.php | 9 + lib/Resource/UpdateGroup.php | 36 + lib/Resource/UserInvite.php | 4 + .../UserManagementAuthenticationProvider.php | 9 + lib/Resource/WaitlistUser.php | 56 ++ lib/Resource/WaitlistUserApproved.php | 50 ++ lib/Resource/WaitlistUserCreated.php | 50 ++ lib/Resource/WaitlistUserDenied.php | 50 ++ lib/Resource/WaitlistUserState.php | 14 + lib/Service/AdminPortal.php | 8 +- lib/Service/Authorization.php | 139 ++-- lib/Service/Groups.php | 244 ++++++ lib/Service/SSO.php | 2 +- lib/Service/UserManagement.php | 68 +- ...ManagementOrganizationMembershipGroups.php | 52 ++ lib/WorkOS.php | 31 +- tests/Fixtures/create_group.json | 4 + tests/Fixtures/create_group_membership.json | 3 + .../domain_verification_intent_options.json | 3 + tests/Fixtures/generate_link.json | 6 + tests/Fixtures/intent_options.json | 3 + tests/Fixtures/invitation.json | 3 +- tests/Fixtures/invitation_accepted.json | 3 +- tests/Fixtures/invitation_accepted_data.json | 3 +- tests/Fixtures/invitation_created.json | 3 +- tests/Fixtures/invitation_created_data.json | 3 +- tests/Fixtures/invitation_resent.json | 3 +- tests/Fixtures/invitation_resent_data.json | 3 +- tests/Fixtures/invitation_revoked.json | 3 +- tests/Fixtures/invitation_revoked_data.json | 3 +- tests/Fixtures/list_group.json | 17 + tests/Fixtures/list_user_invite.json | 1 + tests/Fixtures/update_group.json | 4 + tests/Fixtures/user_invite.json | 3 +- 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/Service/AuthorizationTest.php | 49 +- tests/Service/GroupsTest.php | 144 ++++ ...gementOrganizationMembershipGroupsTest.php | 50 ++ 55 files changed, 1928 insertions(+), 165 deletions(-) create mode 100644 .last-synced-sha create mode 100644 lib/Resource/CreateGroup.php create mode 100644 lib/Resource/CreateGroupMembership.php create mode 100644 lib/Resource/DomainVerificationIntentOptions.php create mode 100644 lib/Resource/UpdateGroup.php create mode 100644 lib/Resource/WaitlistUser.php create mode 100644 lib/Resource/WaitlistUserApproved.php create mode 100644 lib/Resource/WaitlistUserCreated.php create mode 100644 lib/Resource/WaitlistUserDenied.php create mode 100644 lib/Resource/WaitlistUserState.php create mode 100644 lib/Service/Groups.php create mode 100644 lib/Service/UserManagementOrganizationMembershipGroups.php 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/Service/GroupsTest.php create mode 100644 tests/Service/UserManagementOrganizationMembershipGroupsTest.php 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 6208b113..b3c56f64 100644 --- a/.oagen-manifest.json +++ b/.oagen-manifest.json @@ -1,7 +1,7 @@ { - "version": 1, + "version": 2, "language": "php", - "generatedAt": "2026-04-20T18:36:58.288Z", + "generatedAt": "2026-04-27T20:06:31.401Z", "files": [ "lib/Resource/ActionAuthenticationDenied.php", "lib/Resource/ActionAuthenticationDeniedData.php", @@ -149,6 +149,8 @@ "lib/Resource/CreateAuthorizationPermission.php", "lib/Resource/CreateAuthorizationResource.php", "lib/Resource/CreateCORSOrigin.php", + "lib/Resource/CreateGroup.php", + "lib/Resource/CreateGroupMembership.php", "lib/Resource/CreateM2MApplication.php", "lib/Resource/CreateMagicCodeAndReturn.php", "lib/Resource/CreateOAuthApplication.php", @@ -189,6 +191,7 @@ "lib/Resource/DirectoryUserState.php", "lib/Resource/DirectoryUserWithGroups.php", "lib/Resource/DirectoryUserWithGroupsEmail.php", + "lib/Resource/DomainVerificationIntentOptions.php", "lib/Resource/DsyncActivated.php", "lib/Resource/DsyncActivatedData.php", "lib/Resource/DsyncActivatedDataDomain.php", @@ -405,6 +408,7 @@ "lib/Resource/UpdateAuditLogsRetention.php", "lib/Resource/UpdateAuthorizationPermission.php", "lib/Resource/UpdateAuthorizationResource.php", + "lib/Resource/UpdateGroup.php", "lib/Resource/UpdateJWTTemplate.php", "lib/Resource/UpdateOAuthApplication.php", "lib/Resource/UpdateOrganization.php", @@ -459,6 +463,11 @@ "lib/Resource/VaultNamesListedData.php", "lib/Resource/VerifyEmailAddress.php", "lib/Resource/VerifyEmailResponse.php", + "lib/Resource/WaitlistUser.php", + "lib/Resource/WaitlistUserApproved.php", + "lib/Resource/WaitlistUserCreated.php", + "lib/Resource/WaitlistUserDenied.php", + "lib/Resource/WaitlistUserState.php", "lib/Resource/WebhookEndpointJson.php", "lib/Resource/WebhookEndpointJsonStatus.php", "lib/Resource/WidgetSessionToken.php", @@ -472,6 +481,7 @@ "lib/Service/DirectorySync.php", "lib/Service/Events.php", "lib/Service/FeatureFlags.php", + "lib/Service/Groups.php", "lib/Service/MultiFactorAuth.php", "lib/Service/OrganizationDomains.php", "lib/Service/Organizations.php", @@ -489,6 +499,7 @@ "lib/Service/RoleSingle.php", "lib/Service/SSO.php", "lib/Service/UserManagement.php", + "lib/Service/UserManagementOrganizationMembershipGroups.php", "lib/Service/Webhooks.php", "lib/Service/Widgets.php", "lib/WorkOS.php", @@ -622,6 +633,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", @@ -654,6 +667,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", @@ -760,6 +774,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", @@ -872,6 +887,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", @@ -918,6 +934,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", @@ -929,14 +949,686 @@ "tests/Service/DirectorySyncTest.php", "tests/Service/EventsTest.php", "tests/Service/FeatureFlagsTest.php", + "tests/Service/GroupsTest.php", "tests/Service/MultiFactorAuthTest.php", "tests/Service/OrganizationDomainsTest.php", "tests/Service/OrganizationsTest.php", "tests/Service/PipesTest.php", "tests/Service/RadarTest.php", "tests/Service/SSOTest.php", + "tests/Service/UserManagementOrganizationMembershipGroupsTest.php", "tests/Service/UserManagementTest.php", "tests/Service/WebhooksTest.php", "tests/Service/WidgetsTest.php" - ] + ], + "operations": { + "POST /api_keys/validations": { + "sdkMethod": "createValidation", + "service": "apiKeys" + }, + "DELETE /api_keys/{id}": { + "sdkMethod": "deleteApiKey", + "service": "apiKeys" + }, + "POST /auth/challenges/{id}/verify": { + "sdkMethod": "verifyChallenge", + "service": "multiFactorAuth" + }, + "POST /auth/factors/enroll": { + "sdkMethod": "enrollFactor", + "service": "multiFactorAuth" + }, + "GET /auth/factors/{id}": { + "sdkMethod": "getFactor", + "service": "multiFactorAuth" + }, + "DELETE /auth/factors/{id}": { + "sdkMethod": "deleteFactor", + "service": "multiFactorAuth" + }, + "POST /auth/factors/{id}/challenge": { + "sdkMethod": "challengeFactor", + "service": "multiFactorAuth" + }, + "POST /authkit/oauth2/complete": { + "sdkMethod": "completeOAuth2", + "service": "connect" + }, + "POST /authorization/organization_memberships/{organization_membership_id}/check": { + "sdkMethod": "check", + "service": "authorization" + }, + "GET /authorization/organization_memberships/{organization_membership_id}/resources": { + "sdkMethod": "listResourcesForMembership", + "service": "authorization" + }, + "GET /authorization/organization_memberships/{organization_membership_id}/resources/{resource_id}/permissions": { + "sdkMethod": "listEffectivePermissions", + "service": "authorization" + }, + "GET /authorization/organization_memberships/{organization_membership_id}/resources/{resource_type_slug}/{external_id}/permissions": { + "sdkMethod": "listEffectivePermissionsByExternalId", + "service": "authorization" + }, + "GET /authorization/organization_memberships/{organization_membership_id}/role_assignments": { + "sdkMethod": "listRoleAssignments", + "service": "authorization" + }, + "POST /authorization/organization_memberships/{organization_membership_id}/role_assignments": { + "sdkMethod": "assignRole", + "service": "authorization" + }, + "DELETE /authorization/organization_memberships/{organization_membership_id}/role_assignments": { + "sdkMethod": "removeRole", + "service": "authorization" + }, + "DELETE /authorization/organization_memberships/{organization_membership_id}/role_assignments/{role_assignment_id}": { + "sdkMethod": "removeRoleAssignment", + "service": "authorization" + }, + "GET /authorization/organizations/{organizationId}/roles": { + "sdkMethod": "listOrganizationRoles", + "service": "authorization" + }, + "POST /authorization/organizations/{organizationId}/roles": { + "sdkMethod": "createOrganizationRole", + "service": "authorization" + }, + "GET /authorization/organizations/{organizationId}/roles/{slug}": { + "sdkMethod": "getOrganizationRole", + "service": "authorization" + }, + "PATCH /authorization/organizations/{organizationId}/roles/{slug}": { + "sdkMethod": "updateOrganizationRole", + "service": "authorization" + }, + "DELETE /authorization/organizations/{organizationId}/roles/{slug}": { + "sdkMethod": "deleteOrganizationRole", + "service": "authorization" + }, + "POST /authorization/organizations/{organizationId}/roles/{slug}/permissions": { + "sdkMethod": "addOrganizationRolePermission", + "service": "authorization" + }, + "PUT /authorization/organizations/{organizationId}/roles/{slug}/permissions": { + "sdkMethod": "setOrganizationRolePermissions", + "service": "authorization" + }, + "DELETE /authorization/organizations/{organizationId}/roles/{slug}/permissions/{permissionSlug}": { + "sdkMethod": "removeOrganizationRolePermission", + "service": "authorization" + }, + "GET /authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}": { + "sdkMethod": "getResourceByExternalId", + "service": "authorization" + }, + "PATCH /authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}": { + "sdkMethod": "updateResourceByExternalId", + "service": "authorization" + }, + "DELETE /authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}": { + "sdkMethod": "deleteResourceByExternalId", + "service": "authorization" + }, + "GET /authorization/organizations/{organization_id}/resources/{resource_type_slug}/{external_id}/organization_memberships": { + "sdkMethod": "listMembershipsForResourceByExternalId", + "service": "authorization" + }, + "GET /authorization/resources": { + "sdkMethod": "listResources", + "service": "authorization" + }, + "POST /authorization/resources": { + "sdkMethod": "createResource", + "service": "authorization" + }, + "GET /authorization/resources/{resource_id}": { + "sdkMethod": "getResource", + "service": "authorization" + }, + "PATCH /authorization/resources/{resource_id}": { + "sdkMethod": "updateResource", + "service": "authorization" + }, + "DELETE /authorization/resources/{resource_id}": { + "sdkMethod": "deleteResource", + "service": "authorization" + }, + "GET /authorization/resources/{resource_id}/organization_memberships": { + "sdkMethod": "listMembershipsForResource", + "service": "authorization" + }, + "GET /authorization/roles": { + "sdkMethod": "listEnvironmentRoles", + "service": "authorization" + }, + "POST /authorization/roles": { + "sdkMethod": "createEnvironmentRole", + "service": "authorization" + }, + "GET /authorization/roles/{slug}": { + "sdkMethod": "getEnvironmentRole", + "service": "authorization" + }, + "PATCH /authorization/roles/{slug}": { + "sdkMethod": "updateEnvironmentRole", + "service": "authorization" + }, + "POST /authorization/roles/{slug}/permissions": { + "sdkMethod": "addEnvironmentRolePermission", + "service": "authorization" + }, + "PUT /authorization/roles/{slug}/permissions": { + "sdkMethod": "setEnvironmentRolePermissions", + "service": "authorization" + }, + "GET /authorization/permissions": { + "sdkMethod": "listPermissions", + "service": "authorization" + }, + "POST /authorization/permissions": { + "sdkMethod": "createPermission", + "service": "authorization" + }, + "GET /authorization/permissions/{slug}": { + "sdkMethod": "getPermission", + "service": "authorization" + }, + "PATCH /authorization/permissions/{slug}": { + "sdkMethod": "updatePermission", + "service": "authorization" + }, + "DELETE /authorization/permissions/{slug}": { + "sdkMethod": "deletePermission", + "service": "authorization" + }, + "GET /connect/applications": { + "sdkMethod": "listApplications", + "service": "connect" + }, + "POST /connect/applications": { + "sdkMethod": "createApplication", + "service": "connect" + }, + "GET /connect/applications/{id}": { + "sdkMethod": "getApplication", + "service": "connect" + }, + "PUT /connect/applications/{id}": { + "sdkMethod": "updateApplication", + "service": "connect" + }, + "DELETE /connect/applications/{id}": { + "sdkMethod": "deleteApplication", + "service": "connect" + }, + "GET /connect/applications/{id}/client_secrets": { + "sdkMethod": "listApplicationClientSecrets", + "service": "connect" + }, + "POST /connect/applications/{id}/client_secrets": { + "sdkMethod": "createApplicationClientSecret", + "service": "connect" + }, + "DELETE /connect/client_secrets/{id}": { + "sdkMethod": "deleteClientSecret", + "service": "connect" + }, + "GET /connections": { + "sdkMethod": "listConnections", + "service": "sso" + }, + "GET /connections/{id}": { + "sdkMethod": "getConnection", + "service": "sso" + }, + "DELETE /connections/{id}": { + "sdkMethod": "deleteConnection", + "service": "sso" + }, + "POST /data-integrations/{slug}/authorize": { + "sdkMethod": "authorizeDataIntegration", + "service": "pipes" + }, + "POST /data-integrations/{slug}/token": { + "sdkMethod": "createDataIntegrationToken", + "service": "pipes" + }, + "GET /directories": { + "sdkMethod": "listDirectories", + "service": "directorySync" + }, + "GET /directories/{id}": { + "sdkMethod": "getDirectory", + "service": "directorySync" + }, + "DELETE /directories/{id}": { + "sdkMethod": "deleteDirectory", + "service": "directorySync" + }, + "GET /directory_groups": { + "sdkMethod": "listGroups", + "service": "directorySync" + }, + "GET /directory_groups/{id}": { + "sdkMethod": "getGroup", + "service": "directorySync" + }, + "GET /directory_users": { + "sdkMethod": "listUsers", + "service": "directorySync" + }, + "GET /directory_users/{id}": { + "sdkMethod": "getUser", + "service": "directorySync" + }, + "GET /events": { + "sdkMethod": "listEvents", + "service": "events" + }, + "GET /feature-flags": { + "sdkMethod": "listFeatureFlags", + "service": "featureFlags" + }, + "GET /feature-flags/{slug}": { + "sdkMethod": "getFeatureFlag", + "service": "featureFlags" + }, + "PUT /feature-flags/{slug}/disable": { + "sdkMethod": "disableFeatureFlag", + "service": "featureFlags" + }, + "PUT /feature-flags/{slug}/enable": { + "sdkMethod": "enableFeatureFlag", + "service": "featureFlags" + }, + "POST /feature-flags/{slug}/targets/{resourceId}": { + "sdkMethod": "addFlagTarget", + "service": "featureFlags" + }, + "DELETE /feature-flags/{slug}/targets/{resourceId}": { + "sdkMethod": "removeFlagTarget", + "service": "featureFlags" + }, + "POST /organization_domains": { + "sdkMethod": "createOrganizationDomain", + "service": "organizationDomains" + }, + "GET /organization_domains/{id}": { + "sdkMethod": "getOrganizationDomain", + "service": "organizationDomains" + }, + "DELETE /organization_domains/{id}": { + "sdkMethod": "deleteOrganizationDomain", + "service": "organizationDomains" + }, + "POST /organization_domains/{id}/verify": { + "sdkMethod": "verifyOrganizationDomain", + "service": "organizationDomains" + }, + "GET /organizations": { + "sdkMethod": "listOrganizations", + "service": "organizations" + }, + "POST /organizations": { + "sdkMethod": "createOrganization", + "service": "organizations" + }, + "GET /organizations/external_id/{external_id}": { + "sdkMethod": "getOrganizationByExternalId", + "service": "organizations" + }, + "GET /organizations/{id}": { + "sdkMethod": "getOrganization", + "service": "organizations" + }, + "PUT /organizations/{id}": { + "sdkMethod": "updateOrganization", + "service": "organizations" + }, + "DELETE /organizations/{id}": { + "sdkMethod": "deleteOrganization", + "service": "organizations" + }, + "GET /organizations/{id}/audit_log_configuration": { + "sdkMethod": "getAuditLogConfiguration", + "service": "organizations" + }, + "GET /organizations/{id}/audit_logs_retention": { + "sdkMethod": "getOrganizationAuditLogsRetention", + "service": "auditLogs" + }, + "PUT /organizations/{id}/audit_logs_retention": { + "sdkMethod": "updateOrganizationAuditLogsRetention", + "service": "auditLogs" + }, + "GET /organizations/{organizationId}/api_keys": { + "sdkMethod": "listOrganizationApiKeys", + "service": "apiKeys" + }, + "POST /organizations/{organizationId}/api_keys": { + "sdkMethod": "createOrganizationApiKey", + "service": "apiKeys" + }, + "GET /organizations/{organizationId}/feature-flags": { + "sdkMethod": "listOrganizationFeatureFlags", + "service": "featureFlags" + }, + "GET /organizations/{organizationId}/groups": { + "sdkMethod": "listOrganizationGroups", + "service": "groups" + }, + "POST /organizations/{organizationId}/groups": { + "sdkMethod": "createOrganizationGroup", + "service": "groups" + }, + "GET /organizations/{organizationId}/groups/{groupId}": { + "sdkMethod": "getOrganizationGroup", + "service": "groups" + }, + "PATCH /organizations/{organizationId}/groups/{groupId}": { + "sdkMethod": "updateOrganizationGroup", + "service": "groups" + }, + "DELETE /organizations/{organizationId}/groups/{groupId}": { + "sdkMethod": "deleteOrganizationGroup", + "service": "groups" + }, + "GET /organizations/{organizationId}/groups/{groupId}/organization-memberships": { + "sdkMethod": "listGroupOrganizationMemberships", + "service": "groups" + }, + "POST /organizations/{organizationId}/groups/{groupId}/organization-memberships": { + "sdkMethod": "createGroupOrganizationMembership", + "service": "groups" + }, + "DELETE /organizations/{organizationId}/groups/{groupId}/organization-memberships/{omId}": { + "sdkMethod": "deleteGroupOrganizationMembership", + "service": "groups" + }, + "POST /portal/generate_link": { + "sdkMethod": "generateLink", + "service": "adminPortal" + }, + "POST /radar/attempts": { + "sdkMethod": "createAttempt", + "service": "radar" + }, + "PUT /radar/attempts/{id}": { + "sdkMethod": "updateAttempt", + "service": "radar" + }, + "POST /radar/lists/{type}/{action}": { + "sdkMethod": "addListEntry", + "service": "radar" + }, + "DELETE /radar/lists/{type}/{action}": { + "sdkMethod": "removeListEntry", + "service": "radar" + }, + "GET /sso/authorize": { + "sdkMethod": "getAuthorizationUrl", + "service": "sso" + }, + "GET /sso/logout": { + "sdkMethod": "getLogoutUrl", + "service": "sso" + }, + "POST /sso/logout/authorize": { + "sdkMethod": "authorizeLogout", + "service": "sso" + }, + "GET /sso/profile": { + "sdkMethod": "getProfile", + "service": "sso" + }, + "POST /sso/token": { + "sdkMethod": "getProfileAndToken", + "service": "sso" + }, + "GET /sso/jwks/{clientId}": { + "sdkMethod": "getJwks", + "service": "userManagement" + }, + "POST /user_management/authenticate": { + "sdkMethod": "createAuthenticate", + "service": "userManagement" + }, + "GET /user_management/authorize": { + "sdkMethod": "getAuthorizationUrl", + "service": "userManagement" + }, + "POST /user_management/authorize/device": { + "sdkMethod": "createDevice", + "service": "userManagement" + }, + "GET /user_management/sessions/logout": { + "sdkMethod": "getLogoutUrl", + "service": "userManagement" + }, + "POST /user_management/sessions/revoke": { + "sdkMethod": "revokeSession", + "service": "userManagement" + }, + "POST /user_management/cors_origins": { + "sdkMethod": "createCorsOrigin", + "service": "userManagement" + }, + "GET /user_management/email_verification/{id}": { + "sdkMethod": "getEmailVerification", + "service": "userManagement" + }, + "POST /user_management/password_reset": { + "sdkMethod": "resetPassword", + "service": "userManagement" + }, + "POST /user_management/password_reset/confirm": { + "sdkMethod": "confirmPasswordReset", + "service": "userManagement" + }, + "GET /user_management/password_reset/{id}": { + "sdkMethod": "getPasswordReset", + "service": "userManagement" + }, + "GET /user_management/users": { + "sdkMethod": "listUsers", + "service": "userManagement" + }, + "POST /user_management/users": { + "sdkMethod": "createUser", + "service": "userManagement" + }, + "GET /user_management/users/external_id/{external_id}": { + "sdkMethod": "getUserByExternalId", + "service": "userManagement" + }, + "GET /user_management/users/{id}": { + "sdkMethod": "getUser", + "service": "userManagement" + }, + "PUT /user_management/users/{id}": { + "sdkMethod": "updateUser", + "service": "userManagement" + }, + "DELETE /user_management/users/{id}": { + "sdkMethod": "deleteUser", + "service": "userManagement" + }, + "POST /user_management/users/{id}/email_change/confirm": { + "sdkMethod": "confirmEmailChange", + "service": "userManagement" + }, + "POST /user_management/users/{id}/email_change/send": { + "sdkMethod": "sendEmailChange", + "service": "userManagement" + }, + "POST /user_management/users/{id}/email_verification/confirm": { + "sdkMethod": "verifyEmail", + "service": "userManagement" + }, + "POST /user_management/users/{id}/email_verification/send": { + "sdkMethod": "sendVerificationEmail", + "service": "userManagement" + }, + "GET /user_management/users/{id}/identities": { + "sdkMethod": "getUserIdentities", + "service": "userManagement" + }, + "GET /user_management/users/{id}/sessions": { + "sdkMethod": "listSessions", + "service": "userManagement" + }, + "GET /user_management/invitations": { + "sdkMethod": "listInvitations", + "service": "userManagement" + }, + "POST /user_management/invitations": { + "sdkMethod": "sendInvitation", + "service": "userManagement" + }, + "GET /user_management/invitations/by_token/{token}": { + "sdkMethod": "findInvitationByToken", + "service": "userManagement" + }, + "GET /user_management/invitations/{id}": { + "sdkMethod": "getInvitation", + "service": "userManagement" + }, + "POST /user_management/invitations/{id}/accept": { + "sdkMethod": "acceptInvitation", + "service": "userManagement" + }, + "POST /user_management/invitations/{id}/resend": { + "sdkMethod": "resendInvitation", + "service": "userManagement" + }, + "POST /user_management/invitations/{id}/revoke": { + "sdkMethod": "revokeInvitation", + "service": "userManagement" + }, + "PUT /user_management/jwt_template": { + "sdkMethod": "updateJWTTemplate", + "service": "userManagement" + }, + "POST /user_management/magic_auth": { + "sdkMethod": "createMagicAuth", + "service": "userManagement" + }, + "GET /user_management/magic_auth/{id}": { + "sdkMethod": "getMagicAuth", + "service": "userManagement" + }, + "GET /user_management/organization_memberships": { + "sdkMethod": "listOrganizationMemberships", + "service": "userManagement" + }, + "POST /user_management/organization_memberships": { + "sdkMethod": "createOrganizationMembership", + "service": "userManagement" + }, + "GET /user_management/organization_memberships/{id}": { + "sdkMethod": "getOrganizationMembership", + "service": "userManagement" + }, + "PUT /user_management/organization_memberships/{id}": { + "sdkMethod": "updateOrganizationMembership", + "service": "userManagement" + }, + "DELETE /user_management/organization_memberships/{id}": { + "sdkMethod": "deleteOrganizationMembership", + "service": "userManagement" + }, + "PUT /user_management/organization_memberships/{id}/deactivate": { + "sdkMethod": "deactivateOrganizationMembership", + "service": "userManagement" + }, + "PUT /user_management/organization_memberships/{id}/reactivate": { + "sdkMethod": "reactivateOrganizationMembership", + "service": "userManagement" + }, + "GET /user_management/organization_memberships/{omId}/groups": { + "sdkMethod": "listOrganizationMembershipGroups", + "service": "userManagementOrganizationMembershipGroups" + }, + "POST /user_management/redirect_uris": { + "sdkMethod": "createRedirectUri", + "service": "userManagement" + }, + "GET /user_management/users/{userId}/feature-flags": { + "sdkMethod": "listUserFeatureFlags", + "service": "featureFlags" + }, + "GET /user_management/users/{user_id}/authorized_applications": { + "sdkMethod": "listUserAuthorizedApplications", + "service": "userManagement" + }, + "DELETE /user_management/users/{user_id}/authorized_applications/{application_id}": { + "sdkMethod": "deleteUserAuthorizedApplication", + "service": "userManagement" + }, + "GET /user_management/users/{user_id}/connected_accounts/{slug}": { + "sdkMethod": "getUserConnectedAccount", + "service": "pipes" + }, + "DELETE /user_management/users/{user_id}/connected_accounts/{slug}": { + "sdkMethod": "deleteUserConnectedAccount", + "service": "pipes" + }, + "GET /user_management/users/{user_id}/data_providers": { + "sdkMethod": "listUserDataProviders", + "service": "pipes" + }, + "GET /user_management/users/{userlandUserId}/auth_factors": { + "sdkMethod": "listUserAuthFactors", + "service": "multiFactorAuth" + }, + "POST /user_management/users/{userlandUserId}/auth_factors": { + "sdkMethod": "createUserAuthFactor", + "service": "multiFactorAuth" + }, + "GET /webhook_endpoints": { + "sdkMethod": "listWebhookEndpoints", + "service": "webhooks" + }, + "POST /webhook_endpoints": { + "sdkMethod": "createWebhookEndpoint", + "service": "webhooks" + }, + "PATCH /webhook_endpoints/{id}": { + "sdkMethod": "updateWebhookEndpoint", + "service": "webhooks" + }, + "DELETE /webhook_endpoints/{id}": { + "sdkMethod": "deleteWebhookEndpoint", + "service": "webhooks" + }, + "POST /widgets/token": { + "sdkMethod": "createToken", + "service": "widgets" + }, + "GET /audit_logs/actions": { + "sdkMethod": "listActions", + "service": "auditLogs" + }, + "GET /audit_logs/actions/{actionName}/schemas": { + "sdkMethod": "listActionSchemas", + "service": "auditLogs" + }, + "POST /audit_logs/actions/{actionName}/schemas": { + "sdkMethod": "createSchema", + "service": "auditLogs" + }, + "POST /audit_logs/events": { + "sdkMethod": "createEvent", + "service": "auditLogs" + }, + "POST /audit_logs/exports": { + "sdkMethod": "createExport", + "service": "auditLogs" + }, + "GET /audit_logs/exports/{auditLogExportId}": { + "sdkMethod": "getExport", + "service": "auditLogs" + } + } } diff --git a/lib/Resource/CreateGroup.php b/lib/Resource/CreateGroup.php new file mode 100644 index 00000000..afa6a466 --- /dev/null +++ b/lib/Resource/CreateGroup.php @@ -0,0 +1,36 @@ + $this->name, + 'description' => $this->description, + ]; + } +} diff --git a/lib/Resource/CreateGroupMembership.php b/lib/Resource/CreateGroupMembership.php new file mode 100644 index 00000000..367b82d7 --- /dev/null +++ b/lib/Resource/CreateGroupMembership.php @@ -0,0 +1,32 @@ + $this->organizationMembershipId, + ]; + } +} diff --git a/lib/Resource/CreateWebhookEndpointEvents.php b/lib/Resource/CreateWebhookEndpointEvents.php index 2a7a5247..4fb5b9a8 100644 --- a/lib/Resource/CreateWebhookEndpointEvents.php +++ b/lib/Resource/CreateWebhookEndpointEvents.php @@ -82,4 +82,7 @@ enum CreateWebhookEndpointEvents: string case PermissionUpdated = 'permission.updated'; case SessionCreated = 'session.created'; case SessionRevoked = 'session.revoked'; + case WaitlistUserApproved = 'waitlist_user.approved'; + case WaitlistUserCreated = 'waitlist_user.created'; + case WaitlistUserDenied = 'waitlist_user.denied'; } diff --git a/lib/Resource/DirectoryUserWithGroups.php b/lib/Resource/DirectoryUserWithGroups.php index 07adf396..1962d970 100644 --- a/lib/Resource/DirectoryUserWithGroups.php +++ b/lib/Resource/DirectoryUserWithGroups.php @@ -41,8 +41,9 @@ public function __construct( /** An ISO 8601 timestamp. */ public \DateTimeImmutable $updatedAt, /** - * The directory groups the user belongs to. + * The directory groups the user belongs to. Use the List Directory Groups endpoint with a user filter instead. * @var array<\WorkOS\Resource\DirectoryGroup> + * @deprecated */ public array $groups, /** The first name of the user. */ diff --git a/lib/Resource/DomainVerificationIntentOptions.php b/lib/Resource/DomainVerificationIntentOptions.php new file mode 100644 index 00000000..0b86a7da --- /dev/null +++ b/lib/Resource/DomainVerificationIntentOptions.php @@ -0,0 +1,32 @@ + $this->domainName, + ]; + } +} diff --git a/lib/Resource/GenerateLink.php b/lib/Resource/GenerateLink.php index 7ef31908..e3eac62c 100644 --- a/lib/Resource/GenerateLink.php +++ b/lib/Resource/GenerateLink.php @@ -32,10 +32,10 @@ public function __construct( /** Options to configure the Admin Portal based on the intent. */ public ?IntentOptions $intentOptions = null, /** - * The email addresses of the IT admins to grant access to the Admin Portal for the given organization. Accepts up to 20 emails. + * The email addresses of the IT contacts to grant access to the Admin Portal for the given organization. Accepts up to 20 emails. * @var array|null */ - public ?array $adminEmails = null, + public ?array $itContactEmails = null, ) { } @@ -47,7 +47,7 @@ public static function fromArray(array $data): self successUrl: $data['success_url'] ?? null, intent: isset($data['intent']) ? GenerateLinkIntent::from($data['intent']) : null, intentOptions: isset($data['intent_options']) ? IntentOptions::fromArray($data['intent_options']) : null, - adminEmails: $data['admin_emails'] ?? null, + itContactEmails: $data['it_contact_emails'] ?? null, ); } @@ -59,7 +59,7 @@ public function toArray(): array 'success_url' => $this->successUrl, 'intent' => $this->intent?->value, 'intent_options' => $this->intentOptions?->toArray(), - 'admin_emails' => $this->adminEmails, + 'it_contact_emails' => $this->itContactEmails, ]; } } diff --git a/lib/Resource/IntentOptions.php b/lib/Resource/IntentOptions.php index c49652f1..1d895e0c 100644 --- a/lib/Resource/IntentOptions.php +++ b/lib/Resource/IntentOptions.php @@ -12,21 +12,25 @@ public function __construct( /** SSO-specific options for the Admin Portal. */ - public SSOIntentOptions $sso, + public ?SSOIntentOptions $sso = null, + /** Domain verification-specific options for the Admin Portal. */ + public ?DomainVerificationIntentOptions $domainVerification = null, ) { } public static function fromArray(array $data): self { return new self( - sso: SSOIntentOptions::fromArray($data['sso']), + sso: isset($data['sso']) ? SSOIntentOptions::fromArray($data['sso']) : null, + domainVerification: isset($data['domain_verification']) ? DomainVerificationIntentOptions::fromArray($data['domain_verification']) : null, ); } public function toArray(): array { return [ - 'sso' => $this->sso->toArray(), + 'sso' => $this->sso?->toArray(), + 'domain_verification' => $this->domainVerification?->toArray(), ]; } } diff --git a/lib/Resource/Invitation.php b/lib/Resource/Invitation.php index 326c423d..2948c620 100644 --- a/lib/Resource/Invitation.php +++ b/lib/Resource/Invitation.php @@ -31,6 +31,8 @@ public function __construct( public ?string $inviterUserId, /** The ID of the user who accepted the invitation, once accepted. */ public ?string $acceptedUserId, + /** 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. */ + public ?string $roleSlug, /** An ISO 8601 timestamp. */ public \DateTimeImmutable $createdAt, /** An ISO 8601 timestamp. */ @@ -55,6 +57,7 @@ public static function fromArray(array $data): self organizationId: $data['organization_id'] ?? null, inviterUserId: $data['inviter_user_id'] ?? null, acceptedUserId: $data['accepted_user_id'] ?? null, + roleSlug: $data['role_slug'] ?? null, createdAt: new \DateTimeImmutable($data['created_at']), updatedAt: new \DateTimeImmutable($data['updated_at']), token: $data['token'], @@ -75,6 +78,7 @@ public function toArray(): array 'organization_id' => $this->organizationId, 'inviter_user_id' => $this->inviterUserId, 'accepted_user_id' => $this->acceptedUserId, + 'role_slug' => $this->roleSlug, 'created_at' => $this->createdAt->format(\DateTimeInterface::RFC3339_EXTENDED), 'updated_at' => $this->updatedAt->format(\DateTimeInterface::RFC3339_EXTENDED), 'token' => $this->token, diff --git a/lib/Resource/InvitationAcceptedData.php b/lib/Resource/InvitationAcceptedData.php index 37ab7c6d..656054c1 100644 --- a/lib/Resource/InvitationAcceptedData.php +++ b/lib/Resource/InvitationAcceptedData.php @@ -32,6 +32,8 @@ public function __construct( public ?string $inviterUserId, /** The ID of the user who accepted the invitation, once accepted. */ public ?string $acceptedUserId, + /** 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. */ + public ?string $roleSlug, /** An ISO 8601 timestamp. */ public \DateTimeImmutable $createdAt, /** An ISO 8601 timestamp. */ @@ -52,6 +54,7 @@ public static function fromArray(array $data): self organizationId: $data['organization_id'] ?? null, inviterUserId: $data['inviter_user_id'] ?? null, acceptedUserId: $data['accepted_user_id'] ?? null, + roleSlug: $data['role_slug'] ?? null, createdAt: new \DateTimeImmutable($data['created_at']), updatedAt: new \DateTimeImmutable($data['updated_at']), ); @@ -70,6 +73,7 @@ public function toArray(): array 'organization_id' => $this->organizationId, 'inviter_user_id' => $this->inviterUserId, 'accepted_user_id' => $this->acceptedUserId, + 'role_slug' => $this->roleSlug, 'created_at' => $this->createdAt->format(\DateTimeInterface::RFC3339_EXTENDED), 'updated_at' => $this->updatedAt->format(\DateTimeInterface::RFC3339_EXTENDED), ]; diff --git a/lib/Resource/InvitationCreatedData.php b/lib/Resource/InvitationCreatedData.php index b6e91def..a5b8ffa5 100644 --- a/lib/Resource/InvitationCreatedData.php +++ b/lib/Resource/InvitationCreatedData.php @@ -32,6 +32,8 @@ public function __construct( public ?string $inviterUserId, /** The ID of the user who accepted the invitation, once accepted. */ public ?string $acceptedUserId, + /** 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. */ + public ?string $roleSlug, /** An ISO 8601 timestamp. */ public \DateTimeImmutable $createdAt, /** An ISO 8601 timestamp. */ @@ -52,6 +54,7 @@ public static function fromArray(array $data): self organizationId: $data['organization_id'] ?? null, inviterUserId: $data['inviter_user_id'] ?? null, acceptedUserId: $data['accepted_user_id'] ?? null, + roleSlug: $data['role_slug'] ?? null, createdAt: new \DateTimeImmutable($data['created_at']), updatedAt: new \DateTimeImmutable($data['updated_at']), ); @@ -70,6 +73,7 @@ public function toArray(): array 'organization_id' => $this->organizationId, 'inviter_user_id' => $this->inviterUserId, 'accepted_user_id' => $this->acceptedUserId, + 'role_slug' => $this->roleSlug, 'created_at' => $this->createdAt->format(\DateTimeInterface::RFC3339_EXTENDED), 'updated_at' => $this->updatedAt->format(\DateTimeInterface::RFC3339_EXTENDED), ]; diff --git a/lib/Resource/InvitationResentData.php b/lib/Resource/InvitationResentData.php index 6f47bc4f..a68efa67 100644 --- a/lib/Resource/InvitationResentData.php +++ b/lib/Resource/InvitationResentData.php @@ -32,6 +32,8 @@ public function __construct( public ?string $inviterUserId, /** The ID of the user who accepted the invitation, once accepted. */ public ?string $acceptedUserId, + /** 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. */ + public ?string $roleSlug, /** An ISO 8601 timestamp. */ public \DateTimeImmutable $createdAt, /** An ISO 8601 timestamp. */ @@ -52,6 +54,7 @@ public static function fromArray(array $data): self organizationId: $data['organization_id'] ?? null, inviterUserId: $data['inviter_user_id'] ?? null, acceptedUserId: $data['accepted_user_id'] ?? null, + roleSlug: $data['role_slug'] ?? null, createdAt: new \DateTimeImmutable($data['created_at']), updatedAt: new \DateTimeImmutable($data['updated_at']), ); @@ -70,6 +73,7 @@ public function toArray(): array 'organization_id' => $this->organizationId, 'inviter_user_id' => $this->inviterUserId, 'accepted_user_id' => $this->acceptedUserId, + 'role_slug' => $this->roleSlug, 'created_at' => $this->createdAt->format(\DateTimeInterface::RFC3339_EXTENDED), 'updated_at' => $this->updatedAt->format(\DateTimeInterface::RFC3339_EXTENDED), ]; diff --git a/lib/Resource/InvitationRevokedData.php b/lib/Resource/InvitationRevokedData.php index e5649273..3760fdbb 100644 --- a/lib/Resource/InvitationRevokedData.php +++ b/lib/Resource/InvitationRevokedData.php @@ -32,6 +32,8 @@ public function __construct( public ?string $inviterUserId, /** The ID of the user who accepted the invitation, once accepted. */ public ?string $acceptedUserId, + /** 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. */ + public ?string $roleSlug, /** An ISO 8601 timestamp. */ public \DateTimeImmutable $createdAt, /** An ISO 8601 timestamp. */ @@ -52,6 +54,7 @@ public static function fromArray(array $data): self organizationId: $data['organization_id'] ?? null, inviterUserId: $data['inviter_user_id'] ?? null, acceptedUserId: $data['accepted_user_id'] ?? null, + roleSlug: $data['role_slug'] ?? null, createdAt: new \DateTimeImmutable($data['created_at']), updatedAt: new \DateTimeImmutable($data['updated_at']), ); @@ -70,6 +73,7 @@ public function toArray(): array 'organization_id' => $this->organizationId, 'inviter_user_id' => $this->inviterUserId, 'accepted_user_id' => $this->acceptedUserId, + 'role_slug' => $this->roleSlug, 'created_at' => $this->createdAt->format(\DateTimeInterface::RFC3339_EXTENDED), 'updated_at' => $this->updatedAt->format(\DateTimeInterface::RFC3339_EXTENDED), ]; diff --git a/lib/Resource/SSOProvider.php b/lib/Resource/SSOProvider.php index 619f93c0..e98386c9 100644 --- a/lib/Resource/SSOProvider.php +++ b/lib/Resource/SSOProvider.php @@ -9,7 +9,16 @@ enum SSOProvider: string { case AppleOAuth = 'AppleOAuth'; + case BitbucketOAuth = 'BitbucketOAuth'; case GitHubOAuth = 'GitHubOAuth'; + case GitLabOAuth = 'GitLabOAuth'; case GoogleOAuth = 'GoogleOAuth'; + case IntuitOAuth = 'IntuitOAuth'; + case LinkedInOAuth = 'LinkedInOAuth'; case MicrosoftOAuth = 'MicrosoftOAuth'; + case SalesforceOAuth = 'SalesforceOAuth'; + case SlackOAuth = 'SlackOAuth'; + case VercelMarketplaceOAuth = 'VercelMarketplaceOAuth'; + case VercelOAuth = 'VercelOAuth'; + case XeroOAuth = 'XeroOAuth'; } diff --git a/lib/Resource/UpdateGroup.php b/lib/Resource/UpdateGroup.php new file mode 100644 index 00000000..10371c38 --- /dev/null +++ b/lib/Resource/UpdateGroup.php @@ -0,0 +1,36 @@ + $this->name, + 'description' => $this->description, + ]; + } +} diff --git a/lib/Resource/UserInvite.php b/lib/Resource/UserInvite.php index e00d4d49..f9fc9768 100644 --- a/lib/Resource/UserInvite.php +++ b/lib/Resource/UserInvite.php @@ -31,6 +31,8 @@ public function __construct( public ?string $inviterUserId, /** The ID of the user who accepted the invitation, once accepted. */ public ?string $acceptedUserId, + /** 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. */ + public ?string $roleSlug, /** An ISO 8601 timestamp. */ public \DateTimeImmutable $createdAt, /** An ISO 8601 timestamp. */ @@ -55,6 +57,7 @@ public static function fromArray(array $data): self organizationId: $data['organization_id'] ?? null, inviterUserId: $data['inviter_user_id'] ?? null, acceptedUserId: $data['accepted_user_id'] ?? null, + roleSlug: $data['role_slug'] ?? null, createdAt: new \DateTimeImmutable($data['created_at']), updatedAt: new \DateTimeImmutable($data['updated_at']), token: $data['token'], @@ -75,6 +78,7 @@ public function toArray(): array 'organization_id' => $this->organizationId, 'inviter_user_id' => $this->inviterUserId, 'accepted_user_id' => $this->acceptedUserId, + 'role_slug' => $this->roleSlug, 'created_at' => $this->createdAt->format(\DateTimeInterface::RFC3339_EXTENDED), 'updated_at' => $this->updatedAt->format(\DateTimeInterface::RFC3339_EXTENDED), 'token' => $this->token, diff --git a/lib/Resource/UserManagementAuthenticationProvider.php b/lib/Resource/UserManagementAuthenticationProvider.php index e0c480a9..70d33e38 100644 --- a/lib/Resource/UserManagementAuthenticationProvider.php +++ b/lib/Resource/UserManagementAuthenticationProvider.php @@ -10,7 +10,16 @@ enum UserManagementAuthenticationProvider: string { case Authkit = 'authkit'; case AppleOAuth = 'AppleOAuth'; + case BitbucketOAuth = 'BitbucketOAuth'; case GitHubOAuth = 'GitHubOAuth'; + case GitLabOAuth = 'GitLabOAuth'; case GoogleOAuth = 'GoogleOAuth'; + case IntuitOAuth = 'IntuitOAuth'; + case LinkedInOAuth = 'LinkedInOAuth'; case MicrosoftOAuth = 'MicrosoftOAuth'; + case SalesforceOAuth = 'SalesforceOAuth'; + case SlackOAuth = 'SlackOAuth'; + case VercelMarketplaceOAuth = 'VercelMarketplaceOAuth'; + case VercelOAuth = 'VercelOAuth'; + case XeroOAuth = 'XeroOAuth'; } diff --git a/lib/Resource/WaitlistUser.php b/lib/Resource/WaitlistUser.php new file mode 100644 index 00000000..aa1041b7 --- /dev/null +++ b/lib/Resource/WaitlistUser.php @@ -0,0 +1,56 @@ + $this->object, + 'id' => $this->id, + 'email' => $this->email, + 'state' => $this->state->value, + 'approved_at' => $this->approvedAt?->format(\DateTimeInterface::RFC3339_EXTENDED), + 'created_at' => $this->createdAt->format(\DateTimeInterface::RFC3339_EXTENDED), + 'updated_at' => $this->updatedAt->format(\DateTimeInterface::RFC3339_EXTENDED), + ]; + } +} diff --git a/lib/Resource/WaitlistUserApproved.php b/lib/Resource/WaitlistUserApproved.php new file mode 100644 index 00000000..e68e00ab --- /dev/null +++ b/lib/Resource/WaitlistUserApproved.php @@ -0,0 +1,50 @@ + $this->id, + 'event' => $this->event, + 'data' => $this->data->toArray(), + 'created_at' => $this->createdAt->format(\DateTimeInterface::RFC3339_EXTENDED), + 'object' => $this->object, + 'context' => $this->context?->toArray(), + ]; + } +} diff --git a/lib/Resource/WaitlistUserCreated.php b/lib/Resource/WaitlistUserCreated.php new file mode 100644 index 00000000..21c4f509 --- /dev/null +++ b/lib/Resource/WaitlistUserCreated.php @@ -0,0 +1,50 @@ + $this->id, + 'event' => $this->event, + 'data' => $this->data->toArray(), + 'created_at' => $this->createdAt->format(\DateTimeInterface::RFC3339_EXTENDED), + 'object' => $this->object, + 'context' => $this->context?->toArray(), + ]; + } +} diff --git a/lib/Resource/WaitlistUserDenied.php b/lib/Resource/WaitlistUserDenied.php new file mode 100644 index 00000000..fc4e082e --- /dev/null +++ b/lib/Resource/WaitlistUserDenied.php @@ -0,0 +1,50 @@ + $this->id, + 'event' => $this->event, + 'data' => $this->data->toArray(), + 'created_at' => $this->createdAt->format(\DateTimeInterface::RFC3339_EXTENDED), + 'object' => $this->object, + 'context' => $this->context?->toArray(), + ]; + } +} diff --git a/lib/Resource/WaitlistUserState.php b/lib/Resource/WaitlistUserState.php new file mode 100644 index 00000000..8f44f037 --- /dev/null +++ b/lib/Resource/WaitlistUserState.php @@ -0,0 +1,14 @@ +|null $adminEmails The email addresses of the IT admins to grant access to the Admin Portal for the given organization. Accepts up to 20 emails. + * @param array|null $itContactEmails The email addresses of the IT contacts to grant access to the Admin Portal for the given organization. Accepts up to 20 emails. * @return \WorkOS\Resource\PortalLinkResponse * @throws \WorkOS\Exception\WorkOSException */ @@ -42,7 +42,7 @@ public function generateLink( ?string $successUrl = null, ?\WorkOS\Resource\GenerateLinkIntent $intent = null, ?\WorkOS\Resource\IntentOptions $intentOptions = null, - ?array $adminEmails = null, + ?array $itContactEmails = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\PortalLinkResponse { $body = array_filter([ @@ -51,7 +51,7 @@ public function generateLink( 'organization' => $organization, 'intent' => $intent?->value, 'intent_options' => $intentOptions, - 'admin_emails' => $adminEmails, + 'it_contact_emails' => $itContactEmails, ], fn ($v) => $v !== null); $response = $this->client->request( method: 'POST', diff --git a/lib/Service/Authorization.php b/lib/Service/Authorization.php index 1e596ae5..2890f35e 100644 --- a/lib/Service/Authorization.php +++ b/lib/Service/Authorization.php @@ -28,9 +28,6 @@ public function __construct( * Check if an organization membership has a specific permission on a resource. Supports identification by resource_id OR by resource_external_id + resource_type_slug. * @param string $organizationMembershipId The ID of the organization membership to check. * @param string $permissionSlug The slug of the permission to check. - * @param string|null $resourceId The ID of the resource. Mutually exclusive with `resource_external_id` and `resource_type_slug`. - * @param string|null $resourceExternalId The external ID of the resource. Required with `resource_type_slug`. Mutually exclusive with `resource_id`. - * @param string|null $resourceTypeSlug The slug of the resource type. Required with `resource_external_id`. Mutually exclusive with `resource_id`. * @param ResourceTargetById|ResourceTargetByExternalId $resourceTarget * @return \WorkOS\Resource\AuthorizationCheck * @throws \WorkOS\Exception\WorkOSException @@ -39,17 +36,18 @@ public function check( string $organizationMembershipId, string $permissionSlug, ResourceTargetById|ResourceTargetByExternalId $resourceTarget, - ?string $resourceId = null, - ?string $resourceExternalId = null, - ?string $resourceTypeSlug = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\AuthorizationCheck { - $body = array_filter([ + $body = [ 'permission_slug' => $permissionSlug, - 'resource_id' => $resourceId, - 'resource_external_id' => $resourceExternalId, - 'resource_type_slug' => $resourceTypeSlug, - ], fn ($v) => $v !== null); + ]; + if ($resourceTarget instanceof ResourceTargetById) { + $body['resource_id'] = $resourceTarget->resourceId; + } + elseif ($resourceTarget instanceof ResourceTargetByExternalId) { + $body['resource_external_id'] = $resourceTarget->resourceExternalId; + $body['resource_type_slug'] = $resourceTarget->resourceTypeSlug; + } $response = $this->client->request( method: 'POST', path: "authorization/organization_memberships/{$organizationMembershipId}/check", @@ -75,7 +73,7 @@ public function check( * @return \WorkOS\PaginatedResponse<\WorkOS\Resource\AuthorizationResource> * @throws \WorkOS\Exception\WorkOSException */ - public function listOrganizationMembershipResources( + public function listResourcesForMembership( string $organizationMembershipId, ParentResourceById|ParentResourceByExternalId $parentResource, string $permissionSlug, @@ -94,7 +92,8 @@ public function listOrganizationMembershipResources( ], fn ($v) => $v !== null); if ($parentResource instanceof ParentResourceById) { $query['parent_resource_id'] = $parentResource->id; - } elseif ($parentResource instanceof ParentResourceByExternalId) { + } + elseif ($parentResource instanceof ParentResourceByExternalId) { $query['parent_resource_type_slug'] = $parentResource->typeSlug; $query['parent_resource_external_id'] = $parentResource->externalId; } @@ -120,7 +119,7 @@ public function listOrganizationMembershipResources( * @return \WorkOS\PaginatedResponse<\WorkOS\Resource\AuthorizationPermission> * @throws \WorkOS\Exception\WorkOSException */ - public function listResourcePermissions( + public function listEffectivePermissions( string $organizationMembershipId, string $resourceId, ?string $before = null, @@ -195,7 +194,7 @@ public function listEffectivePermissionsByExternalId( * @return \WorkOS\PaginatedResponse<\WorkOS\Resource\RoleAssignment> * @throws \WorkOS\Exception\WorkOSException */ - public function listOrganizationMembershipRoleAssignments( + public function listRoleAssignments( string $organizationMembershipId, ?string $before = null, ?string $after = null, @@ -224,9 +223,6 @@ public function listOrganizationMembershipRoleAssignments( * Assign a role to an organization membership on a specific resource. * @param string $organizationMembershipId The ID of the organization membership. * @param string $roleSlug The slug of the role to assign. - * @param string|null $resourceId The ID of the resource. Mutually exclusive with `resource_external_id` and `resource_type_slug`. - * @param string|null $resourceExternalId The external ID of the resource. Required with `resource_type_slug`. Mutually exclusive with `resource_id`. - * @param string|null $resourceTypeSlug The resource type slug. Required with `resource_external_id`. Mutually exclusive with `resource_id`. * @param ResourceTargetById|ResourceTargetByExternalId $resourceTarget * @return \WorkOS\Resource\RoleAssignment * @throws \WorkOS\Exception\WorkOSException @@ -235,17 +231,18 @@ public function assignRole( string $organizationMembershipId, string $roleSlug, ResourceTargetById|ResourceTargetByExternalId $resourceTarget, - ?string $resourceId = null, - ?string $resourceExternalId = null, - ?string $resourceTypeSlug = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\RoleAssignment { - $body = array_filter([ + $body = [ 'role_slug' => $roleSlug, - 'resource_id' => $resourceId, - 'resource_external_id' => $resourceExternalId, - 'resource_type_slug' => $resourceTypeSlug, - ], fn ($v) => $v !== null); + ]; + if ($resourceTarget instanceof ResourceTargetById) { + $body['resource_id'] = $resourceTarget->resourceId; + } + elseif ($resourceTarget instanceof ResourceTargetByExternalId) { + $body['resource_external_id'] = $resourceTarget->resourceExternalId; + $body['resource_type_slug'] = $resourceTarget->resourceTypeSlug; + } $response = $this->client->request( method: 'POST', path: "authorization/organization_memberships/{$organizationMembershipId}/role_assignments", @@ -261,9 +258,6 @@ public function assignRole( * Remove a role assignment by role slug and resource. * @param string $organizationMembershipId The ID of the organization membership. * @param string $roleSlug The slug of the role to remove. - * @param string|null $resourceId The ID of the resource. Mutually exclusive with `resource_external_id` and `resource_type_slug`. - * @param string|null $resourceExternalId The external ID of the resource. Required with `resource_type_slug`. Mutually exclusive with `resource_id`. - * @param string|null $resourceTypeSlug The resource type slug. Required with `resource_external_id`. Mutually exclusive with `resource_id`. * @param ResourceTargetById|ResourceTargetByExternalId $resourceTarget * @return void * @throws \WorkOS\Exception\WorkOSException @@ -272,17 +266,18 @@ public function removeRole( string $organizationMembershipId, string $roleSlug, ResourceTargetById|ResourceTargetByExternalId $resourceTarget, - ?string $resourceId = null, - ?string $resourceExternalId = null, - ?string $resourceTypeSlug = null, ?\WorkOS\RequestOptions $options = null, ): void { - $body = array_filter([ + $body = [ 'role_slug' => $roleSlug, - 'resource_id' => $resourceId, - 'resource_external_id' => $resourceExternalId, - 'resource_type_slug' => $resourceTypeSlug, - ], fn ($v) => $v !== null); + ]; + if ($resourceTarget instanceof ResourceTargetById) { + $body['resource_id'] = $resourceTarget->resourceId; + } + elseif ($resourceTarget instanceof ResourceTargetByExternalId) { + $body['resource_external_id'] = $resourceTarget->resourceExternalId; + $body['resource_type_slug'] = $resourceTarget->resourceTypeSlug; + } $this->client->request( method: 'DELETE', path: "authorization/organization_memberships/{$organizationMembershipId}/role_assignments", @@ -300,7 +295,7 @@ public function removeRole( * @return void * @throws \WorkOS\Exception\WorkOSException */ - public function deleteOrganizationMembershipRoleAssignment( + public function removeRoleAssignment( string $organizationMembershipId, string $roleAssignmentId, ?\WorkOS\RequestOptions $options = null, @@ -451,7 +446,7 @@ public function deleteOrganizationRole( * @return \WorkOS\Resource\Role * @throws \WorkOS\Exception\WorkOSException */ - public function createRolePermission( + public function addOrganizationRolePermission( string $organizationId, string $slug, string $bodySlug, @@ -479,7 +474,7 @@ public function createRolePermission( * @return \WorkOS\Resource\Role * @throws \WorkOS\Exception\WorkOSException */ - public function updateRolePermissions( + public function setOrganizationRolePermissions( string $organizationId, string $slug, array $permissions, @@ -507,7 +502,7 @@ public function updateRolePermissions( * @return void * @throws \WorkOS\Exception\WorkOSException */ - public function deleteRolePermission( + public function removeOrganizationRolePermission( string $organizationId, string $slug, string $permissionSlug, @@ -530,7 +525,7 @@ public function deleteRolePermission( * @return \WorkOS\Resource\AuthorizationResource * @throws \WorkOS\Exception\WorkOSException */ - public function getOrganizationResource( + public function getResourceByExternalId( string $organizationId, string $resourceTypeSlug, string $externalId, @@ -553,32 +548,30 @@ public function getOrganizationResource( * @param string $externalId An identifier you provide to reference the resource in your system. * @param string|null $name A display name for the resource. * @param string|null $description An optional description of the resource. - * @param string|null $parentResourceId The ID of the parent resource. Mutually exclusive with `parent_resource_external_id` and `parent_resource_type_slug`. - * @param string|null $parentResourceExternalId The external ID of the parent resource. Required with `parent_resource_type_slug`. Mutually exclusive with `parent_resource_id`. - * @param string|null $parentResourceTypeSlug The resource type slug of the parent resource. Required with `parent_resource_external_id`. Mutually exclusive with `parent_resource_id`. * @param null|ParentResourceById|ParentResourceByExternalId $parentResource * @return \WorkOS\Resource\AuthorizationResource * @throws \WorkOS\Exception\WorkOSException */ - public function updateOrganizationResource( + public function updateResourceByExternalId( string $organizationId, string $resourceTypeSlug, string $externalId, ?string $name = null, ?string $description = null, - ?string $parentResourceId = null, - ?string $parentResourceExternalId = null, - ?string $parentResourceTypeSlug = null, null|ParentResourceById|ParentResourceByExternalId $parentResource = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\AuthorizationResource { $body = array_filter([ 'name' => $name, 'description' => $description, - 'parent_resource_id' => $parentResourceId, - 'parent_resource_external_id' => $parentResourceExternalId, - 'parent_resource_type_slug' => $parentResourceTypeSlug, ], fn ($v) => $v !== null); + if ($parentResource instanceof ParentResourceById) { + $body['parent_resource_id'] = $parentResource->id; + } + elseif ($parentResource instanceof ParentResourceByExternalId) { + $body['parent_resource_external_id'] = $parentResource->externalId; + $body['parent_resource_type_slug'] = $parentResource->typeSlug; + } $response = $this->client->request( method: 'PATCH', path: "authorization/organizations/{$organizationId}/resources/{$resourceTypeSlug}/{$externalId}", @@ -599,7 +592,7 @@ public function updateOrganizationResource( * @return void * @throws \WorkOS\Exception\WorkOSException */ - public function deleteOrganizationResource( + public function deleteResourceByExternalId( string $organizationId, string $resourceTypeSlug, string $externalId, @@ -633,7 +626,7 @@ public function deleteOrganizationResource( * @return \WorkOS\PaginatedResponse<\WorkOS\Resource\UserOrganizationMembershipBaseListData> * @throws \WorkOS\Exception\WorkOSException */ - public function listResourceOrganizationMemberships( + public function listMembershipsForResourceByExternalId( string $organizationId, string $resourceTypeSlug, string $externalId, @@ -673,6 +666,7 @@ public function listResourceOrganizationMemberships( * @param \WorkOS\Resource\EventsOrder $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". * @param string|null $organizationId Filter resources by organization ID. * @param string|null $resourceTypeSlug Filter resources by resource type slug. + * @param string|null $resourceExternalId Filter resources by external ID. * @param string|null $search Search resources by name. * @return \WorkOS\PaginatedResponse<\WorkOS\Resource\AuthorizationResource> * @throws \WorkOS\Exception\WorkOSException @@ -685,6 +679,7 @@ public function listResources( \WorkOS\Resource\EventsOrder $order = \WorkOS\Resource\EventsOrder::Desc, ?string $organizationId = null, ?string $resourceTypeSlug = null, + ?string $resourceExternalId = null, ?string $search = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\PaginatedResponse { @@ -695,11 +690,13 @@ public function listResources( 'order' => $order->value, 'organization_id' => $organizationId, 'resource_type_slug' => $resourceTypeSlug, + 'resource_external_id' => $resourceExternalId, 'search' => $search, ], fn ($v) => $v !== null); if ($parent instanceof ParentById) { $query['parent_resource_id'] = $parent->resourceId; - } elseif ($parent instanceof ParentByExternalId) { + } + elseif ($parent instanceof ParentByExternalId) { $query['parent_resource_type_slug'] = $parent->resourceTypeSlug; $query['parent_external_id'] = $parent->externalId; } @@ -721,9 +718,6 @@ public function listResources( * @param string|null $description An optional description of the resource. * @param string $resourceTypeSlug The slug of the resource type. * @param string $organizationId The ID of the organization this resource belongs to. - * @param string|null $parentResourceId The ID of the parent resource. Mutually exclusive with `parent_resource_external_id` and `parent_resource_type_slug`. - * @param string|null $parentResourceExternalId The external ID of the parent resource. Required with `parent_resource_type_slug`. Mutually exclusive with `parent_resource_id`. - * @param string|null $parentResourceTypeSlug The resource type slug of the parent resource. Required with `parent_resource_external_id`. Mutually exclusive with `parent_resource_id`. * @param null|ParentResourceById|ParentResourceByExternalId $parentResource * @return \WorkOS\Resource\AuthorizationResource * @throws \WorkOS\Exception\WorkOSException @@ -734,9 +728,6 @@ public function createResource( string $resourceTypeSlug, string $organizationId, ?string $description = null, - ?string $parentResourceId = null, - ?string $parentResourceExternalId = null, - ?string $parentResourceTypeSlug = null, null|ParentResourceById|ParentResourceByExternalId $parentResource = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\AuthorizationResource { @@ -746,10 +737,14 @@ public function createResource( 'description' => $description, 'resource_type_slug' => $resourceTypeSlug, 'organization_id' => $organizationId, - 'parent_resource_id' => $parentResourceId, - 'parent_resource_external_id' => $parentResourceExternalId, - 'parent_resource_type_slug' => $parentResourceTypeSlug, ], fn ($v) => $v !== null); + if ($parentResource instanceof ParentResourceById) { + $body['parent_resource_id'] = $parentResource->id; + } + elseif ($parentResource instanceof ParentResourceByExternalId) { + $body['parent_resource_external_id'] = $parentResource->externalId; + $body['parent_resource_type_slug'] = $parentResource->typeSlug; + } $response = $this->client->request( method: 'POST', path: 'authorization/resources', @@ -786,9 +781,6 @@ public function getResource( * @param string $resourceId The ID of the authorization resource. * @param string|null $name A display name for the resource. * @param string|null $description An optional description of the resource. - * @param string|null $parentResourceId The ID of the parent resource. Mutually exclusive with `parent_resource_external_id` and `parent_resource_type_slug`. - * @param string|null $parentResourceExternalId The external ID of the parent resource. Required with `parent_resource_type_slug`. Mutually exclusive with `parent_resource_id`. - * @param string|null $parentResourceTypeSlug The resource type slug of the parent resource. Required with `parent_resource_external_id`. Mutually exclusive with `parent_resource_id`. * @param null|ParentResourceById|ParentResourceByExternalId $parentResource * @return \WorkOS\Resource\AuthorizationResource * @throws \WorkOS\Exception\WorkOSException @@ -797,19 +789,20 @@ public function updateResource( string $resourceId, ?string $name = null, ?string $description = null, - ?string $parentResourceId = null, - ?string $parentResourceExternalId = null, - ?string $parentResourceTypeSlug = null, null|ParentResourceById|ParentResourceByExternalId $parentResource = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\AuthorizationResource { $body = array_filter([ 'name' => $name, 'description' => $description, - 'parent_resource_id' => $parentResourceId, - 'parent_resource_external_id' => $parentResourceExternalId, - 'parent_resource_type_slug' => $parentResourceTypeSlug, ], fn ($v) => $v !== null); + if ($parentResource instanceof ParentResourceById) { + $body['parent_resource_id'] = $parentResource->id; + } + elseif ($parentResource instanceof ParentResourceByExternalId) { + $body['parent_resource_external_id'] = $parentResource->externalId; + $body['parent_resource_type_slug'] = $parentResource->typeSlug; + } $response = $this->client->request( method: 'PATCH', path: "authorization/resources/{$resourceId}", diff --git a/lib/Service/Groups.php b/lib/Service/Groups.php new file mode 100644 index 00000000..519d2bdd --- /dev/null +++ b/lib/Service/Groups.php @@ -0,0 +1,244 @@ + + * @throws \WorkOS\Exception\WorkOSException + */ + public function listOrganizationGroups( + string $organizationId, + ?string $before = null, + ?string $after = null, + ?int $limit = null, + \WorkOS\Resource\EventsOrder $order = \WorkOS\Resource\EventsOrder::Desc, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\PaginatedResponse { + $query = array_filter([ + 'before' => $before, + 'after' => $after, + 'limit' => $limit, + 'order' => $order->value, + ], fn ($v) => $v !== null); + return $this->client->requestPage( + method: 'GET', + path: "organizations/{$organizationId}/groups", + query: $query, + modelClass: Group::class, + options: $options, + ); + } + + /** + * Create a group + * + * Create a new group within an organization. + * @param string $organizationId The ID of the organization. + * @param string $name The name of the Group. + * @param string|null $description An optional description of the Group. + * @return \WorkOS\Resource\Group + * @throws \WorkOS\Exception\WorkOSException + */ + public function createOrganizationGroup( + string $organizationId, + string $name, + ?string $description = null, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\Resource\Group { + $body = array_filter([ + 'name' => $name, + 'description' => $description, + ], fn ($v) => $v !== null); + $response = $this->client->request( + method: 'POST', + path: "organizations/{$organizationId}/groups", + body: $body, + options: $options, + ); + return Group::fromArray($response); + } + + /** + * Get a group + * + * Retrieve a group by its ID within an organization. + * @param string $organizationId The ID of the organization. + * @param string $groupId The ID of the group. + * @return \WorkOS\Resource\Group + * @throws \WorkOS\Exception\WorkOSException + */ + public function getOrganizationGroup( + string $organizationId, + string $groupId, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\Resource\Group { + $response = $this->client->request( + method: 'GET', + path: "organizations/{$organizationId}/groups/{$groupId}", + options: $options, + ); + return Group::fromArray($response); + } + + /** + * Update a group + * + * Update an existing group. Only the fields provided in the request body will be updated. + * @param string $organizationId The ID of the organization. + * @param string $groupId The ID of the group. + * @param string|null $name The name of the Group. + * @param string|null $description An optional description of the Group. + * @return \WorkOS\Resource\Group + * @throws \WorkOS\Exception\WorkOSException + */ + public function updateOrganizationGroup( + string $organizationId, + string $groupId, + ?string $name = null, + ?string $description = null, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\Resource\Group { + $body = array_filter([ + 'name' => $name, + 'description' => $description, + ], fn ($v) => $v !== null); + $response = $this->client->request( + method: 'PATCH', + path: "organizations/{$organizationId}/groups/{$groupId}", + body: $body, + options: $options, + ); + return Group::fromArray($response); + } + + /** + * Delete a group + * + * Delete a group from an organization. + * @param string $organizationId The ID of the organization. + * @param string $groupId The ID of the group. + * @return void + * @throws \WorkOS\Exception\WorkOSException + */ + public function deleteOrganizationGroup( + string $organizationId, + string $groupId, + ?\WorkOS\RequestOptions $options = null, + ): void { + $this->client->request( + method: 'DELETE', + path: "organizations/{$organizationId}/groups/{$groupId}", + options: $options, + ); + } + + /** + * List Group members + * + * Get a list of organization memberships in a group. + * @param string $organizationId Unique identifier of the Organization. + * @param string $groupId Unique identifier of the Group. + * @param string|null $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"`. + * @param string|null $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"`. + * @param int|null $limit Upper limit on the number of objects to return, between `1` and `100`. Defaults to 10. + * @param \WorkOS\Resource\EventsOrder $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". + * @return \WorkOS\PaginatedResponse<\WorkOS\Resource\UserOrganizationMembershipBaseListData> + * @throws \WorkOS\Exception\WorkOSException + */ + public function listGroupOrganizationMemberships( + string $organizationId, + string $groupId, + ?string $before = null, + ?string $after = null, + ?int $limit = null, + \WorkOS\Resource\EventsOrder $order = \WorkOS\Resource\EventsOrder::Desc, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\PaginatedResponse { + $query = array_filter([ + 'before' => $before, + 'after' => $after, + 'limit' => $limit, + 'order' => $order->value, + ], fn ($v) => $v !== null); + return $this->client->requestPage( + method: 'GET', + path: "organizations/{$organizationId}/groups/{$groupId}/organization-memberships", + query: $query, + modelClass: UserOrganizationMembershipBaseListData::class, + options: $options, + ); + } + + /** + * Add a member to a Group + * + * Add an organization membership to a group. + * @param string $organizationId Unique identifier of the Organization. + * @param string $groupId Unique identifier of the Group. + * @param string $organizationMembershipId The ID of the Organization Membership to add to the group. + * @return \WorkOS\Resource\Group + * @throws \WorkOS\Exception\WorkOSException + */ + public function createGroupOrganizationMembership( + string $organizationId, + string $groupId, + string $organizationMembershipId, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\Resource\Group { + $body = [ + 'organization_membership_id' => $organizationMembershipId, + ]; + $response = $this->client->request( + method: 'POST', + path: "organizations/{$organizationId}/groups/{$groupId}/organization-memberships", + body: $body, + options: $options, + ); + return Group::fromArray($response); + } + + /** + * Remove a member from a Group + * + * Remove an organization membership from a group. + * @param string $organizationId Unique identifier of the Organization. + * @param string $groupId Unique identifier of the Group. + * @param string $omId Unique identifier of the Organization Membership. + * @return void + * @throws \WorkOS\Exception\WorkOSException + */ + public function deleteGroupOrganizationMembership( + string $organizationId, + string $groupId, + string $omId, + ?\WorkOS\RequestOptions $options = null, + ): void { + $this->client->request( + method: 'DELETE', + path: "organizations/{$organizationId}/groups/{$groupId}/organization-memberships/{$omId}", + options: $options, + ); + } +} diff --git a/lib/Service/SSO.php b/lib/Service/SSO.php index 4c9a8445..6c7ded53 100644 --- a/lib/Service/SSO.php +++ b/lib/Service/SSO.php @@ -109,7 +109,7 @@ public function deleteConnection( * @param array|null $providerScopes Additional scopes to request from the identity provider. Applicable when using OAuth or OpenID Connect connections. * @param array|null $providerQueryParams Key/value pairs of query parameters to pass to the OAuth provider. Only applicable when using OAuth connections. * @param string|null $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. - * @param \WorkOS\Resource\SSOProvider|null $provider Used to initiate OAuth authentication with Google, Microsoft, GitHub, or Apple. + * @param \WorkOS\Resource\SSOProvider|null $provider Used to initiate OAuth authentication with various providers. * @param string $redirectUri 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. * @param string|null $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. * @param string|null $connection Used to initiate SSO for a connection. The value should be a WorkOS connection ID. diff --git a/lib/Service/UserManagement.php b/lib/Service/UserManagement.php index 020f819b..05740b61 100644 --- a/lib/Service/UserManagement.php +++ b/lib/Service/UserManagement.php @@ -657,9 +657,7 @@ public function listUsers( * @param bool|null $emailVerified Whether the user's email has been verified. * @param array|null $metadata Object containing metadata key/value pairs associated with the user. * @param string|null $externalId The external ID of the user. - * @param string|null $password The password to set for the user. Mutually exclusive with `password_hash` and `password_hash_type`. - * @param string|null $passwordHash The hashed password to set for the user. Required with `password_hash_type`. Mutually exclusive with `password`. - * @param \WorkOS\Resource\CreateUserPasswordHashType|null $passwordHashType The algorithm originally used to hash the password, used when providing a `password_hash`. Required with `password_hash`. Mutually exclusive with `password`. + * @param null|PasswordPlaintext|PasswordHashed $password * @return \WorkOS\Resource\User * @throws \WorkOS\Exception\WorkOSException */ @@ -670,9 +668,7 @@ public function createUser( ?bool $emailVerified = null, ?array $metadata = null, ?string $externalId = null, - ?string $password = null, - ?string $passwordHash = null, - ?\WorkOS\Resource\CreateUserPasswordHashType $passwordHashType = null, + null|PasswordPlaintext|PasswordHashed $password = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\User { $body = array_filter([ @@ -682,10 +678,14 @@ public function createUser( 'email_verified' => $emailVerified, 'metadata' => $metadata, 'external_id' => $externalId, - 'password' => $password, - 'password_hash' => $passwordHash, - 'password_hash_type' => $passwordHashType?->value, ], fn ($v) => $v !== null); + if ($password instanceof PasswordPlaintext) { + $body['password'] = $password->password; + } + elseif ($password instanceof PasswordHashed) { + $body['password_hash'] = $password->hash; + $body['password_hash_type'] = $password->hashType; + } $response = $this->client->request( method: 'POST', path: 'user_management/users', @@ -747,9 +747,7 @@ public function getUser( * @param array|null $metadata Object containing metadata key/value pairs associated with the user. * @param string|null $externalId The external ID of the user. * @param string|null $locale The user's preferred locale. - * @param string|null $password The password to set for the user. Mutually exclusive with `password_hash` and `password_hash_type`. - * @param string|null $passwordHash The hashed password to set for the user. Required with `password_hash_type`. Mutually exclusive with `password`. - * @param \WorkOS\Resource\CreateUserPasswordHashType|null $passwordHashType The algorithm originally used to hash the password, used when providing a `password_hash`. Required with `password_hash`. Mutually exclusive with `password`. + * @param null|PasswordPlaintext|PasswordHashed $password * @return \WorkOS\Resource\User * @throws \WorkOS\Exception\WorkOSException */ @@ -762,9 +760,7 @@ public function updateUser( ?array $metadata = null, ?string $externalId = null, ?string $locale = null, - ?string $password = null, - ?string $passwordHash = null, - ?\WorkOS\Resource\CreateUserPasswordHashType $passwordHashType = null, + null|PasswordPlaintext|PasswordHashed $password = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\User { $body = array_filter([ @@ -775,10 +771,14 @@ public function updateUser( 'metadata' => $metadata, 'external_id' => $externalId, 'locale' => $locale, - 'password' => $password, - 'password_hash' => $passwordHash, - 'password_hash_type' => $passwordHashType?->value, ], fn ($v) => $v !== null); + if ($password instanceof PasswordPlaintext) { + $body['password'] = $password->password; + } + elseif ($password instanceof PasswordHashed) { + $body['password_hash'] = $password->hash; + $body['password_hash_type'] = $password->hashType; + } $response = $this->client->request( method: 'PUT', path: "user_management/users/{$id}", @@ -1265,8 +1265,6 @@ public function listOrganizationMemberships( * Calling this API with an organization and user that match an `inactive` organization membership will activate the membership with the specified role(s). * @param string $userId The ID of the [user](https://workos.com/docs/reference/authkit/user). * @param string $organizationId The ID of the [organization](https://workos.com/docs/reference/organization) which the user belongs to. - * @param string|null $roleSlug A single role identifier. Defaults to `member` or the explicit default role. Mutually exclusive with `role_slugs`. - * @param array|null $roleSlugs An array of role identifiers. Limited to one role when Multiple Roles is disabled. Mutually exclusive with `role_slug`. * @param null|RoleSingle|RoleMultiple $role * @return \WorkOS\Resource\OrganizationMembership * @throws \WorkOS\Exception\WorkOSException @@ -1274,17 +1272,19 @@ public function listOrganizationMemberships( public function createOrganizationMembership( string $userId, string $organizationId, - ?string $roleSlug = null, - ?array $roleSlugs = null, null|RoleSingle|RoleMultiple $role = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\OrganizationMembership { - $body = array_filter([ + $body = [ 'user_id' => $userId, 'organization_id' => $organizationId, - 'role_slug' => $roleSlug, - 'role_slugs' => $roleSlugs, - ], fn ($v) => $v !== null); + ]; + if ($role instanceof RoleSingle) { + $body['role_slug'] = $role->slug; + } + elseif ($role instanceof RoleMultiple) { + $body['role_slugs'] = $role->slugs; + } $response = $this->client->request( method: 'POST', path: 'user_management/organization_memberships', @@ -1319,23 +1319,23 @@ public function getOrganizationMembership( * * Update the details of an existing organization membership. * @param string $id The unique ID of the organization membership. - * @param string|null $roleSlug A single role identifier. Defaults to `member` or the explicit default role. Mutually exclusive with `role_slugs`. - * @param array|null $roleSlugs An array of role identifiers. Limited to one role when Multiple Roles is disabled. Mutually exclusive with `role_slug`. * @param null|RoleSingle|RoleMultiple $role * @return \WorkOS\Resource\UserOrganizationMembership * @throws \WorkOS\Exception\WorkOSException */ public function updateOrganizationMembership( string $id, - ?string $roleSlug = null, - ?array $roleSlugs = null, null|RoleSingle|RoleMultiple $role = null, ?\WorkOS\RequestOptions $options = null, ): \WorkOS\Resource\UserOrganizationMembership { - $body = array_filter([ - 'role_slug' => $roleSlug, - 'role_slugs' => $roleSlugs, - ], fn ($v) => $v !== null); + $body = [ + ]; + if ($role instanceof RoleSingle) { + $body['role_slug'] = $role->slug; + } + elseif ($role instanceof RoleMultiple) { + $body['role_slugs'] = $role->slugs; + } $response = $this->client->request( method: 'PUT', path: "user_management/organization_memberships/{$id}", diff --git a/lib/Service/UserManagementOrganizationMembershipGroups.php b/lib/Service/UserManagementOrganizationMembershipGroups.php new file mode 100644 index 00000000..1179033a --- /dev/null +++ b/lib/Service/UserManagementOrganizationMembershipGroups.php @@ -0,0 +1,52 @@ + + * @throws \WorkOS\Exception\WorkOSException + */ + public function listOrganizationMembershipGroups( + string $omId, + ?string $before = null, + ?string $after = null, + ?int $limit = null, + \WorkOS\Resource\EventsOrder $order = \WorkOS\Resource\EventsOrder::Desc, + ?\WorkOS\RequestOptions $options = null, + ): \WorkOS\PaginatedResponse { + $query = array_filter([ + 'before' => $before, + 'after' => $after, + 'limit' => $limit, + 'order' => $order->value, + ], fn ($v) => $v !== null); + return $this->client->requestPage( + method: 'GET', + path: "user_management/organization_memberships/{$omId}/groups", + query: $query, + modelClass: Group::class, + options: $options, + ); + } +} diff --git a/lib/WorkOS.php b/lib/WorkOS.php index 94106477..c4269869 100644 --- a/lib/WorkOS.php +++ b/lib/WorkOS.php @@ -14,6 +14,7 @@ use WorkOS\Service\DirectorySync; use WorkOS\Service\Events; use WorkOS\Service\FeatureFlags; +use WorkOS\Service\Groups; use WorkOS\Service\MultiFactorAuth; use WorkOS\Service\OrganizationDomains; use WorkOS\Service\Organizations; @@ -21,6 +22,7 @@ use WorkOS\Service\Radar; use WorkOS\Service\SSO; use WorkOS\Service\UserManagement; +use WorkOS\Service\UserManagementOrganizationMembershipGroups; use WorkOS\Service\Webhooks; use WorkOS\Service\Widgets; @@ -60,20 +62,14 @@ public static function setClientId(?string $id): void private ?Service\FeatureFlags $featureFlags = null; private ?Service\OrganizationDomains $organizationDomains = null; private ?Service\Organizations $organizations = null; + private ?Service\Groups $groups = null; private ?Service\AdminPortal $adminPortal = null; private ?Service\Radar $radar = null; private ?Service\UserManagement $userManagement = null; + private ?Service\UserManagementOrganizationMembershipGroups $userManagementOrganizationMembershipGroups = null; private ?Service\Webhooks $webhooks = null; private ?Service\Widgets $widgets = null; private ?Service\AuditLogs $auditLogs = null; - // @oagen-ignore-start — non-spec service properties (hand-maintained) - private ?Passwordless $passwordless = null; - private ?Vault $vault = null; - private ?WebhookVerification $webhookVerification = null; - private ?Actions $actions = null; - private ?SessionManager $sessionManager = null; - private ?PKCEHelper $pkce = null; - // @oagen-ignore-end public function __construct( ?string $apiKey = null, @@ -144,6 +140,11 @@ public function organizations(): Organizations return $this->organizations ??= new Service\Organizations($this->httpClient); } + public function groups(): Groups + { + return $this->groups ??= new Service\Groups($this->httpClient); + } + public function adminPortal(): AdminPortal { return $this->adminPortal ??= new Service\AdminPortal($this->httpClient); @@ -159,6 +160,11 @@ public function userManagement(): UserManagement return $this->userManagement ??= new Service\UserManagement($this->httpClient); } + public function userManagementOrganizationMembershipGroups(): UserManagementOrganizationMembershipGroups + { + return $this->userManagementOrganizationMembershipGroups ??= new Service\UserManagementOrganizationMembershipGroups($this->httpClient); + } + public function webhooks(): Webhooks { return $this->webhooks ??= new Service\Webhooks($this->httpClient); @@ -174,6 +180,15 @@ public function auditLogs(): AuditLogs return $this->auditLogs ??= new Service\AuditLogs($this->httpClient); } + // @oagen-ignore-start — non-spec service properties (hand-maintained) + private ?Passwordless $passwordless = null; + private ?Vault $vault = null; + private ?WebhookVerification $webhookVerification = null; + private ?Actions $actions = null; + private ?SessionManager $sessionManager = null; + private ?PKCEHelper $pkce = null; + // @oagen-ignore-end + // @oagen-ignore-start — non-spec service accessors (hand-maintained) public function passwordless(): Passwordless 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..4be367d9 100644 --- a/tests/Fixtures/generate_link.json +++ b/tests/Fixtures/generate_link.json @@ -7,9 +7,15 @@ "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..5dabcd8e 100644 --- a/tests/Fixtures/invitation.json +++ b/tests/Fixtures/invitation.json @@ -12,5 +12,6 @@ "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "token": "Z1uX3RbwcIl5fIGJJJCXXisdI", - "accept_invitation_url": "https://your-app.com/invite?invitation_token=Z1uX3RbwcIl5fIGJJJCXXisdI" + "accept_invitation_url": "https://your-app.com/invite?invitation_token=Z1uX3RbwcIl5fIGJJJCXXisdI", + "role_slug": "admin" } diff --git a/tests/Fixtures/invitation_accepted.json b/tests/Fixtures/invitation_accepted.json index 61b93ab4..100adfa9 100644 --- a/tests/Fixtures/invitation_accepted.json +++ b/tests/Fixtures/invitation_accepted.json @@ -13,7 +13,8 @@ "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" + "updated_at": "2026-01-15T12:00:00.000Z", + "role_slug": "admin" }, "created_at": "2026-01-15T12:00:00.000Z", "context": { diff --git a/tests/Fixtures/invitation_accepted_data.json b/tests/Fixtures/invitation_accepted_data.json index 66102c6b..94f2e81d 100644 --- a/tests/Fixtures/invitation_accepted_data.json +++ b/tests/Fixtures/invitation_accepted_data.json @@ -10,5 +10,6 @@ "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" + "updated_at": "2026-01-15T12:00:00.000Z", + "role_slug": "admin" } diff --git a/tests/Fixtures/invitation_created.json b/tests/Fixtures/invitation_created.json index c83b104d..b1398e12 100644 --- a/tests/Fixtures/invitation_created.json +++ b/tests/Fixtures/invitation_created.json @@ -13,7 +13,8 @@ "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" + "updated_at": "2026-01-15T12:00:00.000Z", + "role_slug": "admin" }, "created_at": "2026-01-15T12:00:00.000Z", "context": { diff --git a/tests/Fixtures/invitation_created_data.json b/tests/Fixtures/invitation_created_data.json index 66102c6b..94f2e81d 100644 --- a/tests/Fixtures/invitation_created_data.json +++ b/tests/Fixtures/invitation_created_data.json @@ -10,5 +10,6 @@ "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" + "updated_at": "2026-01-15T12:00:00.000Z", + "role_slug": "admin" } diff --git a/tests/Fixtures/invitation_resent.json b/tests/Fixtures/invitation_resent.json index 7ace11e7..17adc958 100644 --- a/tests/Fixtures/invitation_resent.json +++ b/tests/Fixtures/invitation_resent.json @@ -13,7 +13,8 @@ "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" + "updated_at": "2026-01-15T12:00:00.000Z", + "role_slug": "admin" }, "created_at": "2026-01-15T12:00:00.000Z", "context": { diff --git a/tests/Fixtures/invitation_resent_data.json b/tests/Fixtures/invitation_resent_data.json index 66102c6b..94f2e81d 100644 --- a/tests/Fixtures/invitation_resent_data.json +++ b/tests/Fixtures/invitation_resent_data.json @@ -10,5 +10,6 @@ "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" + "updated_at": "2026-01-15T12:00:00.000Z", + "role_slug": "admin" } diff --git a/tests/Fixtures/invitation_revoked.json b/tests/Fixtures/invitation_revoked.json index e8f8ff62..e22216e3 100644 --- a/tests/Fixtures/invitation_revoked.json +++ b/tests/Fixtures/invitation_revoked.json @@ -13,7 +13,8 @@ "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" + "updated_at": "2026-01-15T12:00:00.000Z", + "role_slug": "admin" }, "created_at": "2026-01-15T12:00:00.000Z", "context": { diff --git a/tests/Fixtures/invitation_revoked_data.json b/tests/Fixtures/invitation_revoked_data.json index 66102c6b..94f2e81d 100644 --- a/tests/Fixtures/invitation_revoked_data.json +++ b/tests/Fixtures/invitation_revoked_data.json @@ -10,5 +10,6 @@ "inviter_user_id": "user_01HYGBX8ZGD19949T3BM4FW1C3", "accepted_user_id": null, "created_at": "2026-01-15T12:00:00.000Z", - "updated_at": "2026-01-15T12:00:00.000Z" + "updated_at": "2026-01-15T12:00:00.000Z", + "role_slug": "admin" } 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..9c91ed19 100644 --- a/tests/Fixtures/user_invite.json +++ b/tests/Fixtures/user_invite.json @@ -12,5 +12,6 @@ "created_at": "2026-01-15T12:00:00.000Z", "updated_at": "2026-01-15T12:00:00.000Z", "token": "Z1uX3RbwcIl5fIGJJJCXXisdI", - "accept_invitation_url": "https://your-app.com/invite?invitation_token=Z1uX3RbwcIl5fIGJJJCXXisdI" + "accept_invitation_url": "https://your-app.com/invite?invitation_token=Z1uX3RbwcIl5fIGJJJCXXisdI", + "role_slug": "admin" } 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/Service/AuthorizationTest.php b/tests/Service/AuthorizationTest.php index a96939b7..69ca8f67 100644 --- a/tests/Service/AuthorizationTest.php +++ b/tests/Service/AuthorizationTest.php @@ -27,11 +27,11 @@ public function testCheck(): void $this->assertSame('test_value', $body['permission_slug']); } - public function testListOrganizationMembershipResources(): void + public function testListResourcesForMembership(): void { $fixture = $this->loadFixture('list_authorization_resource'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->authorization()->listOrganizationMembershipResources('test_organization_membership_id', parentResource: new \WorkOS\Service\ParentResourceById(id: 'test_value'), before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\EventsOrder::Normal, permissionSlug: 'test_value'); + $result = $client->authorization()->listResourcesForMembership('test_organization_membership_id', parentResource: new \WorkOS\Service\ParentResourceById(id: 'test_value'), before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\EventsOrder::Normal, permissionSlug: 'test_value'); $this->assertInstanceOf(\WorkOS\PaginatedResponse::class, $result); $request = $this->getLastRequest(); $this->assertSame('GET', $request->getMethod()); @@ -45,11 +45,11 @@ public function testListOrganizationMembershipResources(): void $this->assertSame('test_value', $query['permission_slug']); } - public function testListResourcePermissions(): void + public function testListEffectivePermissions(): void { $fixture = $this->loadFixture('list_authorization_permission'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->authorization()->listResourcePermissions('test_organization_membership_id', 'test_resource_id', before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\EventsOrder::Normal); + $result = $client->authorization()->listEffectivePermissions('test_organization_membership_id', 'test_resource_id', before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\EventsOrder::Normal); $this->assertInstanceOf(\WorkOS\PaginatedResponse::class, $result); $request = $this->getLastRequest(); $this->assertSame('GET', $request->getMethod()); @@ -77,11 +77,11 @@ public function testListEffectivePermissionsByExternalId(): void $this->assertSame('normal', $query['order']); } - public function testListOrganizationMembershipRoleAssignments(): void + public function testListRoleAssignments(): void { $fixture = $this->loadFixture('list_role_assignment'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->authorization()->listOrganizationMembershipRoleAssignments('test_organization_membership_id', before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\EventsOrder::Normal); + $result = $client->authorization()->listRoleAssignments('test_organization_membership_id', before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\EventsOrder::Normal); $this->assertInstanceOf(\WorkOS\PaginatedResponse::class, $result); $request = $this->getLastRequest(); $this->assertSame('GET', $request->getMethod()); @@ -119,10 +119,10 @@ public function testRemoveRole(): void $this->assertSame('test_value', $body['role_slug']); } - public function testDeleteOrganizationMembershipRoleAssignment(): void + public function testRemoveRoleAssignment(): void { $client = $this->createMockClient([['status' => 204]]); - $client->authorization()->deleteOrganizationMembershipRoleAssignment('test_organization_membership_id', 'test_role_assignment_id'); + $client->authorization()->removeRoleAssignment('test_organization_membership_id', 'test_role_assignment_id'); $request = $this->getLastRequest(); $this->assertSame('DELETE', $request->getMethod()); $this->assertStringEndsWith('authorization/organization_memberships/test_organization_membership_id/role_assignments/test_role_assignment_id', $request->getUri()->getPath()); @@ -193,11 +193,11 @@ public function testDeleteOrganizationRole(): void $this->assertStringEndsWith('authorization/organizations/test_organizationId/roles/test_slug', $request->getUri()->getPath()); } - public function testCreateRolePermission(): void + public function testAddOrganizationRolePermission(): void { $fixture = $this->loadFixture('role'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->authorization()->createRolePermission('test_organizationId', 'test_slug', bodySlug: 'test_value'); + $result = $client->authorization()->addOrganizationRolePermission('test_organizationId', 'test_slug', bodySlug: 'test_value'); $this->assertInstanceOf(\WorkOS\Resource\Role::class, $result); $this->assertSame($fixture['id'], $result->id); $this->assertSame($fixture['slug'], $result->slug); @@ -207,11 +207,11 @@ public function testCreateRolePermission(): void $this->assertStringEndsWith('authorization/organizations/test_organizationId/roles/test_slug/permissions', $request->getUri()->getPath()); } - public function testUpdateRolePermissions(): void + public function testSetOrganizationRolePermissions(): void { $fixture = $this->loadFixture('role'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->authorization()->updateRolePermissions('test_organizationId', 'test_slug', permissions: []); + $result = $client->authorization()->setOrganizationRolePermissions('test_organizationId', 'test_slug', permissions: []); $this->assertInstanceOf(\WorkOS\Resource\Role::class, $result); $this->assertSame($fixture['id'], $result->id); $this->assertSame($fixture['slug'], $result->slug); @@ -221,20 +221,20 @@ public function testUpdateRolePermissions(): void $this->assertStringEndsWith('authorization/organizations/test_organizationId/roles/test_slug/permissions', $request->getUri()->getPath()); } - public function testDeleteRolePermission(): void + public function testRemoveOrganizationRolePermission(): void { $client = $this->createMockClient([['status' => 204]]); - $client->authorization()->deleteRolePermission('test_organizationId', 'test_slug', 'test_permissionSlug'); + $client->authorization()->removeOrganizationRolePermission('test_organizationId', 'test_slug', 'test_permissionSlug'); $request = $this->getLastRequest(); $this->assertSame('DELETE', $request->getMethod()); $this->assertStringEndsWith('authorization/organizations/test_organizationId/roles/test_slug/permissions/test_permissionSlug', $request->getUri()->getPath()); } - public function testGetOrganizationResource(): void + public function testGetResourceByExternalId(): void { $fixture = $this->loadFixture('authorization_resource'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->authorization()->getOrganizationResource('test_organization_id', 'test_resource_type_slug', 'test_external_id'); + $result = $client->authorization()->getResourceByExternalId('test_organization_id', 'test_resource_type_slug', 'test_external_id'); $this->assertInstanceOf(\WorkOS\Resource\AuthorizationResource::class, $result); $this->assertSame($fixture['id'], $result->id); $this->assertSame($fixture['name'], $result->name); @@ -244,11 +244,11 @@ public function testGetOrganizationResource(): void $this->assertStringEndsWith('authorization/organizations/test_organization_id/resources/test_resource_type_slug/test_external_id', $request->getUri()->getPath()); } - public function testUpdateOrganizationResource(): void + public function testUpdateResourceByExternalId(): void { $fixture = $this->loadFixture('authorization_resource'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->authorization()->updateOrganizationResource('test_organization_id', 'test_resource_type_slug', 'test_external_id'); + $result = $client->authorization()->updateResourceByExternalId('test_organization_id', 'test_resource_type_slug', 'test_external_id'); $this->assertInstanceOf(\WorkOS\Resource\AuthorizationResource::class, $result); $this->assertSame($fixture['id'], $result->id); $this->assertSame($fixture['name'], $result->name); @@ -258,20 +258,20 @@ public function testUpdateOrganizationResource(): void $this->assertStringEndsWith('authorization/organizations/test_organization_id/resources/test_resource_type_slug/test_external_id', $request->getUri()->getPath()); } - public function testDeleteOrganizationResource(): void + public function testDeleteResourceByExternalId(): void { $client = $this->createMockClient([['status' => 204]]); - $client->authorization()->deleteOrganizationResource('test_organization_id', 'test_resource_type_slug', 'test_external_id'); + $client->authorization()->deleteResourceByExternalId('test_organization_id', 'test_resource_type_slug', 'test_external_id'); $request = $this->getLastRequest(); $this->assertSame('DELETE', $request->getMethod()); $this->assertStringEndsWith('authorization/organizations/test_organization_id/resources/test_resource_type_slug/test_external_id', $request->getUri()->getPath()); } - public function testListResourceOrganizationMemberships(): void + public function testListMembershipsForResourceByExternalId(): void { $fixture = $this->loadFixture('list_user_organization_membership_base_list_data'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->authorization()->listResourceOrganizationMemberships('test_organization_id', 'test_resource_type_slug', 'test_external_id', before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\EventsOrder::Normal, permissionSlug: 'test_value', assignment: \WorkOS\Resource\AuthorizationAssignment::Direct); + $result = $client->authorization()->listMembershipsForResourceByExternalId('test_organization_id', 'test_resource_type_slug', 'test_external_id', before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\EventsOrder::Normal, permissionSlug: 'test_value', assignment: \WorkOS\Resource\AuthorizationAssignment::Direct); $this->assertInstanceOf(\WorkOS\PaginatedResponse::class, $result); $request = $this->getLastRequest(); $this->assertSame('GET', $request->getMethod()); @@ -289,7 +289,7 @@ public function testListResources(): void { $fixture = $this->loadFixture('list_authorization_resource'); $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->authorization()->listResources(parent: new \WorkOS\Service\ParentById(resourceId: 'test_value'), before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\EventsOrder::Normal, organizationId: 'test_value', resourceTypeSlug: 'test_value', search: 'test_value'); + $result = $client->authorization()->listResources(parent: new \WorkOS\Service\ParentById(resourceId: 'test_value'), before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\EventsOrder::Normal, organizationId: 'test_value', resourceTypeSlug: 'test_value', resourceExternalId: 'test_value', search: 'test_value'); $this->assertInstanceOf(\WorkOS\PaginatedResponse::class, $result); $request = $this->getLastRequest(); $this->assertSame('GET', $request->getMethod()); @@ -302,6 +302,7 @@ public function testListResources(): void $this->assertSame('normal', $query['order']); $this->assertSame('test_value', $query['organization_id']); $this->assertSame('test_value', $query['resource_type_slug']); + $this->assertSame('test_value', $query['resource_external_id']); $this->assertSame('test_value', $query['search']); } @@ -541,7 +542,7 @@ public function testPaginationBoundary(): void $fixture['list_metadata']['before'] = null; $fixture['list_metadata']['after'] = null; $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); - $result = $client->authorization()->listOrganizationMembershipResources('test_organization_membership_id', parentResource: new \WorkOS\Service\ParentResourceById(id: 'test_value'), permissionSlug: 'test_value'); + $result = $client->authorization()->listResourcesForMembership('test_organization_membership_id', parentResource: new \WorkOS\Service\ParentResourceById(id: 'test_value'), permissionSlug: 'test_value'); $this->assertInstanceOf(\WorkOS\PaginatedResponse::class, $result); // Verify cursors are null on boundary page $this->assertNull($result->listMetadata['before']); diff --git a/tests/Service/GroupsTest.php b/tests/Service/GroupsTest.php new file mode 100644 index 00000000..c09013fe --- /dev/null +++ b/tests/Service/GroupsTest.php @@ -0,0 +1,144 @@ +loadFixture('list_group'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->groups()->listOrganizationGroups('test_organizationId', before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\EventsOrder::Normal); + $this->assertInstanceOf(\WorkOS\PaginatedResponse::class, $result); + $request = $this->getLastRequest(); + $this->assertSame('GET', $request->getMethod()); + $this->assertStringEndsWith('organizations/test_organizationId/groups', $request->getUri()->getPath()); + parse_str($request->getUri()->getQuery(), $query); + $this->assertSame('test_value', $query['before']); + $this->assertSame('test_value', $query['after']); + $this->assertArrayHasKey('limit', $query); + $this->assertSame('normal', $query['order']); + } + + public function testCreateOrganizationGroup(): void + { + $fixture = $this->loadFixture('group'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->groups()->createOrganizationGroup('test_organizationId', name: 'test_value'); + $this->assertInstanceOf(\WorkOS\Resource\Group::class, $result); + $this->assertSame($fixture['id'], $result->id); + $this->assertSame($fixture['organization_id'], $result->organizationId); + $this->assertIsArray($result->toArray()); + $request = $this->getLastRequest(); + $this->assertSame('POST', $request->getMethod()); + $this->assertStringEndsWith('organizations/test_organizationId/groups', $request->getUri()->getPath()); + $body = json_decode((string) $request->getBody(), true); + $this->assertSame('test_value', $body['name']); + } + + public function testGetOrganizationGroup(): void + { + $fixture = $this->loadFixture('group'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->groups()->getOrganizationGroup('test_organizationId', 'test_groupId'); + $this->assertInstanceOf(\WorkOS\Resource\Group::class, $result); + $this->assertSame($fixture['id'], $result->id); + $this->assertSame($fixture['organization_id'], $result->organizationId); + $this->assertIsArray($result->toArray()); + $request = $this->getLastRequest(); + $this->assertSame('GET', $request->getMethod()); + $this->assertStringEndsWith('organizations/test_organizationId/groups/test_groupId', $request->getUri()->getPath()); + } + + public function testUpdateOrganizationGroup(): void + { + $fixture = $this->loadFixture('group'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->groups()->updateOrganizationGroup('test_organizationId', 'test_groupId'); + $this->assertInstanceOf(\WorkOS\Resource\Group::class, $result); + $this->assertSame($fixture['id'], $result->id); + $this->assertSame($fixture['organization_id'], $result->organizationId); + $this->assertIsArray($result->toArray()); + $request = $this->getLastRequest(); + $this->assertSame('PATCH', $request->getMethod()); + $this->assertStringEndsWith('organizations/test_organizationId/groups/test_groupId', $request->getUri()->getPath()); + } + + public function testDeleteOrganizationGroup(): void + { + $client = $this->createMockClient([['status' => 204]]); + $client->groups()->deleteOrganizationGroup('test_organizationId', 'test_groupId'); + $request = $this->getLastRequest(); + $this->assertSame('DELETE', $request->getMethod()); + $this->assertStringEndsWith('organizations/test_organizationId/groups/test_groupId', $request->getUri()->getPath()); + } + + public function testListGroupOrganizationMemberships(): void + { + $fixture = $this->loadFixture('list_user_organization_membership_base_list_data'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->groups()->listGroupOrganizationMemberships('test_organizationId', 'test_groupId', before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\EventsOrder::Normal); + $this->assertInstanceOf(\WorkOS\PaginatedResponse::class, $result); + $request = $this->getLastRequest(); + $this->assertSame('GET', $request->getMethod()); + $this->assertStringEndsWith('organizations/test_organizationId/groups/test_groupId/organization-memberships', $request->getUri()->getPath()); + parse_str($request->getUri()->getQuery(), $query); + $this->assertSame('test_value', $query['before']); + $this->assertSame('test_value', $query['after']); + $this->assertArrayHasKey('limit', $query); + $this->assertSame('normal', $query['order']); + } + + public function testCreateGroupOrganizationMembership(): void + { + $fixture = $this->loadFixture('group'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->groups()->createGroupOrganizationMembership('test_organizationId', 'test_groupId', organizationMembershipId: 'test_value'); + $this->assertInstanceOf(\WorkOS\Resource\Group::class, $result); + $this->assertSame($fixture['id'], $result->id); + $this->assertSame($fixture['organization_id'], $result->organizationId); + $this->assertIsArray($result->toArray()); + $request = $this->getLastRequest(); + $this->assertSame('POST', $request->getMethod()); + $this->assertStringEndsWith('organizations/test_organizationId/groups/test_groupId/organization-memberships', $request->getUri()->getPath()); + $body = json_decode((string) $request->getBody(), true); + $this->assertSame('test_value', $body['organization_membership_id']); + } + + public function testDeleteGroupOrganizationMembership(): void + { + $client = $this->createMockClient([['status' => 204]]); + $client->groups()->deleteGroupOrganizationMembership('test_organizationId', 'test_groupId', 'test_omId'); + $request = $this->getLastRequest(); + $this->assertSame('DELETE', $request->getMethod()); + $this->assertStringEndsWith('organizations/test_organizationId/groups/test_groupId/organization-memberships/test_omId', $request->getUri()->getPath()); + } + + public function testPaginationBoundary(): void + { + $fixture = $this->loadFixture('list_group'); + // Ensure cursors are null (first/last page boundary) + $fixture['list_metadata']['before'] = null; + $fixture['list_metadata']['after'] = null; + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->groups()->listOrganizationGroups('test_organizationId'); + $this->assertInstanceOf(\WorkOS\PaginatedResponse::class, $result); + // Verify cursors are null on boundary page + $this->assertNull($result->listMetadata['before']); + $this->assertNull($result->listMetadata['after']); + // Iterating should not throw on null cursors + foreach ($result as $item) { + $this->assertNotNull($item); + break; + } + } +} diff --git a/tests/Service/UserManagementOrganizationMembershipGroupsTest.php b/tests/Service/UserManagementOrganizationMembershipGroupsTest.php new file mode 100644 index 00000000..f70a541a --- /dev/null +++ b/tests/Service/UserManagementOrganizationMembershipGroupsTest.php @@ -0,0 +1,50 @@ +loadFixture('list_group'); + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->userManagementOrganizationMembershipGroups()->listOrganizationMembershipGroups('test_omId', before: 'test_value', after: 'test_value', limit: 1, order: \WorkOS\Resource\EventsOrder::Normal); + $this->assertInstanceOf(\WorkOS\PaginatedResponse::class, $result); + $request = $this->getLastRequest(); + $this->assertSame('GET', $request->getMethod()); + $this->assertStringEndsWith('user_management/organization_memberships/test_omId/groups', $request->getUri()->getPath()); + parse_str($request->getUri()->getQuery(), $query); + $this->assertSame('test_value', $query['before']); + $this->assertSame('test_value', $query['after']); + $this->assertArrayHasKey('limit', $query); + $this->assertSame('normal', $query['order']); + } + + public function testPaginationBoundary(): void + { + $fixture = $this->loadFixture('list_group'); + // Ensure cursors are null (first/last page boundary) + $fixture['list_metadata']['before'] = null; + $fixture['list_metadata']['after'] = null; + $client = $this->createMockClient([['status' => 200, 'body' => $fixture]]); + $result = $client->userManagementOrganizationMembershipGroups()->listOrganizationMembershipGroups('test_omId'); + $this->assertInstanceOf(\WorkOS\PaginatedResponse::class, $result); + // Verify cursors are null on boundary page + $this->assertNull($result->listMetadata['before']); + $this->assertNull($result->listMetadata['after']); + // Iterating should not throw on null cursors + foreach ($result as $item) { + $this->assertNotNull($item); + break; + } + } +}