From d35cb34f56a01bca99971ee2ac406ba3f22c8124 Mon Sep 17 00:00:00 2001 From: Birdcar <434063+birdcar@users.noreply.github.com> Date: Tue, 6 Jan 2026 17:17:26 -0600 Subject: [PATCH] Introduce a dedicated PaginatedResource object Previously, when returning data from a resource that was paginated, we would parse the pagination args off the resposne, loop over the "data" value in the response, and map each item in the JSON array to a specific resource. An example of this would be in the listUsers() method of the UserManagement class (truncated example below): ```php $users = []; list($before, $after) = Util\Request::parsePaginationArgs($response); foreach ($response["data"] as $responseData) { \array_push($users, Resource\User::constructFromResponse($responseData)); } return [$before, $after, $users]; ``` Performing this pattern over and over again resulted in a lot of duplicate code that was doing basically nothing more than an array_map. Additionally, this return is extremely limited and forces the user into a limited and specific pattern of either bare array destructuring: ```php [$before, $after, $users] = $userManagement->listUsers(); ``` Or dealing with 0-indexed array values: ```php $result = $userManagement->listUsers(); ``` If for example they just want the first 5 users and don't care about paginating, this means they'd to either write destructuring that has empty values: ```php // Huh? [,,$users] = $userManagement->listUsers(limit: 5); ``` Or they'd have to drop down to ```php $results = $userManagement->listUsers(limit: 5); // How do I discover or know what this index is? $users = $results[2]; ``` To fix both of these issues, without affecting current library consumers, I'm proposing that we create a `Resource\PaginatedResource` class that: 1. DRYs and standardizes the creation of a paginated resource 2. Handles the resource mapping from the data array 3. Continues to allow for bare destructuring (backwards compatible) 4. Introduces named destructuring (e.g `$result["after"]` or `["users" => $fiveUsers] = $userManagement->listUsers(limit:5)`) 5. Introduces fluent property access (e.g. `$result->after` or `$result->users`) The change is fully backwards compatible, cleans up existing resource code and allows for developers to use the library in whichever code style is consistent with their project. For example, it lets you turn this code: ```php [$before, $after, $users] = $userManagement->listUsers(); while ($after) { [$before, $after, $currentPage] = $sso->listConnections( limit: 100, after: $after, order: "desc" ); $users = array_merge($users, $currentPage); } ``` Into this code: ```php $users = []; $after = null; do { $result = $userManagement->listUsers(after: $after, limit: 10); $users = array_merge($allUsers, $result->users); $after = $result->after; } while ($after !== null); ``` --- lib/DirectorySync.php | 33 +- lib/Organizations.php | 22 +- lib/Resource/PaginatedResource.php | 198 ++++++++ lib/SSO.php | 11 +- lib/UserManagement.php | 743 +++++++++++++--------------- lib/Vault.php | 11 +- tests/WorkOS/DirectorySyncTest.php | 204 ++++++++ tests/WorkOS/OrganizationsTest.php | 134 +++++ tests/WorkOS/SSOTest.php | 70 +++ tests/WorkOS/UserManagementTest.php | 69 +++ tests/WorkOS/VaultTest.php | 66 +++ 11 files changed, 1105 insertions(+), 456 deletions(-) create mode 100644 lib/Resource/PaginatedResource.php 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); + } +