diff --git a/lib/DirectorySync.php b/lib/DirectorySync.php index a065880..003f2af 100644 --- a/lib/DirectorySync.php +++ b/lib/DirectorySync.php @@ -24,7 +24,8 @@ class DirectorySync * * @throws Exception\WorkOSException * - * @return array{?string, ?string, Resource\Directory[]} An array containing the Directory ID to use as before and after cursor, and an array of Directory instances + * @return Resource\PaginatedResource A paginated resource containing before/after cursors and directories array. + * Supports: [$before, $after, $directories] = $result, ["directories" => $dirs] = $result, $result->directories */ public function listDirectories( ?string $domain = null, @@ -54,13 +55,7 @@ public function listDirectories( true ); - $directories = []; - list($before, $after) = Util\Request::parsePaginationArgs($response); - foreach ($response["data"] as $responseData) { - \array_push($directories, Resource\Directory::constructFromResponse($responseData)); - } - - return [$before, $after, $directories]; + return Resource\PaginatedResource::constructFromResponse($response, Resource\Directory::class, 'directories'); } /** @@ -75,7 +70,8 @@ public function listDirectories( * * @throws Exception\WorkOSException * - * @return array{?string, ?string, Resource\DirectoryGroup[]} An array containing the Directory Group ID to use as before and after cursor, and an array of Directory Group instances + * @return Resource\PaginatedResource A paginated resource containing before/after cursors and groups array. + * Supports: [$before, $after, $groups] = $result, ["groups" => $groups] = $result, $result->groups */ public function listGroups( ?string $directory = null, @@ -108,13 +104,7 @@ public function listGroups( true ); - $groups = []; - list($before, $after) = Util\Request::parsePaginationArgs($response); - foreach ($response["data"] as $response) { - \array_push($groups, Resource\DirectoryGroup::constructFromResponse($response)); - } - - return [$before, $after, $groups]; + return Resource\PaginatedResource::constructFromResponse($response, Resource\DirectoryGroup::class, 'groups'); } /** @@ -151,7 +141,8 @@ public function getGroup($directoryGroup) * @param null|string $after Directory User ID to look after * @param Resource\Order $order The Order in which to paginate records * - * @return array{?string, ?string, Resource\DirectoryUser[]} An array containing the Directory User ID to use as before and after cursor, and an array of Directory User instances + * @return Resource\PaginatedResource A paginated resource containing before/after cursors and users array. + * Supports: [$before, $after, $users] = $result, ["users" => $users] = $result, $result->users * * @throws Exception\WorkOSException */ @@ -186,13 +177,7 @@ public function listUsers( true ); - $users = []; - list($before, $after) = Util\Request::parsePaginationArgs($response); - foreach ($response["data"] as $response) { - \array_push($users, Resource\DirectoryUser::constructFromResponse($response)); - } - - return [$before, $after, $users]; + return Resource\PaginatedResource::constructFromResponse($response, Resource\DirectoryUser::class, 'users'); } /** diff --git a/lib/Organizations.php b/lib/Organizations.php index efd2a6b..a0058db 100644 --- a/lib/Organizations.php +++ b/lib/Organizations.php @@ -21,7 +21,8 @@ class Organizations * @param null|string $after Organization ID to look after * @param Resource\Order $order The Order in which to paginate records * - * @return array{?string, ?string, Resource\Organization[]} An array containing the Organization ID to use as before and after cursor, and an array of Organization instances + * @return Resource\PaginatedResource A paginated resource containing before/after cursors and organizations array. + * Supports: [$before, $after, $organizations] = $result, ["organizations" => $orgs] = $result, $result->organizations * * @throws Exception\WorkOSException */ @@ -49,13 +50,7 @@ public function listOrganizations( true ); - $organizations = []; - list($before, $after) = Util\Request::parsePaginationArgs($response); - foreach ($response["data"] as $responseData) { - \array_push($organizations, Resource\Organization::constructFromResponse($responseData)); - } - - return [$before, $after, $organizations]; + return Resource\PaginatedResource::constructFromResponse($response, Resource\Organization::class, 'organizations'); } /** @@ -262,7 +257,8 @@ public function listOrganizationRoles($organizationId) * * @throws Exception\WorkOSException * - * @return array{?string, ?string, Resource\FeatureFlag[]} An array containing the FeatureFlag ID to use as before and after cursor, and an array of FeatureFlag instances + * @return Resource\PaginatedResource A paginated resource containing before/after cursors and feature_flags array. + * Supports: [$before, $after, $flags] = $result, ["feature_flags" => $flags] = $result, $result->feature_flags */ public function listOrganizationFeatureFlags( $organizationId, @@ -287,12 +283,6 @@ public function listOrganizationFeatureFlags( true ); - $featureFlags = []; - list($before, $after) = Util\Request::parsePaginationArgs($response); - foreach ($response["data"] as $responseData) { - \array_push($featureFlags, Resource\FeatureFlag::constructFromResponse($responseData)); - } - - return [$before, $after, $featureFlags]; + return Resource\PaginatedResource::constructFromResponse($response, Resource\FeatureFlag::class, 'feature_flags'); } } diff --git a/lib/Resource/PaginatedResource.php b/lib/Resource/PaginatedResource.php new file mode 100644 index 0000000..de8db1b --- /dev/null +++ b/lib/Resource/PaginatedResource.php @@ -0,0 +1,198 @@ + $users, "after" => $after] = $result + * 3. Fluent property access: $result->users, $result->after, $result->before + * + * This class standardizes pagination across all WorkOS resources while maintaining + * backwards compatibility with existing code. + */ +class PaginatedResource implements \ArrayAccess, \IteratorAggregate +{ + /** + * @var ?string Before cursor for pagination + */ + private $before; + + /** + * @var ?string After cursor for pagination + */ + private $after; + + /** + * @var array The paginated data items + */ + private $data; + + /** + * @var string The key name for the data array (e.g., 'users', 'directories') + */ + private $dataKey; + + /** + * PaginatedResource constructor. + * + * @param ?string $before Before cursor + * @param ?string $after After cursor + * @param array $data Array of resource objects + * @param string $dataKey The key name for accessing the data + */ + public function __construct(?string $before, ?string $after, array $data, string $dataKey) + { + $this->before = $before; + $this->after = $after; + $this->data = $data; + $this->dataKey = $dataKey; + } + + /** + * Construct a PaginatedResource from an API response + * + * @param array $response The API response containing 'data', 'list_metadata', etc. + * @param string $resourceClass The fully qualified class name of the resource type + * @param string $dataKey The key name for the data array (e.g., 'users', 'directories') + * @return self + */ + public static function constructFromResponse(array $response, string $resourceClass, string $dataKey): self + { + $data = []; + list($before, $after) = \WorkOS\Util\Request::parsePaginationArgs($response); + + foreach ($response["data"] as $responseData) { + \array_push($data, $resourceClass::constructFromResponse($responseData)); + } + + return new self($before, $after, $data, $dataKey); + } + + /** + * Magic getter for fluent property access + * + * @param string $name Property name + * @return mixed + */ + public function __get(string $name) + { + if ($name === 'before') { + return $this->before; + } + + if ($name === 'after') { + return $this->after; + } + + if ($name === 'data' || $name === $this->dataKey) { + return $this->data; + } + + return null; + } + + /** + * ArrayAccess: Check if offset exists + * + * @param mixed $offset + * @return bool + */ + #[\ReturnTypeWillChange] + public function offsetExists($offset): bool + { + // Support numeric indices for bare destructuring + if (is_int($offset)) { + return $offset >= 0 && $offset <= 2; + } + + // Support named keys for named destructuring + return in_array($offset, ['before', 'after', 'data', $this->dataKey], true); + } + + /** + * ArrayAccess: Get value at offset + * + * @param mixed $offset + * @return mixed + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + // Support numeric indices for bare destructuring: [0 => before, 1 => after, 2 => data] + if ($offset === 0) { + return $this->before; + } + + if ($offset === 1) { + return $this->after; + } + + if ($offset === 2) { + return $this->data; + } + + // Support named keys for named destructuring + if ($offset === 'before') { + return $this->before; + } + + if ($offset === 'after') { + return $this->after; + } + + if ($offset === 'data' || $offset === $this->dataKey) { + return $this->data; + } + + return null; + } + + /** + * ArrayAccess: Set value at offset (not supported) + * + * @param mixed $offset + * @param mixed $value + * @return void + */ + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value): void + { + throw new \BadMethodCallException('PaginatedResource is immutable'); + } + + /** + * ArrayAccess: Unset offset (not supported) + * + * @param mixed $offset + * @return void + */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset): void + { + throw new \BadMethodCallException('PaginatedResource is immutable'); + } + + /** + * IteratorAggregate: Get iterator for the data array + * + * @return \ArrayIterator + */ + public function getIterator(): \Traversable + { + return new \ArrayIterator($this->data); + } + + /** + * Magic isset for property checking + * + * @param string $name + * @return bool + */ + public function __isset(string $name): bool + { + return in_array($name, ['before', 'after', 'data', $this->dataKey], true); + } +} diff --git a/lib/SSO.php b/lib/SSO.php index dcc57a4..02d8535 100644 --- a/lib/SSO.php +++ b/lib/SSO.php @@ -214,7 +214,8 @@ public function getConnection($connection) * @param null|string $after Connection ID to look after * @param Resource\Order $order The Order in which to paginate records * - * @return array{?string, ?string, Resource\Connection[]} An array containing the Directory Connection ID to use as before and after cursor, and an array of Connection instances + * @return Resource\PaginatedResource A paginated resource containing before/after cursors and connections array. + * Supports: [$before, $after, $connections] = $result, ["connections" => $connections] = $result, $result->connections * * @throws Exception\WorkOSException */ @@ -246,12 +247,6 @@ public function listConnections( true ); - $connections = []; - list($before, $after) = Util\Request::parsePaginationArgs($response); - foreach ($response["data"] as $responseData) { - \array_push($connections, Resource\Connection::constructFromResponse($responseData)); - } - - return [$before, $after, $connections]; + return Resource\PaginatedResource::constructFromResponse($response, Resource\Connection::class, 'connections'); } } diff --git a/lib/UserManagement.php b/lib/UserManagement.php index a27a22f..6b31f79 100644 --- a/lib/UserManagement.php +++ b/lib/UserManagement.php @@ -8,32 +8,35 @@ class UserManagement { public const DEFAULT_PAGE_SIZE = 10; + public const DEFAULT_TOKEN_EXPIRATION = 1440; - public const AUTHORIZATION_PROVIDER_AUTHKIT = "authkit"; - public const AUTHORIZATION_PROVIDER_APPLE_OAUTH = "AppleOAuth"; - public const AUTHORIZATION_PROVIDER_GITHUB_OAUTH = "GitHubOAuth"; - public const AUTHORIZATION_PROVIDER_GOOGLE_OAUTH = "GoogleOAuth"; - public const AUTHORIZATION_PROVIDER_MICROSOFT_OAUTH = "MicrosoftOAuth"; + public const AUTHORIZATION_PROVIDER_AUTHKIT = 'authkit'; + + public const AUTHORIZATION_PROVIDER_APPLE_OAUTH = 'AppleOAuth'; + + public const AUTHORIZATION_PROVIDER_GITHUB_OAUTH = 'GitHubOAuth'; + + public const AUTHORIZATION_PROVIDER_GOOGLE_OAUTH = 'GoogleOAuth'; + + public const AUTHORIZATION_PROVIDER_MICROSOFT_OAUTH = 'MicrosoftOAuth'; /** * Create User. * - * @param string $email The email address of the user. - * @param string|null $password The password of the user. - * @param string|null $firstName The first name of the user. - * @param string|null $lastName The last name of the user. - * @param boolean|null $emailVerified A boolean declaring if the user's email has been verified. - * @param string|null $passwordHash The hashed password to set for the user. - * @param string|null $passwordHashType The algorithm originally used to hash the password. Valid values are `bcrypt`, `ssha`, and `firebase-scrypt`. - * @param string|null $externalId The user's external ID. - * @param array $metadata The user's metadata. + * @param string $email The email address of the user. + * @param string|null $password The password of the user. + * @param string|null $firstName The first name of the user. + * @param string|null $lastName The last name of the user. + * @param bool|null $emailVerified A boolean declaring if the user's email has been verified. + * @param string|null $passwordHash The hashed password to set for the user. + * @param string|null $passwordHashType The algorithm originally used to hash the password. Valid values are `bcrypt`, `ssha`, and `firebase-scrypt`. + * @param string|null $externalId The user's external ID. + * @param array $metadata The user's metadata. + * @return Resource\User * * @throws Exception\WorkOSException - * - * @return Resource\User */ - public function createUser( $email, ?string $password = null, @@ -45,17 +48,17 @@ public function createUser( ?string $externalId = null, ?array $metadata = null ) { - $path = "user_management/users"; + $path = 'user_management/users'; $params = [ - "email" => $email, - "password" => $password, - "first_name" => $firstName, - "last_name" => $lastName, - "email_verified" => $emailVerified, - "password_hash" => $passwordHash, - "password_hash_type" => $passwordHashType, - "external_id" => $externalId, - "metadata" => $metadata + 'email' => $email, + 'password' => $password, + 'first_name' => $firstName, + 'last_name' => $lastName, + 'email_verified' => $emailVerified, + 'password_hash' => $passwordHash, + 'password_hash_type' => $passwordHashType, + 'external_id' => $externalId, + 'metadata' => $metadata, ]; $response = Client::request(Client::METHOD_POST, $path, null, $params, true); @@ -66,11 +69,10 @@ public function createUser( /** * Get a User. * - * @param string $userId user ID + * @param string $userId user ID + * @return Resource\User * * @throws Exception\WorkOSException - * - * @return Resource\User */ public function getUser($userId) { @@ -84,11 +86,10 @@ public function getUser($userId) /** * Get a User by external ID. * - * @param string $externalId The external ID of the user. + * @param string $externalId The external ID of the user. + * @return Resource\User * * @throws Exception\WorkOSException - * - * @return Resource\User */ public function getUserByExternalId($externalId) { @@ -102,20 +103,19 @@ public function getUserByExternalId($externalId) /** * Update a User * - * @param string $userId The unique ID of the user. - * @param string|null $firstName The first name of the user. - * @param string|null $lastName The last name of the user. - * @param boolean|null $emailVerified A boolean declaring if the user's email has been verified. - * @param string|null $password The password to set for the user. - * @param string|null $passwordHash The hashed password to set for the user. - * @param string|null $passwordHashType The algorithm originally used to hash the password. Valid values are `bcrypt`, `ssha`, and `firebase-scrypt`. - * @param string|null $externalId The user's external ID. - * @param array|null $metadata The user's metadata. - * @param string|null $email The email address of the user. + * @param string $userId The unique ID of the user. + * @param string|null $firstName The first name of the user. + * @param string|null $lastName The last name of the user. + * @param bool|null $emailVerified A boolean declaring if the user's email has been verified. + * @param string|null $password The password to set for the user. + * @param string|null $passwordHash The hashed password to set for the user. + * @param string|null $passwordHashType The algorithm originally used to hash the password. Valid values are `bcrypt`, `ssha`, and `firebase-scrypt`. + * @param string|null $externalId The user's external ID. + * @param array|null $metadata The user's metadata. + * @param string|null $email The email address of the user. + * @return Resource\User * * @throws Exception\WorkOSException - * - * @return Resource\User */ public function updateUser( $userId, @@ -132,15 +132,15 @@ public function updateUser( $path = "user_management/users/{$userId}"; $params = [ - "first_name" => $firstName, - "last_name" => $lastName, - "email_verified" => $emailVerified, - "password" => $password, - "password_hash" => $passwordHash, - "password_hash_type" => $passwordHashType, - "external_id" => $externalId, - "metadata" => $metadata, - "email" => $email + 'first_name' => $firstName, + 'last_name' => $lastName, + 'email_verified' => $emailVerified, + 'password' => $password, + 'password_hash' => $passwordHash, + 'password_hash_type' => $passwordHashType, + 'external_id' => $externalId, + 'metadata' => $metadata, + 'email' => $email, ]; $response = Client::request(Client::METHOD_PUT, $path, null, $params, true); @@ -151,14 +151,13 @@ public function updateUser( /** * List Users. * - * @param null|string $email - * @param null|string $organizationId Organization users are a member of - * @param int $limit Maximum number of records to return - * @param null|string $before User ID to look before - * @param null|string $after User ID to look after - * @param Resource\Order $order The Order in which to paginate records - * - * @return array{?string, ?string, Resource\User[]} An array containing the Directory User ID to use as before and after cursor, and an array of User instances + * @param null|string $organizationId Organization users are a member of + * @param int $limit Maximum number of records to return + * @param null|string $before User ID to look before + * @param null|string $after User ID to look after + * @param Resource\Order $order The Order in which to paginate records + * @return Resource\PaginatedResource A paginated resource containing before/after cursors and users array. + * Supports: [$before, $after, $users] = $result, ["users" => $users] = $result, $result->users * * @throws Exception\WorkOSException */ @@ -170,15 +169,15 @@ public function listUsers( ?string $after = null, ?string $order = null ) { - $path = "user_management/users"; + $path = 'user_management/users'; $params = [ - "email" => $email, - "organization_id" => $organizationId, - "limit" => $limit, - "before" => $before, - "after" => $after, - "order" => $order + 'email' => $email, + 'organization_id' => $organizationId, + 'limit' => $limit, + 'before' => $before, + 'after' => $after, + 'order' => $order, ]; $response = Client::request( @@ -189,23 +188,16 @@ public function listUsers( true ); - $users = []; - list($before, $after) = Util\Request::parsePaginationArgs($response); - foreach ($response["data"] as $responseData) { - \array_push($users, Resource\User::constructFromResponse($responseData)); - } - - return [$before, $after, $users]; + return Resource\PaginatedResource::constructFromResponse($response, Resource\User::class, 'users'); } /** * Delete a user. * - * @param string $userId Unique ID of a user + * @param string $userId Unique ID of a user + * @return Resource\Response * * @throws Exception\WorkOSException - * - * @return Resource\Response */ public function deleteUser($userId) { @@ -219,30 +211,29 @@ public function deleteUser($userId) /** * Add a User to an Organization. * - * @param string $userId User ID - * @param string $organizationId Organization ID - * @param string|null $roleSlug Role Slug - * @param array|null $roleSlugs Role Slugs + * @param string $userId User ID + * @param string $organizationId Organization ID + * @param string|null $roleSlug Role Slug + * @param array|null $roleSlugs Role Slugs + * @return Resource\OrganizationMembership * * @throws Exception\WorkOSException - * - * @return Resource\OrganizationMembership */ public function createOrganizationMembership($userId, $organizationId, ?string $roleSlug = null, ?array $roleSlugs = null) { - $path = "user_management/organization_memberships"; + $path = 'user_management/organization_memberships'; $params = [ - "organization_id" => $organizationId, - "user_id" => $userId + 'organization_id' => $organizationId, + 'user_id' => $userId, ]; - if (!is_null($roleSlug)) { - $params["role_slug"] = $roleSlug; + if (! is_null($roleSlug)) { + $params['role_slug'] = $roleSlug; } - if (!is_null($roleSlugs)) { - $params["role_slugs"] = $roleSlugs; + if (! is_null($roleSlugs)) { + $params['role_slugs'] = $roleSlugs; } $response = Client::request( @@ -259,11 +250,10 @@ public function createOrganizationMembership($userId, $organizationId, ?string $ /** * Get an Organization Membership. * - * @param string $organizationMembershipId Organization Membership ID + * @param string $organizationMembershipId Organization Membership ID + * @return Resource\OrganizationMembership * * @throws Exception\WorkOSException - * - * @return Resource\OrganizationMembership */ public function getOrganizationMembership($organizationMembershipId) { @@ -283,11 +273,10 @@ public function getOrganizationMembership($organizationMembershipId) /** * Remove a user from an organization. * - * @param string $organizationMembershipId Organization Membership ID + * @param string $organizationMembershipId Organization Membership ID + * @return Resource\Response * * @throws Exception\WorkOSException - * - * @return Resource\Response */ public function deleteOrganizationMembership($organizationMembershipId) { @@ -307,13 +296,12 @@ public function deleteOrganizationMembership($organizationMembershipId) /** * Update a User organization membership. * - * @param string $organizationMembershipId Organization Membership ID - * @param string|null $role_slug The unique slug of the role to grant to this membership. - * @param array|null $role_slugs The unique slugs of the roles to grant to this membership. + * @param string $organizationMembershipId Organization Membership ID + * @param string|null $role_slug The unique slug of the role to grant to this membership. + * @param array|null $role_slugs The unique slugs of the roles to grant to this membership. + * @return Resource\OrganizationMembership * * @throws Exception\WorkOSException - * - * @return Resource\OrganizationMembership */ public function updateOrganizationMembership($organizationMembershipId, ?string $roleSlug = null, ?array $roleSlugs = null) { @@ -321,12 +309,12 @@ public function updateOrganizationMembership($organizationMembershipId, ?string $params = []; - if (!is_null($roleSlug)) { - $params["role_slug"] = $roleSlug; + if (! is_null($roleSlug)) { + $params['role_slug'] = $roleSlug; } - if (!is_null($roleSlugs)) { - $params["role_slugs"] = $roleSlugs; + if (! is_null($roleSlugs)) { + $params['role_slugs'] = $roleSlugs; } $response = Client::request( @@ -343,17 +331,17 @@ public function updateOrganizationMembership($organizationMembershipId, ?string /** * List organization memberships. * - * @param string|null $userId User ID - * @param string|null $organizationId Organization ID - * @param array|null $statuses Organization Membership statuses to filter - * @param int $limit Maximum number of records to return - * @param string|null $before Organization Membership ID to look before - * @param string|null $after Organization Membership ID to look after - * @param Resource\Order $order The Order in which to paginate records + * @param string|null $userId User ID + * @param string|null $organizationId Organization ID + * @param array|null $statuses Organization Membership statuses to filter + * @param int $limit Maximum number of records to return + * @param string|null $before Organization Membership ID to look before + * @param string|null $after Organization Membership ID to look after + * @param Resource\Order $order The Order in which to paginate records + * @return Resource\PaginatedResource A paginated resource containing before/after cursors and organization_memberships array. + * Supports: [$before, $after, $memberships] = $result, ["organization_memberships" => $m] = $result, $result->organization_memberships * * @throws Exception\WorkOSException - * - * @return array{?string, ?string, Resource\OrganizationMembership[]} An array containing the Organization Membership ID to use as before and after cursor, and a list of Organization Memberships instances */ public function listOrganizationMemberships( ?string $userId = null, @@ -364,25 +352,25 @@ public function listOrganizationMemberships( ?string $after = null, ?string $order = null ) { - $path = "user_management/organization_memberships"; + $path = 'user_management/organization_memberships'; if (isset($statuses)) { - if (!is_array($statuses)) { - $msg = "Invalid argument: statuses must be an array or null."; + if (! is_array($statuses)) { + $msg = 'Invalid argument: statuses must be an array or null.'; throw new Exception\UnexpectedValueException($msg); } - $statuses = join(",", $statuses); + $statuses = implode(',', $statuses); } $params = [ - "organization_id" => $organizationId, - "user_id" => $userId, - "statuses" => $statuses, - "limit" => $limit, - "before" => $before, - "after" => $after, - "order" => $order + 'organization_id' => $organizationId, + 'user_id' => $userId, + 'statuses' => $statuses, + 'limit' => $limit, + 'before' => $before, + 'after' => $after, + 'order' => $order, ]; $response = Client::request( @@ -393,25 +381,16 @@ public function listOrganizationMemberships( true ); - $organizationMemberships = []; - - foreach ($response["data"] as $responseData) { - \array_push($organizationMemberships, Resource\OrganizationMembership::constructFromResponse($responseData)); - } - - list($before, $after) = Util\Request::parsePaginationArgs($response); - - return [$before, $after, $organizationMemberships]; + return Resource\PaginatedResource::constructFromResponse($response, Resource\OrganizationMembership::class, 'organization_memberships'); } /** * Deactivate an Organization Membership. * - * @param string $organizationMembershipId Organization Membership ID + * @param string $organizationMembershipId Organization Membership ID + * @return Resource\OrganizationMembership * * @throws Exception\WorkOSException - * - * @return Resource\OrganizationMembership */ public function deactivateOrganizationMembership($organizationMembershipId) { @@ -431,11 +410,10 @@ public function deactivateOrganizationMembership($organizationMembershipId) /** * Reactivate an Organization Membership. * - * @param string $organizationMembershipId Organization Membership ID + * @param string $organizationMembershipId Organization Membership ID + * @return Resource\OrganizationMembership * * @throws Exception\WorkOSException - * - * @return Resource\OrganizationMembership */ public function reactivateOrganizationMembership($organizationMembershipId) { @@ -455,15 +433,14 @@ public function reactivateOrganizationMembership($organizationMembershipId) /** * Sends an Invitation * - * @param string $email The email address of the invitee - * @param string|null $organizationId Organization ID - * @param int|null $expiresInDays expiration delay in days - * @param string|null $inviterUserId User ID of the inviter - * @param string|null $roleSlug Slug of the role to assign to the invitee User + * @param string $email The email address of the invitee + * @param string|null $organizationId Organization ID + * @param int|null $expiresInDays expiration delay in days + * @param string|null $inviterUserId User ID of the inviter + * @param string|null $roleSlug Slug of the role to assign to the invitee User + * @return Resource\Invitation * * @throws Exception\WorkOSException - * - * @return Resource\Invitation */ public function sendInvitation( $email, @@ -472,14 +449,14 @@ public function sendInvitation( ?string $inviterUserId = null, ?string $roleSlug = null ) { - $path = "user_management/invitations"; + $path = 'user_management/invitations'; $params = [ - "email" => $email, - "organization_id" => $organizationId, - "expires_in_days" => $expiresInDays, - "inviter_user_id" => $inviterUserId, - "role_slug" => $roleSlug + 'email' => $email, + 'organization_id' => $organizationId, + 'expires_in_days' => $expiresInDays, + 'inviter_user_id' => $inviterUserId, + 'role_slug' => $roleSlug, ]; $response = Client::request( @@ -496,11 +473,10 @@ public function sendInvitation( /** * Get an Invitation * - * @param string $invitationId ID of the Invitation + * @param string $invitationId ID of the Invitation + * @return Resource\Invitation * * @throws Exception\WorkOSException - * - * @return Resource\Invitation */ public function getInvitation($invitationId) { @@ -520,11 +496,10 @@ public function getInvitation($invitationId) /** * Find an Invitation by Token * - * @param string $invitationToken The token of the Invitation + * @param string $invitationToken The token of the Invitation + * @return Resource\Invitation * * @throws Exception\WorkOSException - * - * @return Resource\Invitation */ public function findInvitationByToken($invitationToken) { @@ -544,16 +519,16 @@ public function findInvitationByToken($invitationToken) /** * List Invitations * - * @param string|null $email Email of the invitee - * @param string|null $organizationId Organization ID - * @param int $limit Maximum number of records to return - * @param string|null $before Organization Membership ID to look before - * @param string|null $after Organization Membership ID to look after - * @param Resource\Order $order The Order in which to paginate records + * @param string|null $email Email of the invitee + * @param string|null $organizationId Organization ID + * @param int $limit Maximum number of records to return + * @param string|null $before Organization Membership ID to look before + * @param string|null $after Organization Membership ID to look after + * @param Resource\Order $order The Order in which to paginate records + * @return Resource\PaginatedResource A paginated resource containing before/after cursors and invitations array. + * Supports: [$before, $after, $invitations] = $result, ["invitations" => $invitations] = $result, $result->invitations * * @throws Exception\WorkOSException - * - * @return array{?string, ?string, Resource\Invitation[]} An array containing the Invitation ID to use as before and after cursor, and a list of Invitations instances */ public function listInvitations( ?string $email = null, @@ -563,15 +538,15 @@ public function listInvitations( ?string $after = null, ?string $order = null ) { - $path = "user_management/invitations"; + $path = 'user_management/invitations'; $params = [ - "email" => $email, - "organization_id" => $organizationId, - "limit" => $limit, - "before" => $before, - "after" => $after, - "order" => $order + 'email' => $email, + 'organization_id' => $organizationId, + 'limit' => $limit, + 'before' => $before, + 'after' => $after, + 'order' => $order, ]; $response = Client::request( @@ -582,25 +557,16 @@ public function listInvitations( true ); - $invitations = []; - - foreach ($response["data"] as $responseData) { - \array_push($invitations, Resource\Invitation::constructFromResponse($responseData)); - } - - list($before, $after) = Util\Request::parsePaginationArgs($response); - - return [$before, $after, $invitations]; + return Resource\PaginatedResource::constructFromResponse($response, Resource\Invitation::class, 'invitations'); } /** * Revoke an Invitation * - * @param string $invitationId ID of the Invitation + * @param string $invitationId ID of the Invitation + * @return Resource\Invitation * * @throws Exception\WorkOSException - * - * @return Resource\Invitation */ public function revokeInvitation($invitationId) { @@ -620,11 +586,10 @@ public function revokeInvitation($invitationId) /** * Resend an Invitation * - * @param string $invitationId ID of the Invitation + * @param string $invitationId ID of the Invitation + * @return Resource\Invitation * * @throws Exception\WorkOSException - * - * @return Resource\Invitation */ public function resendInvitation($invitationId) { @@ -644,20 +609,19 @@ public function resendInvitation($invitationId) /** * Generates an OAuth 2.0 authorization URL used to initiate the SSO flow with WorkOS. * - * @param string $redirectUri URI to direct the user to upon successful completion of SSO - * @param null|array $state Associative array containing state that will be returned from WorkOS as a json encoded string - * @param null|string $provider Service provider that handles the identity of the user - * @param null|string $connectionId Unique identifier for a WorkOS Connection - * @param null|string $organizationId Unique identifier for a WorkOS Organization - * @param null|string $domainHint Domain hint that will be passed as a parameter to the IdP login page - * @param null|string $loginHint Username/email hint that will be passed as a parameter to the to IdP login page - * @param null|string $screenHint The page that the user will be redirected to when the provider is authkit - * @param null|array $providerScopes An array of provider-specific scopes + * @param string $redirectUri URI to direct the user to upon successful completion of SSO + * @param null|array $state Associative array containing state that will be returned from WorkOS as a json encoded string + * @param null|string $provider Service provider that handles the identity of the user + * @param null|string $connectionId Unique identifier for a WorkOS Connection + * @param null|string $organizationId Unique identifier for a WorkOS Organization + * @param null|string $domainHint Domain hint that will be passed as a parameter to the IdP login page + * @param null|string $loginHint Username/email hint that will be passed as a parameter to the to IdP login page + * @param null|string $screenHint The page that the user will be redirected to when the provider is authkit + * @param null|array $providerScopes An array of provider-specific scopes + * @return string * * @throws Exception\UnexpectedValueException * @throws Exception\ConfigurationException - * - * @return string */ public function getAuthorizationUrl( $redirectUri, @@ -670,10 +634,10 @@ public function getAuthorizationUrl( ?string $screenHint = null, ?array $providerScopes = null ) { - $path = "user_management/authorize"; + $path = 'user_management/authorize'; - if (!isset($provider) && !isset($connectionId) && !isset($organizationId)) { - $msg = "Either \$provider, \$connectionId, or \$organizationId is required"; + if (! isset($provider) && ! isset($connectionId) && ! isset($organizationId)) { + $msg = 'Either $provider, $connectionId, or $organizationId is required'; throw new Exception\UnexpectedValueException($msg); } @@ -682,56 +646,56 @@ public function getAuthorizationUrl( self::AUTHORIZATION_PROVIDER_APPLE_OAUTH, self::AUTHORIZATION_PROVIDER_GITHUB_OAUTH, self::AUTHORIZATION_PROVIDER_GOOGLE_OAUTH, - self::AUTHORIZATION_PROVIDER_MICROSOFT_OAUTH + self::AUTHORIZATION_PROVIDER_MICROSOFT_OAUTH, ]; - if (isset($provider) && !\in_array($provider, $supportedProviders)) { - $msg = "Only " . implode("','", $supportedProviders) . " providers are supported"; + if (isset($provider) && ! \in_array($provider, $supportedProviders)) { + $msg = 'Only '.implode("','", $supportedProviders).' providers are supported'; throw new Exception\UnexpectedValueException($msg); } $params = [ - "client_id" => WorkOS::getClientId(), - "response_type" => "code" + 'client_id' => WorkOS::getClientId(), + 'response_type' => 'code', ]; if ($redirectUri) { - $params["redirect_uri"] = $redirectUri; + $params['redirect_uri'] = $redirectUri; } - if (null !== $state && !empty($state)) { - $params["state"] = \json_encode($state); + if ($state !== null && ! empty($state)) { + $params['state'] = \json_encode($state); } if ($provider) { - $params["provider"] = $provider; + $params['provider'] = $provider; } if ($connectionId) { - $params["connection_id"] = $connectionId; + $params['connection_id'] = $connectionId; } if ($organizationId) { - $params["organization_id"] = $organizationId; + $params['organization_id'] = $organizationId; } if ($domainHint) { - $params["domain_hint"] = $domainHint; + $params['domain_hint'] = $domainHint; } if ($loginHint) { - $params["login_hint"] = $loginHint; + $params['login_hint'] = $loginHint; } if ($screenHint !== null) { if ($provider !== self::AUTHORIZATION_PROVIDER_AUTHKIT) { throw new Exception\UnexpectedValueException("A 'screenHint' can only be provided when the provider is 'authkit'."); } - $params["screen_hint"] = $screenHint; + $params['screen_hint'] = $screenHint; } if ($providerScopes && is_array($providerScopes)) { - $params["provider_scopes"] = implode(",", $providerScopes); + $params['provider_scopes'] = implode(',', $providerScopes); } return Client::generateUrl($path, $params); @@ -740,27 +704,26 @@ public function getAuthorizationUrl( /** * Authenticate a User with Password * - * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. - * @param string $email The email address of the user. - * @param string $password The password of the user. - * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. - * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. + * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. + * @param string $email The email address of the user. + * @param string $password The password of the user. + * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. + * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. + * @return Resource\AuthenticationResponse * * @throws Exception\WorkOSException - * - * @return Resource\AuthenticationResponse */ public function authenticateWithPassword($clientId, $email, $password, ?string $ipAddress = null, ?string $userAgent = null) { - $path = "user_management/authenticate"; + $path = 'user_management/authenticate'; $params = [ - "client_id" => $clientId, - "email" => $email, - "password" => $password, - "ip_address" => $ipAddress, - "user_agent" => $userAgent, - "grant_type" => "password", - "client_secret" => WorkOS::getApiKey() + 'client_id' => $clientId, + 'email' => $email, + 'password' => $password, + 'ip_address' => $ipAddress, + 'user_agent' => $userAgent, + 'grant_type' => 'password', + 'client_secret' => WorkOS::getApiKey(), ]; $response = Client::request(Client::METHOD_POST, $path, null, $params, true); @@ -771,15 +734,14 @@ public function authenticateWithPassword($clientId, $email, $password, ?string $ /** * Authenticate a User with Selected Organization * - * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. - * @param string $pendingAuthenticationToken Token returned from a failed authentication attempt due to organization selection being required. - * @param string $organizationId The Organization ID the user selected. - * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. - * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. + * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. + * @param string $pendingAuthenticationToken Token returned from a failed authentication attempt due to organization selection being required. + * @param string $organizationId The Organization ID the user selected. + * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. + * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. + * @return Resource\AuthenticationResponse * * @throws Exception\WorkOSException - * - * @return Resource\AuthenticationResponse */ public function authenticateWithSelectedOrganization( $clientId, @@ -788,15 +750,15 @@ public function authenticateWithSelectedOrganization( ?string $ipAddress = null, ?string $userAgent = null ) { - $path = "user_management/authenticate"; + $path = 'user_management/authenticate'; $params = [ - "client_id" => $clientId, - "pending_authentication_token" => $pendingAuthenticationToken, - "organization_id" => $organizationId, - "ip_address" => $ipAddress, - "user_agent" => $userAgent, - "grant_type" => "urn:workos:oauth:grant-type:organization-selection", - "client_secret" => WorkOS::getApiKey() + 'client_id' => $clientId, + 'pending_authentication_token' => $pendingAuthenticationToken, + 'organization_id' => $organizationId, + 'ip_address' => $ipAddress, + 'user_agent' => $userAgent, + 'grant_type' => 'urn:workos:oauth:grant-type:organization-selection', + 'client_secret' => WorkOS::getApiKey(), ]; $response = Client::request(Client::METHOD_POST, $path, null, $params, true); @@ -808,25 +770,24 @@ public function authenticateWithSelectedOrganization( * Authenticate an OAuth or SSO User with a Code * This should be used for "Hosted AuthKit" and "OAuth or SSO" UserAuthentications * - * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. - * @param string $code The authorization value which was passed back as a query parameter in the callback to the Redirect URI. - * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. - * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. + * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. + * @param string $code The authorization value which was passed back as a query parameter in the callback to the Redirect URI. + * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. + * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. + * @return Resource\AuthenticationResponse * * @throws Exception\WorkOSException - * - * @return Resource\AuthenticationResponse */ public function authenticateWithCode($clientId, $code, ?string $ipAddress = null, ?string $userAgent = null) { - $path = "user_management/authenticate"; + $path = 'user_management/authenticate'; $params = [ - "client_id" => $clientId, - "code" => $code, - "ip_address" => $ipAddress, - "user_agent" => $userAgent, - "grant_type" => "authorization_code", - "client_secret" => WorkOS::getApiKey() + 'client_id' => $clientId, + 'code' => $code, + 'ip_address' => $ipAddress, + 'user_agent' => $userAgent, + 'grant_type' => 'authorization_code', + 'client_secret' => WorkOS::getApiKey(), ]; $response = Client::request(Client::METHOD_POST, $path, null, $params, true); @@ -837,27 +798,26 @@ public function authenticateWithCode($clientId, $code, ?string $ipAddress = null /** * Authenticates a user with an unverified email and verifies their email address. * - * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. - * @param string $code The authorization value which was passed back as a query parameter in the callback to the Redirect URI. - * @param string $pendingAuthenticationToken Token returned from a failed authentication attempt due to organization selection being required. - * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. - * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. + * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. + * @param string $code The authorization value which was passed back as a query parameter in the callback to the Redirect URI. + * @param string $pendingAuthenticationToken Token returned from a failed authentication attempt due to organization selection being required. + * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. + * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. + * @return Resource\AuthenticationResponse * * @throws Exception\WorkOSException - * - * @return Resource\AuthenticationResponse */ public function authenticateWithEmailVerification($clientId, $code, $pendingAuthenticationToken, ?string $ipAddress = null, ?string $userAgent = null) { - $path = "user_management/authenticate"; + $path = 'user_management/authenticate'; $params = [ - "client_id" => $clientId, - "code" => $code, - "pending_authentication_token" => $pendingAuthenticationToken, - "ip_address" => $ipAddress, - "user_agent" => $userAgent, - "grant_type" => "urn:workos:oauth:grant-type:email-verification:code", - "client_secret" => WorkOS::getApiKey() + 'client_id' => $clientId, + 'code' => $code, + 'pending_authentication_token' => $pendingAuthenticationToken, + 'ip_address' => $ipAddress, + 'user_agent' => $userAgent, + 'grant_type' => 'urn:workos:oauth:grant-type:email-verification:code', + 'client_secret' => WorkOS::getApiKey(), ]; $response = Client::request(Client::METHOD_POST, $path, null, $params, true); @@ -868,17 +828,15 @@ public function authenticateWithEmailVerification($clientId, $code, $pendingAuth /** * Authenticate with Magic Auth * - * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. - * @param string $code The authorization value which was passed back as a query parameter in the callback to the Redirect URI. - * @param string $userId The unique ID of the user. - * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. - * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. + * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. + * @param string $code The authorization value which was passed back as a query parameter in the callback to the Redirect URI. + * @param string $userId The unique ID of the user. + * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. + * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. + * @return Resource\AuthenticationResponse * * @throws Exception\WorkOSException - * - * @return Resource\AuthenticationResponse */ - public function authenticateWithMagicAuth( $clientId, $code, @@ -886,15 +844,15 @@ public function authenticateWithMagicAuth( ?string $ipAddress = null, ?string $userAgent = null ) { - $path = "user_management/authenticate"; + $path = 'user_management/authenticate'; $params = [ - "client_id" => $clientId, - "code" => $code, - "user_id" => $userId, - "ip_address" => $ipAddress, - "user_agent" => $userAgent, - "grant_type" => "urn:workos:oauth:grant-type:magic-auth:code", - "client_secret" => WorkOS::getApiKey() + 'client_id' => $clientId, + 'code' => $code, + 'user_id' => $userId, + 'ip_address' => $ipAddress, + 'user_agent' => $userAgent, + 'grant_type' => 'urn:workos:oauth:grant-type:magic-auth:code', + 'client_secret' => WorkOS::getApiKey(), ]; $response = Client::request(Client::METHOD_POST, $path, null, $params, true); @@ -904,15 +862,15 @@ public function authenticateWithMagicAuth( /** * Authenticate with Refresh Token - * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. - * @param string $refreshToken The refresh token used to obtain a new access token - * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. - * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. - * @param string|null $organizationId The user agent of the request from the user who is attempting to authenticate. - * - * @throws Exception\WorkOSException * + * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. + * @param string $refreshToken The refresh token used to obtain a new access token + * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. + * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. + * @param string|null $organizationId The user agent of the request from the user who is attempting to authenticate. * @return Resource\AuthenticationResponse + * + * @throws Exception\WorkOSException */ public function authenticateWithRefreshToken( $clientId, @@ -921,15 +879,15 @@ public function authenticateWithRefreshToken( ?string $userAgent = null, ?string $organizationId = null ) { - $path = "user_management/authenticate"; + $path = 'user_management/authenticate'; $params = [ - "client_id" => $clientId, - "refresh_token" => $refreshToken, - "organization_id" => $organizationId, - "ip_address" => $ipAddress, - "user_agent" => $userAgent, - "grant_type" => "refresh_token", - "client_secret" => WorkOS::getApiKey() + 'client_id' => $clientId, + 'refresh_token' => $refreshToken, + 'organization_id' => $organizationId, + 'ip_address' => $ipAddress, + 'user_agent' => $userAgent, + 'grant_type' => 'refresh_token', + 'client_secret' => WorkOS::getApiKey(), ]; $response = Client::request(Client::METHOD_POST, $path, null, $params, true); @@ -940,16 +898,15 @@ public function authenticateWithRefreshToken( /** * Authenticate with TOTP * - * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. - * @param string $pendingAuthenticationToken - * @param string $authenticationChallengeId - * @param string $code - * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. - * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. + * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. + * @param string $pendingAuthenticationToken + * @param string $authenticationChallengeId + * @param string $code + * @param string|null $ipAddress The IP address of the request from the user who is attempting to authenticate. + * @param string|null $userAgent The user agent of the request from the user who is attempting to authenticate. + * @return Resource\AuthenticationResponse * * @throws Exception\WorkOSException - * - * @return Resource\AuthenticationResponse */ public function authenticateWithTotp( $clientId, @@ -959,16 +916,16 @@ public function authenticateWithTotp( ?string $ipAddress = null, ?string $userAgent = null ) { - $path = "user_management/authenticate"; + $path = 'user_management/authenticate'; $params = [ - "client_id" => $clientId, - "pending_authentication_token" => $pendingAuthenticationToken, - "authentication_challenge_id" => $authenticationChallengeId, - "code" => $code, - "ip_address" => $ipAddress, - "user_agent" => $userAgent, - "grant_type" => "urn:workos:oauth:grant-type:mfa-totp", - "client_secret" => WorkOS::getApiKey() + 'client_id' => $clientId, + 'pending_authentication_token' => $pendingAuthenticationToken, + 'authentication_challenge_id' => $authenticationChallengeId, + 'code' => $code, + 'ip_address' => $ipAddress, + 'user_agent' => $userAgent, + 'grant_type' => 'urn:workos:oauth:grant-type:mfa-totp', + 'client_secret' => WorkOS::getApiKey(), ]; $response = Client::request(Client::METHOD_POST, $path, null, $params, true); @@ -979,23 +936,22 @@ public function authenticateWithTotp( /** * Enroll An Authentication Factor. * - * @param string $userId The unique ID of the user. - * @param string $type The type of MFA factor used to authenticate. - * @param string|null $totpIssuer Your application or company name, this helps users distinguish between factors in authenticator apps. - * @param string|null $totpUser Used as the account name in authenticator apps. + * @param string $userId The unique ID of the user. + * @param string $type The type of MFA factor used to authenticate. + * @param string|null $totpIssuer Your application or company name, this helps users distinguish between factors in authenticator apps. + * @param string|null $totpUser Used as the account name in authenticator apps. + * @return Resource\AuthenticationFactorAndChallengeTotp * * @throws Exception\WorkOSException - * - * @return Resource\AuthenticationFactorAndChallengeTotp */ public function enrollAuthFactor($userId, $type, ?string $totpIssuer = null, ?string $totpUser = null) { $path = "user_management/users/{$userId}/auth_factors"; $params = [ - "type" => $type, - "totp_user" => $totpUser, - "totp_issuer" => $totpIssuer + 'type' => $type, + 'totp_user' => $totpUser, + 'totp_issuer' => $totpIssuer, ]; $response = Client::request(Client::METHOD_POST, $path, null, $params, true); @@ -1006,11 +962,10 @@ public function enrollAuthFactor($userId, $type, ?string $totpIssuer = null, ?st /** * List a User's Authentication Factors. * - * @param string $userId The unique ID of the user. + * @param string $userId The unique ID of the user. + * @return Resource\UserAuthenticationFactorTotp[] $authFactors A list of user's authentication factors * * @throws Exception\WorkOSException - * - * @return Resource\UserAuthenticationFactorTotp[] $authFactors A list of user's authentication factors */ public function listAuthFactors($userId) { @@ -1020,7 +975,7 @@ public function listAuthFactors($userId) $authFactors = []; - foreach ($response["data"] as $responseData) { + foreach ($response['data'] as $responseData) { \array_push($authFactors, Resource\UserAuthenticationFactorTotp::constructFromResponse($responseData)); } @@ -1030,11 +985,10 @@ public function listAuthFactors($userId) /** * Get an email verification object * - * @param string $emailVerificationId ID of the email verification object + * @param string $emailVerificationId ID of the email verification object + * @return Resource\EmailVerification * * @throws Exception\WorkOSException - * - * @return Resource\EmailVerification */ public function getEmailVerification($emailVerificationId) { @@ -1054,11 +1008,10 @@ public function getEmailVerification($emailVerificationId) /** * Create Email Verification Challenge. * - * @param string $userId The unique ID of the User whose email address will be verified. + * @param string $userId The unique ID of the User whose email address will be verified. + * @return Resource\UserResponse * * @throws Exception\WorkOSException - * - * @return Resource\UserResponse */ public function sendVerificationEmail($userId) { @@ -1072,19 +1025,18 @@ public function sendVerificationEmail($userId) /** * Complete Email Verification. * - * @param string $userId The unique ID of the user. - * @param string $code The one-time code emailed to the user. + * @param string $userId The unique ID of the user. + * @param string $code The one-time code emailed to the user. + * @return Resource\UserResponse * * @throws Exception\WorkOSException - * - * @return Resource\UserResponse */ public function verifyEmail($userId, $code) { $path = "user_management/users/{$userId}/email_verification/confirm"; $params = [ - "code" => $code + 'code' => $code, ]; $response = Client::request(Client::METHOD_POST, $path, null, $params, true); @@ -1095,11 +1047,10 @@ public function verifyEmail($userId, $code) /** * Get a password reset object * - * @param string $passwordResetId ID of the password reset object + * @param string $passwordResetId ID of the password reset object + * @return Resource\PasswordReset * * @throws Exception\WorkOSException - * - * @return Resource\PasswordReset */ public function getPasswordReset($passwordResetId) { @@ -1119,19 +1070,18 @@ public function getPasswordReset($passwordResetId) /** * Creates a password reset token * - * @param string $email The email address of the user + * @param string $email The email address of the user + * @return Resource\PasswordReset * * @throws Exception\WorkOSException - * - * @return Resource\PasswordReset */ public function createPasswordReset( $email ) { - $path = "user_management/password_reset"; + $path = 'user_management/password_reset'; $params = [ - "email" => $email + 'email' => $email, ]; $response = Client::request( @@ -1149,12 +1099,11 @@ public function createPasswordReset( * @deprecated 4.9.0 Use `createPasswordReset` instead. This method will be removed in a future major version. * Create Password Reset Email. * - * @param string $email The email of the user that wishes to reset their password. - * @param string $passwordResetUrl The URL that will be linked to in the email. + * @param string $email The email of the user that wishes to reset their password. + * @param string $passwordResetUrl The URL that will be linked to in the email. + * @return Resource\Response * * @throws Exception\WorkOSException - * - * @return Resource\Response */ public function sendPasswordResetEmail($email, $passwordResetUrl) { @@ -1162,11 +1111,11 @@ public function sendPasswordResetEmail($email, $passwordResetUrl) error_log($msg); - $path = "user_management/password_reset/send"; + $path = 'user_management/password_reset/send'; $params = [ - "email" => $email, - "password_reset_url" => $passwordResetUrl + 'email' => $email, + 'password_reset_url' => $passwordResetUrl, ]; $response = Client::request(Client::METHOD_POST, $path, null, $params, true); @@ -1177,20 +1126,19 @@ public function sendPasswordResetEmail($email, $passwordResetUrl) /** * Complete Password Reset. * - * @param string $token The reset token emailed to the user. - * @param string $newPassword The new password to be set for the user. + * @param string $token The reset token emailed to the user. + * @param string $newPassword The new password to be set for the user. + * @return Resource\UserResponse * * @throws Exception\WorkOSException - * - * @return Resource\UserResponse */ public function resetPassword($token, $newPassword) { - $path = "user_management/password_reset/confirm"; + $path = 'user_management/password_reset/confirm'; $params = [ - "token" => $token, - "new_password" => $newPassword + 'token' => $token, + 'new_password' => $newPassword, ]; $response = Client::request(Client::METHOD_POST, $path, null, $params, true); @@ -1201,11 +1149,10 @@ public function resetPassword($token, $newPassword) /** * Get a Magic Auth object * - * @param string $magicAuthId ID of the Magic Auth object + * @param string $magicAuthId ID of the Magic Auth object + * @return Resource\MagicAuth * * @throws Exception\WorkOSException - * - * @return Resource\MagicAuth */ public function getMagicAuth($magicAuthId) { @@ -1225,22 +1172,21 @@ public function getMagicAuth($magicAuthId) /** * Creates a Magic Auth code * - * @param string $email The email address of the user - * @param string|null $invitationToken The token of an Invitation, if required. + * @param string $email The email address of the user + * @param string|null $invitationToken The token of an Invitation, if required. + * @return Resource\MagicAuth * * @throws Exception\WorkOSException - * - * @return Resource\MagicAuth */ public function createMagicAuth( $email, ?string $invitationToken = null ) { - $path = "user_management/magic_auth"; + $path = 'user_management/magic_auth'; $params = [ - "email" => $email, - "invitation_token" => $invitationToken + 'email' => $email, + 'invitation_token' => $invitationToken, ]; $response = Client::request( @@ -1258,18 +1204,17 @@ public function createMagicAuth( * @deprecated 4.6.0 Use `createMagicAuth` instead. This method will be removed in a future major version. * Creates a one-time Magic Auth code and emails it to the user. * - * @param string $email The email address the one-time code will be sent to. + * @param string $email The email address the one-time code will be sent to. + * @return Resource\Response * * @throws Exception\WorkOSException - * - * @return Resource\Response */ public function sendMagicAuthCode($email) { - $path = "user_management/magic_auth/send"; + $path = 'user_management/magic_auth/send'; $params = [ - "email" => $email, + 'email' => $email, ]; $msg = "'sendMagicAuthCode' is deprecated. Please use 'createMagicAuth' instead. This method will be removed in a future major version."; @@ -1290,16 +1235,15 @@ public function sendMagicAuthCode($email) /** * Returns the public key host that is used for verifying access tokens. * - * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. + * @param string $clientId This value can be obtained from the API Keys page in the WorkOS dashboard. + * @return string * * @throws Exception\UnexpectedValueException - * - * @return string */ public function getJwksUrl(string $clientId) { - if (!isset($clientId) || empty($clientId)) { - throw new Exception\UnexpectedValueException("clientId must not be empty"); + if (! isset($clientId) || empty($clientId)) { + throw new Exception\UnexpectedValueException('clientId must not be empty'); } $baseUrl = WorkOS::getApiBaseUrl(); @@ -1310,22 +1254,21 @@ public function getJwksUrl(string $clientId) /** * Returns the logout URL to end a user's session and redirect to your home page. * - * @param string $sessionId The session ID of the user. - * @param string|null $return_to The URL to redirect to after the user logs out. - * + * @param string $sessionId The session ID of the user. + * @param string|null $return_to The URL to redirect to after the user logs out. * @return string */ public function getLogoutUrl(string $sessionId, ?string $return_to = null) { - if (!isset($sessionId) || empty($sessionId)) { - throw new Exception\UnexpectedValueException("sessionId must not be empty"); + if (! isset($sessionId) || empty($sessionId)) { + throw new Exception\UnexpectedValueException('sessionId must not be empty'); } - $params = [ "session_id" => $sessionId ]; + $params = ['session_id' => $sessionId]; if ($return_to) { - $params["return_to"] = $return_to; + $params['return_to'] = $return_to; } - return Client::generateUrl("user_management/sessions/logout", $params); + return Client::generateUrl('user_management/sessions/logout', $params); } } diff --git a/lib/Vault.php b/lib/Vault.php index 2f4fd50..d3e7bfe 100644 --- a/lib/Vault.php +++ b/lib/Vault.php @@ -37,7 +37,8 @@ public function getVaultObject($vaultObjectId) * @param null|string $after Vault Object ID to look after * @param Resource\Order $order The Order in which to paginate records * - * @return array{?string, ?string, Resource\VaultObject[]} An array containing the Vault Object ID to use as before and after cursor, and an array of VaultObject instances + * @return Resource\PaginatedResource A paginated resource containing before/after cursors and vault_objects array. + * Supports: [$before, $after, $objects] = $result, ["vault_objects" => $objects] = $result, $result->vault_objects * * @throws Exception\WorkOSException */ @@ -63,13 +64,7 @@ public function listVaultObjects( true ); - $vaultObjects = []; - list($before, $after) = Util\Request::parsePaginationArgs($response); - foreach ($response["data"] as $responseData) { - \array_push($vaultObjects, Resource\VaultObject::constructFromResponse($responseData)); - } - - return [$before, $after, $vaultObjects]; + return Resource\PaginatedResource::constructFromResponse($response, Resource\VaultObject::class, 'vault_objects'); } diff --git a/tests/WorkOS/DirectorySyncTest.php b/tests/WorkOS/DirectorySyncTest.php index 7dfe236..09c140f 100644 --- a/tests/WorkOS/DirectorySyncTest.php +++ b/tests/WorkOS/DirectorySyncTest.php @@ -54,6 +54,76 @@ public function testListDirectories() $this->assertSame($directory, $directories[0]->toArray()); } + public function testListDirectoriesPaginatedResourceAccessPatterns() + { + $directoriesPath = "directories"; + $params = [ + "limit" => DirectorySync::DEFAULT_PAGE_SIZE, + "before" => null, + "after" => null, + "domain" => null, + "search" => null, + "organization_id" => null, + "order" => null + ]; + + $result = $this->directoriesResponseFixture(); + + $this->mockRequest( + Client::METHOD_GET, + $directoriesPath, + null, + $params, + true, + $result + ); + + // Test 1: Bare destructuring (indexed) + [$before1, $after1, $directories1] = $this->ds->listDirectories(); + $this->assertNull($before1); + $this->assertNull($after1); + $this->assertIsArray($directories1); + $this->assertCount(1, $directories1); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $directoriesPath, + null, + $params, + true, + $result + ); + + // Test 2: Named destructuring + ["before" => $before2, "after" => $after2, "directories" => $directories2] = $this->ds->listDirectories(); + $this->assertNull($before2); + $this->assertNull($after2); + $this->assertIsArray($directories2); + $this->assertCount(1, $directories2); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $directoriesPath, + null, + $params, + true, + $result + ); + + // Test 3: Fluent access + $response = $this->ds->listDirectories(); + $this->assertNull($response->before); + $this->assertNull($response->after); + $this->assertIsArray($response->directories); + $this->assertCount(1, $response->directories); + + // Test 4: Generic data accessor + $this->assertIsArray($response->data); + $this->assertSame($response->directories, $response->data); + } + public function testGetDirectory() { $directoryId = "directory_id"; @@ -125,6 +195,73 @@ public function testListGroups() $this->assertSame($group, $groups[0]->toArray()); } + public function testListGroupsPaginatedResourceAccessPatterns() + { + $usersPath = "directory_groups"; + $params = [ + "limit" => DirectorySync::DEFAULT_PAGE_SIZE, + "before" => null, + "after" => null, + "order" => null + ]; + + $result = $this->groupsResponseFixture(); + + $this->mockRequest( + Client::METHOD_GET, + $usersPath, + null, + $params, + true, + $result + ); + + // Test 1: Bare destructuring (indexed) + [$before1, $after1, $groups1] = $this->ds->listGroups(); + $this->assertNull($before1); + $this->assertNull($after1); + $this->assertIsArray($groups1); + $this->assertCount(1, $groups1); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $usersPath, + null, + $params, + true, + $result + ); + + // Test 2: Named destructuring + ["before" => $before2, "after" => $after2, "groups" => $groups2] = $this->ds->listGroups(); + $this->assertNull($before2); + $this->assertNull($after2); + $this->assertIsArray($groups2); + $this->assertCount(1, $groups2); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $usersPath, + null, + $params, + true, + $result + ); + + // Test 3: Fluent access + $response = $this->ds->listGroups(); + $this->assertNull($response->before); + $this->assertNull($response->after); + $this->assertIsArray($response->groups); + $this->assertCount(1, $response->groups); + + // Test 4: Generic data accessor + $this->assertIsArray($response->data); + $this->assertSame($response->groups, $response->data); + } + public function testGetUser() { $directoryUser = "directory_usr_id"; @@ -218,6 +355,73 @@ public function testListUsers() $this->assertEquals($user, $users[0]->toArray()); } + public function testListUsersPaginatedResourceAccessPatterns() + { + $usersPath = "directory_users"; + $params = [ + "limit" => DirectorySync::DEFAULT_PAGE_SIZE, + "before" => null, + "after" => null, + "order" => null + ]; + + $result = $this->usersResponseFixture(); + + $this->mockRequest( + Client::METHOD_GET, + $usersPath, + null, + $params, + true, + $result + ); + + // Test 1: Bare destructuring (indexed) + [$before1, $after1, $users1] = $this->ds->listUsers(); + $this->assertNull($before1); + $this->assertNull($after1); + $this->assertIsArray($users1); + $this->assertCount(1, $users1); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $usersPath, + null, + $params, + true, + $result + ); + + // Test 2: Named destructuring + ["before" => $before2, "after" => $after2, "users" => $users2] = $this->ds->listUsers(); + $this->assertNull($before2); + $this->assertNull($after2); + $this->assertIsArray($users2); + $this->assertCount(1, $users2); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $usersPath, + null, + $params, + true, + $result + ); + + // Test 3: Fluent access + $response = $this->ds->listUsers(); + $this->assertNull($response->before); + $this->assertNull($response->after); + $this->assertIsArray($response->users); + $this->assertCount(1, $response->users); + + // Test 4: Generic data accessor + $this->assertIsArray($response->data); + $this->assertSame($response->users, $response->data); + } + public function testDeleteDirectory() { $directory = "directory_id"; diff --git a/tests/WorkOS/OrganizationsTest.php b/tests/WorkOS/OrganizationsTest.php index 6826798..eea6911 100644 --- a/tests/WorkOS/OrganizationsTest.php +++ b/tests/WorkOS/OrganizationsTest.php @@ -174,6 +174,74 @@ public function testListOrganizations() $this->assertSame($organization, $organizations[0]->toArray()); } + public function testListOrganizationsPaginatedResourceAccessPatterns() + { + $organizationsPath = "organizations"; + $params = [ + "limit" => Organizations::DEFAULT_PAGE_SIZE, + "before" => null, + "after" => null, + "domains" => null, + "order" => null + ]; + + $result = $this->organizationsResponseFixture(); + + $this->mockRequest( + Client::METHOD_GET, + $organizationsPath, + null, + $params, + true, + $result + ); + + // Test 1: Bare destructuring (indexed) + [$before1, $after1, $organizations1] = $this->organizations->listOrganizations(); + $this->assertSame("before-id", $before1); + $this->assertNull($after1); + $this->assertIsArray($organizations1); + $this->assertCount(3, $organizations1); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $organizationsPath, + null, + $params, + true, + $result + ); + + // Test 2: Named destructuring + ["before" => $before2, "after" => $after2, "organizations" => $organizations2] = $this->organizations->listOrganizations(); + $this->assertSame("before-id", $before2); + $this->assertNull($after2); + $this->assertIsArray($organizations2); + $this->assertCount(3, $organizations2); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $organizationsPath, + null, + $params, + true, + $result + ); + + // Test 3: Fluent access + $response = $this->organizations->listOrganizations(); + $this->assertSame("before-id", $response->before); + $this->assertNull($response->after); + $this->assertIsArray($response->organizations); + $this->assertCount(3, $response->organizations); + + // Test 4: Generic data accessor + $this->assertIsArray($response->data); + $this->assertSame($response->organizations, $response->data); + } + public function testListOrganizationRoles() { $organizationRolesPath = "organizations/org_01EHQMYV6MBK39QC5PZXHY59C3/roles"; @@ -223,6 +291,72 @@ public function testListOrganizationFeatureFlags() $this->assertSame($featureFlag, $featureFlags[0]->toArray()); } + public function testListOrganizationFeatureFlagsPaginatedResourceAccessPatterns() + { + $featureFlagsPath = "organizations/org_01EHQMYV6MBK39QC5PZXHY59C3/feature-flags"; + $result = $this->featureFlagsResponseFixture(); + $params = [ + "limit" => 10, + "before" => null, + "after" => null, + "order" => null + ]; + + $this->mockRequest( + Client::METHOD_GET, + $featureFlagsPath, + null, + $params, + true, + $result + ); + + // Test 1: Bare destructuring (indexed) + [$before1, $after1, $flags1] = $this->organizations->listOrganizationFeatureFlags("org_01EHQMYV6MBK39QC5PZXHY59C3"); + $this->assertSame("", $before1); + $this->assertSame("", $after1); + $this->assertIsArray($flags1); + $this->assertCount(3, $flags1); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $featureFlagsPath, + null, + $params, + true, + $result + ); + + // Test 2: Named destructuring + ["before" => $before2, "after" => $after2, "feature_flags" => $flags2] = $this->organizations->listOrganizationFeatureFlags("org_01EHQMYV6MBK39QC5PZXHY59C3"); + $this->assertSame("", $before2); + $this->assertSame("", $after2); + $this->assertIsArray($flags2); + $this->assertCount(3, $flags2); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $featureFlagsPath, + null, + $params, + true, + $result + ); + + // Test 3: Fluent access + $response = $this->organizations->listOrganizationFeatureFlags("org_01EHQMYV6MBK39QC5PZXHY59C3"); + $this->assertSame("", $response->before); + $this->assertSame("", $response->after); + $this->assertIsArray($response->feature_flags); + $this->assertCount(3, $response->feature_flags); + + // Test 4: Generic data accessor + $this->assertIsArray($response->data); + $this->assertSame($response->feature_flags, $response->data); + } + // Fixtures private function createOrganizationResponseFixture() diff --git a/tests/WorkOS/SSOTest.php b/tests/WorkOS/SSOTest.php index 1d16824..7c25663 100644 --- a/tests/WorkOS/SSOTest.php +++ b/tests/WorkOS/SSOTest.php @@ -172,6 +172,76 @@ public function testListConnections() $this->assertSame($connection, $connections[0]->toArray()); } + public function testListConnectionsPaginatedResourceAccessPatterns() + { + $connectionsPath = "connections"; + $params = [ + "limit" => SSO::DEFAULT_PAGE_SIZE, + "before" => null, + "after" => null, + "domain" => null, + "connection_type" => null, + "organization_id" => null, + "order" => null + ]; + + $result = $this->connectionsResponseFixture(); + + $this->mockRequest( + Client::METHOD_GET, + $connectionsPath, + null, + $params, + true, + $result + ); + + // Test 1: Bare destructuring (indexed) + [$before1, $after1, $connections1] = $this->sso->listConnections(); + $this->assertNull($before1); + $this->assertNull($after1); + $this->assertIsArray($connections1); + $this->assertCount(1, $connections1); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $connectionsPath, + null, + $params, + true, + $result + ); + + // Test 2: Named destructuring + ["before" => $before2, "after" => $after2, "connections" => $connections2] = $this->sso->listConnections(); + $this->assertNull($before2); + $this->assertNull($after2); + $this->assertIsArray($connections2); + $this->assertCount(1, $connections2); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $connectionsPath, + null, + $params, + true, + $result + ); + + // Test 3: Fluent access + $response = $this->sso->listConnections(); + $this->assertNull($response->before); + $this->assertNull($response->after); + $this->assertIsArray($response->connections); + $this->assertCount(1, $response->connections); + + // Test 4: Generic data accessor + $this->assertIsArray($response->data); + $this->assertSame($response->connections, $response->data); + } + public function testDeleteConnection() { $connection = "connection_id"; diff --git a/tests/WorkOS/UserManagementTest.php b/tests/WorkOS/UserManagementTest.php index 3a71d17..14e91ff 100644 --- a/tests/WorkOS/UserManagementTest.php +++ b/tests/WorkOS/UserManagementTest.php @@ -881,6 +881,75 @@ public function testListUsers() $this->assertSame($user, $users[0]->toArray()); } + public function testListUsersPaginatedResourceAccessPatterns() + { + $path = "user_management/users"; + $params = [ + "email" => null, + "organization_id" => null, + "limit" => UserManagement::DEFAULT_PAGE_SIZE, + "before" => null, + "after" => null, + "order" => null + ]; + + $result = $this->listUsersResponseFixture(); + + $this->mockRequest( + Client::METHOD_GET, + $path, + null, + $params, + true, + $result + ); + + // Test 1: Bare destructuring (indexed) + [$before1, $after1, $users1] = $this->userManagement->listUsers(); + $this->assertNull($before1); + $this->assertNull($after1); + $this->assertIsArray($users1); + $this->assertCount(1, $users1); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $path, + null, + $params, + true, + $result + ); + + // Test 2: Named destructuring + ["before" => $before2, "after" => $after2, "users" => $users2] = $this->userManagement->listUsers(); + $this->assertNull($before2); + $this->assertNull($after2); + $this->assertIsArray($users2); + $this->assertCount(1, $users2); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $path, + null, + $params, + true, + $result + ); + + // Test 3: Fluent access + $response = $this->userManagement->listUsers(); + $this->assertNull($response->before); + $this->assertNull($response->after); + $this->assertIsArray($response->users); + $this->assertCount(1, $response->users); + + // Test 4: Generic data accessor + $this->assertIsArray($response->data); + $this->assertSame($response->users, $response->data); + } + public function testGetMagicAuth() { $magicAuthId = "magic_auth_01E4ZCR3C56J083X43JQXF3JK5"; diff --git a/tests/WorkOS/VaultTest.php b/tests/WorkOS/VaultTest.php index 2b3e00d..2160b35 100644 --- a/tests/WorkOS/VaultTest.php +++ b/tests/WorkOS/VaultTest.php @@ -72,6 +72,72 @@ public function testListVaultObjects() $this->assertSame($vaultObjects, $response[0]->toArray()); } + public function testListVaultObjectsPaginatedResourceAccessPatterns() + { + $vaultObjectsPath = "vault/v1/kv"; + $result = $this->vaultObjectsResponseFixture(); + $params = [ + "limit" => 10, + "before" => null, + "after" => null, + "order" => null + ]; + + $this->mockRequest( + Client::METHOD_GET, + $vaultObjectsPath, + null, + $params, + true, + $result + ); + + // Test 1: Bare destructuring (indexed) + [$before1, $after1, $objects1] = $this->vault->listVaultObjects(); + $this->assertNull($before1); + $this->assertNull($after1); + $this->assertIsArray($objects1); + $this->assertCount(1, $objects1); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $vaultObjectsPath, + null, + $params, + true, + $result + ); + + // Test 2: Named destructuring + ["before" => $before2, "after" => $after2, "vault_objects" => $objects2] = $this->vault->listVaultObjects(); + $this->assertNull($before2); + $this->assertNull($after2); + $this->assertIsArray($objects2); + $this->assertCount(1, $objects2); + + // Mock the request again for the next test + $this->mockRequest( + Client::METHOD_GET, + $vaultObjectsPath, + null, + $params, + true, + $result + ); + + // Test 3: Fluent access + $response = $this->vault->listVaultObjects(); + $this->assertNull($response->before); + $this->assertNull($response->after); + $this->assertIsArray($response->vault_objects); + $this->assertCount(1, $response->vault_objects); + + // Test 4: Generic data accessor + $this->assertIsArray($response->data); + $this->assertSame($response->vault_objects, $response->data); + } +