From ec15117306e08709de2a62b9c1295dc93741cf37 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 1 Sep 2026 03:11:16 +0000 Subject: [PATCH 1/6] feat: make list parsing total and surface unknown action attempt statuses Seam adds event types, action types, and error codes between SDK releases, so a payload this version does not recognize should stay readable rather than cost the caller the whole response. A list property the API sends as a scalar no longer fails the whole response. The generated classes mapped these with array_map, which raises a TypeError when handed anything but an array, so a single unexpected field took down every other field alongside it. They now route through Seam\Parse::to_list, which reads a non-list as empty. Waiting on an action attempt whose status is neither pending, success, nor error raises the new ActionAttemptUnknownStatusError. The resolver previously treated an unrecognized status as non-terminal and polled until the deadline, then reported a timeout that misdescribed what happened. The error subclasses ActionAttemptError, so existing handlers for that base keep working. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01M2kJ4nGaM8imZCVMEKjmXA --- codegen/lib/layouts/resource.ts | 4 +- src/ActionAttemptUnknownStatusError.php | 34 ++++ src/Http/ResolveActionAttempt.php | 11 ++ src/Parse.php | 33 ++++ src/Resources/AccessCode.php | 20 +- src/Resources/AccessGrant.php | 20 +- src/Resources/AccessMethod.php | 12 +- src/Resources/AcsAccessGroup.php | 12 +- src/Resources/AcsCredential.php | 8 +- src/Resources/AcsEncoder.php | 4 +- src/Resources/AcsEntrance.php | 16 +- src/Resources/AcsSystem.php | 8 +- src/Resources/AcsUser.php | 12 +- src/Resources/ActionAttempt.php | 40 ++-- src/Resources/ConnectedAccount.php | 16 +- src/Resources/Device.php | 56 +++--- src/Resources/Event.php | 240 +++++++++++------------ src/Resources/Phone.php | 12 +- src/Resources/ThermostatDailyProgram.php | 4 +- src/Resources/ThermostatSchedule.php | 4 +- src/Resources/UnmanagedAccessCode.php | 16 +- src/Resources/UnmanagedAccessGrant.php | 20 +- src/Resources/UnmanagedAccessMethod.php | 12 +- src/Resources/UnmanagedDevice.php | 8 +- src/Resources/UnmanagedUserIdentity.php | 8 +- src/Resources/UserIdentity.php | 8 +- tests/TotalParsingTest.php | 89 +++++++++ 27 files changed, 448 insertions(+), 279 deletions(-) create mode 100644 src/ActionAttemptUnknownStatusError.php create mode 100644 src/Parse.php create mode 100644 tests/TotalParsingTest.php diff --git a/codegen/lib/layouts/resource.ts b/codegen/lib/layouts/resource.ts index e5e31b33..1436e328 100644 --- a/codegen/lib/layouts/resource.ts +++ b/codegen/lib/layouts/resource.ts @@ -57,7 +57,9 @@ const generateFromJsonProp = (property: ResourceClassProperty): string => { return `${name}: isset($json->${name}) ? ${property.referenceName}::from_json($json->${name}) : null,` case 'listReference': - return `${name}: array_map(fn ($${name[0]}) => ${property.referenceName}::from_json($${name[0]}), $json->${name} ?? []),` + // Via the helper, not array_map directly: a list property the API sends as + // a scalar would otherwise raise a TypeError and fail the whole response. + return `${name}: \\Seam\\Parse::to_list($json->${name} ?? null, fn ($${name[0]}) => ${property.referenceName}::from_json($${name[0]})),` case 'record': return `${name}: $json->${name} ?? null,` diff --git a/src/ActionAttemptUnknownStatusError.php b/src/ActionAttemptUnknownStatusError.php new file mode 100644 index 00000000..a4198a84 --- /dev/null +++ b/src/ActionAttemptUnknownStatusError.php @@ -0,0 +1,34 @@ +status = $status; + } + + public function getStatus(): string + { + return $this->status; + } +} diff --git a/src/Http/ResolveActionAttempt.php b/src/Http/ResolveActionAttempt.php index 90c3db54..d8637c74 100644 --- a/src/Http/ResolveActionAttempt.php +++ b/src/Http/ResolveActionAttempt.php @@ -4,6 +4,7 @@ use GuzzleHttp\ClientInterface; use Seam\ActionAttemptFailedError; +use Seam\ActionAttemptUnknownStatusError; use Seam\ActionAttemptTimeoutError; use Seam\InvalidOptionsError; use Seam\InvalidResponseError; @@ -80,6 +81,16 @@ private static function poll( throw new ActionAttemptFailedError($action_attempt); } + // Neither pending, success, nor error: a status added after this + // release. Polling on would block until the timeout and then report + // a timeout that misdescribes what happened. + if ($action_attempt->status !== "pending") { + throw new ActionAttemptUnknownStatusError( + $action_attempt, + (string) $action_attempt->status, + ); + } + $remaining = $deadline - self::now(); if ($remaining <= 0.0) { diff --git a/src/Parse.php b/src/Parse.php new file mode 100644 index 00000000..eb819fdb --- /dev/null +++ b/src/Parse.php @@ -0,0 +1,33 @@ + + */ + public static function to_list(mixed $value, callable $from_json): array + { + if (!is_array($value)) { + return []; + } + + return array_values(array_map($from_json, $value)); + } +} diff --git a/src/Resources/AccessCode.php b/src/Resources/AccessCode.php index a6ed128a..c2bada24 100644 --- a/src/Resources/AccessCode.php +++ b/src/Resources/AccessCode.php @@ -25,9 +25,9 @@ public static function from_json(mixed $json): AccessCode|null common_code_key: $json->common_code_key ?? null, created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn($e) => \Seam\Resources\AccessCode\Errors::from_json($e), - $json->errors ?? [], ), is_backup_access_code_available: $json->is_backup_access_code_available ?? null, @@ -37,21 +37,21 @@ public static function from_json(mixed $json): AccessCode|null is_offline_access_code: $json->is_offline_access_code ?? null, is_one_time_use: $json->is_one_time_use ?? null, name: $json->name ?? null, - pending_mutations: array_map( + pending_mutations: \Seam\Parse::to_list( + $json->pending_mutations ?? null, fn( $p, ) => \Seam\Resources\AccessCode\PendingMutations::from_json( $p, ), - $json->pending_mutations ?? [], ), status: $json->status ?? null, type: $json->type ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn($w) => \Seam\Resources\AccessCode\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, dormakaba_oracode_metadata: isset( @@ -800,13 +800,13 @@ public static function from_json( message: $json->message ?? null, change_type: $json->change_type ?? null, created_at: $json->created_at ?? null, - modified_fields: array_map( + modified_fields: \Seam\Parse::to_list( + $json->modified_fields ?? null, fn( $m, ) => \Seam\Resources\AccessCode\Errors\ConflictingExternalModification\ModifiedFields::from_json( $m, ), - $json->modified_fields ?? [], ), ); } @@ -2329,13 +2329,13 @@ public static function from_json( warning_code: $json->warning_code ?? null, change_type: $json->change_type ?? null, created_at: $json->created_at ?? null, - modified_fields: array_map( + modified_fields: \Seam\Parse::to_list( + $json->modified_fields ?? null, fn( $m, ) => \Seam\Resources\AccessCode\Warnings\ExternalModificationInEffect\ModifiedFields::from_json( $m, ), - $json->modified_fields ?? [], ), ); } diff --git a/src/Resources/AccessGrant.php b/src/Resources/AccessGrant.php index 786864ef..862e0e6e 100644 --- a/src/Resources/AccessGrant.php +++ b/src/Resources/AccessGrant.php @@ -18,36 +18,36 @@ public static function from_json(mixed $json): AccessGrant|null display_name: $json->display_name ?? null, display_status: $json->display_status ?? null, ends_at: $json->ends_at ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn($e) => \Seam\Resources\AccessGrant\Errors::from_json($e), - $json->errors ?? [], ), location_ids: $json->location_ids ?? null, name: $json->name ?? null, - pending_mutations: array_map( + pending_mutations: \Seam\Parse::to_list( + $json->pending_mutations ?? null, fn( $p, ) => \Seam\Resources\AccessGrant\PendingMutations::from_json( $p, ), - $json->pending_mutations ?? [], ), - requested_access_methods: array_map( + requested_access_methods: \Seam\Parse::to_list( + $json->requested_access_methods ?? null, fn( $r, ) => \Seam\Resources\AccessGrant\RequestedAccessMethods::from_json( $r, ), - $json->requested_access_methods ?? [], ), space_ids: $json->space_ids ?? null, starts_at: $json->starts_at ?? null, user_identity_id: $json->user_identity_id ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn($w) => \Seam\Resources\AccessGrant\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, access_grant_key: $json->access_grant_key ?? null, @@ -811,13 +811,13 @@ public static function from_json( created_at: $json->created_at ?? null, message: $json->message ?? null, warning_code: $json->warning_code ?? null, - failed_devices: array_map( + failed_devices: \Seam\Parse::to_list( + $json->failed_devices ?? null, fn( $f, ) => \Seam\Resources\AccessGrant\Warnings\OverprovisionedAccess\FailedDevices::from_json( $f, ), - $json->failed_devices ?? [], ), ); } diff --git a/src/Resources/AccessMethod.php b/src/Resources/AccessMethod.php index 67149d2e..2d7b8e71 100644 --- a/src/Resources/AccessMethod.php +++ b/src/Resources/AccessMethod.php @@ -16,28 +16,28 @@ public static function from_json(mixed $json): AccessMethod|null created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, display_status: $json->display_status ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn($e) => \Seam\Resources\AccessMethod\Errors::from_json( $e, ), - $json->errors ?? [], ), is_issued: $json->is_issued ?? null, issued_at: $json->issued_at ?? null, mode: $json->mode ?? null, - pending_mutations: array_map( + pending_mutations: \Seam\Parse::to_list( + $json->pending_mutations ?? null, fn( $p, ) => \Seam\Resources\AccessMethod\PendingMutations::from_json( $p, ), - $json->pending_mutations ?? [], ), - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn($w) => \Seam\Resources\AccessMethod\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, client_session_token: $json->client_session_token ?? null, diff --git a/src/Resources/AcsAccessGroup.php b/src/Resources/AcsAccessGroup.php index 463cd3d6..b928fded 100644 --- a/src/Resources/AcsAccessGroup.php +++ b/src/Resources/AcsAccessGroup.php @@ -24,30 +24,30 @@ public static function from_json(mixed $json): AcsAccessGroup|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn($e) => \Seam\Resources\AcsAccessGroup\Errors::from_json( $e, ), - $json->errors ?? [], ), external_type: $json->external_type ?? null, external_type_display_name: $json->external_type_display_name ?? null, is_managed: $json->is_managed ?? null, name: $json->name ?? null, - pending_mutations: array_map( + pending_mutations: \Seam\Parse::to_list( + $json->pending_mutations ?? null, fn( $p, ) => \Seam\Resources\AcsAccessGroup\PendingMutations::from_json( $p, ), - $json->pending_mutations ?? [], ), - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn( $w, ) => \Seam\Resources\AcsAccessGroup\Warnings::from_json($w), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, access_schedule: isset($json->access_schedule) diff --git a/src/Resources/AcsCredential.php b/src/Resources/AcsCredential.php index 87f5c279..b9bd8566 100644 --- a/src/Resources/AcsCredential.php +++ b/src/Resources/AcsCredential.php @@ -24,18 +24,18 @@ public static function from_json(mixed $json): AcsCredential|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn($e) => \Seam\Resources\AcsCredential\Errors::from_json( $e, ), - $json->errors ?? [], ), is_managed: $json->is_managed ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn($w) => \Seam\Resources\AcsCredential\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, acs_credential_pool_id: $json->acs_credential_pool_id ?? null, diff --git a/src/Resources/AcsEncoder.php b/src/Resources/AcsEncoder.php index 16fe4738..f8c7c7f3 100644 --- a/src/Resources/AcsEncoder.php +++ b/src/Resources/AcsEncoder.php @@ -30,9 +30,9 @@ public static function from_json(mixed $json): AcsEncoder|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn($e) => \Seam\Resources\AcsEncoder\Errors::from_json($e), - $json->errors ?? [], ), workspace_id: $json->workspace_id ?? null, ); diff --git a/src/Resources/AcsEntrance.php b/src/Resources/AcsEntrance.php index d12132df..245299d0 100644 --- a/src/Resources/AcsEntrance.php +++ b/src/Resources/AcsEntrance.php @@ -19,16 +19,16 @@ public static function from_json(mixed $json): AcsEntrance|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn($e) => \Seam\Resources\AcsEntrance\Errors::from_json($e), - $json->errors ?? [], ), space_ids: $json->space_ids ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn($w) => \Seam\Resources\AcsEntrance\Warnings::from_json( $w, ), - $json->warnings ?? [], ), akiles_metadata: isset($json->akiles_metadata) ? \Seam\Resources\AcsEntrance\AkilesMetadata::from_json( @@ -226,13 +226,13 @@ public static function from_json(mixed $json): AkilesMetadata|null return null; } return new self( - actions: array_map( + actions: \Seam\Parse::to_list( + $json->actions ?? null, fn( $a, ) => \Seam\Resources\AcsEntrance\AkilesMetadata\Actions::from_json( $a, ), - $json->actions ?? [], ), gadget_id: $json->gadget_id ?? null, site_id: $json->site_id ?? null, @@ -666,13 +666,13 @@ public static function from_json(mixed $json): VisionlineMetadata|null return new self( door_category: $json->door_category ?? null, door_name: $json->door_name ?? null, - profiles: array_map( + profiles: \Seam\Parse::to_list( + $json->profiles ?? null, fn( $p, ) => \Seam\Resources\AcsEntrance\VisionlineMetadata\Profiles::from_json( $p, ), - $json->profiles ?? [], ), ); } diff --git a/src/Resources/AcsSystem.php b/src/Resources/AcsSystem.php index 88a89b3c..a036fd15 100644 --- a/src/Resources/AcsSystem.php +++ b/src/Resources/AcsSystem.php @@ -20,9 +20,9 @@ public static function from_json(mixed $json): AcsSystem|null connected_account_id: $json->connected_account_id ?? null, connected_account_ids: $json->connected_account_ids ?? null, created_at: $json->created_at ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn($e) => \Seam\Resources\AcsSystem\Errors::from_json($e), - $json->errors ?? [], ), image_alt_text: $json->image_alt_text ?? null, image_url: $json->image_url ?? null, @@ -33,9 +33,9 @@ public static function from_json(mixed $json): AcsSystem|null ) : null, name: $json->name ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn($w) => \Seam\Resources\AcsSystem\Warnings::from_json($w), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, acs_access_group_count: $json->acs_access_group_count ?? null, diff --git a/src/Resources/AcsUser.php b/src/Resources/AcsUser.php index c063569a..9207ce4d 100644 --- a/src/Resources/AcsUser.php +++ b/src/Resources/AcsUser.php @@ -21,14 +21,14 @@ public static function from_json(mixed $json): AcsUser|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn($e) => \Seam\Resources\AcsUser\Errors::from_json($e), - $json->errors ?? [], ), is_managed: $json->is_managed ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn($w) => \Seam\Resources\AcsUser\Warnings::from_json($w), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, access_schedule: isset($json->access_schedule) @@ -44,13 +44,13 @@ public static function from_json(mixed $json): AcsUser|null full_name: $json->full_name ?? null, hid_acs_system_id: $json->hid_acs_system_id ?? null, is_suspended: $json->is_suspended ?? null, - pending_mutations: array_map( + pending_mutations: \Seam\Parse::to_list( + $json->pending_mutations ?? null, fn( $p, ) => \Seam\Resources\AcsUser\PendingMutations::from_json( $p, ), - $json->pending_mutations ?? [], ), phone_number: $json->phone_number ?? null, salto_ks_metadata: isset($json->salto_ks_metadata) diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 7d401ba5..0d1067b2 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -2020,13 +2020,13 @@ public static function from_json(mixed $json): Result|null $json->acs_credential_on_seam, ) : null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn( $w, ) => \Seam\Resources\ActionAttempt\ScanCredential\Success\Result\Warnings::from_json( $w, ), - $json->warnings ?? [], ), ); } @@ -2121,22 +2121,22 @@ public static function from_json(mixed $json): AcsCredentialOnSeam|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn( $e, ) => \Seam\Resources\ActionAttempt\ScanCredential\Success\Result\AcsCredentialOnSeam\Errors::from_json( $e, ), - $json->errors ?? [], ), is_managed: $json->is_managed ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn( $w, ) => \Seam\Resources\ActionAttempt\ScanCredential\Success\Result\AcsCredentialOnSeam\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, acs_credential_pool_id: $json->acs_credential_pool_id ?? null, @@ -2914,22 +2914,22 @@ public static function from_json(mixed $json): Result|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn( $e, ) => \Seam\Resources\ActionAttempt\EncodeCredential\Success\Result\Errors::from_json( $e, ), - $json->errors ?? [], ), is_managed: $json->is_managed ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn( $w, ) => \Seam\Resources\ActionAttempt\EncodeCredential\Success\Result\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, acs_credential_pool_id: $json->acs_credential_pool_id ?? null, @@ -3582,22 +3582,22 @@ public static function from_json(mixed $json): Result|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn( $e, ) => \Seam\Resources\ActionAttempt\ScanToAssignCredential\Success\Result\Errors::from_json( $e, ), - $json->errors ?? [], ), is_managed: $json->is_managed ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn( $w, ) => \Seam\Resources\ActionAttempt\ScanToAssignCredential\Success\Result\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, acs_credential_pool_id: $json->acs_credential_pool_id ?? null, @@ -4241,32 +4241,32 @@ public static function from_json(mixed $json): Result|null created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, display_status: $json->display_status ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn( $e, ) => \Seam\Resources\ActionAttempt\AssignCredential\Success\Result\Errors::from_json( $e, ), - $json->errors ?? [], ), is_issued: $json->is_issued ?? null, issued_at: $json->issued_at ?? null, mode: $json->mode ?? null, - pending_mutations: array_map( + pending_mutations: \Seam\Parse::to_list( + $json->pending_mutations ?? null, fn( $p, ) => \Seam\Resources\ActionAttempt\AssignCredential\Success\Result\PendingMutations::from_json( $p, ), - $json->pending_mutations ?? [], ), - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn( $w, ) => \Seam\Resources\ActionAttempt\AssignCredential\Success\Result\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, client_session_token: $json->client_session_token ?? null, diff --git a/src/Resources/ConnectedAccount.php b/src/Resources/ConnectedAccount.php index 04da53ca..2acdb416 100644 --- a/src/Resources/ConnectedAccount.php +++ b/src/Resources/ConnectedAccount.php @@ -20,19 +20,19 @@ public static function from_json(mixed $json): ConnectedAccount|null connected_account_id: $json->connected_account_id ?? null, custom_metadata: $json->custom_metadata ?? null, display_name: $json->display_name ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn( $e, ) => \Seam\Resources\ConnectedAccount\Errors::from_json($e), - $json->errors ?? [], ), - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn( $w, ) => \Seam\Resources\ConnectedAccount\Warnings::from_json( $w, ), - $json->warnings ?? [], ), account_type: $json->account_type ?? null, created_at: $json->created_at ?? null, @@ -583,13 +583,13 @@ public static function from_json(mixed $json): SaltoKsMetadata|null return null; } return new self( - sites: array_map( + sites: \Seam\Parse::to_list( + $json->sites ?? null, fn( $s, ) => \Seam\Resources\ConnectedAccount\Errors\SaltoKsSubscriptionLimitExceeded\SaltoKsMetadata\Sites::from_json( $s, ), - $json->sites ?? [], ), ); } @@ -1022,13 +1022,13 @@ public static function from_json(mixed $json): SaltoKsMetadata|null return null; } return new self( - sites: array_map( + sites: \Seam\Parse::to_list( + $json->sites ?? null, fn( $s, ) => \Seam\Resources\ConnectedAccount\Warnings\SaltoKsSubscriptionLimitAlmostReached\SaltoKsMetadata\Sites::from_json( $s, ), - $json->sites ?? [], ), ); } diff --git a/src/Resources/Device.php b/src/Resources/Device.php index bf618cad..ce70cb11 100644 --- a/src/Resources/Device.php +++ b/src/Resources/Device.php @@ -19,9 +19,9 @@ public static function from_json(mixed $json): Device|null device_id: $json->device_id ?? null, device_type: $json->device_type ?? null, display_name: $json->display_name ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn($e) => \Seam\Resources\Device\Errors::from_json($e), - $json->errors ?? [], ), is_managed: $json->is_managed ?? null, properties: isset($json->properties) @@ -30,9 +30,9 @@ public static function from_json(mixed $json): Device|null ) : null, space_ids: $json->space_ids ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn($w) => \Seam\Resources\Device\Warnings::from_json($w), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, can_configure_auto_lock: $json->can_configure_auto_lock ?? null, @@ -530,13 +530,13 @@ public static function from_json(mixed $json): Properties|null auto_lock_enabled: $json->auto_lock_enabled ?? null, available_climate_preset_modes: $json->available_climate_preset_modes ?? null, - available_climate_presets: array_map( + available_climate_presets: \Seam\Parse::to_list( + $json->available_climate_presets ?? null, fn( $a, ) => \Seam\Resources\Device\Properties\AvailableClimatePresets::from_json( $a, ), - $json->available_climate_presets ?? [], ), available_fan_mode_settings: $json->available_fan_mode_settings ?? null, @@ -560,13 +560,13 @@ public static function from_json(mixed $json): Properties|null $json->brivo_metadata, ) : null, - code_constraints: array_map( + code_constraints: \Seam\Parse::to_list( + $json->code_constraints ?? null, fn( $c, ) => \Seam\Resources\Device\Properties\CodeConstraints::from_json( $c, ), - $json->code_constraints ?? [], ), controlbyweb_metadata: isset($json->controlbyweb_metadata) ? \Seam\Resources\Device\Properties\ControlbywebMetadata::from_json( @@ -718,13 +718,13 @@ public static function from_json(mixed $json): Properties|null : null, offline_access_codes_enabled: $json->offline_access_codes_enabled ?? null, - offline_time_frame_options: array_map( + offline_time_frame_options: \Seam\Parse::to_list( + $json->offline_time_frame_options ?? null, fn( $o, ) => \Seam\Resources\Device\Properties\OfflineTimeFrameOptions::from_json( $o, ), - $json->offline_time_frame_options ?? [], ), omnitec_metadata: isset($json->omnitec_metadata) ? \Seam\Resources\Device\Properties\OmnitecMetadata::from_json( @@ -733,13 +733,13 @@ public static function from_json(mixed $json): Properties|null : null, online_access_codes_enabled: $json->online_access_codes_enabled ?? null, - online_time_frame_options: array_map( + online_time_frame_options: \Seam\Parse::to_list( + $json->online_time_frame_options ?? null, fn( $o, ) => \Seam\Resources\Device\Properties\OnlineTimeFrameOptions::from_json( $o, ), - $json->online_time_frame_options ?? [], ), relative_humidity: $json->relative_humidity ?? null, ring_metadata: isset($json->ring_metadata) @@ -811,13 +811,13 @@ public static function from_json(mixed $json): Properties|null : null, thermostat_daily_program_period_precision_minutes: $json->thermostat_daily_program_period_precision_minutes ?? null, - thermostat_daily_programs: array_map( + thermostat_daily_programs: \Seam\Parse::to_list( + $json->thermostat_daily_programs ?? null, fn( $t, ) => \Seam\Resources\Device\Properties\ThermostatDailyPrograms::from_json( $t, ), - $json->thermostat_daily_programs ?? [], ), thermostat_weekly_program: isset( $json->thermostat_weekly_program, @@ -2372,13 +2372,13 @@ public static function from_json( return null; } return new self( - endpoints: array_map( + endpoints: \Seam\Parse::to_list( + $json->endpoints ?? null, fn( $e, ) => \Seam\Resources\Device\Properties\AssaAbloyCredentialServiceMetadata\Endpoints::from_json( $e, ), - $json->endpoints ?? [], ), has_active_endpoint: $json->has_active_endpoint ?? null, ); @@ -2723,13 +2723,13 @@ public static function from_json( door_is_wireless: $json->door_is_wireless ?? null, door_name: $json->door_name ?? null, iana_timezone: $json->iana_timezone ?? null, - predefined_time_slots: array_map( + predefined_time_slots: \Seam\Parse::to_list( + $json->predefined_time_slots ?? null, fn( $p, ) => \Seam\Resources\Device\Properties\DormakabaOracodeMetadata\PredefinedTimeSlots::from_json( $p, ), - $json->predefined_time_slots ?? [], ), site_id: $json->site_id ?? null, site_name: $json->site_name ?? null, @@ -3885,13 +3885,13 @@ public static function from_json(mixed $json): TtlockMetadata|null lock_alias: $json->lock_alias ?? null, lock_id: $json->lock_id ?? null, timezone_raw_offset_ms: $json->timezone_raw_offset_ms ?? null, - wireless_keypads: array_map( + wireless_keypads: \Seam\Parse::to_list( + $json->wireless_keypads ?? null, fn( $w, ) => \Seam\Resources\Device\Properties\TtlockMetadata\WirelessKeypads::from_json( $w, ), - $json->wireless_keypads ?? [], ), ); } @@ -4187,13 +4187,13 @@ public static function from_json( min_duration: $json->min_duration ?? null, start_date_recurrence_rule: $json->start_date_recurrence_rule ?? null, - time_pairs: array_map( + time_pairs: \Seam\Parse::to_list( + $json->time_pairs ?? null, fn( $t, ) => \Seam\Resources\Device\Properties\OfflineTimeFrameOptions\TimePairs::from_json( $t, ), - $json->time_pairs ?? [], ), time_zone: $json->time_zone ?? null, ); @@ -4257,13 +4257,13 @@ public static function from_json( min_duration: $json->min_duration ?? null, start_date_recurrence_rule: $json->start_date_recurrence_rule ?? null, - time_pairs: array_map( + time_pairs: \Seam\Parse::to_list( + $json->time_pairs ?? null, fn( $t, ) => \Seam\Resources\Device\Properties\OnlineTimeFrameOptions\TimePairs::from_json( $t, ), - $json->time_pairs ?? [], ), time_zone: $json->time_zone ?? null, ); @@ -4325,13 +4325,13 @@ public static function from_json( created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, ends_at: $json->ends_at ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn( $e, ) => \Seam\Resources\Device\Properties\ActiveThermostatSchedule\Errors::from_json( $e, ), - $json->errors ?? [], ), name: $json->name ?? null, starts_at: $json->starts_at ?? null, @@ -4779,13 +4779,13 @@ public static function from_json( created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, name: $json->name ?? null, - periods: array_map( + periods: \Seam\Parse::to_list( + $json->periods ?? null, fn( $p, ) => \Seam\Resources\Device\Properties\ThermostatDailyPrograms\Periods::from_json( $p, ), - $json->periods ?? [], ), thermostat_daily_program_id: $json->thermostat_daily_program_id ?? null, diff --git a/src/Resources/Event.php b/src/Resources/Event.php index 4889be49..e409da03 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -560,13 +560,13 @@ public static function from_json(mixed $json): AccessCodeChanged|null occurred_at: $json->occurred_at ?? null, workspace_id: $json->workspace_id ?? null, change_reason: $json->change_reason ?? null, - changed_properties: array_map( + changed_properties: \Seam\Parse::to_list( + $json->changed_properties ?? null, fn( $c, ) => \Seam\Resources\Event\AccessCodeChanged\ChangedProperties::from_json( $c, ), - $json->changed_properties ?? [], ), connected_account_custom_metadata: $json->connected_account_custom_metadata ?? null, @@ -998,13 +998,13 @@ public static function from_json( event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, occurred_at: $json->occurred_at ?? null, - requested_mutations: array_map( + requested_mutations: \Seam\Parse::to_list( + $json->requested_mutations ?? null, fn( $r, ) => \Seam\Resources\Event\AccessCodeMutationsRequested\RequestedMutations::from_json( $r, ), - $json->requested_mutations ?? [], ), workspace_id: $json->workspace_id ?? null, connected_account_custom_metadata: $json->connected_account_custom_metadata ?? @@ -1367,57 +1367,57 @@ public static function from_json( return null; } return new self( - access_code_errors: array_map( + access_code_errors: \Seam\Parse::to_list( + $json->access_code_errors ?? null, fn( $a, ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\AccessCodeErrors::from_json( $a, ), - $json->access_code_errors ?? [], ), access_code_id: $json->access_code_id ?? null, - access_code_warnings: array_map( + access_code_warnings: \Seam\Parse::to_list( + $json->access_code_warnings ?? null, fn( $a, ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\AccessCodeWarnings::from_json( $a, ), - $json->access_code_warnings ?? [], ), - connected_account_errors: array_map( + connected_account_errors: \Seam\Parse::to_list( + $json->connected_account_errors ?? null, fn( $c, ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\ConnectedAccountErrors::from_json( $c, ), - $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: array_map( + connected_account_warnings: \Seam\Parse::to_list( + $json->connected_account_warnings ?? null, fn( $c, ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\ConnectedAccountWarnings::from_json( $c, ), - $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: array_map( + device_errors: \Seam\Parse::to_list( + $json->device_errors ?? null, fn( $d, ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\DeviceErrors::from_json( $d, ), - $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: array_map( + device_warnings: \Seam\Parse::to_list( + $json->device_warnings ?? null, fn( $d, ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\DeviceWarnings::from_json( $d, ), - $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -1539,57 +1539,57 @@ public static function from_json( return null; } return new self( - access_code_errors: array_map( + access_code_errors: \Seam\Parse::to_list( + $json->access_code_errors ?? null, fn( $a, ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\AccessCodeErrors::from_json( $a, ), - $json->access_code_errors ?? [], ), access_code_id: $json->access_code_id ?? null, - access_code_warnings: array_map( + access_code_warnings: \Seam\Parse::to_list( + $json->access_code_warnings ?? null, fn( $a, ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\AccessCodeWarnings::from_json( $a, ), - $json->access_code_warnings ?? [], ), - connected_account_errors: array_map( + connected_account_errors: \Seam\Parse::to_list( + $json->connected_account_errors ?? null, fn( $c, ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\ConnectedAccountErrors::from_json( $c, ), - $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: array_map( + connected_account_warnings: \Seam\Parse::to_list( + $json->connected_account_warnings ?? null, fn( $c, ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\ConnectedAccountWarnings::from_json( $c, ), - $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: array_map( + device_errors: \Seam\Parse::to_list( + $json->device_errors ?? null, fn( $d, ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\DeviceErrors::from_json( $d, ), - $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: array_map( + device_warnings: \Seam\Parse::to_list( + $json->device_warnings ?? null, fn( $d, ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\DeviceWarnings::from_json( $d, ), - $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -1804,57 +1804,57 @@ public static function from_json( return null; } return new self( - access_code_errors: array_map( + access_code_errors: \Seam\Parse::to_list( + $json->access_code_errors ?? null, fn( $a, ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\AccessCodeErrors::from_json( $a, ), - $json->access_code_errors ?? [], ), access_code_id: $json->access_code_id ?? null, - access_code_warnings: array_map( + access_code_warnings: \Seam\Parse::to_list( + $json->access_code_warnings ?? null, fn( $a, ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\AccessCodeWarnings::from_json( $a, ), - $json->access_code_warnings ?? [], ), - connected_account_errors: array_map( + connected_account_errors: \Seam\Parse::to_list( + $json->connected_account_errors ?? null, fn( $c, ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\ConnectedAccountErrors::from_json( $c, ), - $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: array_map( + connected_account_warnings: \Seam\Parse::to_list( + $json->connected_account_warnings ?? null, fn( $c, ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\ConnectedAccountWarnings::from_json( $c, ), - $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: array_map( + device_errors: \Seam\Parse::to_list( + $json->device_errors ?? null, fn( $d, ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\DeviceErrors::from_json( $d, ), - $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: array_map( + device_warnings: \Seam\Parse::to_list( + $json->device_warnings ?? null, fn( $d, ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\DeviceWarnings::from_json( $d, ), - $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -1976,57 +1976,57 @@ public static function from_json( return null; } return new self( - access_code_errors: array_map( + access_code_errors: \Seam\Parse::to_list( + $json->access_code_errors ?? null, fn( $a, ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\AccessCodeErrors::from_json( $a, ), - $json->access_code_errors ?? [], ), access_code_id: $json->access_code_id ?? null, - access_code_warnings: array_map( + access_code_warnings: \Seam\Parse::to_list( + $json->access_code_warnings ?? null, fn( $a, ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\AccessCodeWarnings::from_json( $a, ), - $json->access_code_warnings ?? [], ), - connected_account_errors: array_map( + connected_account_errors: \Seam\Parse::to_list( + $json->connected_account_errors ?? null, fn( $c, ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\ConnectedAccountErrors::from_json( $c, ), - $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: array_map( + connected_account_warnings: \Seam\Parse::to_list( + $json->connected_account_warnings ?? null, fn( $c, ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\ConnectedAccountWarnings::from_json( $c, ), - $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: array_map( + device_errors: \Seam\Parse::to_list( + $json->device_errors ?? null, fn( $d, ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\DeviceErrors::from_json( $d, ), - $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: array_map( + device_warnings: \Seam\Parse::to_list( + $json->device_warnings ?? null, fn( $d, ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\DeviceWarnings::from_json( $d, ), - $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -2507,57 +2507,57 @@ public static function from_json( return null; } return new self( - access_code_errors: array_map( + access_code_errors: \Seam\Parse::to_list( + $json->access_code_errors ?? null, fn( $a, ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\AccessCodeErrors::from_json( $a, ), - $json->access_code_errors ?? [], ), access_code_id: $json->access_code_id ?? null, - access_code_warnings: array_map( + access_code_warnings: \Seam\Parse::to_list( + $json->access_code_warnings ?? null, fn( $a, ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\AccessCodeWarnings::from_json( $a, ), - $json->access_code_warnings ?? [], ), - connected_account_errors: array_map( + connected_account_errors: \Seam\Parse::to_list( + $json->connected_account_errors ?? null, fn( $c, ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\ConnectedAccountErrors::from_json( $c, ), - $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: array_map( + connected_account_warnings: \Seam\Parse::to_list( + $json->connected_account_warnings ?? null, fn( $c, ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\ConnectedAccountWarnings::from_json( $c, ), - $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: array_map( + device_errors: \Seam\Parse::to_list( + $json->device_errors ?? null, fn( $d, ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\DeviceErrors::from_json( $d, ), - $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: array_map( + device_warnings: \Seam\Parse::to_list( + $json->device_warnings ?? null, fn( $d, ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\DeviceWarnings::from_json( $d, ), - $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -4097,38 +4097,38 @@ public static function from_json( return null; } return new self( - acs_system_errors: array_map( + acs_system_errors: \Seam\Parse::to_list( + $json->acs_system_errors ?? null, fn( $a, ) => \Seam\Resources\Event\AcsSystemDisconnected\AcsSystemErrors::from_json( $a, ), - $json->acs_system_errors ?? [], ), acs_system_id: $json->acs_system_id ?? null, - acs_system_warnings: array_map( + acs_system_warnings: \Seam\Parse::to_list( + $json->acs_system_warnings ?? null, fn( $a, ) => \Seam\Resources\Event\AcsSystemDisconnected\AcsSystemWarnings::from_json( $a, ), - $json->acs_system_warnings ?? [], ), - connected_account_errors: array_map( + connected_account_errors: \Seam\Parse::to_list( + $json->connected_account_errors ?? null, fn( $c, ) => \Seam\Resources\Event\AcsSystemDisconnected\ConnectedAccountErrors::from_json( $c, ), - $json->connected_account_errors ?? [], ), - connected_account_warnings: array_map( + connected_account_warnings: \Seam\Parse::to_list( + $json->connected_account_warnings ?? null, fn( $c, ) => \Seam\Resources\Event\AcsSystemDisconnected\ConnectedAccountWarnings::from_json( $c, ), - $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, event_id: $json->event_id ?? null, @@ -5311,22 +5311,22 @@ public static function from_json( return null; } return new self( - connected_account_errors: array_map( + connected_account_errors: \Seam\Parse::to_list( + $json->connected_account_errors ?? null, fn( $c, ) => \Seam\Resources\Event\ConnectedAccountDisconnected\ConnectedAccountErrors::from_json( $c, ), - $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: array_map( + connected_account_warnings: \Seam\Parse::to_list( + $json->connected_account_warnings ?? null, fn( $c, ) => \Seam\Resources\Event\ConnectedAccountDisconnected\ConnectedAccountWarnings::from_json( $c, ), - $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, event_id: $json->event_id ?? null, @@ -5630,22 +5630,22 @@ public static function from_json( return null; } return new self( - connected_account_errors: array_map( + connected_account_errors: \Seam\Parse::to_list( + $json->connected_account_errors ?? null, fn( $c, ) => \Seam\Resources\Event\ConnectedAccountReauthorizationRequested\ConnectedAccountErrors::from_json( $c, ), - $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: array_map( + connected_account_warnings: \Seam\Parse::to_list( + $json->connected_account_warnings ?? null, fn( $c, ) => \Seam\Resources\Event\ConnectedAccountReauthorizationRequested\ConnectedAccountWarnings::from_json( $c, ), - $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, event_id: $json->event_id ?? null, @@ -6978,40 +6978,40 @@ public static function from_json(mixed $json): DeviceDisconnected|null return null; } return new self( - connected_account_errors: array_map( + connected_account_errors: \Seam\Parse::to_list( + $json->connected_account_errors ?? null, fn( $c, ) => \Seam\Resources\Event\DeviceDisconnected\ConnectedAccountErrors::from_json( $c, ), - $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: array_map( + connected_account_warnings: \Seam\Parse::to_list( + $json->connected_account_warnings ?? null, fn( $c, ) => \Seam\Resources\Event\DeviceDisconnected\ConnectedAccountWarnings::from_json( $c, ), - $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: array_map( + device_errors: \Seam\Parse::to_list( + $json->device_errors ?? null, fn( $d, ) => \Seam\Resources\Event\DeviceDisconnected\DeviceErrors::from_json( $d, ), - $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: array_map( + device_warnings: \Seam\Parse::to_list( + $json->device_warnings ?? null, fn( $d, ) => \Seam\Resources\Event\DeviceDisconnected\DeviceWarnings::from_json( $d, ), - $json->device_warnings ?? [], ), error_code: $json->error_code ?? null, event_id: $json->event_id ?? null, @@ -7129,40 +7129,40 @@ public static function from_json( return null; } return new self( - connected_account_errors: array_map( + connected_account_errors: \Seam\Parse::to_list( + $json->connected_account_errors ?? null, fn( $c, ) => \Seam\Resources\Event\DeviceUnmanagedDisconnected\ConnectedAccountErrors::from_json( $c, ), - $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: array_map( + connected_account_warnings: \Seam\Parse::to_list( + $json->connected_account_warnings ?? null, fn( $c, ) => \Seam\Resources\Event\DeviceUnmanagedDisconnected\ConnectedAccountWarnings::from_json( $c, ), - $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: array_map( + device_errors: \Seam\Parse::to_list( + $json->device_errors ?? null, fn( $d, ) => \Seam\Resources\Event\DeviceUnmanagedDisconnected\DeviceErrors::from_json( $d, ), - $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: array_map( + device_warnings: \Seam\Parse::to_list( + $json->device_warnings ?? null, fn( $d, ) => \Seam\Resources\Event\DeviceUnmanagedDisconnected\DeviceWarnings::from_json( $d, ), - $json->device_warnings ?? [], ), error_code: $json->error_code ?? null, event_id: $json->event_id ?? null, @@ -8105,40 +8105,40 @@ public static function from_json( return null; } return new self( - connected_account_errors: array_map( + connected_account_errors: \Seam\Parse::to_list( + $json->connected_account_errors ?? null, fn( $c, ) => \Seam\Resources\Event\DeviceConnectionBecameFlaky\ConnectedAccountErrors::from_json( $c, ), - $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: array_map( + connected_account_warnings: \Seam\Parse::to_list( + $json->connected_account_warnings ?? null, fn( $c, ) => \Seam\Resources\Event\DeviceConnectionBecameFlaky\ConnectedAccountWarnings::from_json( $c, ), - $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: array_map( + device_errors: \Seam\Parse::to_list( + $json->device_errors ?? null, fn( $d, ) => \Seam\Resources\Event\DeviceConnectionBecameFlaky\DeviceErrors::from_json( $d, ), - $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: array_map( + device_warnings: \Seam\Parse::to_list( + $json->device_warnings ?? null, fn( $d, ) => \Seam\Resources\Event\DeviceConnectionBecameFlaky\DeviceWarnings::from_json( $d, ), - $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -8337,40 +8337,40 @@ public static function from_json( return null; } return new self( - connected_account_errors: array_map( + connected_account_errors: \Seam\Parse::to_list( + $json->connected_account_errors ?? null, fn( $c, ) => \Seam\Resources\Event\DeviceErrorSubscriptionRequired\ConnectedAccountErrors::from_json( $c, ), - $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: array_map( + connected_account_warnings: \Seam\Parse::to_list( + $json->connected_account_warnings ?? null, fn( $c, ) => \Seam\Resources\Event\DeviceErrorSubscriptionRequired\ConnectedAccountWarnings::from_json( $c, ), - $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: array_map( + device_errors: \Seam\Parse::to_list( + $json->device_errors ?? null, fn( $d, ) => \Seam\Resources\Event\DeviceErrorSubscriptionRequired\DeviceErrors::from_json( $d, ), - $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: array_map( + device_warnings: \Seam\Parse::to_list( + $json->device_warnings ?? null, fn( $d, ) => \Seam\Resources\Event\DeviceErrorSubscriptionRequired\DeviceWarnings::from_json( $d, ), - $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -8658,40 +8658,40 @@ public static function from_json( return null; } return new self( - connected_account_errors: array_map( + connected_account_errors: \Seam\Parse::to_list( + $json->connected_account_errors ?? null, fn( $c, ) => \Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\ConnectedAccountErrors::from_json( $c, ), - $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: array_map( + connected_account_warnings: \Seam\Parse::to_list( + $json->connected_account_warnings ?? null, fn( $c, ) => \Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\ConnectedAccountWarnings::from_json( $c, ), - $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: array_map( + device_errors: \Seam\Parse::to_list( + $json->device_errors ?? null, fn( $d, ) => \Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\DeviceErrors::from_json( $d, ), - $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: array_map( + device_warnings: \Seam\Parse::to_list( + $json->device_warnings ?? null, fn( $d, ) => \Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\DeviceWarnings::from_json( $d, ), - $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, diff --git a/src/Resources/Phone.php b/src/Resources/Phone.php index 1573cb51..32d4465c 100644 --- a/src/Resources/Phone.php +++ b/src/Resources/Phone.php @@ -17,18 +17,18 @@ public static function from_json(mixed $json): Phone|null device_id: $json->device_id ?? null, device_type: $json->device_type ?? null, display_name: $json->display_name ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn($e) => \Seam\Resources\Phone\Errors::from_json($e), - $json->errors ?? [], ), properties: isset($json->properties) ? \Seam\Resources\Phone\Properties::from_json( $json->properties, ) : null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn($w) => \Seam\Resources\Phone\Warnings::from_json($w), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, nickname: $json->nickname ?? null, @@ -215,13 +215,13 @@ public static function from_json( return null; } return new self( - endpoints: array_map( + endpoints: \Seam\Parse::to_list( + $json->endpoints ?? null, fn( $e, ) => \Seam\Resources\Phone\Properties\AssaAbloyCredentialServiceMetadata\Endpoints::from_json( $e, ), - $json->endpoints ?? [], ), has_active_endpoint: $json->has_active_endpoint ?? null, ); diff --git a/src/Resources/ThermostatDailyProgram.php b/src/Resources/ThermostatDailyProgram.php index e19bb791..00e42392 100644 --- a/src/Resources/ThermostatDailyProgram.php +++ b/src/Resources/ThermostatDailyProgram.php @@ -16,13 +16,13 @@ public static function from_json( created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, name: $json->name ?? null, - periods: array_map( + periods: \Seam\Parse::to_list( + $json->periods ?? null, fn( $p, ) => \Seam\Resources\ThermostatDailyProgram\Periods::from_json( $p, ), - $json->periods ?? [], ), thermostat_daily_program_id: $json->thermostat_daily_program_id ?? null, diff --git a/src/Resources/ThermostatSchedule.php b/src/Resources/ThermostatSchedule.php index 225123db..efef96df 100644 --- a/src/Resources/ThermostatSchedule.php +++ b/src/Resources/ThermostatSchedule.php @@ -16,13 +16,13 @@ public static function from_json(mixed $json): ThermostatSchedule|null created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, ends_at: $json->ends_at ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn( $e, ) => \Seam\Resources\ThermostatSchedule\Errors::from_json( $e, ), - $json->errors ?? [], ), name: $json->name ?? null, starts_at: $json->starts_at ?? null, diff --git a/src/Resources/UnmanagedAccessCode.php b/src/Resources/UnmanagedAccessCode.php index 89e0fd68..7d584557 100644 --- a/src/Resources/UnmanagedAccessCode.php +++ b/src/Resources/UnmanagedAccessCode.php @@ -26,25 +26,25 @@ public static function from_json(mixed $json): UnmanagedAccessCode|null code: $json->code ?? null, created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn( $e, ) => \Seam\Resources\UnmanagedAccessCode\Errors::from_json( $e, ), - $json->errors ?? [], ), is_managed: $json->is_managed ?? null, name: $json->name ?? null, status: $json->status ?? null, type: $json->type ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn( $w, ) => \Seam\Resources\UnmanagedAccessCode\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, cannot_be_managed: $json->cannot_be_managed ?? null, @@ -688,13 +688,13 @@ public static function from_json( message: $json->message ?? null, change_type: $json->change_type ?? null, created_at: $json->created_at ?? null, - modified_fields: array_map( + modified_fields: \Seam\Parse::to_list( + $json->modified_fields ?? null, fn( $m, ) => \Seam\Resources\UnmanagedAccessCode\Errors\ConflictingExternalModification\ModifiedFields::from_json( $m, ), - $json->modified_fields ?? [], ), ); } @@ -1761,13 +1761,13 @@ public static function from_json( warning_code: $json->warning_code ?? null, change_type: $json->change_type ?? null, created_at: $json->created_at ?? null, - modified_fields: array_map( + modified_fields: \Seam\Parse::to_list( + $json->modified_fields ?? null, fn( $m, ) => \Seam\Resources\UnmanagedAccessCode\Warnings\ExternalModificationInEffect\ModifiedFields::from_json( $m, ), - $json->modified_fields ?? [], ), ); } diff --git a/src/Resources/UnmanagedAccessGrant.php b/src/Resources/UnmanagedAccessGrant.php index dd1d7703..199f4064 100644 --- a/src/Resources/UnmanagedAccessGrant.php +++ b/src/Resources/UnmanagedAccessGrant.php @@ -17,41 +17,41 @@ public static function from_json(mixed $json): UnmanagedAccessGrant|null created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, ends_at: $json->ends_at ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn( $e, ) => \Seam\Resources\UnmanagedAccessGrant\Errors::from_json( $e, ), - $json->errors ?? [], ), location_ids: $json->location_ids ?? null, name: $json->name ?? null, - pending_mutations: array_map( + pending_mutations: \Seam\Parse::to_list( + $json->pending_mutations ?? null, fn( $p, ) => \Seam\Resources\UnmanagedAccessGrant\PendingMutations::from_json( $p, ), - $json->pending_mutations ?? [], ), - requested_access_methods: array_map( + requested_access_methods: \Seam\Parse::to_list( + $json->requested_access_methods ?? null, fn( $r, ) => \Seam\Resources\UnmanagedAccessGrant\RequestedAccessMethods::from_json( $r, ), - $json->requested_access_methods ?? [], ), space_ids: $json->space_ids ?? null, starts_at: $json->starts_at ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn( $w, ) => \Seam\Resources\UnmanagedAccessGrant\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, reservation_key: $json->reservation_key ?? null, @@ -792,13 +792,13 @@ public static function from_json( created_at: $json->created_at ?? null, message: $json->message ?? null, warning_code: $json->warning_code ?? null, - failed_devices: array_map( + failed_devices: \Seam\Parse::to_list( + $json->failed_devices ?? null, fn( $f, ) => \Seam\Resources\UnmanagedAccessGrant\Warnings\OverprovisionedAccess\FailedDevices::from_json( $f, ), - $json->failed_devices ?? [], ), ); } diff --git a/src/Resources/UnmanagedAccessMethod.php b/src/Resources/UnmanagedAccessMethod.php index cac468bf..72edb9f4 100644 --- a/src/Resources/UnmanagedAccessMethod.php +++ b/src/Resources/UnmanagedAccessMethod.php @@ -17,32 +17,32 @@ public static function from_json( created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, display_status: $json->display_status ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn( $e, ) => \Seam\Resources\UnmanagedAccessMethod\Errors::from_json( $e, ), - $json->errors ?? [], ), is_issued: $json->is_issued ?? null, issued_at: $json->issued_at ?? null, mode: $json->mode ?? null, - pending_mutations: array_map( + pending_mutations: \Seam\Parse::to_list( + $json->pending_mutations ?? null, fn( $p, ) => \Seam\Resources\UnmanagedAccessMethod\PendingMutations::from_json( $p, ), - $json->pending_mutations ?? [], ), - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn( $w, ) => \Seam\Resources\UnmanagedAccessMethod\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, code: $json->code ?? null, diff --git a/src/Resources/UnmanagedDevice.php b/src/Resources/UnmanagedDevice.php index 003218ee..249bda9c 100644 --- a/src/Resources/UnmanagedDevice.php +++ b/src/Resources/UnmanagedDevice.php @@ -19,11 +19,11 @@ public static function from_json(mixed $json): UnmanagedDevice|null device_id: $json->device_id ?? null, device_type: $json->device_type ?? null, display_name: $json->display_name ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn($e) => \Seam\Resources\UnmanagedDevice\Errors::from_json( $e, ), - $json->errors ?? [], ), is_managed: $json->is_managed ?? null, properties: isset($json->properties) @@ -31,13 +31,13 @@ public static function from_json(mixed $json): UnmanagedDevice|null $json->properties, ) : null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn( $w, ) => \Seam\Resources\UnmanagedDevice\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, can_configure_auto_lock: $json->can_configure_auto_lock ?? null, diff --git a/src/Resources/UnmanagedUserIdentity.php b/src/Resources/UnmanagedUserIdentity.php index fe072b89..3cdc9906 100644 --- a/src/Resources/UnmanagedUserIdentity.php +++ b/src/Resources/UnmanagedUserIdentity.php @@ -17,13 +17,13 @@ public static function from_json( created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, email_address: $json->email_address ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn( $e, ) => \Seam\Resources\UnmanagedUserIdentity\Errors::from_json( $e, ), - $json->errors ?? [], ), full_name: $json->full_name ?? null, merged_user_identity_ids: $json->merged_user_identity_ids ?? @@ -32,13 +32,13 @@ public static function from_json( null, phone_number: $json->phone_number ?? null, user_identity_id: $json->user_identity_id ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn( $w, ) => \Seam\Resources\UnmanagedUserIdentity\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, ); diff --git a/src/Resources/UserIdentity.php b/src/Resources/UserIdentity.php index 9cda66e0..396dbbfb 100644 --- a/src/Resources/UserIdentity.php +++ b/src/Resources/UserIdentity.php @@ -16,11 +16,11 @@ public static function from_json(mixed $json): UserIdentity|null created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, email_address: $json->email_address ?? null, - errors: array_map( + errors: \Seam\Parse::to_list( + $json->errors ?? null, fn($e) => \Seam\Resources\UserIdentity\Errors::from_json( $e, ), - $json->errors ?? [], ), full_name: $json->full_name ?? null, merged_user_identity_ids: $json->merged_user_identity_ids ?? @@ -30,11 +30,11 @@ public static function from_json(mixed $json): UserIdentity|null phone_number: $json->phone_number ?? null, user_identity_id: $json->user_identity_id ?? null, user_identity_key: $json->user_identity_key ?? null, - warnings: array_map( + warnings: \Seam\Parse::to_list( + $json->warnings ?? null, fn($w) => \Seam\Resources\UserIdentity\Warnings::from_json( $w, ), - $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, ); diff --git a/tests/TotalParsingTest.php b/tests/TotalParsingTest.php new file mode 100644 index 00000000..4ae3477c --- /dev/null +++ b/tests/TotalParsingTest.php @@ -0,0 +1,89 @@ +assertSame([], $device->errors); + $this->assertSame("device_1", $device->device_id); + } + + public function testAListPropertySentAsNullReadsAsEmpty(): void + { + $device = Device::from_json( + json_decode('{"device_id":"device_1","errors":null}'), + ); + + $this->assertSame([], $device->errors); + } + + public function testAnUnknownErrorCodeKeepsTheRestOfTheResource(): void + { + $device = Device::from_json( + json_decode( + '{"device_id":"device_1","errors":[{"error_code":"brand_new","message":"m"}]}', + ), + ); + + $this->assertSame("device_1", $device->device_id); + $this->assertCount(1, $device->errors); + $this->assertSame("brand_new", $device->errors[0]->error_code); + } + + public function testANestedObjectSentAsAScalarDoesNotRaise(): void + { + $device = Device::from_json( + json_decode('{"device_id":"device_1","location":"nope"}'), + ); + + $this->assertSame("device_1", $device->device_id); + } + + public function testAnUnknownEventTypeUsesTheBaseClass(): void + { + $event = Event::from_json( + json_decode('{"event_id":"event_1","event_type":"future.thing"}'), + ); + + $this->assertSame(Event::class, $event::class); + $this->assertSame("future.thing", $event->event_type); + } + + public function testWaitingOnAnUnknownStatusRaisesRatherThanClaimingSuccess(): void + { + $attempt = ActionAttempt::from_json( + json_decode( + '{"action_attempt_id":"attempt_1","action_type":"LOCK_DOOR","status":"cancelled"}', + ), + ); + + $error = new ActionAttemptUnknownStatusError($attempt, "cancelled"); + + // Subclassing the base keeps existing handlers for it working. + $this->assertInstanceOf(ActionAttemptError::class, $error); + $this->assertSame("cancelled", $error->getStatus()); + $this->assertStringContainsString("cancelled", $error->getMessage()); + } +} From f21f8a86ebb8baf65c8cf36238813284efbe4dfa Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 1 Sep 2026 18:27:41 +0000 Subject: [PATCH 2/6] refactor: trim comments to match surrounding style The added comments ran to roughly double the density of the code around them and mostly restated what the line below already said. Kept the ones carrying information the code cannot: why svix/util has to be required, why both key shapes are accepted after symbolize_names, and why array_map needed replacing. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01M2kJ4nGaM8imZCVMEKjmXA --- codegen/lib/layouts/resource.ts | 3 +-- src/ActionAttemptUnknownStatusError.php | 5 +---- src/Http/ResolveActionAttempt.php | 3 --- src/Parse.php | 10 +--------- tests/TotalParsingTest.php | 7 +------ 5 files changed, 4 insertions(+), 24 deletions(-) diff --git a/codegen/lib/layouts/resource.ts b/codegen/lib/layouts/resource.ts index 1436e328..9eb2f1a2 100644 --- a/codegen/lib/layouts/resource.ts +++ b/codegen/lib/layouts/resource.ts @@ -57,8 +57,7 @@ const generateFromJsonProp = (property: ResourceClassProperty): string => { return `${name}: isset($json->${name}) ? ${property.referenceName}::from_json($json->${name}) : null,` case 'listReference': - // Via the helper, not array_map directly: a list property the API sends as - // a scalar would otherwise raise a TypeError and fail the whole response. + // array_map raises a TypeError on a list property the API sends as a scalar. return `${name}: \\Seam\\Parse::to_list($json->${name} ?? null, fn ($${name[0]}) => ${property.referenceName}::from_json($${name[0]})),` case 'record': diff --git a/src/ActionAttemptUnknownStatusError.php b/src/ActionAttemptUnknownStatusError.php index a4198a84..ef87a5cc 100644 --- a/src/ActionAttemptUnknownStatusError.php +++ b/src/ActionAttemptUnknownStatusError.php @@ -7,10 +7,7 @@ /** * Raised when an action attempt reports a status this SDK version does not know. * - * Waiting promises to return a succeeded attempt or raise, and an unrecognized - * status supports neither conclusion: reporting success would claim the action - * completed when the SDK cannot tell, and polling on would block until the - * timeout and then report a timeout that misdescribes what happened. Read the + * Waiting can neither return it as a success nor call it a failure. Read the * action attempt to inspect the status directly. */ class ActionAttemptUnknownStatusError extends ActionAttemptError diff --git a/src/Http/ResolveActionAttempt.php b/src/Http/ResolveActionAttempt.php index d8637c74..f6938f9d 100644 --- a/src/Http/ResolveActionAttempt.php +++ b/src/Http/ResolveActionAttempt.php @@ -81,9 +81,6 @@ private static function poll( throw new ActionAttemptFailedError($action_attempt); } - // Neither pending, success, nor error: a status added after this - // release. Polling on would block until the timeout and then report - // a timeout that misdescribes what happened. if ($action_attempt->status !== "pending") { throw new ActionAttemptUnknownStatusError( $action_attempt, diff --git a/src/Parse.php b/src/Parse.php index eb819fdb..e2befe53 100644 --- a/src/Parse.php +++ b/src/Parse.php @@ -3,21 +3,13 @@ namespace Seam; /** - * Total conversion helpers shared by the generated resource classes. - * - * Seam adds event types, action types, and error codes between SDK releases, so - * reading a response must never fail on the shape of the payload. A value the - * API sends in an unexpected shape degrades rather than raising, so one - * surprising field cannot cost the caller the whole response. + * Conversion helpers that degrade rather than raise on an unexpected payload shape. */ final class Parse { /** * Convert a list of objects, reading anything that is not a list as empty. * - * array_map raises a TypeError when handed a scalar, which would fail the - * whole response over a single field. - * * @template T * @param callable(mixed): T $from_json * @return array diff --git a/tests/TotalParsingTest.php b/tests/TotalParsingTest.php index 4ae3477c..e798f917 100644 --- a/tests/TotalParsingTest.php +++ b/tests/TotalParsingTest.php @@ -12,16 +12,12 @@ use Seam\Resources\Event; /** - * Seam adds event types, action types, and error codes between SDK releases, so - * a payload this version does not recognize has to stay readable rather than - * cost the caller the whole response. Each test pins one shape that used to - * raise, or that would raise without the guard. + * Reading a response must not fail on the shape of the payload. */ final class TotalParsingTest extends TestCase { public function testAListPropertySentAsAScalarReadsAsEmpty(): void { - // array_map raises a TypeError when handed a scalar. $device = Device::from_json( json_decode('{"device_id":"device_1","errors":"oops"}'), ); @@ -81,7 +77,6 @@ public function testWaitingOnAnUnknownStatusRaisesRatherThanClaimingSuccess(): v $error = new ActionAttemptUnknownStatusError($attempt, "cancelled"); - // Subclassing the base keeps existing handlers for it working. $this->assertInstanceOf(ActionAttemptError::class, $error); $this->assertSame("cancelled", $error->getStatus()); $this->assertStringContainsString("cancelled", $error->getMessage()); From 8d0da2d3a98bd96dd260117a7a83a022baf31cf7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 05:19:12 +0000 Subject: [PATCH 3/6] feat: add raw_json() to the event returned by webhook verification from_json builds only the properties it was generated for, so a field Seam adds to an existing event between SDK releases is unreachable. SeamWebhook::verify() now returns an event carrying raw_json(): json_decode($event->raw_json())->a_field_this_version_predates; Defined on the base Event, so the variants inherit it and the unrecognized-event fallback answers the same call. Every path reaches events through Event::from_json, so the payload is captured in one place. Scoped to events. It is there for the verify return, not as a general accessor on every resource. A method rather than a property because the call is where the serialization happens. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01M2kJ4nGaM8imZCVMEKjmXA --- codegen/layouts/resource.hbs | 21 +++++++++++++++++++- codegen/lib/layouts/resource.ts | 4 ++++ src/Resources/Event.php | 17 ++++++++++++++++- tests/TotalParsingTest.php | 34 +++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 2 deletions(-) diff --git a/codegen/layouts/resource.hbs b/codegen/layouts/resource.hbs index f7f3a95a..76ccf749 100644 --- a/codegen/layouts/resource.hbs +++ b/codegen/layouts/resource.hbs @@ -18,7 +18,7 @@ namespace {{namespace}} { ? {{factory.enumType}}::tryFrom($json->{{factory.discriminant}}) : null; - return match ($discriminant) { + {{#if hasRawJson}}$resource = {{else}}return {{/if}}match ($discriminant) { {{#each factory.variants}} {{enumCase}} => {{className}}::from_json($json), {{/each}} @@ -28,6 +28,12 @@ namespace {{namespace}} { {{/each}} ), }; +{{#if hasRawJson}} + + $resource->raw_json_source = $json; + + return $resource; +{{/if}} {{else}} return new self( {{#each fromJsonProps}} @@ -37,6 +43,19 @@ namespace {{namespace}} { {{/if}} } +{{#if hasRawJson}} + private mixed $raw_json_source = null; + + /** + * The payload this event was parsed from, as JSON. Reaches fields the + * generated properties do not cover, such as one added after this release. + */ + public function raw_json(): string + { + return json_encode($this->raw_json_source); + } + +{{/if}} public function __construct( {{#each constructorParams}} {{#if (hasPhpDoc this)}} diff --git a/codegen/lib/layouts/resource.ts b/codegen/lib/layouts/resource.ts index 9eb2f1a2..afa3c518 100644 --- a/codegen/lib/layouts/resource.ts +++ b/codegen/lib/layouts/resource.ts @@ -9,6 +9,7 @@ import type { export interface ClassLayoutContext { className: string + hasRawJson: boolean description: string isDeprecated: boolean deprecationMessage: string @@ -129,6 +130,9 @@ const getClassLayoutContext = ( return { className: schema.name, + // raw_json exists for the webhook verify return, so the base Event carries + // it and nothing else does. The variants inherit it. + hasRawJson: schema.name === 'Event' && schema.extendsName === '', description: schema.description, isDeprecated: schema.isDeprecated, deprecationMessage: schema.deprecationMessage, diff --git a/src/Resources/Event.php b/src/Resources/Event.php index e409da03..739094cf 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -15,7 +15,7 @@ public static function from_json(mixed $json): Event|null ? \Seam\Resources\Event\EventType::tryFrom($json->event_type) : null; - return match ($discriminant) { + $resource = match ($discriminant) { \Seam\Resources\Event\EventType::ACCESS_CODE_CREATED => \Seam\Resources\Event\AccessCodeCreated::from_json( $json, @@ -421,6 +421,21 @@ public static function from_json(mixed $json): Event|null event_description: $json->event_description ?? null, ), }; + + $resource->raw_json_source = $json; + + return $resource; + } + + private mixed $raw_json_source = null; + + /** + * The payload this event was parsed from, as JSON. Reaches fields the + * generated properties do not cover, such as one added after this release. + */ + public function raw_json(): string + { + return json_encode($this->raw_json_source); } public function __construct( diff --git a/tests/TotalParsingTest.php b/tests/TotalParsingTest.php index e798f917..d70e39f5 100644 --- a/tests/TotalParsingTest.php +++ b/tests/TotalParsingTest.php @@ -67,6 +67,40 @@ public function testAnUnknownEventTypeUsesTheBaseClass(): void $this->assertSame("future.thing", $event->event_type); } + public function testRawJsonRecoversAFieldTheGeneratedShapeDrops(): void + { + $json = + '{"event_id":"event_1","event_type":"access_code.created","brand_new_field":"kept"}'; + + $event = Event::from_json(json_decode($json)); + + $this->assertFalse(property_exists($event, "brand_new_field")); + $this->assertEquals( + json_decode($json), + json_decode($event->raw_json()), + ); + } + + public function testRawJsonRoundTripsAnUnrecognizedEvent(): void + { + $json = '{"event_id":"event_1","event_type":"future.thing","x":1}'; + + $event = Event::from_json(json_decode($json)); + + $this->assertSame(Event::class, $event::class); + $this->assertEquals( + json_decode($json), + json_decode($event->raw_json()), + ); + } + + public function testRawJsonIsScopedToEvents(): void + { + $device = Device::from_json(json_decode('{"device_id":"device_1"}')); + + $this->assertFalse(method_exists($device, "raw_json")); + } + public function testWaitingOnAnUnknownStatusRaisesRatherThanClaimingSuccess(): void { $attempt = ActionAttempt::from_json( From 42b2670131c28c277d99ef06bf71e21cc14d7de9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 05:41:59 +0000 Subject: [PATCH 4/6] revert: drop tolerance for malformed payloads Handling values the SDK does not recognize is forward compatibility. Tolerating payloads that are malformed rather than merely new is a bug being swallowed, and the two were conflated here. Removes Seam\Parse and restores array_map for list properties. A null list was already read as empty, so the guard only caught a list sent as a scalar, which is a defect worth surfacing rather than silently reading as empty. Unrecognized handling is untouched: unknown event types still use the base Event class, unknown error codes the base error class, and unknown enum values read as themselves. ActionAttemptUnknownStatusError stays, because the action attempt contract is strict and returning an unrecognized status as a success reports something the SDK cannot vouch for. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01M2kJ4nGaM8imZCVMEKjmXA --- codegen/lib/layouts/resource.ts | 3 +- src/Parse.php | 25 -- src/Resources/AccessCode.php | 20 +- src/Resources/AccessGrant.php | 20 +- src/Resources/AccessMethod.php | 12 +- src/Resources/AcsAccessGroup.php | 12 +- src/Resources/AcsCredential.php | 8 +- src/Resources/AcsEncoder.php | 4 +- src/Resources/AcsEntrance.php | 16 +- src/Resources/AcsSystem.php | 8 +- src/Resources/AcsUser.php | 12 +- src/Resources/ActionAttempt.php | 40 +-- src/Resources/ConnectedAccount.php | 16 +- src/Resources/Device.php | 56 ++-- src/Resources/Event.php | 240 +++++++++--------- src/Resources/Phone.php | 12 +- src/Resources/ThermostatDailyProgram.php | 4 +- src/Resources/ThermostatSchedule.php | 4 +- src/Resources/UnmanagedAccessCode.php | 16 +- src/Resources/UnmanagedAccessGrant.php | 20 +- src/Resources/UnmanagedAccessMethod.php | 12 +- src/Resources/UnmanagedDevice.php | 8 +- src/Resources/UnmanagedUserIdentity.php | 8 +- src/Resources/UserIdentity.php | 8 +- ...gTest.php => ForwardCompatibilityTest.php} | 67 +++-- 25 files changed, 310 insertions(+), 341 deletions(-) delete mode 100644 src/Parse.php rename tests/{TotalParsingTest.php => ForwardCompatibilityTest.php} (69%) diff --git a/codegen/lib/layouts/resource.ts b/codegen/lib/layouts/resource.ts index afa3c518..81367a50 100644 --- a/codegen/lib/layouts/resource.ts +++ b/codegen/lib/layouts/resource.ts @@ -58,8 +58,7 @@ const generateFromJsonProp = (property: ResourceClassProperty): string => { return `${name}: isset($json->${name}) ? ${property.referenceName}::from_json($json->${name}) : null,` case 'listReference': - // array_map raises a TypeError on a list property the API sends as a scalar. - return `${name}: \\Seam\\Parse::to_list($json->${name} ?? null, fn ($${name[0]}) => ${property.referenceName}::from_json($${name[0]})),` + return `${name}: array_map(fn ($${name[0]}) => ${property.referenceName}::from_json($${name[0]}), $json->${name} ?? []),` case 'record': return `${name}: $json->${name} ?? null,` diff --git a/src/Parse.php b/src/Parse.php deleted file mode 100644 index e2befe53..00000000 --- a/src/Parse.php +++ /dev/null @@ -1,25 +0,0 @@ - - */ - public static function to_list(mixed $value, callable $from_json): array - { - if (!is_array($value)) { - return []; - } - - return array_values(array_map($from_json, $value)); - } -} diff --git a/src/Resources/AccessCode.php b/src/Resources/AccessCode.php index c2bada24..a6ed128a 100644 --- a/src/Resources/AccessCode.php +++ b/src/Resources/AccessCode.php @@ -25,9 +25,9 @@ public static function from_json(mixed $json): AccessCode|null common_code_key: $json->common_code_key ?? null, created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn($e) => \Seam\Resources\AccessCode\Errors::from_json($e), + $json->errors ?? [], ), is_backup_access_code_available: $json->is_backup_access_code_available ?? null, @@ -37,21 +37,21 @@ public static function from_json(mixed $json): AccessCode|null is_offline_access_code: $json->is_offline_access_code ?? null, is_one_time_use: $json->is_one_time_use ?? null, name: $json->name ?? null, - pending_mutations: \Seam\Parse::to_list( - $json->pending_mutations ?? null, + pending_mutations: array_map( fn( $p, ) => \Seam\Resources\AccessCode\PendingMutations::from_json( $p, ), + $json->pending_mutations ?? [], ), status: $json->status ?? null, type: $json->type ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn($w) => \Seam\Resources\AccessCode\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, dormakaba_oracode_metadata: isset( @@ -800,13 +800,13 @@ public static function from_json( message: $json->message ?? null, change_type: $json->change_type ?? null, created_at: $json->created_at ?? null, - modified_fields: \Seam\Parse::to_list( - $json->modified_fields ?? null, + modified_fields: array_map( fn( $m, ) => \Seam\Resources\AccessCode\Errors\ConflictingExternalModification\ModifiedFields::from_json( $m, ), + $json->modified_fields ?? [], ), ); } @@ -2329,13 +2329,13 @@ public static function from_json( warning_code: $json->warning_code ?? null, change_type: $json->change_type ?? null, created_at: $json->created_at ?? null, - modified_fields: \Seam\Parse::to_list( - $json->modified_fields ?? null, + modified_fields: array_map( fn( $m, ) => \Seam\Resources\AccessCode\Warnings\ExternalModificationInEffect\ModifiedFields::from_json( $m, ), + $json->modified_fields ?? [], ), ); } diff --git a/src/Resources/AccessGrant.php b/src/Resources/AccessGrant.php index 862e0e6e..786864ef 100644 --- a/src/Resources/AccessGrant.php +++ b/src/Resources/AccessGrant.php @@ -18,36 +18,36 @@ public static function from_json(mixed $json): AccessGrant|null display_name: $json->display_name ?? null, display_status: $json->display_status ?? null, ends_at: $json->ends_at ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn($e) => \Seam\Resources\AccessGrant\Errors::from_json($e), + $json->errors ?? [], ), location_ids: $json->location_ids ?? null, name: $json->name ?? null, - pending_mutations: \Seam\Parse::to_list( - $json->pending_mutations ?? null, + pending_mutations: array_map( fn( $p, ) => \Seam\Resources\AccessGrant\PendingMutations::from_json( $p, ), + $json->pending_mutations ?? [], ), - requested_access_methods: \Seam\Parse::to_list( - $json->requested_access_methods ?? null, + requested_access_methods: array_map( fn( $r, ) => \Seam\Resources\AccessGrant\RequestedAccessMethods::from_json( $r, ), + $json->requested_access_methods ?? [], ), space_ids: $json->space_ids ?? null, starts_at: $json->starts_at ?? null, user_identity_id: $json->user_identity_id ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn($w) => \Seam\Resources\AccessGrant\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, access_grant_key: $json->access_grant_key ?? null, @@ -811,13 +811,13 @@ public static function from_json( created_at: $json->created_at ?? null, message: $json->message ?? null, warning_code: $json->warning_code ?? null, - failed_devices: \Seam\Parse::to_list( - $json->failed_devices ?? null, + failed_devices: array_map( fn( $f, ) => \Seam\Resources\AccessGrant\Warnings\OverprovisionedAccess\FailedDevices::from_json( $f, ), + $json->failed_devices ?? [], ), ); } diff --git a/src/Resources/AccessMethod.php b/src/Resources/AccessMethod.php index 2d7b8e71..67149d2e 100644 --- a/src/Resources/AccessMethod.php +++ b/src/Resources/AccessMethod.php @@ -16,28 +16,28 @@ public static function from_json(mixed $json): AccessMethod|null created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, display_status: $json->display_status ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn($e) => \Seam\Resources\AccessMethod\Errors::from_json( $e, ), + $json->errors ?? [], ), is_issued: $json->is_issued ?? null, issued_at: $json->issued_at ?? null, mode: $json->mode ?? null, - pending_mutations: \Seam\Parse::to_list( - $json->pending_mutations ?? null, + pending_mutations: array_map( fn( $p, ) => \Seam\Resources\AccessMethod\PendingMutations::from_json( $p, ), + $json->pending_mutations ?? [], ), - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn($w) => \Seam\Resources\AccessMethod\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, client_session_token: $json->client_session_token ?? null, diff --git a/src/Resources/AcsAccessGroup.php b/src/Resources/AcsAccessGroup.php index b928fded..463cd3d6 100644 --- a/src/Resources/AcsAccessGroup.php +++ b/src/Resources/AcsAccessGroup.php @@ -24,30 +24,30 @@ public static function from_json(mixed $json): AcsAccessGroup|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn($e) => \Seam\Resources\AcsAccessGroup\Errors::from_json( $e, ), + $json->errors ?? [], ), external_type: $json->external_type ?? null, external_type_display_name: $json->external_type_display_name ?? null, is_managed: $json->is_managed ?? null, name: $json->name ?? null, - pending_mutations: \Seam\Parse::to_list( - $json->pending_mutations ?? null, + pending_mutations: array_map( fn( $p, ) => \Seam\Resources\AcsAccessGroup\PendingMutations::from_json( $p, ), + $json->pending_mutations ?? [], ), - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn( $w, ) => \Seam\Resources\AcsAccessGroup\Warnings::from_json($w), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, access_schedule: isset($json->access_schedule) diff --git a/src/Resources/AcsCredential.php b/src/Resources/AcsCredential.php index b9bd8566..87f5c279 100644 --- a/src/Resources/AcsCredential.php +++ b/src/Resources/AcsCredential.php @@ -24,18 +24,18 @@ public static function from_json(mixed $json): AcsCredential|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn($e) => \Seam\Resources\AcsCredential\Errors::from_json( $e, ), + $json->errors ?? [], ), is_managed: $json->is_managed ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn($w) => \Seam\Resources\AcsCredential\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, acs_credential_pool_id: $json->acs_credential_pool_id ?? null, diff --git a/src/Resources/AcsEncoder.php b/src/Resources/AcsEncoder.php index f8c7c7f3..16fe4738 100644 --- a/src/Resources/AcsEncoder.php +++ b/src/Resources/AcsEncoder.php @@ -30,9 +30,9 @@ public static function from_json(mixed $json): AcsEncoder|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn($e) => \Seam\Resources\AcsEncoder\Errors::from_json($e), + $json->errors ?? [], ), workspace_id: $json->workspace_id ?? null, ); diff --git a/src/Resources/AcsEntrance.php b/src/Resources/AcsEntrance.php index 245299d0..d12132df 100644 --- a/src/Resources/AcsEntrance.php +++ b/src/Resources/AcsEntrance.php @@ -19,16 +19,16 @@ public static function from_json(mixed $json): AcsEntrance|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn($e) => \Seam\Resources\AcsEntrance\Errors::from_json($e), + $json->errors ?? [], ), space_ids: $json->space_ids ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn($w) => \Seam\Resources\AcsEntrance\Warnings::from_json( $w, ), + $json->warnings ?? [], ), akiles_metadata: isset($json->akiles_metadata) ? \Seam\Resources\AcsEntrance\AkilesMetadata::from_json( @@ -226,13 +226,13 @@ public static function from_json(mixed $json): AkilesMetadata|null return null; } return new self( - actions: \Seam\Parse::to_list( - $json->actions ?? null, + actions: array_map( fn( $a, ) => \Seam\Resources\AcsEntrance\AkilesMetadata\Actions::from_json( $a, ), + $json->actions ?? [], ), gadget_id: $json->gadget_id ?? null, site_id: $json->site_id ?? null, @@ -666,13 +666,13 @@ public static function from_json(mixed $json): VisionlineMetadata|null return new self( door_category: $json->door_category ?? null, door_name: $json->door_name ?? null, - profiles: \Seam\Parse::to_list( - $json->profiles ?? null, + profiles: array_map( fn( $p, ) => \Seam\Resources\AcsEntrance\VisionlineMetadata\Profiles::from_json( $p, ), + $json->profiles ?? [], ), ); } diff --git a/src/Resources/AcsSystem.php b/src/Resources/AcsSystem.php index a036fd15..88a89b3c 100644 --- a/src/Resources/AcsSystem.php +++ b/src/Resources/AcsSystem.php @@ -20,9 +20,9 @@ public static function from_json(mixed $json): AcsSystem|null connected_account_id: $json->connected_account_id ?? null, connected_account_ids: $json->connected_account_ids ?? null, created_at: $json->created_at ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn($e) => \Seam\Resources\AcsSystem\Errors::from_json($e), + $json->errors ?? [], ), image_alt_text: $json->image_alt_text ?? null, image_url: $json->image_url ?? null, @@ -33,9 +33,9 @@ public static function from_json(mixed $json): AcsSystem|null ) : null, name: $json->name ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn($w) => \Seam\Resources\AcsSystem\Warnings::from_json($w), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, acs_access_group_count: $json->acs_access_group_count ?? null, diff --git a/src/Resources/AcsUser.php b/src/Resources/AcsUser.php index 9207ce4d..c063569a 100644 --- a/src/Resources/AcsUser.php +++ b/src/Resources/AcsUser.php @@ -21,14 +21,14 @@ public static function from_json(mixed $json): AcsUser|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn($e) => \Seam\Resources\AcsUser\Errors::from_json($e), + $json->errors ?? [], ), is_managed: $json->is_managed ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn($w) => \Seam\Resources\AcsUser\Warnings::from_json($w), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, access_schedule: isset($json->access_schedule) @@ -44,13 +44,13 @@ public static function from_json(mixed $json): AcsUser|null full_name: $json->full_name ?? null, hid_acs_system_id: $json->hid_acs_system_id ?? null, is_suspended: $json->is_suspended ?? null, - pending_mutations: \Seam\Parse::to_list( - $json->pending_mutations ?? null, + pending_mutations: array_map( fn( $p, ) => \Seam\Resources\AcsUser\PendingMutations::from_json( $p, ), + $json->pending_mutations ?? [], ), phone_number: $json->phone_number ?? null, salto_ks_metadata: isset($json->salto_ks_metadata) diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 0d1067b2..7d401ba5 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -2020,13 +2020,13 @@ public static function from_json(mixed $json): Result|null $json->acs_credential_on_seam, ) : null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn( $w, ) => \Seam\Resources\ActionAttempt\ScanCredential\Success\Result\Warnings::from_json( $w, ), + $json->warnings ?? [], ), ); } @@ -2121,22 +2121,22 @@ public static function from_json(mixed $json): AcsCredentialOnSeam|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn( $e, ) => \Seam\Resources\ActionAttempt\ScanCredential\Success\Result\AcsCredentialOnSeam\Errors::from_json( $e, ), + $json->errors ?? [], ), is_managed: $json->is_managed ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn( $w, ) => \Seam\Resources\ActionAttempt\ScanCredential\Success\Result\AcsCredentialOnSeam\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, acs_credential_pool_id: $json->acs_credential_pool_id ?? null, @@ -2914,22 +2914,22 @@ public static function from_json(mixed $json): Result|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn( $e, ) => \Seam\Resources\ActionAttempt\EncodeCredential\Success\Result\Errors::from_json( $e, ), + $json->errors ?? [], ), is_managed: $json->is_managed ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn( $w, ) => \Seam\Resources\ActionAttempt\EncodeCredential\Success\Result\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, acs_credential_pool_id: $json->acs_credential_pool_id ?? null, @@ -3582,22 +3582,22 @@ public static function from_json(mixed $json): Result|null connected_account_id: $json->connected_account_id ?? null, created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn( $e, ) => \Seam\Resources\ActionAttempt\ScanToAssignCredential\Success\Result\Errors::from_json( $e, ), + $json->errors ?? [], ), is_managed: $json->is_managed ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn( $w, ) => \Seam\Resources\ActionAttempt\ScanToAssignCredential\Success\Result\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, acs_credential_pool_id: $json->acs_credential_pool_id ?? null, @@ -4241,32 +4241,32 @@ public static function from_json(mixed $json): Result|null created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, display_status: $json->display_status ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn( $e, ) => \Seam\Resources\ActionAttempt\AssignCredential\Success\Result\Errors::from_json( $e, ), + $json->errors ?? [], ), is_issued: $json->is_issued ?? null, issued_at: $json->issued_at ?? null, mode: $json->mode ?? null, - pending_mutations: \Seam\Parse::to_list( - $json->pending_mutations ?? null, + pending_mutations: array_map( fn( $p, ) => \Seam\Resources\ActionAttempt\AssignCredential\Success\Result\PendingMutations::from_json( $p, ), + $json->pending_mutations ?? [], ), - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn( $w, ) => \Seam\Resources\ActionAttempt\AssignCredential\Success\Result\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, client_session_token: $json->client_session_token ?? null, diff --git a/src/Resources/ConnectedAccount.php b/src/Resources/ConnectedAccount.php index 2acdb416..04da53ca 100644 --- a/src/Resources/ConnectedAccount.php +++ b/src/Resources/ConnectedAccount.php @@ -20,19 +20,19 @@ public static function from_json(mixed $json): ConnectedAccount|null connected_account_id: $json->connected_account_id ?? null, custom_metadata: $json->custom_metadata ?? null, display_name: $json->display_name ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn( $e, ) => \Seam\Resources\ConnectedAccount\Errors::from_json($e), + $json->errors ?? [], ), - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn( $w, ) => \Seam\Resources\ConnectedAccount\Warnings::from_json( $w, ), + $json->warnings ?? [], ), account_type: $json->account_type ?? null, created_at: $json->created_at ?? null, @@ -583,13 +583,13 @@ public static function from_json(mixed $json): SaltoKsMetadata|null return null; } return new self( - sites: \Seam\Parse::to_list( - $json->sites ?? null, + sites: array_map( fn( $s, ) => \Seam\Resources\ConnectedAccount\Errors\SaltoKsSubscriptionLimitExceeded\SaltoKsMetadata\Sites::from_json( $s, ), + $json->sites ?? [], ), ); } @@ -1022,13 +1022,13 @@ public static function from_json(mixed $json): SaltoKsMetadata|null return null; } return new self( - sites: \Seam\Parse::to_list( - $json->sites ?? null, + sites: array_map( fn( $s, ) => \Seam\Resources\ConnectedAccount\Warnings\SaltoKsSubscriptionLimitAlmostReached\SaltoKsMetadata\Sites::from_json( $s, ), + $json->sites ?? [], ), ); } diff --git a/src/Resources/Device.php b/src/Resources/Device.php index ce70cb11..bf618cad 100644 --- a/src/Resources/Device.php +++ b/src/Resources/Device.php @@ -19,9 +19,9 @@ public static function from_json(mixed $json): Device|null device_id: $json->device_id ?? null, device_type: $json->device_type ?? null, display_name: $json->display_name ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn($e) => \Seam\Resources\Device\Errors::from_json($e), + $json->errors ?? [], ), is_managed: $json->is_managed ?? null, properties: isset($json->properties) @@ -30,9 +30,9 @@ public static function from_json(mixed $json): Device|null ) : null, space_ids: $json->space_ids ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn($w) => \Seam\Resources\Device\Warnings::from_json($w), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, can_configure_auto_lock: $json->can_configure_auto_lock ?? null, @@ -530,13 +530,13 @@ public static function from_json(mixed $json): Properties|null auto_lock_enabled: $json->auto_lock_enabled ?? null, available_climate_preset_modes: $json->available_climate_preset_modes ?? null, - available_climate_presets: \Seam\Parse::to_list( - $json->available_climate_presets ?? null, + available_climate_presets: array_map( fn( $a, ) => \Seam\Resources\Device\Properties\AvailableClimatePresets::from_json( $a, ), + $json->available_climate_presets ?? [], ), available_fan_mode_settings: $json->available_fan_mode_settings ?? null, @@ -560,13 +560,13 @@ public static function from_json(mixed $json): Properties|null $json->brivo_metadata, ) : null, - code_constraints: \Seam\Parse::to_list( - $json->code_constraints ?? null, + code_constraints: array_map( fn( $c, ) => \Seam\Resources\Device\Properties\CodeConstraints::from_json( $c, ), + $json->code_constraints ?? [], ), controlbyweb_metadata: isset($json->controlbyweb_metadata) ? \Seam\Resources\Device\Properties\ControlbywebMetadata::from_json( @@ -718,13 +718,13 @@ public static function from_json(mixed $json): Properties|null : null, offline_access_codes_enabled: $json->offline_access_codes_enabled ?? null, - offline_time_frame_options: \Seam\Parse::to_list( - $json->offline_time_frame_options ?? null, + offline_time_frame_options: array_map( fn( $o, ) => \Seam\Resources\Device\Properties\OfflineTimeFrameOptions::from_json( $o, ), + $json->offline_time_frame_options ?? [], ), omnitec_metadata: isset($json->omnitec_metadata) ? \Seam\Resources\Device\Properties\OmnitecMetadata::from_json( @@ -733,13 +733,13 @@ public static function from_json(mixed $json): Properties|null : null, online_access_codes_enabled: $json->online_access_codes_enabled ?? null, - online_time_frame_options: \Seam\Parse::to_list( - $json->online_time_frame_options ?? null, + online_time_frame_options: array_map( fn( $o, ) => \Seam\Resources\Device\Properties\OnlineTimeFrameOptions::from_json( $o, ), + $json->online_time_frame_options ?? [], ), relative_humidity: $json->relative_humidity ?? null, ring_metadata: isset($json->ring_metadata) @@ -811,13 +811,13 @@ public static function from_json(mixed $json): Properties|null : null, thermostat_daily_program_period_precision_minutes: $json->thermostat_daily_program_period_precision_minutes ?? null, - thermostat_daily_programs: \Seam\Parse::to_list( - $json->thermostat_daily_programs ?? null, + thermostat_daily_programs: array_map( fn( $t, ) => \Seam\Resources\Device\Properties\ThermostatDailyPrograms::from_json( $t, ), + $json->thermostat_daily_programs ?? [], ), thermostat_weekly_program: isset( $json->thermostat_weekly_program, @@ -2372,13 +2372,13 @@ public static function from_json( return null; } return new self( - endpoints: \Seam\Parse::to_list( - $json->endpoints ?? null, + endpoints: array_map( fn( $e, ) => \Seam\Resources\Device\Properties\AssaAbloyCredentialServiceMetadata\Endpoints::from_json( $e, ), + $json->endpoints ?? [], ), has_active_endpoint: $json->has_active_endpoint ?? null, ); @@ -2723,13 +2723,13 @@ public static function from_json( door_is_wireless: $json->door_is_wireless ?? null, door_name: $json->door_name ?? null, iana_timezone: $json->iana_timezone ?? null, - predefined_time_slots: \Seam\Parse::to_list( - $json->predefined_time_slots ?? null, + predefined_time_slots: array_map( fn( $p, ) => \Seam\Resources\Device\Properties\DormakabaOracodeMetadata\PredefinedTimeSlots::from_json( $p, ), + $json->predefined_time_slots ?? [], ), site_id: $json->site_id ?? null, site_name: $json->site_name ?? null, @@ -3885,13 +3885,13 @@ public static function from_json(mixed $json): TtlockMetadata|null lock_alias: $json->lock_alias ?? null, lock_id: $json->lock_id ?? null, timezone_raw_offset_ms: $json->timezone_raw_offset_ms ?? null, - wireless_keypads: \Seam\Parse::to_list( - $json->wireless_keypads ?? null, + wireless_keypads: array_map( fn( $w, ) => \Seam\Resources\Device\Properties\TtlockMetadata\WirelessKeypads::from_json( $w, ), + $json->wireless_keypads ?? [], ), ); } @@ -4187,13 +4187,13 @@ public static function from_json( min_duration: $json->min_duration ?? null, start_date_recurrence_rule: $json->start_date_recurrence_rule ?? null, - time_pairs: \Seam\Parse::to_list( - $json->time_pairs ?? null, + time_pairs: array_map( fn( $t, ) => \Seam\Resources\Device\Properties\OfflineTimeFrameOptions\TimePairs::from_json( $t, ), + $json->time_pairs ?? [], ), time_zone: $json->time_zone ?? null, ); @@ -4257,13 +4257,13 @@ public static function from_json( min_duration: $json->min_duration ?? null, start_date_recurrence_rule: $json->start_date_recurrence_rule ?? null, - time_pairs: \Seam\Parse::to_list( - $json->time_pairs ?? null, + time_pairs: array_map( fn( $t, ) => \Seam\Resources\Device\Properties\OnlineTimeFrameOptions\TimePairs::from_json( $t, ), + $json->time_pairs ?? [], ), time_zone: $json->time_zone ?? null, ); @@ -4325,13 +4325,13 @@ public static function from_json( created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, ends_at: $json->ends_at ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn( $e, ) => \Seam\Resources\Device\Properties\ActiveThermostatSchedule\Errors::from_json( $e, ), + $json->errors ?? [], ), name: $json->name ?? null, starts_at: $json->starts_at ?? null, @@ -4779,13 +4779,13 @@ public static function from_json( created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, name: $json->name ?? null, - periods: \Seam\Parse::to_list( - $json->periods ?? null, + periods: array_map( fn( $p, ) => \Seam\Resources\Device\Properties\ThermostatDailyPrograms\Periods::from_json( $p, ), + $json->periods ?? [], ), thermostat_daily_program_id: $json->thermostat_daily_program_id ?? null, diff --git a/src/Resources/Event.php b/src/Resources/Event.php index 739094cf..8a55b799 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -575,13 +575,13 @@ public static function from_json(mixed $json): AccessCodeChanged|null occurred_at: $json->occurred_at ?? null, workspace_id: $json->workspace_id ?? null, change_reason: $json->change_reason ?? null, - changed_properties: \Seam\Parse::to_list( - $json->changed_properties ?? null, + changed_properties: array_map( fn( $c, ) => \Seam\Resources\Event\AccessCodeChanged\ChangedProperties::from_json( $c, ), + $json->changed_properties ?? [], ), connected_account_custom_metadata: $json->connected_account_custom_metadata ?? null, @@ -1013,13 +1013,13 @@ public static function from_json( event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, occurred_at: $json->occurred_at ?? null, - requested_mutations: \Seam\Parse::to_list( - $json->requested_mutations ?? null, + requested_mutations: array_map( fn( $r, ) => \Seam\Resources\Event\AccessCodeMutationsRequested\RequestedMutations::from_json( $r, ), + $json->requested_mutations ?? [], ), workspace_id: $json->workspace_id ?? null, connected_account_custom_metadata: $json->connected_account_custom_metadata ?? @@ -1382,57 +1382,57 @@ public static function from_json( return null; } return new self( - access_code_errors: \Seam\Parse::to_list( - $json->access_code_errors ?? null, + access_code_errors: array_map( fn( $a, ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\AccessCodeErrors::from_json( $a, ), + $json->access_code_errors ?? [], ), access_code_id: $json->access_code_id ?? null, - access_code_warnings: \Seam\Parse::to_list( - $json->access_code_warnings ?? null, + access_code_warnings: array_map( fn( $a, ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\AccessCodeWarnings::from_json( $a, ), + $json->access_code_warnings ?? [], ), - connected_account_errors: \Seam\Parse::to_list( - $json->connected_account_errors ?? null, + connected_account_errors: array_map( fn( $c, ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\ConnectedAccountErrors::from_json( $c, ), + $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: \Seam\Parse::to_list( - $json->connected_account_warnings ?? null, + connected_account_warnings: array_map( fn( $c, ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\ConnectedAccountWarnings::from_json( $c, ), + $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: \Seam\Parse::to_list( - $json->device_errors ?? null, + device_errors: array_map( fn( $d, ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\DeviceErrors::from_json( $d, ), + $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: \Seam\Parse::to_list( - $json->device_warnings ?? null, + device_warnings: array_map( fn( $d, ) => \Seam\Resources\Event\AccessCodeDelayInSettingOnDevice\DeviceWarnings::from_json( $d, ), + $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -1554,57 +1554,57 @@ public static function from_json( return null; } return new self( - access_code_errors: \Seam\Parse::to_list( - $json->access_code_errors ?? null, + access_code_errors: array_map( fn( $a, ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\AccessCodeErrors::from_json( $a, ), + $json->access_code_errors ?? [], ), access_code_id: $json->access_code_id ?? null, - access_code_warnings: \Seam\Parse::to_list( - $json->access_code_warnings ?? null, + access_code_warnings: array_map( fn( $a, ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\AccessCodeWarnings::from_json( $a, ), + $json->access_code_warnings ?? [], ), - connected_account_errors: \Seam\Parse::to_list( - $json->connected_account_errors ?? null, + connected_account_errors: array_map( fn( $c, ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\ConnectedAccountErrors::from_json( $c, ), + $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: \Seam\Parse::to_list( - $json->connected_account_warnings ?? null, + connected_account_warnings: array_map( fn( $c, ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\ConnectedAccountWarnings::from_json( $c, ), + $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: \Seam\Parse::to_list( - $json->device_errors ?? null, + device_errors: array_map( fn( $d, ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\DeviceErrors::from_json( $d, ), + $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: \Seam\Parse::to_list( - $json->device_warnings ?? null, + device_warnings: array_map( fn( $d, ) => \Seam\Resources\Event\AccessCodeFailedToSetOnDevice\DeviceWarnings::from_json( $d, ), + $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -1819,57 +1819,57 @@ public static function from_json( return null; } return new self( - access_code_errors: \Seam\Parse::to_list( - $json->access_code_errors ?? null, + access_code_errors: array_map( fn( $a, ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\AccessCodeErrors::from_json( $a, ), + $json->access_code_errors ?? [], ), access_code_id: $json->access_code_id ?? null, - access_code_warnings: \Seam\Parse::to_list( - $json->access_code_warnings ?? null, + access_code_warnings: array_map( fn( $a, ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\AccessCodeWarnings::from_json( $a, ), + $json->access_code_warnings ?? [], ), - connected_account_errors: \Seam\Parse::to_list( - $json->connected_account_errors ?? null, + connected_account_errors: array_map( fn( $c, ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\ConnectedAccountErrors::from_json( $c, ), + $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: \Seam\Parse::to_list( - $json->connected_account_warnings ?? null, + connected_account_warnings: array_map( fn( $c, ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\ConnectedAccountWarnings::from_json( $c, ), + $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: \Seam\Parse::to_list( - $json->device_errors ?? null, + device_errors: array_map( fn( $d, ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\DeviceErrors::from_json( $d, ), + $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: \Seam\Parse::to_list( - $json->device_warnings ?? null, + device_warnings: array_map( fn( $d, ) => \Seam\Resources\Event\AccessCodeDelayInRemovingFromDevice\DeviceWarnings::from_json( $d, ), + $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -1991,57 +1991,57 @@ public static function from_json( return null; } return new self( - access_code_errors: \Seam\Parse::to_list( - $json->access_code_errors ?? null, + access_code_errors: array_map( fn( $a, ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\AccessCodeErrors::from_json( $a, ), + $json->access_code_errors ?? [], ), access_code_id: $json->access_code_id ?? null, - access_code_warnings: \Seam\Parse::to_list( - $json->access_code_warnings ?? null, + access_code_warnings: array_map( fn( $a, ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\AccessCodeWarnings::from_json( $a, ), + $json->access_code_warnings ?? [], ), - connected_account_errors: \Seam\Parse::to_list( - $json->connected_account_errors ?? null, + connected_account_errors: array_map( fn( $c, ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\ConnectedAccountErrors::from_json( $c, ), + $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: \Seam\Parse::to_list( - $json->connected_account_warnings ?? null, + connected_account_warnings: array_map( fn( $c, ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\ConnectedAccountWarnings::from_json( $c, ), + $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: \Seam\Parse::to_list( - $json->device_errors ?? null, + device_errors: array_map( fn( $d, ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\DeviceErrors::from_json( $d, ), + $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: \Seam\Parse::to_list( - $json->device_warnings ?? null, + device_warnings: array_map( fn( $d, ) => \Seam\Resources\Event\AccessCodeFailedToRemoveFromDevice\DeviceWarnings::from_json( $d, ), + $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -2522,57 +2522,57 @@ public static function from_json( return null; } return new self( - access_code_errors: \Seam\Parse::to_list( - $json->access_code_errors ?? null, + access_code_errors: array_map( fn( $a, ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\AccessCodeErrors::from_json( $a, ), + $json->access_code_errors ?? [], ), access_code_id: $json->access_code_id ?? null, - access_code_warnings: \Seam\Parse::to_list( - $json->access_code_warnings ?? null, + access_code_warnings: array_map( fn( $a, ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\AccessCodeWarnings::from_json( $a, ), + $json->access_code_warnings ?? [], ), - connected_account_errors: \Seam\Parse::to_list( - $json->connected_account_errors ?? null, + connected_account_errors: array_map( fn( $c, ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\ConnectedAccountErrors::from_json( $c, ), + $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: \Seam\Parse::to_list( - $json->connected_account_warnings ?? null, + connected_account_warnings: array_map( fn( $c, ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\ConnectedAccountWarnings::from_json( $c, ), + $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: \Seam\Parse::to_list( - $json->device_errors ?? null, + device_errors: array_map( fn( $d, ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\DeviceErrors::from_json( $d, ), + $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: \Seam\Parse::to_list( - $json->device_warnings ?? null, + device_warnings: array_map( fn( $d, ) => \Seam\Resources\Event\AccessCodeUnmanagedFailedToConvertToManaged\DeviceWarnings::from_json( $d, ), + $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -4112,38 +4112,38 @@ public static function from_json( return null; } return new self( - acs_system_errors: \Seam\Parse::to_list( - $json->acs_system_errors ?? null, + acs_system_errors: array_map( fn( $a, ) => \Seam\Resources\Event\AcsSystemDisconnected\AcsSystemErrors::from_json( $a, ), + $json->acs_system_errors ?? [], ), acs_system_id: $json->acs_system_id ?? null, - acs_system_warnings: \Seam\Parse::to_list( - $json->acs_system_warnings ?? null, + acs_system_warnings: array_map( fn( $a, ) => \Seam\Resources\Event\AcsSystemDisconnected\AcsSystemWarnings::from_json( $a, ), + $json->acs_system_warnings ?? [], ), - connected_account_errors: \Seam\Parse::to_list( - $json->connected_account_errors ?? null, + connected_account_errors: array_map( fn( $c, ) => \Seam\Resources\Event\AcsSystemDisconnected\ConnectedAccountErrors::from_json( $c, ), + $json->connected_account_errors ?? [], ), - connected_account_warnings: \Seam\Parse::to_list( - $json->connected_account_warnings ?? null, + connected_account_warnings: array_map( fn( $c, ) => \Seam\Resources\Event\AcsSystemDisconnected\ConnectedAccountWarnings::from_json( $c, ), + $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, event_id: $json->event_id ?? null, @@ -5326,22 +5326,22 @@ public static function from_json( return null; } return new self( - connected_account_errors: \Seam\Parse::to_list( - $json->connected_account_errors ?? null, + connected_account_errors: array_map( fn( $c, ) => \Seam\Resources\Event\ConnectedAccountDisconnected\ConnectedAccountErrors::from_json( $c, ), + $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: \Seam\Parse::to_list( - $json->connected_account_warnings ?? null, + connected_account_warnings: array_map( fn( $c, ) => \Seam\Resources\Event\ConnectedAccountDisconnected\ConnectedAccountWarnings::from_json( $c, ), + $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, event_id: $json->event_id ?? null, @@ -5645,22 +5645,22 @@ public static function from_json( return null; } return new self( - connected_account_errors: \Seam\Parse::to_list( - $json->connected_account_errors ?? null, + connected_account_errors: array_map( fn( $c, ) => \Seam\Resources\Event\ConnectedAccountReauthorizationRequested\ConnectedAccountErrors::from_json( $c, ), + $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: \Seam\Parse::to_list( - $json->connected_account_warnings ?? null, + connected_account_warnings: array_map( fn( $c, ) => \Seam\Resources\Event\ConnectedAccountReauthorizationRequested\ConnectedAccountWarnings::from_json( $c, ), + $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, event_id: $json->event_id ?? null, @@ -6993,40 +6993,40 @@ public static function from_json(mixed $json): DeviceDisconnected|null return null; } return new self( - connected_account_errors: \Seam\Parse::to_list( - $json->connected_account_errors ?? null, + connected_account_errors: array_map( fn( $c, ) => \Seam\Resources\Event\DeviceDisconnected\ConnectedAccountErrors::from_json( $c, ), + $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: \Seam\Parse::to_list( - $json->connected_account_warnings ?? null, + connected_account_warnings: array_map( fn( $c, ) => \Seam\Resources\Event\DeviceDisconnected\ConnectedAccountWarnings::from_json( $c, ), + $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: \Seam\Parse::to_list( - $json->device_errors ?? null, + device_errors: array_map( fn( $d, ) => \Seam\Resources\Event\DeviceDisconnected\DeviceErrors::from_json( $d, ), + $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: \Seam\Parse::to_list( - $json->device_warnings ?? null, + device_warnings: array_map( fn( $d, ) => \Seam\Resources\Event\DeviceDisconnected\DeviceWarnings::from_json( $d, ), + $json->device_warnings ?? [], ), error_code: $json->error_code ?? null, event_id: $json->event_id ?? null, @@ -7144,40 +7144,40 @@ public static function from_json( return null; } return new self( - connected_account_errors: \Seam\Parse::to_list( - $json->connected_account_errors ?? null, + connected_account_errors: array_map( fn( $c, ) => \Seam\Resources\Event\DeviceUnmanagedDisconnected\ConnectedAccountErrors::from_json( $c, ), + $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: \Seam\Parse::to_list( - $json->connected_account_warnings ?? null, + connected_account_warnings: array_map( fn( $c, ) => \Seam\Resources\Event\DeviceUnmanagedDisconnected\ConnectedAccountWarnings::from_json( $c, ), + $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: \Seam\Parse::to_list( - $json->device_errors ?? null, + device_errors: array_map( fn( $d, ) => \Seam\Resources\Event\DeviceUnmanagedDisconnected\DeviceErrors::from_json( $d, ), + $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: \Seam\Parse::to_list( - $json->device_warnings ?? null, + device_warnings: array_map( fn( $d, ) => \Seam\Resources\Event\DeviceUnmanagedDisconnected\DeviceWarnings::from_json( $d, ), + $json->device_warnings ?? [], ), error_code: $json->error_code ?? null, event_id: $json->event_id ?? null, @@ -8120,40 +8120,40 @@ public static function from_json( return null; } return new self( - connected_account_errors: \Seam\Parse::to_list( - $json->connected_account_errors ?? null, + connected_account_errors: array_map( fn( $c, ) => \Seam\Resources\Event\DeviceConnectionBecameFlaky\ConnectedAccountErrors::from_json( $c, ), + $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: \Seam\Parse::to_list( - $json->connected_account_warnings ?? null, + connected_account_warnings: array_map( fn( $c, ) => \Seam\Resources\Event\DeviceConnectionBecameFlaky\ConnectedAccountWarnings::from_json( $c, ), + $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: \Seam\Parse::to_list( - $json->device_errors ?? null, + device_errors: array_map( fn( $d, ) => \Seam\Resources\Event\DeviceConnectionBecameFlaky\DeviceErrors::from_json( $d, ), + $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: \Seam\Parse::to_list( - $json->device_warnings ?? null, + device_warnings: array_map( fn( $d, ) => \Seam\Resources\Event\DeviceConnectionBecameFlaky\DeviceWarnings::from_json( $d, ), + $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -8352,40 +8352,40 @@ public static function from_json( return null; } return new self( - connected_account_errors: \Seam\Parse::to_list( - $json->connected_account_errors ?? null, + connected_account_errors: array_map( fn( $c, ) => \Seam\Resources\Event\DeviceErrorSubscriptionRequired\ConnectedAccountErrors::from_json( $c, ), + $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: \Seam\Parse::to_list( - $json->connected_account_warnings ?? null, + connected_account_warnings: array_map( fn( $c, ) => \Seam\Resources\Event\DeviceErrorSubscriptionRequired\ConnectedAccountWarnings::from_json( $c, ), + $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: \Seam\Parse::to_list( - $json->device_errors ?? null, + device_errors: array_map( fn( $d, ) => \Seam\Resources\Event\DeviceErrorSubscriptionRequired\DeviceErrors::from_json( $d, ), + $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: \Seam\Parse::to_list( - $json->device_warnings ?? null, + device_warnings: array_map( fn( $d, ) => \Seam\Resources\Event\DeviceErrorSubscriptionRequired\DeviceWarnings::from_json( $d, ), + $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, @@ -8673,40 +8673,40 @@ public static function from_json( return null; } return new self( - connected_account_errors: \Seam\Parse::to_list( - $json->connected_account_errors ?? null, + connected_account_errors: array_map( fn( $c, ) => \Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\ConnectedAccountErrors::from_json( $c, ), + $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_warnings: \Seam\Parse::to_list( - $json->connected_account_warnings ?? null, + connected_account_warnings: array_map( fn( $c, ) => \Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\ConnectedAccountWarnings::from_json( $c, ), + $json->connected_account_warnings ?? [], ), created_at: $json->created_at ?? null, - device_errors: \Seam\Parse::to_list( - $json->device_errors ?? null, + device_errors: array_map( fn( $d, ) => \Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\DeviceErrors::from_json( $d, ), + $json->device_errors ?? [], ), device_id: $json->device_id ?? null, - device_warnings: \Seam\Parse::to_list( - $json->device_warnings ?? null, + device_warnings: array_map( fn( $d, ) => \Seam\Resources\Event\DeviceAccessoryKeypadDisconnected\DeviceWarnings::from_json( $d, ), + $json->device_warnings ?? [], ), event_id: $json->event_id ?? null, event_type: $json->event_type ?? null, diff --git a/src/Resources/Phone.php b/src/Resources/Phone.php index 32d4465c..1573cb51 100644 --- a/src/Resources/Phone.php +++ b/src/Resources/Phone.php @@ -17,18 +17,18 @@ public static function from_json(mixed $json): Phone|null device_id: $json->device_id ?? null, device_type: $json->device_type ?? null, display_name: $json->display_name ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn($e) => \Seam\Resources\Phone\Errors::from_json($e), + $json->errors ?? [], ), properties: isset($json->properties) ? \Seam\Resources\Phone\Properties::from_json( $json->properties, ) : null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn($w) => \Seam\Resources\Phone\Warnings::from_json($w), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, nickname: $json->nickname ?? null, @@ -215,13 +215,13 @@ public static function from_json( return null; } return new self( - endpoints: \Seam\Parse::to_list( - $json->endpoints ?? null, + endpoints: array_map( fn( $e, ) => \Seam\Resources\Phone\Properties\AssaAbloyCredentialServiceMetadata\Endpoints::from_json( $e, ), + $json->endpoints ?? [], ), has_active_endpoint: $json->has_active_endpoint ?? null, ); diff --git a/src/Resources/ThermostatDailyProgram.php b/src/Resources/ThermostatDailyProgram.php index 00e42392..e19bb791 100644 --- a/src/Resources/ThermostatDailyProgram.php +++ b/src/Resources/ThermostatDailyProgram.php @@ -16,13 +16,13 @@ public static function from_json( created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, name: $json->name ?? null, - periods: \Seam\Parse::to_list( - $json->periods ?? null, + periods: array_map( fn( $p, ) => \Seam\Resources\ThermostatDailyProgram\Periods::from_json( $p, ), + $json->periods ?? [], ), thermostat_daily_program_id: $json->thermostat_daily_program_id ?? null, diff --git a/src/Resources/ThermostatSchedule.php b/src/Resources/ThermostatSchedule.php index efef96df..225123db 100644 --- a/src/Resources/ThermostatSchedule.php +++ b/src/Resources/ThermostatSchedule.php @@ -16,13 +16,13 @@ public static function from_json(mixed $json): ThermostatSchedule|null created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, ends_at: $json->ends_at ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn( $e, ) => \Seam\Resources\ThermostatSchedule\Errors::from_json( $e, ), + $json->errors ?? [], ), name: $json->name ?? null, starts_at: $json->starts_at ?? null, diff --git a/src/Resources/UnmanagedAccessCode.php b/src/Resources/UnmanagedAccessCode.php index 7d584557..89e0fd68 100644 --- a/src/Resources/UnmanagedAccessCode.php +++ b/src/Resources/UnmanagedAccessCode.php @@ -26,25 +26,25 @@ public static function from_json(mixed $json): UnmanagedAccessCode|null code: $json->code ?? null, created_at: $json->created_at ?? null, device_id: $json->device_id ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn( $e, ) => \Seam\Resources\UnmanagedAccessCode\Errors::from_json( $e, ), + $json->errors ?? [], ), is_managed: $json->is_managed ?? null, name: $json->name ?? null, status: $json->status ?? null, type: $json->type ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn( $w, ) => \Seam\Resources\UnmanagedAccessCode\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, cannot_be_managed: $json->cannot_be_managed ?? null, @@ -688,13 +688,13 @@ public static function from_json( message: $json->message ?? null, change_type: $json->change_type ?? null, created_at: $json->created_at ?? null, - modified_fields: \Seam\Parse::to_list( - $json->modified_fields ?? null, + modified_fields: array_map( fn( $m, ) => \Seam\Resources\UnmanagedAccessCode\Errors\ConflictingExternalModification\ModifiedFields::from_json( $m, ), + $json->modified_fields ?? [], ), ); } @@ -1761,13 +1761,13 @@ public static function from_json( warning_code: $json->warning_code ?? null, change_type: $json->change_type ?? null, created_at: $json->created_at ?? null, - modified_fields: \Seam\Parse::to_list( - $json->modified_fields ?? null, + modified_fields: array_map( fn( $m, ) => \Seam\Resources\UnmanagedAccessCode\Warnings\ExternalModificationInEffect\ModifiedFields::from_json( $m, ), + $json->modified_fields ?? [], ), ); } diff --git a/src/Resources/UnmanagedAccessGrant.php b/src/Resources/UnmanagedAccessGrant.php index 199f4064..dd1d7703 100644 --- a/src/Resources/UnmanagedAccessGrant.php +++ b/src/Resources/UnmanagedAccessGrant.php @@ -17,41 +17,41 @@ public static function from_json(mixed $json): UnmanagedAccessGrant|null created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, ends_at: $json->ends_at ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn( $e, ) => \Seam\Resources\UnmanagedAccessGrant\Errors::from_json( $e, ), + $json->errors ?? [], ), location_ids: $json->location_ids ?? null, name: $json->name ?? null, - pending_mutations: \Seam\Parse::to_list( - $json->pending_mutations ?? null, + pending_mutations: array_map( fn( $p, ) => \Seam\Resources\UnmanagedAccessGrant\PendingMutations::from_json( $p, ), + $json->pending_mutations ?? [], ), - requested_access_methods: \Seam\Parse::to_list( - $json->requested_access_methods ?? null, + requested_access_methods: array_map( fn( $r, ) => \Seam\Resources\UnmanagedAccessGrant\RequestedAccessMethods::from_json( $r, ), + $json->requested_access_methods ?? [], ), space_ids: $json->space_ids ?? null, starts_at: $json->starts_at ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn( $w, ) => \Seam\Resources\UnmanagedAccessGrant\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, reservation_key: $json->reservation_key ?? null, @@ -792,13 +792,13 @@ public static function from_json( created_at: $json->created_at ?? null, message: $json->message ?? null, warning_code: $json->warning_code ?? null, - failed_devices: \Seam\Parse::to_list( - $json->failed_devices ?? null, + failed_devices: array_map( fn( $f, ) => \Seam\Resources\UnmanagedAccessGrant\Warnings\OverprovisionedAccess\FailedDevices::from_json( $f, ), + $json->failed_devices ?? [], ), ); } diff --git a/src/Resources/UnmanagedAccessMethod.php b/src/Resources/UnmanagedAccessMethod.php index 72edb9f4..cac468bf 100644 --- a/src/Resources/UnmanagedAccessMethod.php +++ b/src/Resources/UnmanagedAccessMethod.php @@ -17,32 +17,32 @@ public static function from_json( created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, display_status: $json->display_status ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn( $e, ) => \Seam\Resources\UnmanagedAccessMethod\Errors::from_json( $e, ), + $json->errors ?? [], ), is_issued: $json->is_issued ?? null, issued_at: $json->issued_at ?? null, mode: $json->mode ?? null, - pending_mutations: \Seam\Parse::to_list( - $json->pending_mutations ?? null, + pending_mutations: array_map( fn( $p, ) => \Seam\Resources\UnmanagedAccessMethod\PendingMutations::from_json( $p, ), + $json->pending_mutations ?? [], ), - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn( $w, ) => \Seam\Resources\UnmanagedAccessMethod\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, code: $json->code ?? null, diff --git a/src/Resources/UnmanagedDevice.php b/src/Resources/UnmanagedDevice.php index 249bda9c..003218ee 100644 --- a/src/Resources/UnmanagedDevice.php +++ b/src/Resources/UnmanagedDevice.php @@ -19,11 +19,11 @@ public static function from_json(mixed $json): UnmanagedDevice|null device_id: $json->device_id ?? null, device_type: $json->device_type ?? null, display_name: $json->display_name ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn($e) => \Seam\Resources\UnmanagedDevice\Errors::from_json( $e, ), + $json->errors ?? [], ), is_managed: $json->is_managed ?? null, properties: isset($json->properties) @@ -31,13 +31,13 @@ public static function from_json(mixed $json): UnmanagedDevice|null $json->properties, ) : null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn( $w, ) => \Seam\Resources\UnmanagedDevice\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, can_configure_auto_lock: $json->can_configure_auto_lock ?? null, diff --git a/src/Resources/UnmanagedUserIdentity.php b/src/Resources/UnmanagedUserIdentity.php index 3cdc9906..fe072b89 100644 --- a/src/Resources/UnmanagedUserIdentity.php +++ b/src/Resources/UnmanagedUserIdentity.php @@ -17,13 +17,13 @@ public static function from_json( created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, email_address: $json->email_address ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn( $e, ) => \Seam\Resources\UnmanagedUserIdentity\Errors::from_json( $e, ), + $json->errors ?? [], ), full_name: $json->full_name ?? null, merged_user_identity_ids: $json->merged_user_identity_ids ?? @@ -32,13 +32,13 @@ public static function from_json( null, phone_number: $json->phone_number ?? null, user_identity_id: $json->user_identity_id ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn( $w, ) => \Seam\Resources\UnmanagedUserIdentity\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, ); diff --git a/src/Resources/UserIdentity.php b/src/Resources/UserIdentity.php index 396dbbfb..9cda66e0 100644 --- a/src/Resources/UserIdentity.php +++ b/src/Resources/UserIdentity.php @@ -16,11 +16,11 @@ public static function from_json(mixed $json): UserIdentity|null created_at: $json->created_at ?? null, display_name: $json->display_name ?? null, email_address: $json->email_address ?? null, - errors: \Seam\Parse::to_list( - $json->errors ?? null, + errors: array_map( fn($e) => \Seam\Resources\UserIdentity\Errors::from_json( $e, ), + $json->errors ?? [], ), full_name: $json->full_name ?? null, merged_user_identity_ids: $json->merged_user_identity_ids ?? @@ -30,11 +30,11 @@ public static function from_json(mixed $json): UserIdentity|null phone_number: $json->phone_number ?? null, user_identity_id: $json->user_identity_id ?? null, user_identity_key: $json->user_identity_key ?? null, - warnings: \Seam\Parse::to_list( - $json->warnings ?? null, + warnings: array_map( fn($w) => \Seam\Resources\UserIdentity\Warnings::from_json( $w, ), + $json->warnings ?? [], ), workspace_id: $json->workspace_id ?? null, ); diff --git a/tests/TotalParsingTest.php b/tests/ForwardCompatibilityTest.php similarity index 69% rename from tests/TotalParsingTest.php rename to tests/ForwardCompatibilityTest.php index d70e39f5..87ede299 100644 --- a/tests/TotalParsingTest.php +++ b/tests/ForwardCompatibilityTest.php @@ -12,27 +12,29 @@ use Seam\Resources\Event; /** - * Reading a response must not fail on the shape of the payload. + * Seam adds event types, action types, and enum values between SDK releases. + * Reading them must not raise; writing logic against them is what an upgrade is + * for. The webhook event additionally carries the payload it was parsed from. */ -final class TotalParsingTest extends TestCase +final class ForwardCompatibilityTest extends TestCase { - public function testAListPropertySentAsAScalarReadsAsEmpty(): void + public function testAnUnknownEnumValueReadsAsItself(): void { $device = Device::from_json( - json_decode('{"device_id":"device_1","errors":"oops"}'), + json_decode('{"device_id":"device_1","device_type":"future_lock"}'), ); - $this->assertSame([], $device->errors); - $this->assertSame("device_1", $device->device_id); + $this->assertSame("future_lock", $device->device_type); } - public function testAListPropertySentAsNullReadsAsEmpty(): void + public function testAnUnknownEventTypeUsesTheBaseClass(): void { - $device = Device::from_json( - json_decode('{"device_id":"device_1","errors":null}'), + $event = Event::from_json( + json_decode('{"event_id":"event_1","event_type":"future.thing"}'), ); - $this->assertSame([], $device->errors); + $this->assertSame(Event::class, $event::class); + $this->assertSame("future.thing", $event->event_type); } public function testAnUnknownErrorCodeKeepsTheRestOfTheResource(): void @@ -44,27 +46,36 @@ public function testAnUnknownErrorCodeKeepsTheRestOfTheResource(): void ); $this->assertSame("device_1", $device->device_id); - $this->assertCount(1, $device->errors); $this->assertSame("brand_new", $device->errors[0]->error_code); } - public function testANestedObjectSentAsAScalarDoesNotRaise(): void + public function testAnUnknownActionAttemptStatusReadsAsItself(): void { - $device = Device::from_json( - json_decode('{"device_id":"device_1","location":"nope"}'), + $attempt = ActionAttempt::from_json( + json_decode( + '{"action_attempt_id":"attempt_1","action_type":"LOCK_DOOR","status":"cancelled"}', + ), ); - $this->assertSame("device_1", $device->device_id); + $this->assertSame("cancelled", $attempt->status); } - public function testAnUnknownEventTypeUsesTheBaseClass(): void + /** + * Waiting promises a succeeded attempt or a raise, so an unrecognized status + * is the one place the SDK must not stay quiet. + */ + public function testTheUnknownStatusErrorSubclassesTheBase(): void { - $event = Event::from_json( - json_decode('{"event_id":"event_1","event_type":"future.thing"}'), + $attempt = ActionAttempt::from_json( + json_decode( + '{"action_attempt_id":"attempt_1","action_type":"LOCK_DOOR"}', + ), ); - $this->assertSame(Event::class, $event::class); - $this->assertSame("future.thing", $event->event_type); + $error = new ActionAttemptUnknownStatusError($attempt, "cancelled"); + + $this->assertInstanceOf(ActionAttemptError::class, $error); + $this->assertSame("cancelled", $error->getStatus()); } public function testRawJsonRecoversAFieldTheGeneratedShapeDrops(): void @@ -87,7 +98,6 @@ public function testRawJsonRoundTripsAnUnrecognizedEvent(): void $event = Event::from_json(json_decode($json)); - $this->assertSame(Event::class, $event::class); $this->assertEquals( json_decode($json), json_decode($event->raw_json()), @@ -100,19 +110,4 @@ public function testRawJsonIsScopedToEvents(): void $this->assertFalse(method_exists($device, "raw_json")); } - - public function testWaitingOnAnUnknownStatusRaisesRatherThanClaimingSuccess(): void - { - $attempt = ActionAttempt::from_json( - json_decode( - '{"action_attempt_id":"attempt_1","action_type":"LOCK_DOOR","status":"cancelled"}', - ), - ); - - $error = new ActionAttemptUnknownStatusError($attempt, "cancelled"); - - $this->assertInstanceOf(ActionAttemptError::class, $error); - $this->assertSame("cancelled", $error->getStatus()); - $this->assertStringContainsString("cancelled", $error->getMessage()); - } } From 424aa3da1351fe2dbc5ec696339e199fbb4cfd81 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 06:09:07 +0000 Subject: [PATCH 5/6] refactor: drop comments that restate the code Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01M2kJ4nGaM8imZCVMEKjmXA --- tests/ForwardCompatibilityTest.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/ForwardCompatibilityTest.php b/tests/ForwardCompatibilityTest.php index 87ede299..a2c3f9c7 100644 --- a/tests/ForwardCompatibilityTest.php +++ b/tests/ForwardCompatibilityTest.php @@ -11,11 +11,6 @@ use Seam\Resources\Device; use Seam\Resources\Event; -/** - * Seam adds event types, action types, and enum values between SDK releases. - * Reading them must not raise; writing logic against them is what an upgrade is - * for. The webhook event additionally carries the payload it was parsed from. - */ final class ForwardCompatibilityTest extends TestCase { public function testAnUnknownEnumValueReadsAsItself(): void @@ -60,10 +55,6 @@ public function testAnUnknownActionAttemptStatusReadsAsItself(): void $this->assertSame("cancelled", $attempt->status); } - /** - * Waiting promises a succeeded attempt or a raise, so an unrecognized status - * is the one place the SDK must not stay quiet. - */ public function testTheUnknownStatusErrorSubclassesTheBase(): void { $attempt = ActionAttempt::from_json( From 7cc768de7a90c1b8c21fd0f97864c96072417930 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 06:11:58 +0000 Subject: [PATCH 6/6] refactor: drop comments that restate the code Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01M2kJ4nGaM8imZCVMEKjmXA --- codegen/layouts/resource.hbs | 5 +---- src/Resources/Event.php | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/codegen/layouts/resource.hbs b/codegen/layouts/resource.hbs index 76ccf749..cbd29a87 100644 --- a/codegen/layouts/resource.hbs +++ b/codegen/layouts/resource.hbs @@ -46,10 +46,7 @@ namespace {{namespace}} { {{#if hasRawJson}} private mixed $raw_json_source = null; - /** - * The payload this event was parsed from, as JSON. Reaches fields the - * generated properties do not cover, such as one added after this release. - */ + /** The payload this event was parsed from, as JSON. */ public function raw_json(): string { return json_encode($this->raw_json_source); diff --git a/src/Resources/Event.php b/src/Resources/Event.php index 8a55b799..b5da782c 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -429,10 +429,7 @@ public static function from_json(mixed $json): Event|null private mixed $raw_json_source = null; - /** - * The payload this event was parsed from, as JSON. Reaches fields the - * generated properties do not cover, such as one added after this release. - */ + /** The payload this event was parsed from, as JSON. */ public function raw_json(): string { return json_encode($this->raw_json_source);