diff --git a/package-lock.json b/package-lock.json index a16514e..6238e4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@seamapi/blueprint": "^1.8.0", "@seamapi/fake-seam-connect": "2.0.4", "@seamapi/smith": "^1.1.0", - "@seamapi/types": "1.1001.0", + "@seamapi/types": "1.1014.0", "change-case": "^5.4.4", "execa": "^10.0.1", "prettier": "^3.9.6" @@ -872,9 +872,9 @@ } }, "node_modules/@seamapi/types": { - "version": "1.1001.0", - "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.1001.0.tgz", - "integrity": "sha512-pwIEqMYCdOLlIHUzzLlMq/4K6QwAM3kWXooSxNWOlPyCeWk21SvrbTN/joXwZtky504g3unLPKMbFad1mlQyfQ==", + "version": "1.1014.0", + "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.1014.0.tgz", + "integrity": "sha512-PjgO79X+ug/cpHp5EJ8S6PqccV+Vr8rNGdZGDuc8WhufoksX3k+6FlJg1IkTe3wXnpCUqyc9n9DREg2hYkTnGw==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 3d08a03..d42534b 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "@seamapi/blueprint": "^1.8.0", "@seamapi/fake-seam-connect": "2.0.4", "@seamapi/smith": "^1.1.0", - "@seamapi/types": "1.1001.0", + "@seamapi/types": "1.1014.0", "change-case": "^5.4.4", "execa": "^10.0.1", "prettier": "^3.9.6" diff --git a/src/Resources/AccessCode.php b/src/Resources/AccessCode.php index 1188ccc..fc8f020 100644 --- a/src/Resources/AccessCode.php +++ b/src/Resources/AccessCode.php @@ -288,6 +288,10 @@ public static function from_json(mixed $json): Errors|null => \Seam\Resources\AccessCode\Errors\AccessCodeInactive::from_json( $json, ), + \Seam\Resources\AccessCode\Errors\ErrorCode::CODE_CONSTRAINTS_VIOLATED + => \Seam\Resources\AccessCode\Errors\CodeConstraintsViolated::from_json( + $json, + ), \Seam\Resources\AccessCode\Errors\ErrorCode::ACCOUNT_DISCONNECTED => \Seam\Resources\AccessCode\Errors\AccountDisconnected::from_json( $json, @@ -869,6 +873,50 @@ public function __construct( } } + /** + * The code cannot be set on the device because it violates the device's code constraints (for example, its length, digits, or a too-simple value). The code will not be retried until you change it. See the device's `code_constraints` and `supported_code_lengths`. + */ + final class CodeConstraintsViolated extends + \Seam\Resources\AccessCode\Errors + { + public static function from_json( + mixed $json, + ): CodeConstraintsViolated|null { + if (!$json) { + return null; + } + return new self( + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\AccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that this is an access code error. + */ + public true|null $is_access_code_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + /** * Indicates that the account is disconnected. */ @@ -1508,6 +1556,7 @@ enum ErrorCode: string case NO_SPACE_FOR_ACCESS_CODE_ON_DEVICE = "no_space_for_access_code_on_device"; case CONFLICTING_EXTERNAL_MODIFICATION = "conflicting_external_modification"; case ACCESS_CODE_INACTIVE = "access_code_inactive"; + case CODE_CONSTRAINTS_VIOLATED = "code_constraints_violated"; case ACCOUNT_DISCONNECTED = "account_disconnected"; case SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED = "salto_ks_subscription_limit_exceeded"; case INSUFFICIENT_PERMISSIONS = "insufficient_permissions"; diff --git a/src/Resources/AccessGrant.php b/src/Resources/AccessGrant.php index 678b906..cefe84d 100644 --- a/src/Resources/AccessGrant.php +++ b/src/Resources/AccessGrant.php @@ -16,6 +16,7 @@ public static function from_json(mixed $json): AccessGrant|null access_method_ids: $json->access_method_ids ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, + display_status: $json->display_status ?? null, ends_at: $json->ends_at ?? null, errors: array_map( fn($e) => \Seam\Resources\AccessGrant\Errors::from_json($e), @@ -77,6 +78,10 @@ public function __construct( * Display name of the Access Grant. */ public string|null $display_name, + /** + * Human-readable sentence answering whether the user can currently get in, for example `Awaiting encoding` on an access method or `Upcoming` here. For display only. The wording is not stable and is not an enumeration — it may change at any time, so never compare against or branch on it. To make decisions, read `starts_at`, `ends_at`, `errors`, and the access methods' own fields. + */ + public string|null $display_status, /** * Date and time at which the Access Grant ends. */ diff --git a/src/Resources/AccessMethod.php b/src/Resources/AccessMethod.php index d331fb4..67149d2 100644 --- a/src/Resources/AccessMethod.php +++ b/src/Resources/AccessMethod.php @@ -15,6 +15,7 @@ public static function from_json(mixed $json): AccessMethod|null access_method_id: $json->access_method_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, + display_status: $json->display_status ?? null, errors: array_map( fn($e) => \Seam\Resources\AccessMethod\Errors::from_json( $e, @@ -64,6 +65,10 @@ public function __construct( * Display name of the access method. */ public string|null $display_name, + /** + * Human-readable sentence describing where the access method sits in its relationship with the device or access system, for example `Awaiting encoding`. For display only. The wording is not stable and is not an enumeration — it may change at any time, so never compare against or branch on it. To make decisions, read `is_issued`, `errors`, and `pending_mutations`. + */ + public string|null $display_status, /** * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). * diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 7977ece..20a36c2 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -3105,6 +3105,7 @@ public static function from_json(mixed $json): Result|null access_method_id: $json->access_method_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, + display_status: $json->display_status ?? null, errors: array_map( fn( $e, @@ -3158,6 +3159,10 @@ public function __construct( * Display name of the access method. */ public string|null $display_name, + /** + * Human-readable sentence describing where the access method sits in its relationship with the device or access system, for example `Awaiting encoding`. For display only. The wording is not stable and is not an enumeration — it may change at any time, so never compare against or branch on it. To make decisions, read `is_issued`, `errors`, and `pending_mutations`. + */ + public string|null $display_status, /** * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). * diff --git a/src/Resources/UnmanagedAccessCode.php b/src/Resources/UnmanagedAccessCode.php index 35991da..8ae1d4c 100644 --- a/src/Resources/UnmanagedAccessCode.php +++ b/src/Resources/UnmanagedAccessCode.php @@ -244,6 +244,10 @@ public static function from_json(mixed $json): Errors|null => \Seam\Resources\UnmanagedAccessCode\Errors\AccessCodeInactive::from_json( $json, ), + \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::CODE_CONSTRAINTS_VIOLATED + => \Seam\Resources\UnmanagedAccessCode\Errors\CodeConstraintsViolated::from_json( + $json, + ), \Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode::ACCOUNT_DISCONNECTED => \Seam\Resources\UnmanagedAccessCode\Errors\AccountDisconnected::from_json( $json, @@ -758,6 +762,50 @@ public function __construct( } } + /** + * The code cannot be set on the device because it violates the device's code constraints (for example, its length, digits, or a too-simple value). The code will not be retried until you change it. See the device's `code_constraints` and `supported_code_lengths`. + */ + final class CodeConstraintsViolated extends + \Seam\Resources\UnmanagedAccessCode\Errors + { + public static function from_json( + mixed $json, + ): CodeConstraintsViolated|null { + if (!$json) { + return null; + } + return new self( + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + message: $json->message ?? null, + created_at: $json->created_at ?? null, + ); + } + + public function __construct( + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * + * @var value-of<\Seam\Resources\UnmanagedAccessCode\Errors\ErrorCode>|string|null + */ + string|null $error_code, + /** + * Indicates that this is an access code error. + */ + public true|null $is_access_code_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + string|null $message, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at = null, + ) { + parent::__construct(error_code: $error_code, message: $message); + } + } + /** * Indicates that the account is disconnected. */ @@ -1403,6 +1451,7 @@ enum ErrorCode: string case NO_SPACE_FOR_ACCESS_CODE_ON_DEVICE = "no_space_for_access_code_on_device"; case CONFLICTING_EXTERNAL_MODIFICATION = "conflicting_external_modification"; case ACCESS_CODE_INACTIVE = "access_code_inactive"; + case CODE_CONSTRAINTS_VIOLATED = "code_constraints_violated"; case ACCOUNT_DISCONNECTED = "account_disconnected"; case SALTO_KS_SUBSCRIPTION_LIMIT_EXCEEDED = "salto_ks_subscription_limit_exceeded"; case INSUFFICIENT_PERMISSIONS = "insufficient_permissions"; diff --git a/src/Resources/UnmanagedAccessMethod.php b/src/Resources/UnmanagedAccessMethod.php index f24a7e2..cac468b 100644 --- a/src/Resources/UnmanagedAccessMethod.php +++ b/src/Resources/UnmanagedAccessMethod.php @@ -16,6 +16,7 @@ public static function from_json( access_method_id: $json->access_method_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, + display_status: $json->display_status ?? null, errors: array_map( fn( $e, @@ -65,6 +66,10 @@ public function __construct( * Display name of the access method. */ public string|null $display_name, + /** + * Human-readable sentence describing where the access method sits in its relationship with the device or access system, for example `Awaiting encoding`. For display only. The wording is not stable and is not an enumeration — it may change at any time, so never compare against or branch on it. To make decisions, read `is_issued`, `errors`, and `pending_mutations`. + */ + public string|null $display_status, /** * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). * diff --git a/src/Resources/UnmanagedUserIdentity.php b/src/Resources/UnmanagedUserIdentity.php index c7b8cfe..fe072b8 100644 --- a/src/Resources/UnmanagedUserIdentity.php +++ b/src/Resources/UnmanagedUserIdentity.php @@ -26,6 +26,10 @@ public static function from_json( $json->errors ?? [], ), full_name: $json->full_name ?? null, + merged_user_identity_ids: $json->merged_user_identity_ids ?? + null, + merged_user_identity_keys: $json->merged_user_identity_keys ?? + null, phone_number: $json->phone_number ?? null, user_identity_id: $json->user_identity_id ?? null, warnings: array_map( @@ -69,6 +73,18 @@ public function __construct( * Full name of the user associated with the user identity. */ public string|null $full_name, + /** + * IDs that other user identities used to have before they were merged into this user identity. Looking up any of them returns this user identity. + * + * @var list|null + */ + public array|null $merged_user_identity_ids, + /** + * Keys that other user identities used to have before they were merged into this user identity. Looking up any of them returns this user identity. + * + * @var list|null + */ + public array|null $merged_user_identity_keys, /** * Unique phone number for the user identity in [E.164 format](https://www.itu.int/rec/T-REC-E.164/en) (for example, +15555550100). */ diff --git a/src/Resources/UserIdentity.php b/src/Resources/UserIdentity.php index 3153515..9cda66e 100644 --- a/src/Resources/UserIdentity.php +++ b/src/Resources/UserIdentity.php @@ -23,6 +23,10 @@ public static function from_json(mixed $json): UserIdentity|null $json->errors ?? [], ), full_name: $json->full_name ?? null, + merged_user_identity_ids: $json->merged_user_identity_ids ?? + null, + merged_user_identity_keys: $json->merged_user_identity_keys ?? + null, phone_number: $json->phone_number ?? null, user_identity_id: $json->user_identity_id ?? null, user_identity_key: $json->user_identity_key ?? null, @@ -65,6 +69,18 @@ public function __construct( * Full name of the user associated with the user identity. */ public string|null $full_name, + /** + * IDs that other user identities used to have before they were merged into this user identity. Looking up any of them returns this user identity. + * + * @var list|null + */ + public array|null $merged_user_identity_ids, + /** + * Keys that other user identities used to have before they were merged into this user identity. Looking up any of them returns this user identity. + * + * @var list|null + */ + public array|null $merged_user_identity_keys, /** * Unique phone number for the user identity in [E.164 format](https://www.itu.int/rec/T-REC-E.164/en) (for example, +15555550100). */ diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index ab4826e..fb53a62 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -189,38 +189,35 @@ public function get(string $acs_credential_id): AcsCredential /** * Returns a list of all [credentials](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). * - * @param string $acs_user_id ID of the access system user for which you want to retrieve all credentials. * @param string $acs_system_id ID of the access system for which you want to retrieve all credentials. - * @param string $user_identity_id ID of the user identity for which you want to retrieve all credentials. + * @param string $acs_user_id ID of the access system user for which you want to retrieve all credentials. * @param string $created_before Date and time, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format, before which events to return were created. * @param bool $is_multi_phone_sync_credential Indicates whether you want to retrieve only multi-phone sync credentials or non-multi-phone sync credentials. * @param float $limit Number of credentials to return. * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned credentials to include all records that satisfy a partial match using `display_name`, `code`, `card_number`, `acs_user_id` or `acs_credential_id`. + * @param string $user_identity_id ID of the user identity for which you want to retrieve all credentials. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( - ?string $acs_user_id = null, ?string $acs_system_id = null, - ?string $user_identity_id = null, + ?string $acs_user_id = null, ?string $created_before = null, ?bool $is_multi_phone_sync_credential = null, ?float $limit = null, string|NullValue|null $page_cursor = null, ?string $search = null, + ?string $user_identity_id = null, ?callable $on_response = null, ): array { $request_payload = []; - if ($acs_user_id !== null) { - $request_payload["acs_user_id"] = $acs_user_id; - } if ($acs_system_id !== null) { $request_payload["acs_system_id"] = $acs_system_id; } - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; + if ($acs_user_id !== null) { + $request_payload["acs_user_id"] = $acs_user_id; } if ($created_before !== null) { $request_payload["created_before"] = $created_before; @@ -239,6 +236,9 @@ public function list( if ($search !== null) { $request_payload["search"] = $search; } + if ($user_identity_id !== null) { + $request_payload["user_identity_id"] = $user_identity_id; + } $res = Body::decode( $this->client->request("GET", "/acs/credentials/list", [ diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index e5fec99..48bd8e4 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -99,33 +99,33 @@ public function get(string $acs_encoder_id): AcsEncoder /** * Returns a list of all [encoders](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). * + * @param list $acs_encoder_ids IDs of the encoders that you want to retrieve. * @param string $acs_system_id ID of the access system for which you want to retrieve all encoders. * @param list $acs_system_ids IDs of the access systems for which you want to retrieve all encoders. - * @param list $acs_encoder_ids IDs of the encoders that you want to retrieve. * @param float $limit Number of encoders to return. * @param string|NullValue $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( + ?array $acs_encoder_ids = null, ?string $acs_system_id = null, ?array $acs_system_ids = null, - ?array $acs_encoder_ids = null, ?float $limit = null, string|NullValue|null $page_cursor = null, ?callable $on_response = null, ): array { $request_payload = []; + if ($acs_encoder_ids !== null) { + $request_payload["acs_encoder_ids"] = $acs_encoder_ids; + } if ($acs_system_id !== null) { $request_payload["acs_system_id"] = $acs_system_id; } if ($acs_system_ids !== null) { $request_payload["acs_system_ids"] = $acs_system_ids; } - if ($acs_encoder_ids !== null) { - $request_payload["acs_encoder_ids"] = $acs_encoder_ids; - } if ($limit !== null) { $request_payload["limit"] = $limit; } diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index 3bb35ff..c45d7ca 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -406,6 +406,61 @@ public function list_acs_users(string $user_identity_id): array ); } + /** + * Merges one or more [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) into a primary user identity, for when the same person ended up with more than one user identity. + * + * The primary user identity takes on any email address or phone number it was missing from the user identities merged into it, and the merged user identities are then deleted. Their IDs and keys keep working: looking one up returns the primary user identity, and they are listed on it as `merged_user_identity_ids` and `merged_user_identity_keys`. + * + * Access grants, access system users, client sessions and other resources belonging to the merged user identities are moved to the primary user identity. + * + * Identify the user identities either by ID or by key, but not both in the same request. Repeating a merge that has already been applied makes no further changes. + * + * @param list $merged_user_identity_ids IDs of the user identities to merge into the primary user identity. These user identities are deleted. + * @param string $user_identity_id ID of the primary user identity to keep. + * @param list $merged_user_identity_keys Keys of the user identities to merge into the primary user identity. These user identities are deleted. + * @param string $user_identity_key Key of the primary user identity to keep. + * @return void OK + */ + public function merge( + ?array $merged_user_identity_ids = null, + ?string $user_identity_id = null, + ?array $merged_user_identity_keys = null, + ?string $user_identity_key = null, + ): void { + if ( + $merged_user_identity_ids === null && + $user_identity_id === null && + $merged_user_identity_keys === null && + $user_identity_key === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/merge", + ); + } + $request_payload = []; + + if ($merged_user_identity_ids !== null) { + $request_payload[ + "merged_user_identity_ids" + ] = $merged_user_identity_ids; + } + if ($user_identity_id !== null) { + $request_payload["user_identity_id"] = $user_identity_id; + } + if ($merged_user_identity_keys !== null) { + $request_payload[ + "merged_user_identity_keys" + ] = $merged_user_identity_keys; + } + if ($user_identity_key !== null) { + $request_payload["user_identity_key"] = $user_identity_key; + } + + $this->client->request("POST", "/user_identities/merge", [ + "json" => (object) $request_payload, + ]); + } + /** * Removes a specified [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) from a specified [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity). *