From c760d61b04fc5825044b7a7801dba4bb1d61984d Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 6 Aug 2026 08:10:08 +0000 Subject: [PATCH 01/35] feat: unify the SDK with the Seam SDKs for other languages The Python, Ruby, and JavaScript SDKs share a runtime core, a common README skeleton, and a test suite aligned to the same baseline. This SDK had the codegen and release tooling but not the runtime surface. This brings it in line. Added: - Personal access token authentication, with the seam-workspace header, and token format validation that rejects client session tokens, JWTs, and publishable keys with a specific message. - from_api_key, from_personal_access_token, and from_client factories. - SeamMultiWorkspace for the endpoints that are not scoped to a workspace. - SeamWebhook, verifying incoming webhooks with svix. - SEAM_ENDPOINT support, plus the deprecated SEAM_API_URL and its warnings. - Retries, two by default with exponential backoff, via caseyamcl/guzzle_retry_middleware. A request that never reached the server is always retried; a status code is only retried for idempotent methods, since retrying a POST the server may already have processed could duplicate a write. The other Seam SDKs make the same trade. - HTTP layer configuration: guzzle_options, retries, and an injectable client. - A client level wait_for_action_attempt default, accepting a bool or a timeout and polling_interval. - A test suite covering auth, env, headers, errors, malformed responses, retries, pagination, serialization, action attempts, and webhooks, run against @seamapi/fake-seam-connect. - Psalm, wired into composer lint. Fixed: - Responses in the 3xx range were treated as successful. - The Seam error check accepted any body with a truthy error key. It now checks the content type and that error.type and error.message are strings, matching the other SDKs. - throw_http_errors let Guzzle throw before the SDK could map the error, making the whole error mapping unreachable. The option is gone. - Malformed JSON silently decoded to null and then failed on property access. - Non-Seam error responses raised an exception built from a fabricated request rather than the real one. - getRequestId returned an empty string rather than null when the header was absent, and the fallback error type was unknown rather than unknown_error. - HttpInvalidInputError never actually overrode the error code, and the action attempt errors wrote to an undeclared property. - Paginator::firstPage indexed its cache unconditionally, and the null cursor guard was unreachable. BREAKING CHANGE: The client is Seam\Seam; Seam\SeamClient remains as a deprecated alias. The constructor takes named options, so endpoint is no longer the second positional argument, and throw_http_errors is removed. Exceptions moved to the Seam\Exceptions namespace. poll_until_ready is removed in favor of wait_for_action_attempt, whose defaults change from 20s/0.4s to 10s/1s. $seam->client is a Seam\Http\SeamHttpClient rather than a Guzzle client. The $api_key property and the global LTS_VERSION constant are removed. Responses in the 3xx range are no longer treated as successful. Requests are now retried. Pagination metadata is a Seam\Pagination object. PHP 8.1 or later is required, and svix/svix is a new dependency. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- .gitattributes | 2 +- .github/workflows/check.yml | 12 +- .gitignore | 2 +- README.md | 374 +- codegen/layouts/partials/client-class.hbs | 20 +- codegen/layouts/partials/poll-until-ready.hbs | 24 - codegen/layouts/partials/route-method.hbs | 24 +- codegen/layouts/seam-client.hbs | 212 +- codegen/lib/handlebars-helpers.ts | 10 +- codegen/lib/layouts/route.ts | 115 +- codegen/lib/routes.ts | 2 +- codegen/smith.ts | 2 +- composer.json | 27 +- composer.lock | 5112 ++++++++++++++--- package-lock.json | 105 + package.json | 4 +- phpunit.xml.dist | 15 +- psalm.xml | 20 + src/Auth.php | 149 + src/Exceptions/ActionAttemptError.php | 8 +- src/Exceptions/ActionAttemptFailedError.php | 20 +- src/Exceptions/ActionAttemptTimeoutError.php | 9 +- src/Exceptions/HttpApiError.php | 28 +- src/Exceptions/HttpInvalidInputError.php | 13 +- src/Exceptions/HttpUnauthorizedError.php | 20 +- src/Exceptions/InvalidOptionsError.php | 16 + src/Exceptions/InvalidTokenError.php | 15 + src/Exceptions/SeamException.php | 9 + src/Http/ResolveActionAttempt.php | 103 + src/Http/SeamHttpClient.php | 287 + src/Options.php | 103 + src/Pagination.php | 32 + src/Paginator.php | 57 +- src/Routes/AccessCodesClient.php | 74 +- src/Routes/AccessCodesSimulateClient.php | 33 +- src/Routes/AccessCodesUnmanagedClient.php | 48 +- src/Routes/AccessGrantsClient.php | 58 +- src/Routes/AccessGrantsUnmanagedClient.php | 36 +- src/Routes/AccessMethodsClient.php | 115 +- src/Routes/AccessMethodsUnmanagedClient.php | 29 +- src/Routes/AcsAccessGroupsClient.php | 55 +- src/Routes/AcsClient.php | 29 +- src/Routes/AcsCredentialsClient.php | 62 +- src/Routes/AcsEncodersClient.php | 95 +- src/Routes/AcsEncodersSimulateClient.php | 41 +- src/Routes/AcsEntrancesClient.php | 65 +- src/Routes/AcsSystemsClient.php | 37 +- src/Routes/AcsUsersClient.php | 60 +- src/Routes/ActionAttemptsClient.php | 83 +- src/Routes/ClientSessionsClient.php | 39 +- src/Routes/ConnectWebviewsClient.php | 34 +- src/Routes/ConnectedAccountsClient.php | 45 +- .../ConnectedAccountsSimulateClient.php | 23 +- src/Routes/CustomersClient.php | 27 +- src/Routes/DevicesClient.php | 40 +- src/Routes/DevicesSimulateClient.php | 57 +- src/Routes/DevicesUnmanagedClient.php | 28 +- src/Routes/EventsClient.php | 21 +- src/Routes/InstantKeysClient.php | 27 +- src/Routes/LocksClient.php | 95 +- src/Routes/LocksSimulateClient.php | 66 +- src/Routes/NoiseSensorsClient.php | 27 +- .../NoiseSensorsNoiseThresholdsClient.php | 63 +- src/Routes/NoiseSensorsSimulateClient.php | 23 +- src/Routes/PhonesClient.php | 33 +- src/Routes/PhonesSimulateClient.php | 23 +- src/Routes/SpacesClient.php | 98 +- src/Routes/ThermostatsClient.php | 260 +- src/Routes/ThermostatsDailyProgramsClient.php | 80 +- src/Routes/ThermostatsSchedulesClient.php | 65 +- src/Routes/ThermostatsSimulateClient.php | 33 +- src/Routes/UserIdentitiesClient.php | 107 +- src/Routes/UserIdentitiesUnmanagedClient.php | 36 +- src/Routes/WebhooksClient.php | 47 +- src/Routes/WorkspacesClient.php | 48 +- src/Seam.php | 256 + src/SeamClient.php | 156 +- src/SeamMultiWorkspace.php | 91 + src/SeamWebhook.php | 53 + src/Token.php | 67 + src/WorkspacesProxy.php | 54 + tests/ApiKeyTest.php | 89 + tests/ClientTest.php | 168 + tests/EnvTest.php | 143 + tests/HeadersTest.php | 141 + tests/HttpErrorTest.php | 117 +- tests/MalformedResponseTest.php | 112 + tests/PackageVersionTest.php | 4 +- tests/PaginatorTest.php | 123 + tests/PersonalAccessTokenTest.php | 140 + tests/RetryTest.php | 139 + tests/SeamWebhookTest.php | 103 + tests/SerializationTest.php | 55 + tests/Support/FakeSeamConnect.php | 171 + tests/Support/FakeSeamConnectTestCase.php | 45 + tests/Support/RecordingClient.php | 107 + tests/WaitForActionAttemptTest.php | 283 + 97 files changed, 9326 insertions(+), 2607 deletions(-) delete mode 100644 codegen/layouts/partials/poll-until-ready.hbs create mode 100644 psalm.xml create mode 100644 src/Auth.php create mode 100644 src/Exceptions/InvalidOptionsError.php create mode 100644 src/Exceptions/InvalidTokenError.php create mode 100644 src/Exceptions/SeamException.php create mode 100644 src/Http/ResolveActionAttempt.php create mode 100644 src/Http/SeamHttpClient.php create mode 100644 src/Options.php create mode 100644 src/Pagination.php create mode 100644 src/Seam.php create mode 100644 src/SeamMultiWorkspace.php create mode 100644 src/SeamWebhook.php create mode 100644 src/Token.php create mode 100644 src/WorkspacesProxy.php create mode 100644 tests/ApiKeyTest.php create mode 100644 tests/ClientTest.php create mode 100644 tests/EnvTest.php create mode 100644 tests/HeadersTest.php create mode 100644 tests/MalformedResponseTest.php create mode 100644 tests/PaginatorTest.php create mode 100644 tests/PersonalAccessTokenTest.php create mode 100644 tests/RetryTest.php create mode 100644 tests/SeamWebhookTest.php create mode 100644 tests/SerializationTest.php create mode 100644 tests/Support/FakeSeamConnect.php create mode 100644 tests/Support/FakeSeamConnectTestCase.php create mode 100644 tests/Support/RecordingClient.php create mode 100644 tests/WaitForActionAttemptTest.php diff --git a/.gitattributes b/.gitattributes index 641e8a3b..a4e1b5c5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,6 @@ src/Resources/*.php linguist-generated src/Routes/*.php linguist-generated -src/SeamClient.php linguist-generated +src/Seam.php linguist-generated # Keep development files out of the published package. # GitHub builds the archives Composer downloads as dist with git archive, diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 95ca05d1..749b0e39 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,7 +20,10 @@ jobs: os: - ubuntu-latest php: - - '8.0' + - '8.1' + - '8.2' + - '8.3' + - '8.4' - '8.5' include: - os: ubuntu-latest @@ -44,7 +47,7 @@ jobs: fail-fast: false matrix: php: - - '8.0' + - '8.1' - '8.5' steps: - name: Checkout @@ -73,7 +76,10 @@ jobs: os: - ubuntu-latest php: - - '8.0' + - '8.1' + - '8.2' + - '8.3' + - '8.4' - '8.5' include: - os: ubuntu-latest diff --git a/.gitignore b/.gitignore index 78aaf436..b75b485f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ vendor tmp # PHPUnit result cache -.phpunit.result.cache +.phpunit.cache/ # Build directories package diff --git a/README.md b/README.md index 567a864c..35ef6120 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,167 @@ # Seam PHP SDK -Control locks, lights and other internet of things devices with Seam's simple API. +[![Packagist](https://img.shields.io/packagist/v/seamapi/seam.svg)](https://packagist.org/packages/seamapi/seam) +[![Seam LTS Version](https://img.shields.io/badge/Seam_LTS-1.0.0-blue)](https://docs.seam.co/lts) +[![GitHub Actions](https://github.com/seamapi/php/actions/workflows/check.yml/badge.svg)](https://github.com/seamapi/php/actions/workflows/check.yml) -Check out [the documentation](https://docs.seam.co) or the usage below. +PHP SDK for the Seam API. + +## Description + +[Seam] makes it easy to integrate IoT devices with your applications. +This is an official SDK for the Seam API. +Please refer to the official [Seam Docs] to get started. + +Parts of this SDK are generated from always up-to-date type information +provided by [@seamapi/types]. +This ensures all API methods, request shapes, and response shapes are +accurate and fully typed. + +The underlying HTTP client is [Guzzle]. + +[Seam]: https://www.seam.co/ +[Seam Docs]: https://docs.seam.co/latest/ +[@seamapi/types]: https://github.com/seamapi/types/ +[Guzzle]: https://docs.guzzlephp.org/ + +## Installation + +Add this as a dependency to your project using [Composer] with + +``` +$ composer require seamapi/seam +``` + +[Composer]: https://getcomposer.org/ ## Usage +> [!NOTE] +> These examples assume `SEAM_API_KEY` is set in your environment. + +### Examples + +#### List devices + ```php -$seam = new Seam\SeamClient("YOUR_API_KEY"); +$seam = new Seam\Seam(); -# Create a Connect Webview to login to a provider -$connect_webview = $seam->connect_webviews->create( - accepted_providers: ["august"] -); +$devices = $seam->devices->list(); +``` -print "Please Login at this url: " . $connect_webview->url; +#### Unlock a door -# Poll until connect webview is completed -while (true) { - $connect_webview = $seam->connect_webviews->get( - $connect_webview->connect_webview_id - ); - if ($connect_webview->status == "authorized") { - break; - } else { - sleep(1); - } -} +```php +$seam = new Seam\Seam(); -$connected_account = $seam->connected_accounts->get( - $connect_webview->connected_account_id -); +$lock = $seam->locks->get(name: "Front Door"); +$seam->locks->unlock_door($lock->device_id); +``` + +### Authentication Method + +The SDK supports two authentication mechanisms. +Configure either by passing the corresponding options to the `Seam` +constructor, or with the more ergonomic static factory methods. -print "Looks like you connected with " . - json_encode($connected_account->user_identifier); +#### API Key + +An API key is scoped to a single workspace and should only be used on the +server. Obtain one from the Seam Console. + +```php +// Set the SEAM_API_KEY environment variable +$seam = new Seam\Seam(); -$devices = $seam->devices->list( - connected_account_id: $connected_account->connected_account_id +// Pass as an option to the constructor +$seam = new Seam\Seam(api_key: "your-api-key"); + +// Use the factory method +$seam = Seam\Seam::from_api_key("your-api-key"); +``` + +#### Personal Access Token + +A Personal Access Token is scoped to a Seam Console user. +It must be used with a workspace id. + +```php +// Pass as options to the constructor +$seam = new Seam\Seam( + personal_access_token: "your-personal-access-token", + workspace_id: "your-workspace-id" ); -print "You have " . count($devices) . " devices"; +// Use the factory method +$seam = Seam\Seam::from_personal_access_token( + "your-personal-access-token", + "your-workspace-id" +); +``` + +### Action Attempts + +Some operations tell a device to do something, and the device may take time +to report back. Those endpoints return an [action attempt]. + +By default the SDK waits for the action attempt to finish: + +- It polls up to a timeout at a polling interval. +- It returns a fresh copy of the successful action attempt. +- It throws `Seam\Exceptions\ActionAttemptFailedError` if the action failed. +- It throws `Seam\Exceptions\ActionAttemptTimeoutError` if the timeout + elapses first. + +Both errors extend `Seam\Exceptions\ActionAttemptError` and expose the action +attempt with `getActionAttempt()`. -$device_id = $devices[0]->device_id; +[action attempt]: https://docs.seam.co/latest/core-concepts/action-attempts -# Lock a Door -$seam->locks->lock_door($device_id); +```php +use Seam\Exceptions\ActionAttemptFailedError; +use Seam\Exceptions\ActionAttemptTimeoutError; + +try { + $seam->locks->unlock_door($device_id); +} catch (ActionAttemptFailedError $error) { + print "Could not unlock the door: " . $error->getMessage(); + print "Error code: " . $error->getErrorCode(); +} catch (ActionAttemptTimeoutError $error) { + print "The door did not unlock in time"; + print "Action attempt: " . $error->getActionAttempt()->action_attempt_id; +} +``` + +Waiting may be disabled for the whole client: -$updated_device = $seam->devices->get($device_id); -$updated_device->properties->locked; // true +```php +$seam = new Seam\Seam(wait_for_action_attempt: false); + +$action_attempt = $seam->locks->unlock_door($device_id); +$action_attempt->status; // "pending" +``` -# Unlock a Door -$seam->locks->unlock_door($device_id); -$updated_device->properties->locked; // false +or for a single request: -# Create an access code on a device -$access_code = $seam->access_codes->create( - device_id: $device_id, - code: "1234", - name: "Test Code" +```php +$action_attempt = $seam->locks->unlock_door( + $device_id, + wait_for_action_attempt: false ); +``` + +The timeout and polling interval, both in seconds, may be configured either +on the client or per request: -# Check the status of an access code -$access_code->status; // 'setting' (it will go to 'set' when active on the device) +```php +$seam = new Seam\Seam( + wait_for_action_attempt: ["timeout" => 30.0, "polling_interval" => 2.0] +); -$seam->access_codes->delete($access_code->access_code_id); +$seam->locks->unlock_door( + $device_id, + wait_for_action_attempt: ["timeout" => 5.0] +); ``` ### Pagination @@ -153,21 +251,171 @@ $pages = $seam->createPaginator( $connectedAccounts = $pages->flattenToArray(); ``` -## Installation +### Interacting with Multiple Workspaces + +Some endpoints are not scoped to a workspace. Use `SeamMultiWorkspace` with a +personal access token to reach them. + +```php +$seam = Seam\SeamMultiWorkspace::from_personal_access_token( + "your-personal-access-token" +); + +// List workspaces authorized for this Personal Access Token +$workspaces = $seam->workspaces->list(); + +$workspace = $seam->workspaces->create( + name: "New Workspace", + connect_partner_name: "Your Company" +); +``` + +### Webhooks + +Seam delivers webhooks with [Svix]. Verify and parse an incoming request with +`SeamWebhook`, which returns the typed event. + +[Svix]: https://www.svix.com/ + +```php +$webhook = new Seam\SeamWebhook($webhook_secret); + +try { + $event = $webhook->verify($request_body, $request_headers); + print $event->event_type; +} catch (Svix\Exception\WebhookVerificationException $error) { + http_response_code(400); +} +``` + +### Advanced Usage + +#### Setting the endpoint + +The endpoint may be set with the `SEAM_ENDPOINT` environment variable, or +passed directly. + +```php +$seam = new Seam\Seam(endpoint: "https://example.com"); +``` + +#### Configuring the Guzzle client + +Pass any [Guzzle request option] with `guzzle_options`. They are merged into +the client the SDK builds, so the authorization and SDK headers are kept. + +[Guzzle request option]: https://docs.guzzlephp.org/en/stable/request-options.html -To install the latest version of the automatically generated SDK, run: +```php +$seam = new Seam\Seam( + guzzle_options: [ + "timeout" => 30, + "headers" => ["X-Custom-Header" => "value"], + "proxy" => "http://localhost:8125", + ] +); +``` + +> [!NOTE] +> Unlike the other Seam SDKs, this one sets a default request timeout of 60 +> seconds so a hung connection eventually fails. Pass `timeout` to change it, +> or `0` to disable it. + +#### Retries + +Failed requests are retried twice by default, with exponential backoff. + +A request that never reached the server, e.g. a connection failure, is always +retried. A request that did reach the server is only retried on a retryable +status when the HTTP method is idempotent. Every Seam endpoint is a `POST`, so +retrying one that the server may already have processed could duplicate a +write. The other Seam SDKs make the same trade. + +```php +// Retry more times +$seam = new Seam\Seam(retries: 5); + +// Turn retries off +$seam = new Seam\Seam(retries: 0); +``` + +#### Overriding the client + +Pass an already configured Guzzle client. It carries its own endpoint and +authorization, so no other authentication option may be given alongside it. -`composer require seamapi/seam` +```php +$client = new GuzzleHttp\Client([ + "base_uri" => "https://connect.getseam.com", + "headers" => ["authorization" => "Bearer " . $api_key], +]); + +$seam = Seam\Seam::from_client($client); +``` + +#### Errors -If you want to install our previous handwritten version, run: +Every exception the SDK raises implements `Seam\Exceptions\SeamException`. -`composer require seamapi/seam:1.1` +| Error | Raised when | +| --------------------------- | -------------------------------------------------- | +| `HttpApiError` | The API returned an error response. | +| `HttpUnauthorizedError` | The credentials were rejected. | +| `HttpInvalidInputError` | The request parameters were rejected. | +| `InvalidOptionsError` | The client options are incomplete or incompatible. | +| `InvalidTokenError` | The token is of the wrong kind or format. | +| `ActionAttemptFailedError` | An action attempt finished in the error state. | +| `ActionAttemptTimeoutError` | An action attempt did not finish in time. | + +An error response that is not shaped like a Seam error, e.g. a gateway +returning HTML, surfaces as the underlying `GuzzleHttp\Exception\ +BadResponseException` instead. Transport failures surface as the +corresponding Guzzle exception. + +```php +use Seam\Exceptions\HttpApiError; +use Seam\Exceptions\HttpInvalidInputError; + +try { + $seam->devices->get($device_id); +} catch (HttpInvalidInputError $error) { + print_r($error->getValidationErrorMessages("device_id")); +} catch (HttpApiError $error) { + print $error->getErrorCode(); + print $error->getStatusCode(); + print $error->getRequestId(); +} +``` + +## Upgrading from 3.x + +Version 4 brings this SDK in line with the Seam SDKs for other languages. + +- PHP 8.1 or later is now required. +- The client class is `Seam\Seam`. `Seam\SeamClient` still works as a + deprecated alias. +- The constructor takes named options. `$endpoint` is no longer the second + positional argument, and `$throw_http_errors` is gone; API errors always + raise a Seam exception. +- Exceptions moved to the `Seam\Exceptions` namespace, e.g. + `Seam\HttpApiError` is now `Seam\Exceptions\HttpApiError`. +- `$seam->action_attempts->poll_until_ready()` was removed. Use + `wait_for_action_attempt`, which now also accepts a `timeout` and + `polling_interval`. The defaults changed from 20s/0.4s to 10s/1s. +- `$seam->client` is a `Seam\Http\SeamHttpClient`. The Guzzle client is + available with `$seam->client->get_client()`. +- The `$seam->api_key` property and the global `LTS_VERSION` constant were + removed. Use `Seam\Seam::LTS_VERSION`. +- Responses in the 3xx range are no longer treated as successful. +- Requests are now retried; see [Retries](#retries). +- Pagination metadata is a `Seam\Pagination` object rather than a + `stdClass`. ## Development and Testing ### Quickstart -Install [PHP](https://www.php.net/) 8.0 or later, +Install [PHP](https://www.php.net/) 8.1 or later, [Composer](https://getcomposer.org/) and [Node.js](https://nodejs.org/), then run @@ -186,13 +434,13 @@ View them with $ composer run-script --list ``` -| Task | Command | -| ----------------- | ------------------ | -| Run the tests | `composer test` | -| Lint | `composer lint` | -| Format | `npm run format` | -| Build the package | `composer build` | -| Generate the SDK | `npm run generate` | +| Task | Command | +| ---------------------- | ------------------ | +| Run the tests | `composer test` | +| Lint and analyze types | `composer lint` | +| Format | `npm run format` | +| Build the package | `composer build` | +| Generate the SDK | `npm run generate` | Formatting is handled by [Prettier](https://prettier.io/) via [@prettier/plugin-php](https://github.com/prettier/plugin-php), @@ -215,10 +463,20 @@ $ composer test -- tests/MyTest.php PHPUnit is configured in `phpunit.xml.dist`. +Static analysis is handled by [Psalm](https://psalm.dev/), configured in +`psalm.xml` and run as part of `composer lint`. The generated sources under +`src/Resources` and `src/Routes` are excluded, since analyzing them would only +create pressure to change the generator. + ### Requirements -This package supports PHP 8.0 and later. -Continuous integration exercises both ends of that range, PHP 8.0 and 8.5. +This package supports PHP 8.1 and later. +Continuous integration exercises every supported version, PHP 8.1 through 8.5. + +The test suite runs against [@seamapi/fake-seam-connect], which is started +automatically for each test, so `npm install` must have been run first. + +[@seamapi/fake-seam-connect]: https://github.com/seamapi/fake-seam-connect ### Publishing diff --git a/codegen/layouts/partials/client-class.hbs b/codegen/layouts/partials/client-class.hbs index a205fc3f..79d6dd3d 100644 --- a/codegen/layouts/partials/client-class.hbs +++ b/codegen/layouts/partials/client-class.hbs @@ -1,6 +1,11 @@ class {{clientName}}Client { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; {{#if hasChildClients}} {{#each childClients}} public {{clientName}}Client ${{namespace}}; @@ -8,18 +13,19 @@ class {{clientName}}Client {{else}} {{/if}} - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; {{#each childClients}} - $this->{{namespace}} = new {{clientName}}Client($seam); + $this->{{namespace}} = new {{clientName}}Client($client, $defaults); {{/each}} } {{#each methods}} {{> route-method}} {{/each}} -{{#if isActionAttempts}} -{{> poll-until-ready}} -{{/if}} } diff --git a/codegen/layouts/partials/poll-until-ready.hbs b/codegen/layouts/partials/poll-until-ready.hbs deleted file mode 100644 index 87ce726d..00000000 --- a/codegen/layouts/partials/poll-until-ready.hbs +++ /dev/null @@ -1,24 +0,0 @@ - public function poll_until_ready(string $action_attempt_id, float $timeout = 20.0): ActionAttempt - { - $seam = $this->seam; - $time_waiting = 0.0; - $polling_interval = 0.4; - $action_attempt = $seam->action_attempts->get($action_attempt_id); - - while ($action_attempt->status == 'pending') { - $action_attempt = $seam->action_attempts->get( - $action_attempt->action_attempt_id - ); - if ($time_waiting > $timeout) { - throw new ActionAttemptTimeoutError($action_attempt, $timeout); - } - $time_waiting += $polling_interval; - usleep($polling_interval * 1000000); - } - - if ($action_attempt->status == 'error') { - throw new ActionAttemptFailedError($action_attempt); - } - - return $action_attempt; - } diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 9c73dfd9..bf344f74 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -3,14 +3,18 @@ {{#if hasParams}} $request_payload = []; -{{#each paramNames}} - if (${{this}} !== null) { - $request_payload["{{this}}"] = ${{this}}; +{{#each parameters}} +{{#if required}} + $request_payload["{{name}}"] = ${{name}}; +{{else}} + if (${{name}} !== null) { + $request_payload["{{name}}"] = ${{name}}; } +{{/if}} {{/each}} {{/if}} - {{#unless returnsVoid}}$res = {{/unless}}$this->seam->request( + {{#unless returnsVoid}}$res = {{/unless}}$this->client->request( "POST", "{{path}}", {{#if hasParams}} @@ -19,13 +23,11 @@ ); {{#if usesActionAttempt}} - if (!$wait_for_action_attempt) { - return {{returnResource}}::from_json($res->{{returnPath}}); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready($res->action_attempt->action_attempt_id); - - return $action_attempt; + return ResolveActionAttempt::resolve_action_attempt( + {{returnResource}}::from_json($res->{{returnPath}}), + $this->client, + $wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"] + ); {{else}} {{#if usesOnResponse}} diff --git a/codegen/layouts/seam-client.hbs b/codegen/layouts/seam-client.hbs index d39a7c01..c452d625 100644 --- a/codegen/layouts/seam-client.hbs +++ b/codegen/layouts/seam-client.hbs @@ -5,96 +5,158 @@ namespace Seam; {{#each useStatements}} use {{this}}; {{/each}} -use Seam\Utils\PackageVersion; -use GuzzleHttp\Client as HTTPClient; -use \Exception as Exception; -use Seam\HttpApiError; -use Seam\HttpUnauthorizedError; -use Seam\HttpInvalidInputError; - -define('LTS_VERSION', '1.0.0'); - -class SeamClient +use GuzzleHttp\ClientInterface; +use Seam\Http\SeamHttpClient; + +/** + * Client for the Seam API. + * + * Authenticate with an API key, which is scoped to a single workspace, or with + * a personal access token together with the id of the workspace to act on. + * When neither is given, the SEAM_API_KEY environment variable is used. + * + * @see https://docs.seam.co/ + */ +class Seam { {{#each parentClients}} public {{clientName}}Client ${{namespace}}; {{/each}} - public string $api_key; - public HTTPClient $client; - public string $ltsVersion = LTS_VERSION; - + /** + * The long term support version of the Seam API this SDK targets. + */ + public const LTS_VERSION = SeamHttpClient::LTS_VERSION; + + public SeamHttpClient $client; + + /** + * Default request options applied to every call, currently just + * wait_for_action_attempt. + * + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + public array $defaults; + + /** + * @param bool|array{timeout?: float, polling_interval?: float}|null $wait_for_action_attempt Whether to wait for action attempts to finish, optionally with timeout and polling_interval in seconds. Defaults to true. + * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. timeout or headers. + * @param int|null $retries How many times to retry a failed request. Defaults to 2; pass 0 to disable. + * @param ClientInterface|null $client A preconfigured Guzzle client. It carries its own endpoint and authorization, so no other authentication option may be given with it. + */ public function __construct( - $api_key = null, - $endpoint = "https://connect.getseam.com", - $throw_http_errors = false + ?string $api_key = null, + ?string $personal_access_token = null, + ?string $workspace_id = null, + ?string $endpoint = null, + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null, + ?ClientInterface $client = null ) { - $this->api_key = $api_key ?: (getenv('SEAM_API_KEY') ?: null); - $seam_sdk_version = PackageVersion::get(); - $this->client = new HTTPClient([ - "base_uri" => $endpoint, - "timeout" => 60.0, - "headers" => [ - "Authorization" => "Bearer " . $this->api_key, - "User-Agent" => "Seam PHP Client ". $seam_sdk_version, - "seam-sdk-name" => "seamapi/php", - "seam-sdk-version" => $seam_sdk_version, - "seam-lts-version" => $this->ltsVersion - ], - "http_errors" => $throw_http_errors, - ]); + $this->defaults = [ + "wait_for_action_attempt" => $wait_for_action_attempt ?? true, + ]; + + // A client carries its own endpoint and authorization, so the + // authentication options are only read when one has to be built. + $this->client = $client !== null + ? SeamHttpClient::from_client($client) + : new SeamHttpClient( + Options::get_endpoint($endpoint), + Auth::get_auth_headers($api_key, $personal_access_token, $workspace_id), + $guzzle_options, + $retries + ); + {{#each parentClients}} - $this->{{namespace}} = new {{clientName}}Client($this); + $this->{{namespace}} = new {{clientName}}Client($this->client, $this->defaults); {{/each}} } + /** + * Creates a client authorized with an API key. + */ + public static function from_api_key( + string $api_key, + ?string $endpoint = null, + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null + ): static { + return new static( + api_key: $api_key, + endpoint: $endpoint, + wait_for_action_attempt: $wait_for_action_attempt, + guzzle_options: $guzzle_options, + retries: $retries + ); + } + + /** + * Creates a client authorized with a personal access token, scoped to the + * given workspace. + */ + public static function from_personal_access_token( + string $personal_access_token, + string $workspace_id, + ?string $endpoint = null, + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null + ): static { + return new static( + personal_access_token: $personal_access_token, + workspace_id: $workspace_id, + endpoint: $endpoint, + wait_for_action_attempt: $wait_for_action_attempt, + retries: $retries, + guzzle_options: $guzzle_options + ); + } + + /** + * Creates a client from a preconfigured Guzzle client. + */ + public static function from_client( + ClientInterface $client, + bool|array|null $wait_for_action_attempt = null + ): static { + return new static( + client: $client, + wait_for_action_attempt: $wait_for_action_attempt + ); + } + + public function lts_version(): string + { + return self::LTS_VERSION; + } + + /** + * Makes a request against the Seam API with this client's authorization. + * + * @param array|object|null $json + * @param array|null $query + */ public function request( - $method, - $path, - $json = null, - $query = null, - ) { - $options = [ - "json" => $json, - "query" => $query, - ]; - $options = array_filter($options, fn($option) => $option !== null); - - $response = $this->client->request($method, $path, $options); - $status_code = $response->getStatusCode(); - $request_id = $response->getHeaderLine("seam-request-id"); - - $res_json = null; - try { - $res_json = json_decode($response->getBody()); - } catch (Exception $ignoreError) { - } - - if ($status_code >= 400) { - if ($status_code === 401) { - throw new HttpUnauthorizedError($request_id); - } - - if (($res_json->error ?? null) != null) { - if ($res_json->error->type === 'invalid_input') { - throw new HttpInvalidInputError($res_json->error, $status_code, $request_id); - } - - throw new HttpApiError($res_json->error, $status_code, $request_id); - } - - throw \GuzzleHttp\Exception\RequestException::create( - new \GuzzleHttp\Psr7\Request($method, $path), - $response - ); - } - - return $res_json; + string $method, + string $path, + mixed $json = null, + ?array $query = null + ): mixed { + return $this->client->request($method, $path, $json, $query); } - public function createPaginator($request, $params = []) + /** + * Creates a paginator for a list endpoint. + * + * @param callable $request Invokes the list method with a params array, e.g. fn($params) => $seam->devices->list(...$params) + * @param array $params + */ + public function createPaginator(callable $request, array $params = []): Paginator { - return new Paginator($request, $params); + return new Paginator($request, $params); } } diff --git a/codegen/lib/handlebars-helpers.ts b/codegen/lib/handlebars-helpers.ts index 263be9ce..62b6574a 100644 --- a/codegen/lib/handlebars-helpers.ts +++ b/codegen/lib/handlebars-helpers.ts @@ -9,7 +9,13 @@ export interface DeprecatedPhpDocContext { export interface MethodPhpDocContext extends DeprecatedPhpDocContext { returnType: string responseDescription: string - parameters: Array<{ name: string; type: string; description: string }> + // The endpoint parameters plus the SDK level ones, e.g. + // wait_for_action_attempt, so editors surface all of them. + documentedParameters: Array<{ + name: string + type: string + description: string + }> } export const resourcePhpDoc = (context: DeprecatedPhpDocContext): string => @@ -25,7 +31,7 @@ export const methodPhpDoc = (context: MethodPhpDocContext): string => createPhpDoc( context.description, [ - ...context.parameters.map( + ...context.documentedParameters.map( (parameter) => `@param ${parameter.type} $${parameter.name}${parameter.description === '' ? '' : ` ${parameter.description}`}`, ), diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index eb32944a..0cc1ac3e 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -8,12 +8,9 @@ import { sortPhpClientMethodParameters, } from '../class-model.js' -const seamClientClass = 'Seam\\SeamClient' +const seamHttpClientClass = 'Seam\\Http\\SeamHttpClient' +const resolveActionAttemptClass = 'Seam\\Http\\ResolveActionAttempt' const resourcesNamespace = 'Seam\\Resources' -const actionAttemptErrorClasses = [ - 'Seam\\ActionAttemptFailedError', - 'Seam\\ActionAttemptTimeoutError', -] export interface MethodLayoutContext { methodName: string @@ -21,12 +18,21 @@ export interface MethodLayoutContext { responseDescription: string isDeprecated: boolean deprecationMessage: string - parameters: Array<{ name: string; type: string; description: string }> + parameters: Array<{ + name: string + type: string + description: string + required: boolean + }> + documentedParameters: Array<{ + name: string + type: string + description: string + }> path: string returnType: string hasParams: boolean signatureParams: string - paramNames: string[] usesActionAttempt: boolean usesOnResponse: boolean returnsVoid: boolean @@ -40,21 +46,34 @@ export interface ClientLayoutContext { hasChildClients: boolean childClients: Array<{ clientName: string; namespace: string }> methods: MethodLayoutContext[] - isActionAttempts: boolean } export interface RouteLayoutContext extends ClientLayoutContext { useStatements: string[] } +const waitForActionAttemptParameter = { + name: 'wait_for_action_attempt', + type: 'bool|array|null', + description: + 'Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client.', + required: false, +} + +const onResponseParameter = { + name: 'on_response', + type: 'callable|null', + description: + 'Called with the raw response envelope, used by the paginator to read the pagination metadata.', + required: false, +} + const getMethodLayoutContext = ( method: PhpClientMethod, - clientName: string, ): MethodLayoutContext => { const { methodName, path, parameters, returnResource, returnPath } = method - const usesActionAttempt = - returnResource === 'ActionAttempt' && clientName !== 'ActionAttempts' + const usesActionAttempt = returnResource === 'ActionAttempt' const usesOnResponse = parameters.some((p) => p.name === 'page_cursor') && methodName === 'list' const returnsVoid = returnResource === '' @@ -71,26 +90,42 @@ const getMethodLayoutContext = ( (p) => `${!(p.required ?? false) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${(p.required ?? false) ? '' : ' = null'}`, ) - .concat(usesActionAttempt ? ['bool $wait_for_action_attempt = true'] : []) + .concat( + usesActionAttempt + ? ['bool|array|null $wait_for_action_attempt = null'] + : [], + ) .concat(usesOnResponse ? ['?callable $on_response = null'] : []) .join(', ') + const documentedEndpointParameters = sortedParameters.map( + ({ name, type, description, required }) => ({ + name, + type, + description, + required: required ?? false, + }), + ) + return { methodName, description: method.description, responseDescription: method.responseDescription, isDeprecated: method.isDeprecated, deprecationMessage: method.deprecationMessage, - parameters: sortedParameters.map(({ name, type, description }) => ({ - name, - type, - description, - })), + // The request payload is built from the endpoint parameters alone. + parameters: documentedEndpointParameters, + // The SDK level parameters are documented alongside them so editors + // surface all of them, but they never reach the payload. + documentedParameters: [ + ...documentedEndpointParameters, + ...(usesActionAttempt ? [waitForActionAttemptParameter] : []), + ...(usesOnResponse ? [onResponseParameter] : []), + ], path, returnType, hasParams: parameters.length > 0, signatureParams, - paramNames: sortedParameters.map((p) => p.name), usesActionAttempt, usesOnResponse, returnsVoid, @@ -100,44 +135,34 @@ const getMethodLayoutContext = ( } } -// Child clients live in the same namespace as their parent, so only the -// SeamClient, the resource classes returned by the methods, and the action -// attempt errors thrown by poll_until_ready need importing. -const getUseStatements = ( - client: PhpClient, - isActionAttempts: boolean, -): string[] => { +// Child clients live in the same namespace as their parent, so only the HTTP +// client, the action attempt resolver, and the resource classes returned by +// the methods need importing. +const getUseStatements = (client: PhpClient): string[] => { const resourceNames = new Set( client.methods .map((m) => m.returnResource) .filter((resourceName) => resourceName !== ''), ) - if (isActionAttempts) resourceNames.add('ActionAttempt') + const usesActionAttempt = resourceNames.has('ActionAttempt') return [ - seamClientClass, + seamHttpClientClass, + ...(usesActionAttempt ? [resolveActionAttemptClass] : []), ...[...resourceNames].map((name) => `${resourcesNamespace}\\${name}`), - ...(isActionAttempts ? actionAttemptErrorClasses : []), ].sort((a, b) => a.localeCompare(b)) } export const setRouteLayoutContext = ( client: PhpClient, -): RouteLayoutContext => { - const isActionAttempts = client.clientName === 'ActionAttempts' - - return { - useStatements: getUseStatements(client, isActionAttempts), - clientName: client.clientName, - hasChildClients: client.childClientIdentifiers.length > 0, - childClients: client.childClientIdentifiers.map((i) => ({ - clientName: i.clientName, - namespace: i.namespace, - })), - methods: client.methods.map((m) => - getMethodLayoutContext(m, client.clientName), - ), - isActionAttempts, - } -} +): RouteLayoutContext => ({ + useStatements: getUseStatements(client), + clientName: client.clientName, + hasChildClients: client.childClientIdentifiers.length > 0, + childClients: client.childClientIdentifiers.map((i) => ({ + clientName: i.clientName, + namespace: i.namespace, + })), + methods: client.methods.map(getMethodLayoutContext), +}) diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index f18f155a..e49bb63b 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -21,7 +21,7 @@ interface Metadata { const resourcesPath = 'src/Resources' const routesPath = 'src/Routes' -const seamClientPath = 'src/SeamClient.php' +const seamClientPath = 'src/Seam.php' export const routes = ( files: Metalsmith.Files, diff --git a/codegen/smith.ts b/codegen/smith.ts index 744efd43..ba1384fa 100644 --- a/codegen/smith.ts +++ b/codegen/smith.ts @@ -12,7 +12,7 @@ import { helpers, routes } from './lib/index.js' const rootDir = dirname(fileURLToPath(import.meta.url)) await Promise.all([ - deleteAsync(['./src/Resources', './src/Routes', './src/SeamClient.php']), + deleteAsync(['./src/Resources', './src/Routes', './src/Seam.php']), ]) const partials = await getHandlebarsPartials(`${rootDir}/layouts/partials`) diff --git a/composer.json b/composer.json index 6827b5ce..486452eb 100644 --- a/composer.json +++ b/composer.json @@ -25,19 +25,18 @@ "source": "https://github.com/seamapi/php" }, "require": { - "php": "^8.0", - "guzzlehttp/guzzle": "^7.5" + "php": "^8.1", + "caseyamcl/guzzle_retry_middleware": "^2.13", + "guzzlehttp/guzzle": "^7.5", + "svix/svix": "^1.40" }, "require-dev": { - "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "^3.7" + "phpunit/phpunit": "^10.5", + "vimeo/psalm": "^6.0" }, "autoload": { "psr-4": { - "Seam\\": [ - "src/", - "src/Exceptions/" - ] + "Seam\\": "src/" }, "classmap": [ "src/Resources/" @@ -58,7 +57,7 @@ "/.env.example", "/.github", "/.npmrc", - "/.phpunit.result.cache", + "/.phpunit.cache", "/.prettierignore", "/.prettierrc.json", "/.releaserc.json", @@ -69,6 +68,7 @@ "/package.json", "/phpunit.xml.dist", "/pkg", + "/psalm.xml", "/tests", "/tmp", "/tsconfig.json", @@ -81,16 +81,19 @@ "test": "phpunit", "lint": [ "@lint:composer", - "@lint:syntax" + "@lint:syntax", + "@lint:types" ], "lint:composer": "@composer validate --strict", - "lint:syntax": "! find src tests -name '*.php' -exec php -l {} \\; | grep -v '^No syntax errors detected'" + "lint:syntax": "! find src tests -name '*.php' -exec php -l {} \\; | grep -v '^No syntax errors detected'", + "lint:types": "psalm --no-cache" }, "scripts-descriptions": { "build": "Build a distributable archive of this package into pkg/.", "test": "Run the test suite.", "lint": "Run all lint checks.", "lint:composer": "Validate composer.json and composer.lock.", - "lint:syntax": "Check every PHP source file for syntax errors." + "lint:syntax": "Check every PHP source file for syntax errors.", + "lint:types": "Run static analysis with Psalm." } } diff --git a/composer.lock b/composer.lock index bc6794ae..a604302c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,38 +4,114 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "47b28c106be0ff4f75680c1fe23e931a", + "content-hash": "25d67117228a0425a94bd9f40c53f692", "packages": [ + { + "name": "caseyamcl/guzzle_retry_middleware", + "version": "v2.13.0", + "source": { + "type": "git", + "url": "https://github.com/caseyamcl/guzzle_retry_middleware.git", + "reference": "17c9299cde438b00bbeb099c6480319a81636a60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/caseyamcl/guzzle_retry_middleware/zipball/17c9299cde438b00bbeb099c6480319a81636a60", + "reference": "17c9299cde438b00bbeb099c6480319a81636a60", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.3|^7.0", + "php": "^7.1|^8.0" + }, + "require-dev": { + "jaschilz/php-coverage-badger": "^2.0", + "nesbot/carbon": "^2.0|^3.0", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.0", + "phpunit/phpunit": "^7.5|^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5", + "symfony/var-dumper": "^5.0|^6.0|^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleRetry\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Casey McLaughlin", + "email": "caseyamcl@gmail.com", + "homepage": "https://caseymclaughlin.com", + "role": "Developer" + } + ], + "description": "Guzzle v6+ retry middleware that handles 429/503 status codes and connection timeouts", + "homepage": "https://github.com/caseyamcl/guzzle_retry_middleware", + "keywords": [ + "Guzzle", + "back-off", + "caseyamcl", + "guzzle_retry_middleware", + "middleware", + "retry", + "retry-after" + ], + "support": { + "issues": "https://github.com/caseyamcl/guzzle_retry_middleware/issues", + "source": "https://github.com/caseyamcl/guzzle_retry_middleware/tree/v2.13.0" + }, + "funding": [ + { + "url": "https://github.com/caseyamcl", + "type": "github" + } + ], + "time": "2025-07-11T12:33:22+00:00" + }, { "name": "guzzlehttp/guzzle", - "version": "7.5.0", + "version": "7.15.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba" + "reference": "ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc", + "reference": "ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5", - "guzzlehttp/psr7": "^1.9 || ^2.4", + "guzzlehttp/promises": "^2.5.2", + "guzzlehttp/psr7": "^2.13", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-client-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", + "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "php-http/client-integration-tests": "^3.0", - "phpunit/phpunit": "^8.5.29 || ^9.5.23", + "guzzle/client-integration-tests": "3.0.3", + "guzzlehttp/test-server": "^0.7", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -48,9 +124,6 @@ "bamarni-bin": { "bin-links": true, "forward-command": false - }, - "branch-alias": { - "dev-master": "7.5-dev" } }, "autoload": { @@ -116,7 +189,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.5.0" + "source": "https://github.com/guzzle/guzzle/tree/7.15.3" }, "funding": [ { @@ -132,38 +205,38 @@ "type": "tidelift" } ], - "time": "2022-08-28T15:39:27+00:00" + "time": "2026-08-05T19:48:21+00:00" }, { "name": "guzzlehttp/promises", - "version": "1.5.2", + "version": "2.5.2", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "b94b2807d85443f9719887892882d0329d1e2598" + "reference": "2823687acff28b2dbe67b2508a6b300e2c3fa4ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", - "reference": "b94b2807d85443f9719887892882d0329d1e2598", + "url": "https://api.github.com/repos/guzzle/promises/zipball/2823687acff28b2dbe67b2508a6b300e2c3fa4ce", + "reference": "2823687acff28b2dbe67b2508a6b300e2c3fa4ce", "shasum": "" }, "require": { - "php": ">=5.5" + "php": "^7.2.5 || ^8.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.5-dev" + "bamarni-bin": { + "bin-links": true, + "forward-command": false } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\Promise\\": "src/" } @@ -200,7 +273,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.2" + "source": "https://github.com/guzzle/promises/tree/2.5.2" }, "funding": [ { @@ -216,36 +289,39 @@ "type": "tidelift" } ], - "time": "2022-08-28T14:55:35+00:00" + "time": "2026-08-05T19:30:54+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.4.1", + "version": "2.13.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379" + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/69568e4293f4fa993f3b0e51c9723e1e17c41379", - "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/dad89620b7a6edb60c15858442eb2e408b45d8f4", + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", - "ralouphie/getallheaders": "^3.0" + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "1.1.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -255,9 +331,6 @@ "bamarni-bin": { "bin-links": true, "forward-command": false - }, - "branch-alias": { - "dev-master": "2.4-dev" } }, "autoload": { @@ -319,7 +392,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.1" + "source": "https://github.com/guzzle/psr7/tree/2.13.0" }, "funding": [ { @@ -335,25 +408,25 @@ "type": "tidelift" } ], - "time": "2022-08-28T14:45:39+00:00" + "time": "2026-07-16T22:23:49+00:00" }, { "name": "psr/http-client", - "version": "1.0.1", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", "shasum": "" }, "require": { "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -373,7 +446,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP clients", @@ -385,27 +458,27 @@ "psr-18" ], "support": { - "source": "https://github.com/php-fig/http-client/tree/master" + "source": "https://github.com/php-fig/http-client" }, - "time": "2020-06-29T06:28:15+00:00" + "time": "2023-09-23T14:17:50+00:00" }, { "name": "psr/http-factory", - "version": "1.0.1", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", "shasum": "" }, "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0" + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -425,10 +498,10 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], - "description": "Common interfaces for PSR-7 HTTP message factories", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ "factory", "http", @@ -440,31 +513,31 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" + "source": "https://github.com/php-fig/http-factory" }, - "time": "2019-04-30T12:38:16+00:00" + "time": "2024-04-15T12:06:14+00:00" }, { "name": "psr/http-message", - "version": "1.0.1", + "version": "2.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -479,7 +552,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP messages", @@ -493,9 +566,9 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/master" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "time": "2016-08-06T14:39:51+00:00" + "time": "2023-04-04T09:54:51+00:00" }, { "name": "ralouphie/getallheaders", @@ -541,31 +614,84 @@ }, "time": "2019-03-08T08:55:37+00:00" }, + { + "name": "svix/svix", + "version": "v1.99.1", + "source": { + "type": "git", + "url": "https://github.com/svix/svix-webhooks.git", + "reference": "6cd12c1d6d19c6d222bab889d08315f6469702c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/svix/svix-webhooks/zipball/6cd12c1d6d19c6d222bab889d08315f6469702c8", + "reference": "6cd12c1d6d19c6d222bab889d08315f6469702c8", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^7.0", + "php": ">=8.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": ">=10.0 <=12.9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Svix\\": "php/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Svix", + "email": "support@svix.com", + "homepage": "http://www.svix.com" + } + ], + "description": "Svix PHP Library", + "homepage": "https://www.svix.com", + "keywords": [ + "api", + "webhooks" + ], + "support": { + "issues": "https://github.com/svix/svix-webhooks/issues", + "source": "https://github.com/svix/svix-webhooks/tree/v1.99.1" + }, + "time": "2026-07-23T16:05:36+00:00" + }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.2", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { @@ -590,7 +716,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" }, "funding": [ { @@ -601,47 +727,51 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" - } - ], - "packages-dev": [ + "time": "2026-06-05T06:23:12+00:00" + }, { - "name": "doctrine/instantiator", - "version": "1.4.1", + "name": "symfony/polyfill-php80", + "version": "v1.37.0", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", - "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.22" + "php": ">=7.2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -649,1330 +779,1819 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", "keywords": [ - "constructor", - "instantiate" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.1" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" }, "funding": [ { - "url": "https://www.doctrine-project.org/sponsorship.html", + "url": "https://symfony.com/sponsor", "type": "custom" }, { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2022-03-03T08:28:38+00:00" - }, + "time": "2026-04-10T16:19:22+00:00" + } + ], + "packages-dev": [ { - "name": "myclabs/deep-copy", - "version": "1.11.0", + "name": "amphp/amp", + "version": "v3.1.3", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + "url": "https://github.com/amphp/amp.git", + "reference": "73c38b323ff8d790abf0f76c56fcc892bda4111a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "url": "https://api.github.com/repos/amphp/amp/zipball/73c38b323ff8d790abf0f76c56fcc892bda4111a", + "reference": "73c38b323ff8d790abf0f76c56fcc892bda4111a", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" }, "type": "library", "autoload": { "files": [ - "src/DeepCopy/deep_copy.php" + "src/functions.php", + "src/Future/functions.php", + "src/Internal/functions.php" ], "psr-4": { - "DeepCopy\\": "src/DeepCopy/" + "Amp\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" ], "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v3.1.3" }, "funding": [ { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" + "url": "https://github.com/amphp", + "type": "github" } ], - "time": "2022-03-03T13:19:32+00:00" + "time": "2026-07-19T17:59:20+00:00" }, { - "name": "nikic/php-parser", - "version": "v4.15.1", + "name": "amphp/byte-stream", + "version": "v2.1.2", "source": { "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900" + "url": "https://github.com/amphp/byte-stream.git", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", - "reference": "0ef6c55a3f47f89d7a374e6f835197a0b5fcf900", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": ">=7.0" + "amphp/amp": "^3", + "amphp/parser": "^1.1", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2.3" }, "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.22.1" }, - "bin": [ - "bin/php-parse" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php" + ], "psr-4": { - "PhpParser\\": "lib/PhpParser" + "Amp\\ByteStream\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Nikita Popov" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "A PHP parser written in PHP", + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "https://amphp.org/byte-stream", "keywords": [ - "parser", - "php" + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" ], "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.1" + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v2.1.2" }, - "time": "2022-09-04T07:30:47+00:00" + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-03-16T17:10:27+00:00" }, { - "name": "phar-io/manifest", - "version": "2.0.3", + "name": "amphp/cache", + "version": "v2.0.1", "source": { "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "url": "https://github.com/amphp/cache.git", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" + "amphp/amp": "^3", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" }, + "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Amp\\Cache\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Niklas Keller", + "email": "me@kelunik.com" }, { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" } ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "description": "A fiber-aware cache API based on Amp and Revolt.", + "homepage": "https://amphp.org/cache", "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "issues": "https://github.com/amphp/cache/issues", + "source": "https://github.com/amphp/cache/tree/v2.0.1" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:38:06+00:00" }, { - "name": "phar-io/version", - "version": "3.2.1", + "name": "amphp/dns", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + "url": "https://github.com/amphp/dns.git", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "url": "https://api.github.com/repos/amphp/dns/zipball/78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/process": "^2", + "daverandom/libdns": "^2.0.2", + "ext-filter": "*", + "ext-json": "*", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" }, "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Dns\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Chris Wright", + "email": "addr@daverandom.com" }, { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" } ], - "description": "Library for handling version information and constraints", + "description": "Async DNS resolution for Amp.", + "homepage": "https://github.com/amphp/dns", + "keywords": [ + "amp", + "amphp", + "async", + "client", + "dns", + "resolve" + ], "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" + "issues": "https://github.com/amphp/dns/issues", + "source": "https://github.com/amphp/dns/tree/v2.4.0" }, - "time": "2022-02-21T01:04:05+00:00" + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-01-19T15:43:40+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "9.2.17", + "name": "amphp/parallel", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8" + "url": "https://github.com/amphp/parallel.git", + "reference": "37f5b2754fadc229c00f9416bd68fb8d04529a81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aa94dc41e8661fe90c7316849907cba3007b10d8", - "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8", + "url": "https://api.github.com/repos/amphp/parallel/zipball/37f5b2754fadc229c00f9416bd68fb8d04529a81", + "reference": "37f5b2754fadc229c00f9416bd68fb8d04529a81", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.14", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/pipeline": "^1", + "amphp/process": "^2", + "amphp/serialization": "^1", + "amphp/socket": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1" }, "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.2-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/Context/functions.php", + "src/Context/Internal/functions.php", + "src/Ipc/functions.php", + "src/Worker/functions.php" + ], + "psr-4": { + "Amp\\Parallel\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "description": "Parallel processing component for Amp.", + "homepage": "https://github.com/amphp/parallel", "keywords": [ - "coverage", - "testing", - "xunit" + "async", + "asynchronous", + "concurrent", + "multi-processing", + "multi-threading" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.17" + "issues": "https://github.com/amphp/parallel/issues", + "source": "https://github.com/amphp/parallel/tree/v2.4.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2022-08-30T12:24:04+00:00" + "time": "2026-05-16T16:54:01+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "name": "amphp/parser", + "version": "v1.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "url": "https://github.com/amphp/parser.git", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=7.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Amp\\Parser\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", "keywords": [ - "filesystem", - "iterator" + "async", + "non-blocking", + "parser", + "stream" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2024-03-21T19:16:53+00:00" }, { - "name": "phpunit/php-invoker", - "version": "3.1.1", + "name": "amphp/pipeline", + "version": "v1.2.7", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "url": "https://github.com/amphp/pipeline.git", + "reference": "cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/amphp/pipeline/zipball/cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17", + "reference": "cf2d67696c2015ea7c7fd8f6ec5f8ed7c2d17c17", "shasum": "" }, "require": { - "php": ">=7.3" + "amphp/amp": "^3", + "php": ">=8.1", + "revolt/event-loop": "^1" }, "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-pcntl": "*" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Amp\\Pipeline\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "description": "Asynchronous iterators and operators.", + "homepage": "https://amphp.org/pipeline", "keywords": [ - "process" + "amp", + "amphp", + "async", + "io", + "iterator", + "non-blocking" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "issues": "https://github.com/amphp/pipeline/issues", + "source": "https://github.com/amphp/pipeline/tree/v1.2.7" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2026-07-26T14:50:43+00:00" }, { - "name": "phpunit/php-text-template", - "version": "2.0.4", + "name": "amphp/process", + "version": "v2.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "url": "https://github.com/amphp/process.git", + "reference": "583959df17d00304ad7b0b32285373f985935643" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/amphp/process/zipball/583959df17d00304ad7b0b32285373f985935643", + "reference": "583959df17d00304ad7b0b32285373f985935643", "shasum": "" }, "require": { - "php": ">=7.3" + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], + "description": "A fiber-aware process manager based on Amp and Revolt.", + "homepage": "https://amphp.org/process", "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v2.1.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2026-05-31T15:11:55+00:00" }, { - "name": "phpunit/php-timer", - "version": "5.0.3", + "name": "amphp/serialization", + "version": "v1.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "url": "https://github.com/amphp/serialization.git", + "reference": "fdf2834d78cebb0205fb2672676c1b1eb84371f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/amphp/serialization/zipball/fdf2834d78cebb0205fb2672676c1b1eb84371f0", + "reference": "fdf2834d78cebb0205fb2672676c1b1eb84371f0", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=7.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "amphp/php-cs-fixer-config": "^2", + "ext-json": "*", + "ext-zlib": "*", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", "keywords": [ - "timer" + "async", + "asynchronous", + "serialization", + "serialize" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/v1.1.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2026-04-05T15:59:53+00:00" }, { - "name": "phpunit/phpunit", - "version": "9.5.24", + "name": "amphp/socket", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5" + "url": "https://github.com/amphp/socket.git", + "reference": "dadb63c5d3179fd83803e29dfeac27350e619314" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d0aa6097bef9fd42458a9b3c49da32c6ce6129c5", - "reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5", + "url": "https://api.github.com/repos/amphp/socket/zipball/dadb63c5d3179fd83803e29dfeac27350e619314", + "reference": "dadb63c5d3179fd83803e29dfeac27350e619314", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.1", - "sebastian/version": "^3.0.2" + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/dns": "^2", + "ext-openssl": "*", + "kelunik/certificate": "^1.1", + "league/uri": "^7", + "league/uri-interfaces": "^7", + "php": ">=8.1", + "revolt/event-loop": "^1" }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "amphp/process": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.1" }, - "bin": [ - "phpunit" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.5-dev" - } - }, "autoload": { "files": [ - "src/Framework/Assert/Functions.php" + "src/functions.php", + "src/Internal/functions.php", + "src/SocketAddress/functions.php" ], - "classmap": [ - "src/" - ] + "psr-4": { + "Amp\\Socket\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", + "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.", + "homepage": "https://github.com/amphp/socket", "keywords": [ - "phpunit", - "testing", - "xunit" + "amp", + "async", + "encryption", + "non-blocking", + "sockets", + "tcp", + "tls" ], "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.24" + "issues": "https://github.com/amphp/socket/issues", + "source": "https://github.com/amphp/socket/tree/v2.4.0" }, "funding": [ { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2022-08-30T07:42:16+00:00" + "time": "2026-04-19T15:09:56+00:00" }, { - "name": "sebastian/cli-parser", - "version": "1.0.1", + "name": "amphp/sync", + "version": "v2.3.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "url": "https://github.com/amphp/sync.git", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1", "shasum": "" }, "require": { - "php": ">=7.3" + "amphp/amp": "^3", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" } ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", + "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v2.3.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/amphp", "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2024-08-03T19:31:26+00:00" }, { - "name": "sebastian/code-unit", - "version": "1.0.8", + "name": "composer/pcre", + "version": "3.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + "url": "https://github.com/composer/pcre.git", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed", "shasum": "" }, "require": { - "php": ">=7.3" + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<2.2.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^9" }, "type": "library", "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "3.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Composer\\Pcre\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.4.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2026-06-07T11:47:49+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "name": "composer/semver", + "version": "3.4.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { - "php": ">=7.3" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "3.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Composer\\Semver\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", "type": "github" } ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2025-08-20T19:15:30+00:00" }, { - "name": "sebastian/comparator", - "version": "4.0.8", + "name": "composer/xdebug-handler", + "version": "3.0.5", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", + "description": "Restarts a process without Xdebug.", "keywords": [ - "comparator", - "compare", - "equality" + "Xdebug", + "performance" ], "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2024-05-06T16:37:16+00:00" }, { - "name": "sebastian/complexity", - "version": "2.0.2", + "name": "danog/advanced-json-rpc", + "version": "v3.2.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + "url": "https://github.com/danog/php-advanced-json-rpc.git", + "reference": "ae703ea7b4811797a10590b6078de05b3b33dd91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "url": "https://api.github.com/repos/danog/php-advanced-json-rpc/zipball/ae703ea7b4811797a10590b6078de05b3b33dd91", + "reference": "ae703ea7b4811797a10590b6078de05b3b33dd91", "shasum": "" }, "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" + "netresearch/jsonmapper": "^5", + "php": ">=8.1", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0 || ^6" + }, + "replace": { + "felixfbecker/php-advanced-json-rpc": "^3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "ISC" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Felix Becker", + "email": "felix.b@outlook.com" + }, + { + "name": "Daniil Gentili", + "email": "daniil@daniil.it" } ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", + "description": "A more advanced JSONRPC implementation", "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "issues": "https://github.com/danog/php-advanced-json-rpc/issues", + "source": "https://github.com/danog/php-advanced-json-rpc/tree/v3.2.3" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2026-01-12T21:07:10+00:00" }, { - "name": "sebastian/diff", - "version": "4.0.4", + "name": "daverandom/libdns", + "version": "v2.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "url": "https://github.com/DaveRandom/LibDNS.git", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", "shasum": "" }, "require": { - "php": ">=7.3" + "ext-ctype": "*", + "php": ">=7.1" }, - "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" + "suggest": { + "ext-intl": "Required for IDN support" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions.php" + ], + "psr-4": { + "LibDNS\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } + "MIT" ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", + "description": "DNS protocol implementation written in pure PHP", "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" + "dns" ], "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "issues": "https://github.com/DaveRandom/LibDNS/issues", + "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "time": "2024-04-12T12:12:48+00:00" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" ], - "time": "2020-10-26T13:10:38+00:00" + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" }, { - "name": "sebastian/environment", - "version": "5.1.4", + "name": "doctrine/deprecations", + "version": "1.1.6", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" + "url": "https://github.com/doctrine/deprecations.git", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { - "php": ">=7.3" + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=14" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", + "psr/log": "^1 || ^2 || ^3" }, "suggest": { - "ext-posix": "*" + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" + }, + "time": "2026-02-07T07:09:04+00:00" + }, + { + "name": "felixfbecker/language-server-protocol", + "version": "v1.5.3", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-master": "1.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "LanguageServerProtocol\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "ISC" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Felix Becker", + "email": "felix.b@outlook.com" } ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "description": "PHP classes for the Language Server Protocol", "keywords": [ - "Xdebug", - "environment", - "hhvm" + "language", + "microsoft", + "php", + "server" ], "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.3" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2022-04-03T09:37:03+00:00" + "time": "2024-04-30T00:40:11+00:00" }, { - "name": "sebastian/exporter", - "version": "4.0.5", + "name": "fidry/cpu-core-counter", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "php": "^7.2 || ^8.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", + "description": "Tiny utility to get the number of CPU cores.", "keywords": [ - "export", - "exporter" + "CPU", + "core" ], "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/theofidry", "type": "github" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2025-08-14T07:29:31+00:00" }, { - "name": "sebastian/global-state", - "version": "5.0.5", + "name": "kelunik/certificate", + "version": "v1.1.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + "url": "https://github.com/kelunik/certificate.git", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "ext-openssl": "*", + "php": ">=7.0" }, "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^6 | 7 | ^8 | ^9" }, - "suggest": { - "ext-uopz": "*" + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Kelunik\\Certificate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Access certificate details and transform between different formats.", + "keywords": [ + "DER", + "certificate", + "certificates", + "openssl", + "pem", + "x509" + ], + "support": { + "issues": "https://github.com/kelunik/certificate/issues", + "source": "https://github.com/kelunik/certificate/tree/v1.1.3" + }, + "time": "2023-02-03T21:26:53+00:00" + }, + { + "name": "league/uri", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.8.1", + "php": "^8.1", + "psr/http-factory": "^1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-uri": "to use the PHP native URI class", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "7.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "League\\Uri\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" } ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", "keywords": [ - "global state" + "URN", + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc2141", + "rfc3986", + "rfc3987", + "rfc6570", + "rfc8141", + "uri", + "uri-template", + "url", + "ws" ], "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.8.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/sponsors/nyamsprod", "type": "github" } ], - "time": "2022-02-14T08:28:10+00:00" + "time": "2026-03-15T20:22:25+00:00" }, { - "name": "sebastian/lines-of-code", - "version": "1.0.3", + "name": "league/uri-interfaces", + "version": "7.8.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928", "shasum": "" }, "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" + "ext-filter": "*", + "php": "^8.1", + "psr/http-message": "^1.1 || ^2.0" }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "7.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "League\\Uri\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" } ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/sponsors/nyamsprod", "type": "github" } ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2026-03-08T20:05:35+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "4.0.4", + "name": "myclabs/deep-copy", + "version": "1.13.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v5.0.1", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "980674efdda65913492d29a8fd51c82270dd37bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/980674efdda65913492d29a8fd51c82270dd37bb", + "reference": "980674efdda65913492d29a8fd51c82270dd37bb", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v5.0.1" + }, + "time": "2026-02-22T16:28:03+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.8.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/044a6a392ff8ad0d61f14370a5fbbd0a0107152f", + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.8.0" + }, + "time": "2026-07-04T14:30:18+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -1985,277 +2604,2783 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/theseer", "type": "github" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/7bae67520aa9f5ecc506d646810bd40d9da54582", + "reference": "7bae67520aa9f5ecc506d646810bd40d9da54582", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^2.0", + "phpstan/phpdoc-parser": "^2.0", + "webmozart/assert": "^1.9.1 || ^2" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26", + "shipmonk/dead-code-detector": "^0.5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.3" + }, + "time": "2026-03-18T20:49:53+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", + "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" + }, + "time": "2026-01-06T21:53:42+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fb19eedd2bb67ff8cf7a5502ad329e701d6398a3", + "reference": "fb19eedd2bb67ff8cf7a5502ad329e701d6398a3", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.3" + }, + "time": "2026-07-08T07:01:06+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "10.1.16", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^10.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:31:57+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.5.64", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "0e8c1d19cea35ad97d4887f363d07c78e30fbf06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0e8c1d19cea35ad97d4887f363d07c78e30fbf06", + "reference": "0e8c1d19cea35ad97d4887f363d07c78e30fbf06", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-filter": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.5", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.4", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.1", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.64" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsoring.html", + "type": "other" + } + ], + "time": "2026-07-06T14:50:35+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "revolt/event-loop", + "version": "v1.0.9", + "source": { + "type": "git", + "url": "https://github.com/revoltphp/event-loop.git", + "reference": "44061cf513e53c6200372fc935ac42271566295d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/44061cf513e53c6200372fc935ac42271566295d", + "reference": "44061cf513e53c6200372fc935ac42271566295d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^9", + "psalm/phar": "6.16.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Revolt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Rock-solid event loop for concurrent PHP applications.", + "keywords": [ + "async", + "asynchronous", + "concurrency", + "event", + "event-loop", + "non-blocking", + "scheduler" + ], + "support": { + "issues": "https://github.com/revoltphp/event-loop/issues", + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.9" + }, + "time": "2026-05-16T17:55:38+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55dfef806eb7dfeb6e7a6935601fef866f8ca48d", + "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-01-24T09:25:16+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:37:17+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:47:14+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "0735b90f4da94969541dac1da743446e276defa6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", + "reference": "0735b90f4da94969541dac1da743446e276defa6", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:09:11+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:38:20+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-10T07:50:56+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "spatie/array-to-xml", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/spatie/array-to-xml.git", + "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/88b2f3852a922dd73177a68938f8eb2ec70c7224", + "reference": "88b2f3852a922dd73177a68938f8eb2ec70c7224", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": "^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.2", + "pestphp/pest": "^1.21", + "spatie/pest-plugin-snapshots": "^1.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Spatie\\ArrayToXml\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://freek.dev", + "role": "Developer" + } + ], + "description": "Convert an array to xml", + "homepage": "https://github.com/spatie/array-to-xml", + "keywords": [ + "array", + "convert", + "xml" + ], + "support": { + "source": "https://github.com/spatie/array-to-xml/tree/3.4.4" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-12-15T09:00:41+00:00" + }, + { + "name": "symfony/console", + "version": "v8.1.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "535e18a1b8925f6c01a55b171d157ab66c2ace15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/535e18a1b8925f6c01a55b171d157ab66c2ace15", + "reference": "535e18a1b8925f6c01a55b171d157ab66c2ace15", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php85": "^1.32", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.4.6|^8.0.6" + }, + "conflict": { + "symfony/dependency-injection": "<8.1", + "symfony/event-dispatcher": "<8.1" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^8.1", + "symfony/event-dispatcher": "^8.1", + "symfony/filesystem": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v8.1.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-27T13:58:19+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v8.1.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "17856b7a222664a26a5ea1cb06ee0721c2438217" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/17856b7a222664a26a5ea1cb06ee0721c2438217", + "reference": "17856b7a222664a26a5ea1cb06ee0721c2438217", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v8.1.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-22T15:42:13+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.41.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-07-28T08:25:59+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.38.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-25T13:48:31+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.38.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-27T06:59:30+00:00" }, { - "name": "sebastian/object-reflector", - "version": "2.0.4", + "name": "symfony/polyfill-php84", + "version": "v1.38.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "php": ">=7.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "2.0-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, "classmap": [ - "src/" + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { - "name": "sebastian/recursion-context", - "version": "4.0.4", + "name": "symfony/polyfill-php85", + "version": "v1.41.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/255fab485aaa1006ed411040c42aecd7b5302d7a", + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a", "shasum": "" }, "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" + "php": ">=7.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "4.0-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, "classmap": [ - "src/" + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.41.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2020-10-26T13:17:30+00:00" + "time": "2026-07-01T12:47:55+00:00" }, { - "name": "sebastian/resource-operations", - "version": "3.0.3", + "name": "symfony/service-contracts", + "version": "v3.7.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, - "require-dev": { - "phpunit/phpunit": "^9.0" + "conflict": { + "ext-psr": "<1.1|>=2" }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "3.7-dev" } }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2026-06-16T09:55:08+00:00" }, { - "name": "sebastian/type", - "version": "3.2.0", + "name": "symfony/string", + "version": "v8.1.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" + "url": "https://github.com/symfony/string.git", + "reference": "286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", - "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "url": "https://api.github.com/repos/symfony/string/zipball/286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc", + "reference": "286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4.1", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.0" + "source": "https://github.com/symfony/string/tree/v8.1.2" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2022-09-12T14:47:03+00:00" + "time": "2026-07-28T07:35:25+00:00" }, { - "name": "sebastian/version", - "version": "3.0.2", + "name": "theseer/tokenizer", + "version": "1.3.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { - "php": ">=7.3" + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, "autoload": { "classmap": [ "src/" @@ -2267,130 +5392,207 @@ ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/theseer", "type": "github" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2025-11-17T20:03:58+00:00" }, { - "name": "squizlabs/php_codesniffer", - "version": "3.7.1", + "name": "vimeo/psalm", + "version": "6.16.1", "source": { "type": "git", - "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619" + "url": "https://github.com/vimeo/psalm.git", + "reference": "f1f5de594dc76faf8784e02d3dc4716c91c6f6ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/f1f5de594dc76faf8784e02d3dc4716c91c6f6ac", + "reference": "f1f5de594dc76faf8784e02d3dc4716c91c6f6ac", "shasum": "" }, "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/parallel": "^2.3", + "composer-runtime-api": "^2", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^2.0 || ^3.0", + "danog/advanced-json-rpc": "^3.1", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", "ext-simplexml": "*", "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" + "felixfbecker/language-server-protocol": "^1.5.3", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", + "netresearch/jsonmapper": "^5.0", + "nikic/php-parser": "^5.0.0", + "php": "~8.1.31 || ~8.2.27 || ~8.3.16 || ~8.4.3 || ~8.5.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "spatie/array-to-xml": "^2.17.0 || ^3.0", + "symfony/console": "^6.0 || ^7.0 || ^8.0", + "symfony/filesystem": "~6.3.12 || ~6.4.3 || ^7.0.3 || ^8.0", + "symfony/polyfill-php84": "^1.31.0" + }, + "provide": { + "psalm/psalm": "self.version" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + "amphp/phpunit-util": "^3", + "bamarni/composer-bin-plugin": "^1.4", + "brianium/paratest": "^6.9", + "danog/class-finder": "^0.4.8", + "dg/bypass-finals": "^1.5", + "ext-curl": "*", + "mockery/mockery": "^1.5", + "nunomaduro/mock-final-classes": "^1.1", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpdoc-parser": "^1.6", + "phpunit/phpunit": "^9.6", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.19", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/process": "^6.0 || ^7.0 || ^8.0" + }, + "suggest": { + "ext-curl": "In order to send data to shepherd", + "ext-igbinary": "^2.0.5 is required, used to serialize caching data" }, "bin": [ - "bin/phpcs", - "bin/phpcbf" + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalm-review", + "psalter" ], - "type": "library", + "type": "project", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-1.x": "1.x-dev", + "dev-2.x": "2.x-dev", + "dev-3.x": "3.x-dev", + "dev-4.x": "4.x-dev", + "dev-5.x": "5.x-dev", + "dev-6.x": "6.x-dev", + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psalm\\": "src/Psalm/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Greg Sherwood", - "role": "lead" + "name": "Matthew Brown" + }, + { + "name": "Daniil Gentili", + "email": "daniil@daniil.it" } ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "description": "A static analysis tool for finding errors in PHP applications", "keywords": [ - "phpcs", - "standards" + "code", + "inspection", + "php", + "static analysis" ], "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + "docs": "https://psalm.dev/docs", + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm" }, - "time": "2022-06-18T07:21:10+00:00" + "time": "2026-03-19T10:56:09+00:00" }, { - "name": "theseer/tokenizer", - "version": "1.2.1", + "name": "webmozart/assert", + "version": "2.4.1", "source": { "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "url": "https://github.com/webmozarts/assert.git", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" }, "type": "library", + "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, + "branch-alias": { + "dev-master": "2.0-dev", + "dev-feature/2-0": "2.0-dev" + } + }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Webmozart\\Assert\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" } ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.4.1" }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2026-06-15T15:31:57+00:00" } ], "aliases": [], @@ -2399,7 +5601,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^8.0" + "php": "^8.1" }, "platform-dev": {}, "plugin-api-version": "2.6.0" diff --git a/package-lock.json b/package-lock.json index dc179e80..35c7c127 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "devDependencies": { "@prettier/plugin-php": "^0.25.0", "@seamapi/blueprint": "^1.2.0", + "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.985.0", "change-case": "^5.4.4", @@ -819,6 +820,42 @@ "npm": ">=10.0.0" } }, + "node_modules/@seamapi/fake-devicedb": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@seamapi/fake-devicedb/-/fake-devicedb-1.6.1.tgz", + "integrity": "sha512-w4Ar/s2kPnE5ExJSlpD3sKL8lkF+rLHRROArIRxtR2reHfnDSVwnDt9TzBYkHqgMP/x4o7LUzlRRpobn2xn24A==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18.12.0", + "npm": ">= 9.0.0" + }, + "optionalDependencies": { + "zod": "^3.21.4", + "zustand": "^4.3.7", + "zustand-hoist": "^2.0.0" + } + }, + "node_modules/@seamapi/fake-seam-connect": { + "version": "1.86.0", + "resolved": "https://registry.npmjs.org/@seamapi/fake-seam-connect/-/fake-seam-connect-1.86.0.tgz", + "integrity": "sha512-iO5fwtSIPhzmIiLxrFDtCYF/7HTb+ywcGmc3WzWt8Sr0bLQlmwCyTJ8YYZy++Hx0FsnNpa5AmlJuJGul9Y5gZA==", + "dev": true, + "license": "MIT", + "bin": { + "fake-seam-connect": "dist/server.js" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">= 9.0.0" + }, + "optionalDependencies": { + "@seamapi/fake-devicedb": ">=1.0.0-rc.0", + "zustand": "^4.3.7", + "zustand-hoist": "^2.0.0" + } + }, "node_modules/@seamapi/smith": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@seamapi/smith/-/smith-1.1.0.tgz", @@ -4881,6 +4918,18 @@ "license": "MIT", "peer": true }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -5866,6 +5915,17 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "dev": true, + "license": "MIT", + "optional": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/ware": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz", @@ -6065,6 +6125,51 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/zustand-hoist": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/zustand-hoist/-/zustand-hoist-2.0.1.tgz", + "integrity": "sha512-Lhvv3RlLQx1NSUtuhk8jegXe1Wyav9RAOnLd4CRs1SbB5qcFoarAGQTE43vIxXizrm1UQJl1q5uRbOZuXGXGpQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18.12.0", + "npm": ">= 9.0.0" + }, + "peerDependencies": { + "zustand": ">=4.0.0" + } } } } diff --git a/package.json b/package.json index da30a5a8..c1f2a9ed 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "lint": "eslint .", "postlint": "prettier --check --ignore-path .gitignore --ignore-path .prettierignore .", "format": "prettier --write --ignore-path .gitignore --ignore-path .prettierignore .", - "preformat": "eslint --fix ." + "preformat": "eslint --fix .", + "start": "fake-seam-connect --seed" }, "engines": { "node": ">=22.11.0", @@ -32,6 +33,7 @@ "devDependencies": { "@prettier/plugin-php": "^0.25.0", "@seamapi/blueprint": "^1.2.0", + "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.985.0", "change-case": "^5.4.4", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e953736f..f29c67a2 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,9 +1,9 @@ tests - + src - + + + src/Resources + src/Routes + + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 00000000..f3cb128b --- /dev/null +++ b/psalm.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + diff --git a/src/Auth.php b/src/Auth.php new file mode 100644 index 00000000..127ae84e --- /dev/null +++ b/src/Auth.php @@ -0,0 +1,149 @@ + + */ + public static function get_auth_headers( + ?string $api_key = null, + ?string $personal_access_token = null, + ?string $workspace_id = null, + ): array { + // The environment is only consulted when no credential was passed at + // all, so an explicit personal access token is not second guessed by + // a stray SEAM_API_KEY. + if ($api_key === null && $personal_access_token === null) { + $api_key = Options::get_env("SEAM_API_KEY"); + } + + if ( + Options::is_seam_options_with_api_key( + $api_key, + $personal_access_token, + ) + ) { + return self::get_auth_headers_for_api_key($api_key); + } + + if ( + Options::is_seam_options_with_personal_access_token( + $personal_access_token, + $api_key, + $workspace_id, + ) + ) { + return self::get_auth_headers_for_personal_access_token( + $personal_access_token, + $workspace_id, + ); + } + + throw new InvalidOptionsError( + "Must specify an api_key or personal_access_token. " . + "Attempted reading configuration from the environment, " . + "but the environment variable SEAM_API_KEY is not set.", + ); + } + + /** + * @return array + */ + public static function get_auth_headers_for_api_key(string $api_key): array + { + if (Token::is_client_session_token($api_key)) { + throw new InvalidTokenError( + "A Client Session Token cannot be used as an api_key", + ); + } + + if (Token::is_jwt($api_key)) { + throw new InvalidTokenError("A JWT cannot be used as an api_key"); + } + + if (Token::is_access_token($api_key)) { + throw new InvalidTokenError( + "An Access Token cannot be used as an api_key", + ); + } + + if (Token::is_publishable_key($api_key)) { + throw new InvalidTokenError( + "A Publishable Key cannot be used as an api_key", + ); + } + + if (!Token::is_seam_token($api_key)) { + throw new InvalidTokenError("Unknown or invalid api_key format"); + } + + return ["authorization" => "Bearer " . $api_key]; + } + + /** + * @return array + */ + public static function get_auth_headers_for_personal_access_token( + string $personal_access_token, + string $workspace_id, + ): array { + self::assert_personal_access_token($personal_access_token); + + return [ + "authorization" => "Bearer " . $personal_access_token, + "seam-workspace" => $workspace_id, + ]; + } + + /** + * Headers for a personal access token that is not scoped to a workspace. + * + * @return array + */ + public static function get_auth_headers_for_multi_workspace_personal_access_token( + string $personal_access_token, + ): array { + self::assert_personal_access_token($personal_access_token); + + return ["authorization" => "Bearer " . $personal_access_token]; + } + + private static function assert_personal_access_token(string $token): void + { + if (Token::is_client_session_token($token)) { + throw new InvalidTokenError( + "A Client Session Token cannot be used as a personal_access_token", + ); + } + + if (Token::is_jwt($token)) { + throw new InvalidTokenError( + "A JWT cannot be used as a personal_access_token", + ); + } + + if (Token::is_publishable_key($token)) { + throw new InvalidTokenError( + "A Publishable Key cannot be used as a personal_access_token", + ); + } + + if (!Token::is_access_token($token)) { + throw new InvalidTokenError( + "Unknown or invalid personal_access_token format", + ); + } + } +} diff --git a/src/Exceptions/ActionAttemptError.php b/src/Exceptions/ActionAttemptError.php index 26530d06..7368770b 100644 --- a/src/Exceptions/ActionAttemptError.php +++ b/src/Exceptions/ActionAttemptError.php @@ -1,17 +1,19 @@ name = get_class($this); $this->actionAttempt = $actionAttempt; } diff --git a/src/Exceptions/ActionAttemptFailedError.php b/src/Exceptions/ActionAttemptFailedError.php index 59e307fe..d2d08a37 100644 --- a/src/Exceptions/ActionAttemptFailedError.php +++ b/src/Exceptions/ActionAttemptFailedError.php @@ -1,20 +1,32 @@ error->message, $actionAttempt); - $this->name = get_class($this); - $this->errorCode = $actionAttempt->error->type; + parent::__construct( + $actionAttempt->error->message ?? "Action attempt failed", + $actionAttempt, + ); + $this->errorCode = $actionAttempt->error->type ?? "unknown_error"; } + /** + * The action attempt error type. + * + * Named `getErrorCode` rather than `getCode` because `Exception::getCode` + * is final and returns an int. This is the equivalent of the `code` + * property on the same error in the other Seam SDKs. + */ public function getErrorCode(): string { return $this->errorCode; diff --git a/src/Exceptions/ActionAttemptTimeoutError.php b/src/Exceptions/ActionAttemptTimeoutError.php index d0b6a343..87cfa2bc 100644 --- a/src/Exceptions/ActionAttemptTimeoutError.php +++ b/src/Exceptions/ActionAttemptTimeoutError.php @@ -1,9 +1,15 @@ name = get_class($this); } } diff --git a/src/Exceptions/HttpApiError.php b/src/Exceptions/HttpApiError.php index a79a8dd5..960b1c10 100644 --- a/src/Exceptions/HttpApiError.php +++ b/src/Exceptions/HttpApiError.php @@ -1,27 +1,32 @@ message ?? "Unknown error"; - parent::__construct($message); - $this->errorCode = $error->type ?? "unknown"; + parent::__construct($error->message ?? "Unknown error"); + $this->errorCode = $error->type ?? "unknown_error"; $this->statusCode = $statusCode; $this->requestId = $requestId; $this->data = $error->data ?? null; } + /** + * The Seam error type, e.g. `device_not_found`. + */ public function getErrorCode(): string { return $this->errorCode; @@ -32,7 +37,10 @@ public function getStatusCode(): int return $this->statusCode; } - public function getRequestId(): string + /** + * The `seam-request-id` response header, or null when absent. + */ + public function getRequestId(): ?string { return $this->requestId; } diff --git a/src/Exceptions/HttpInvalidInputError.php b/src/Exceptions/HttpInvalidInputError.php index 38cd8d74..fc20f172 100644 --- a/src/Exceptions/HttpInvalidInputError.php +++ b/src/Exceptions/HttpInvalidInputError.php @@ -1,7 +1,10 @@ errorCode = "invalid_input"; $this->validationErrors = $error->validation_errors ?? (object) []; } + /** + * The validation messages for a request parameter, or an empty array when + * that parameter has none. + * + * @return string[] + */ public function getValidationErrorMessages(string $paramName): array { return $this->validationErrors->{$paramName}->_errors ?? []; diff --git a/src/Exceptions/HttpUnauthorizedError.php b/src/Exceptions/HttpUnauthorizedError.php index 921ee2eb..2b43bffd 100644 --- a/src/Exceptions/HttpUnauthorizedError.php +++ b/src/Exceptions/HttpUnauthorizedError.php @@ -1,15 +1,21 @@ "unauthorized", - "message" => "Unauthorized", - ]; - parent::__construct($error, 401, $requestId); + parent::__construct( + (object) [ + "type" => "unauthorized", + "message" => "Unauthorized", + ], + 401, + $requestId, + ); } } diff --git a/src/Exceptions/InvalidOptionsError.php b/src/Exceptions/InvalidOptionsError.php new file mode 100644 index 00000000..65781860 --- /dev/null +++ b/src/Exceptions/InvalidOptionsError.php @@ -0,0 +1,16 @@ +status === "success") { + return $action_attempt; + } + + if ($action_attempt->status === "error") { + throw new ActionAttemptFailedError($action_attempt); + } + + if (self::now() + $polling_interval > $deadline) { + throw new ActionAttemptTimeoutError($action_attempt, $timeout); + } + + usleep((int) ($polling_interval * 1000000.0)); + + $action_attempt = self::get_action_attempt( + $client, + $action_attempt->action_attempt_id, + ); + } + } + + private static function get_action_attempt( + SeamHttpClient $client, + string $action_attempt_id, + ): ActionAttempt { + $res = $client->request( + "POST", + "/action_attempts/get", + json: (object) ["action_attempt_id" => $action_attempt_id], + ); + + $action_attempt = ActionAttempt::from_json( + $res->action_attempt ?? null, + ); + + if ($action_attempt === null) { + throw new \UnexpectedValueException( + "Seam returned no action attempt for {$action_attempt_id}", + ); + } + + return $action_attempt; + } + + private static function now(): float + { + return microtime(true); + } +} diff --git a/src/Http/SeamHttpClient.php b/src/Http/SeamHttpClient.php new file mode 100644 index 00000000..b9deb34c --- /dev/null +++ b/src/Http/SeamHttpClient.php @@ -0,0 +1,287 @@ + $auth_headers + * @param array $guzzle_options + */ + public function __construct( + string $endpoint, + array $auth_headers, + array $guzzle_options = [], + ?int $retries = null, + ?ClientInterface $client = null, + ) { + $this->client = + $client ?? + new GuzzleClient( + self::create_config( + $endpoint, + $auth_headers, + $guzzle_options, + $retries ?? self::DEFAULT_RETRIES, + ), + ); + } + + /** + * Wraps an already configured Guzzle client. The client carries its own + * endpoint and authorization, so none are resolved here. + */ + public static function from_client(ClientInterface $client): self + { + return new self("", [], [], null, $client); + } + + public function get_client(): ClientInterface + { + return $this->client; + } + + /** + * @param array $auth_headers + * @param array $guzzle_options + * @return array + */ + private static function create_config( + string $endpoint, + array $auth_headers, + array $guzzle_options, + int $retries, + ): array { + // The middleware has to be on the handler stack the client is built + // with: a stack pushed onto after construction does not apply. + $handler = $guzzle_options["handler"] ?? HandlerStack::create(); + + if ($handler instanceof HandlerStack) { + $handler->push( + GuzzleRetryMiddleware::factory([ + "retry_enabled" => $retries > 0, + "max_retry_attempts" => $retries, + // A connection failure never reached the server, so it is + // safe to retry whatever the method. Status codes are + // opted into per request, see request(). + "retry_on_timeout" => true, + "retry_on_status" => [], + ]), + "seam_retry", + ); + } + + $headers = array_merge( + $auth_headers, + $guzzle_options["headers"] ?? [], + self::sdk_headers(), + ); + + return array_merge( + [ + "base_uri" => $endpoint, + "timeout" => self::DEFAULT_TIMEOUT, + ], + $guzzle_options, + [ + "handler" => $handler, + "headers" => $headers, + // Error mapping happens in request(); letting Guzzle throw + // first would bypass it entirely. + "http_errors" => false, + ], + ); + } + + /** + * @return array + */ + private static function sdk_headers(): array + { + $version = PackageVersion::get(); + + return [ + "User-Agent" => "seam-php/" . $version, + "seam-sdk-name" => "seamapi/php", + "seam-sdk-version" => $version, + "seam-lts-version" => self::LTS_VERSION, + ]; + } + + /** + * @param array|object|null $json + * @param array|null $query + */ + public function request( + string $method, + string $path, + mixed $json = null, + ?array $query = null, + ): mixed { + $options = array_filter( + [ + "json" => $json, + "query" => $query, + ], + fn($option) => $option !== null, + ); + + if (in_array(strtoupper($method), self::IDEMPOTENT_METHODS, true)) { + $options["retry_on_status"] = self::RETRYABLE_STATUS_CODES; + } + + $response = $this->client->request($method, $path, $options); + $status_code = $response->getStatusCode(); + + if ($status_code < 200 || $status_code >= 300) { + $this->handle_error_response( + new Request($method, $path), + $response, + ); + } + + return self::decode_body($response); + } + + private function handle_error_response( + RequestInterface $request, + ResponseInterface $response, + ): void { + $status_code = $response->getStatusCode(); + $request_id = self::get_request_id($response); + + if ($status_code === 401) { + throw new HttpUnauthorizedError($request_id); + } + + if (!self::is_api_error_response($response)) { + // Not a Seam error response, so there is nothing to map onto a + // Seam exception. The other Seam SDKs surface the underlying + // transport error here too. + throw BadResponseException::create($request, $response); + } + + $error = self::decode_body($response)->error; + + if (($error->type ?? null) === "invalid_input") { + throw new HttpInvalidInputError($error, $status_code, $request_id); + } + + throw new HttpApiError($error, $status_code, $request_id); + } + + /** + * True when the response body is a Seam error envelope, i.e. JSON holding + * an `error` object with a string `type` and `message`. + */ + private static function is_api_error_response( + ResponseInterface $response, + ): bool { + if ( + !str_starts_with( + $response->getHeaderLine("content-type"), + "application/json", + ) + ) { + return false; + } + + $body = self::decode_body($response); + + if (!is_object($body)) { + return false; + } + + $error = $body->error ?? null; + + if (!is_object($error)) { + return false; + } + + return is_string($error->type ?? null) && + is_string($error->message ?? null); + } + + private static function decode_body(ResponseInterface $response): mixed + { + $body = $response->getBody(); + $body->rewind(); + $contents = $body->getContents(); + + if ($contents === "") { + return null; + } + + try { + return Utils::jsonDecode($contents); + } catch (\InvalidArgumentException) { + return null; + } + } + + private static function get_request_id(ResponseInterface $response): ?string + { + return $response->hasHeader("seam-request-id") + ? $response->getHeaderLine("seam-request-id") + : null; + } +} diff --git a/src/Options.php b/src/Options.php new file mode 100644 index 00000000..7f4b7151 --- /dev/null +++ b/src/Options.php @@ -0,0 +1,103 @@ +has_next_page ?? false), + next_page_cursor: $json->next_page_cursor ?? null, + next_page_url: $json->next_page_url ?? null, + ); + } +} diff --git a/src/Paginator.php b/src/Paginator.php index a913cf80..5b86e6db 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -2,11 +2,17 @@ namespace Seam; +/** + * Fetches and walks the pages of a list endpoint. + * + * Create one with `Seam::createPaginator`, passing a callable that invokes the + * list method with a params array. + */ class Paginator { private $request; - private $params; - private $pagination_cache = []; + private array $params; + private array $pagination_cache = []; private const FIRST_PAGE = "FIRST_PAGE"; public function __construct(callable $request, array $params = []) @@ -15,46 +21,55 @@ public function __construct(callable $request, array $params = []) $this->params = $params; } + /** + * @return array{0: array, 1: Pagination} + */ public function firstPage(): array { - $request = $this->request; - $params = $this->params; - - $params["on_response"] = fn($response) => $this->cachePagination( - $response, - self::FIRST_PAGE, - ); - - $data = $request($params); - - return [$data, $this->pagination_cache[self::FIRST_PAGE]]; + return $this->fetchPage(self::FIRST_PAGE); } - public function nextPage(string $next_page_cursor): array + /** + * @return array{0: array, 1: Pagination} + */ + public function nextPage(?string $next_page_cursor): array { - if ($next_page_cursor === null) { + if ($next_page_cursor === null || $next_page_cursor === "") { throw new \InvalidArgumentException( - "Cannot get the next page with a null next_page_cursor", + "Cannot get the next page without a next_page_cursor", ); } + return $this->fetchPage($next_page_cursor); + } + + /** + * @return array{0: array, 1: Pagination} + */ + private function fetchPage(string $cursor): array + { $request = $this->request; $params = $this->params; - $params["page_cursor"] = $next_page_cursor; + if ($cursor !== self::FIRST_PAGE) { + $params["page_cursor"] = $cursor; + } + $params["on_response"] = fn($response) => $this->cachePagination( $response, - $next_page_cursor, + $cursor, ); $data = $request($params); - return [$data, $this->pagination_cache[$next_page_cursor]]; + return [$data, $this->pagination_cache[$cursor] ?? new Pagination()]; } - private function cachePagination($response, $next_page_cursor) + private function cachePagination($response, string $cursor): void { - $this->pagination_cache[$next_page_cursor] = $response->pagination; + $this->pagination_cache[$cursor] = Pagination::from_json( + $response->pagination ?? null, + ); } public function flattenToArray(): array diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index 1c53b718..a3465a8d 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -2,19 +2,28 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AccessCode; -use Seam\SeamClient; class AccessCodesClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public AccessCodesSimulateClient $simulate; public AccessCodesUnmanagedClient $unmanaged; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->simulate = new AccessCodesSimulateClient($seam); - $this->unmanaged = new AccessCodesUnmanagedClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->simulate = new AccessCodesSimulateClient($client, $defaults); + $this->unmanaged = new AccessCodesUnmanagedClient($client, $defaults); } /** @@ -64,9 +73,7 @@ public function create( ): AccessCode { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($allow_external_modification !== null) { $request_payload[ "allow_external_modification" @@ -127,7 +134,7 @@ public function create( ] = $use_offline_access_code; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/create", json: (object) $request_payload, @@ -185,9 +192,7 @@ public function create_multiple( ): array { $request_payload = []; - if ($device_ids !== null) { - $request_payload["device_ids"] = $device_ids; - } + $request_payload["device_ids"] = $device_ids; if ($allow_external_modification !== null) { $request_payload[ "allow_external_modification" @@ -234,7 +239,7 @@ public function create_multiple( ] = $use_backup_access_code_pool; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/create_multiple", json: (object) $request_payload, @@ -259,14 +264,12 @@ public function delete( ): void { $request_payload = []; - if ($access_code_id !== null) { - $request_payload["access_code_id"] = $access_code_id; - } + $request_payload["access_code_id"] = $access_code_id; if ($device_id !== null) { $request_payload["device_id"] = $device_id; } - $this->seam->request( + $this->client->request( "POST", "/access_codes/delete", json: (object) $request_payload, @@ -283,11 +286,9 @@ public function generate_code(string $device_id): AccessCode { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/generate_code", json: (object) $request_payload, @@ -323,7 +324,7 @@ public function get( $request_payload["device_id"] = $device_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/get", json: (object) $request_payload, @@ -347,6 +348,7 @@ public function get( * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned access codes to include all records that satisfy a partial match using `name`, `code` or `access_code_id`. * @param string $user_identifier_key Your user ID for the user by which to filter access codes. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -395,7 +397,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/list", json: (object) $request_payload, @@ -429,11 +431,9 @@ public function pull_backup_access_code(string $access_code_id): AccessCode { $request_payload = []; - if ($access_code_id !== null) { - $request_payload["access_code_id"] = $access_code_id; - } + $request_payload["access_code_id"] = $access_code_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/pull_backup_access_code", json: (object) $request_payload, @@ -461,9 +461,7 @@ public function report_device_constraints( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($max_code_length !== null) { $request_payload["max_code_length"] = $max_code_length; } @@ -476,7 +474,7 @@ public function report_device_constraints( ] = $supported_code_lengths; } - $this->seam->request( + $this->client->request( "POST", "/access_codes/report_device_constraints", json: (object) $request_payload, @@ -536,9 +534,7 @@ public function update( ): void { $request_payload = []; - if ($access_code_id !== null) { - $request_payload["access_code_id"] = $access_code_id; - } + $request_payload["access_code_id"] = $access_code_id; if ($allow_external_modification !== null) { $request_payload[ "allow_external_modification" @@ -605,7 +601,7 @@ public function update( ] = $use_offline_access_code; } - $this->seam->request( + $this->client->request( "POST", "/access_codes/update", json: (object) $request_payload, @@ -639,9 +635,7 @@ public function update_multiple( ): void { $request_payload = []; - if ($common_code_key !== null) { - $request_payload["common_code_key"] = $common_code_key; - } + $request_payload["common_code_key"] = $common_code_key; if ($ends_at !== null) { $request_payload["ends_at"] = $ends_at; } @@ -652,7 +646,7 @@ public function update_multiple( $request_payload["starts_at"] = $starts_at; } - $this->seam->request( + $this->client->request( "POST", "/access_codes/update_multiple", json: (object) $request_payload, diff --git a/src/Routes/AccessCodesSimulateClient.php b/src/Routes/AccessCodesSimulateClient.php index 7f1ded04..0750e822 100644 --- a/src/Routes/AccessCodesSimulateClient.php +++ b/src/Routes/AccessCodesSimulateClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\UnmanagedAccessCode; -use Seam\SeamClient; class AccessCodesSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -29,17 +38,11 @@ public function create_unmanaged_access_code( ): UnmanagedAccessCode { $request_payload = []; - if ($code !== null) { - $request_payload["code"] = $code; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($name !== null) { - $request_payload["name"] = $name; - } - - $res = $this->seam->request( + $request_payload["code"] = $code; + $request_payload["device_id"] = $device_id; + $request_payload["name"] = $name; + + $res = $this->client->request( "POST", "/access_codes/simulate/create_unmanaged_access_code", json: (object) $request_payload, diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index 93ec0001..e18b0ab3 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\UnmanagedAccessCode; -use Seam\SeamClient; class AccessCodesUnmanagedClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -35,9 +44,7 @@ public function convert_to_managed( ): void { $request_payload = []; - if ($access_code_id !== null) { - $request_payload["access_code_id"] = $access_code_id; - } + $request_payload["access_code_id"] = $access_code_id; if ($allow_external_modification !== null) { $request_payload[ "allow_external_modification" @@ -52,7 +59,7 @@ public function convert_to_managed( ] = $is_external_modification_allowed; } - $this->seam->request( + $this->client->request( "POST", "/access_codes/unmanaged/convert_to_managed", json: (object) $request_payload, @@ -69,11 +76,9 @@ public function delete(string $access_code_id): void { $request_payload = []; - if ($access_code_id !== null) { - $request_payload["access_code_id"] = $access_code_id; - } + $request_payload["access_code_id"] = $access_code_id; - $this->seam->request( + $this->client->request( "POST", "/access_codes/unmanaged/delete", json: (object) $request_payload, @@ -107,7 +112,7 @@ public function get( $request_payload["device_id"] = $device_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/unmanaged/get", json: (object) $request_payload, @@ -124,6 +129,7 @@ public function get( * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned access codes to include all records that satisfy a partial match using `name`, `code` or `access_code_id`. * @param string $user_identifier_key Your user ID for the user by which to filter unmanaged access codes. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -136,9 +142,7 @@ public function list( ): array { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($limit !== null) { $request_payload["limit"] = $limit; } @@ -152,7 +156,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_codes/unmanaged/list", json: (object) $request_payload, @@ -187,12 +191,8 @@ public function update( ): void { $request_payload = []; - if ($access_code_id !== null) { - $request_payload["access_code_id"] = $access_code_id; - } - if ($is_managed !== null) { - $request_payload["is_managed"] = $is_managed; - } + $request_payload["access_code_id"] = $access_code_id; + $request_payload["is_managed"] = $is_managed; if ($allow_external_modification !== null) { $request_payload[ "allow_external_modification" @@ -207,7 +207,7 @@ public function update( ] = $is_external_modification_allowed; } - $this->seam->request( + $this->client->request( "POST", "/access_codes/unmanaged/update", json: (object) $request_payload, diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index 42c99bd3..188412e5 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -2,18 +2,27 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AccessGrant; use Seam\Resources\Batch; -use Seam\SeamClient; class AccessGrantsClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public AccessGrantsUnmanagedClient $unmanaged; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->unmanaged = new AccessGrantsUnmanagedClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->unmanaged = new AccessGrantsUnmanagedClient($client, $defaults); } /** @@ -55,11 +64,9 @@ public function create( ): AccessGrant { $request_payload = []; - if ($requested_access_methods !== null) { - $request_payload[ - "requested_access_methods" - ] = $requested_access_methods; - } + $request_payload[ + "requested_access_methods" + ] = $requested_access_methods; if ($user_identity_id !== null) { $request_payload["user_identity_id"] = $user_identity_id; } @@ -105,7 +112,7 @@ public function create( $request_payload["starts_at"] = $starts_at; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/create", json: (object) $request_payload, @@ -124,11 +131,9 @@ public function delete(string $access_grant_id): void { $request_payload = []; - if ($access_grant_id !== null) { - $request_payload["access_grant_id"] = $access_grant_id; - } + $request_payload["access_grant_id"] = $access_grant_id; - $this->seam->request( + $this->client->request( "POST", "/access_grants/delete", json: (object) $request_payload, @@ -155,7 +160,7 @@ public function get( $request_payload["access_grant_key"] = $access_grant_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/get", json: (object) $request_payload, @@ -194,7 +199,7 @@ public function get_related( $request_payload["include"] = $include; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/get_related", json: (object) $request_payload, @@ -219,6 +224,7 @@ public function get_related( * @param string $reservation_key Filter Access Grants by reservation_key. * @param string $space_id ID of the space by which you want to filter the list of Access Grants. * @param string $user_identity_id ID of user identity by which you want to filter the list of Access Grants. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -279,7 +285,7 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/list", json: (object) $request_payload, @@ -308,16 +314,12 @@ public function request_access_methods( ): AccessGrant { $request_payload = []; - if ($access_grant_id !== null) { - $request_payload["access_grant_id"] = $access_grant_id; - } - if ($requested_access_methods !== null) { - $request_payload[ - "requested_access_methods" - ] = $requested_access_methods; - } + $request_payload["access_grant_id"] = $access_grant_id; + $request_payload[ + "requested_access_methods" + ] = $requested_access_methods; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/request_access_methods", json: (object) $request_payload, @@ -361,7 +363,7 @@ public function update( $request_payload["starts_at"] = $starts_at; } - $this->seam->request( + $this->client->request( "POST", "/access_grants/update", json: (object) $request_payload, diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index 84c4c449..72955f56 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\UnmanagedAccessGrant; -use Seam\SeamClient; class AccessGrantsUnmanagedClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -24,11 +33,9 @@ public function get(string $access_grant_id): UnmanagedAccessGrant { $request_payload = []; - if ($access_grant_id !== null) { - $request_payload["access_grant_id"] = $access_grant_id; - } + $request_payload["access_grant_id"] = $access_grant_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/unmanaged/get", json: (object) $request_payload, @@ -46,6 +53,7 @@ public function get(string $access_grant_id): UnmanagedAccessGrant * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $reservation_key Filter unmanaged Access Grants by reservation_key. * @param string $user_identity_id ID of user identity by which you want to filter the list of unmanaged Access Grants. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -78,7 +86,7 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_grants/unmanaged/list", json: (object) $request_payload, @@ -113,17 +121,13 @@ public function update( ): void { $request_payload = []; - if ($access_grant_id !== null) { - $request_payload["access_grant_id"] = $access_grant_id; - } - if ($is_managed !== null) { - $request_payload["is_managed"] = $is_managed; - } + $request_payload["access_grant_id"] = $access_grant_id; + $request_payload["is_managed"] = $is_managed; if ($access_grant_key !== null) { $request_payload["access_grant_key"] = $access_grant_key; } - $this->seam->request( + $this->client->request( "POST", "/access_grants/unmanaged/update", json: (object) $request_payload, diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index 6e9d975d..09421e8e 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -2,19 +2,29 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\AccessMethod; use Seam\Resources\ActionAttempt; use Seam\Resources\Batch; -use Seam\SeamClient; class AccessMethodsClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public AccessMethodsUnmanagedClient $unmanaged; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->unmanaged = new AccessMethodsUnmanagedClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->unmanaged = new AccessMethodsUnmanagedClient($client, $defaults); } /** @@ -22,37 +32,31 @@ public function __construct(SeamClient $seam) * * @param string $access_method_id ID of the `access_method` to assign the credential to. * @param string $card_number Card number of the credential to assign. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function assign_card( string $access_method_id, string $card_number, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($access_method_id !== null) { - $request_payload["access_method_id"] = $access_method_id; - } - if ($card_number !== null) { - $request_payload["card_number"] = $card_number; - } + $request_payload["access_method_id"] = $access_method_id; + $request_payload["card_number"] = $card_number; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/assign_card", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -80,7 +84,7 @@ public function delete( $request_payload["reservation_key"] = $reservation_key; } - $this->seam->request( + $this->client->request( "POST", "/access_methods/delete", json: (object) $request_payload, @@ -92,37 +96,31 @@ public function delete( * * @param string $access_method_id ID of the `access_method` to encode onto a card. * @param string $acs_encoder_id ID of the `acs_encoder` to use to encode the `access_method`. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function encode( string $access_method_id, string $acs_encoder_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($access_method_id !== null) { - $request_payload["access_method_id"] = $access_method_id; - } - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["access_method_id"] = $access_method_id; + $request_payload["acs_encoder_id"] = $acs_encoder_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/encode", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -135,11 +133,9 @@ public function get(string $access_method_id): AccessMethod { $request_payload = []; - if ($access_method_id !== null) { - $request_payload["access_method_id"] = $access_method_id; - } + $request_payload["access_method_id"] = $access_method_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/get", json: (object) $request_payload, @@ -163,9 +159,7 @@ public function get_related( ): Batch { $request_payload = []; - if ($access_method_ids !== null) { - $request_payload["access_method_ids"] = $access_method_ids; - } + $request_payload["access_method_ids"] = $access_method_ids; if ($exclude !== null) { $request_payload["exclude"] = $exclude; } @@ -173,7 +167,7 @@ public function get_related( $request_payload["include"] = $include; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/get_related", json: (object) $request_payload, @@ -193,6 +187,7 @@ public function get_related( * @param int $limit Maximum number of records to return per page. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $space_id ID of the space by which to filter the returned access methods. Must be combined with `access_grant_id`, `access_grant_key`, or `acs_entrance_id`. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -233,7 +228,7 @@ public function list( $request_payload["space_id"] = $space_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/list", json: (object) $request_payload, @@ -254,36 +249,30 @@ public function list( * * @param string $access_method_id ID of the cloud_key `access_method` to use for the unlock operation. * @param string $acs_entrance_id ID of the entrance to unlock. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function unlock_door( string $access_method_id, string $acs_entrance_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($access_method_id !== null) { - $request_payload["access_method_id"] = $access_method_id; - } - if ($acs_entrance_id !== null) { - $request_payload["acs_entrance_id"] = $acs_entrance_id; - } + $request_payload["access_method_id"] = $access_method_id; + $request_payload["acs_entrance_id"] = $acs_entrance_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/unlock_door", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/AccessMethodsUnmanagedClient.php b/src/Routes/AccessMethodsUnmanagedClient.php index 94f9eb15..4599a2b4 100644 --- a/src/Routes/AccessMethodsUnmanagedClient.php +++ b/src/Routes/AccessMethodsUnmanagedClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\UnmanagedAccessMethod; -use Seam\SeamClient; class AccessMethodsUnmanagedClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -24,11 +33,9 @@ public function get(string $access_method_id): UnmanagedAccessMethod { $request_payload = []; - if ($access_method_id !== null) { - $request_payload["access_method_id"] = $access_method_id; - } + $request_payload["access_method_id"] = $access_method_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/unmanaged/get", json: (object) $request_payload, @@ -54,9 +61,7 @@ public function list( ): array { $request_payload = []; - if ($access_grant_id !== null) { - $request_payload["access_grant_id"] = $access_grant_id; - } + $request_payload["access_grant_id"] = $access_grant_id; if ($acs_entrance_id !== null) { $request_payload["acs_entrance_id"] = $acs_entrance_id; } @@ -67,7 +72,7 @@ public function list( $request_payload["space_id"] = $space_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/access_methods/unmanaged/list", json: (object) $request_payload, diff --git a/src/Routes/AcsAccessGroupsClient.php b/src/Routes/AcsAccessGroupsClient.php index f4878d23..8fea3af4 100644 --- a/src/Routes/AcsAccessGroupsClient.php +++ b/src/Routes/AcsAccessGroupsClient.php @@ -2,18 +2,27 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsAccessGroup; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsUser; -use Seam\SeamClient; class AcsAccessGroupsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -31,9 +40,7 @@ public function add_user( ): void { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -41,7 +48,7 @@ public function add_user( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/access_groups/add_user", json: (object) $request_payload, @@ -58,11 +65,9 @@ public function delete(string $acs_access_group_id): void { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; - $this->seam->request( + $this->client->request( "POST", "/acs/access_groups/delete", json: (object) $request_payload, @@ -79,11 +84,9 @@ public function get(string $acs_access_group_id): AcsAccessGroup { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/access_groups/get", json: (object) $request_payload, @@ -122,7 +125,7 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/access_groups/list", json: (object) $request_payload, @@ -145,11 +148,9 @@ public function list_accessible_entrances( ): array { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/access_groups/list_accessible_entrances", json: (object) $request_payload, @@ -171,11 +172,9 @@ public function list_users(string $acs_access_group_id): array { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/access_groups/list_users", json: (object) $request_payload, @@ -199,9 +198,7 @@ public function remove_user( ): void { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -209,7 +206,7 @@ public function remove_user( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/access_groups/remove_user", json: (object) $request_payload, diff --git a/src/Routes/AcsClient.php b/src/Routes/AcsClient.php index e6454811..1d5f11e0 100644 --- a/src/Routes/AcsClient.php +++ b/src/Routes/AcsClient.php @@ -2,25 +2,34 @@ namespace Seam\Routes; -use Seam\SeamClient; +use Seam\Http\SeamHttpClient; class AcsClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public AcsAccessGroupsClient $access_groups; public AcsCredentialsClient $credentials; public AcsEncodersClient $encoders; public AcsEntrancesClient $entrances; public AcsSystemsClient $systems; public AcsUsersClient $users; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->access_groups = new AcsAccessGroupsClient($seam); - $this->credentials = new AcsCredentialsClient($seam); - $this->encoders = new AcsEncodersClient($seam); - $this->entrances = new AcsEntrancesClient($seam); - $this->systems = new AcsSystemsClient($seam); - $this->users = new AcsUsersClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->access_groups = new AcsAccessGroupsClient($client, $defaults); + $this->credentials = new AcsCredentialsClient($client, $defaults); + $this->encoders = new AcsEncodersClient($client, $defaults); + $this->entrances = new AcsEntrancesClient($client, $defaults); + $this->systems = new AcsSystemsClient($client, $defaults); + $this->users = new AcsUsersClient($client, $defaults); } } diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index b4151455..43b41e38 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -2,17 +2,26 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsCredential; use Seam\Resources\AcsEntrance; -use Seam\SeamClient; class AcsCredentialsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -30,9 +39,7 @@ public function assign( ): void { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -40,7 +47,7 @@ public function assign( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/credentials/assign", json: (object) $request_payload, @@ -82,9 +89,7 @@ public function create( ): AcsCredential { $request_payload = []; - if ($access_method !== null) { - $request_payload["access_method"] = $access_method; - } + $request_payload["access_method"] = $access_method; if ($acs_system_id !== null) { $request_payload["acs_system_id"] = $acs_system_id; } @@ -130,7 +135,7 @@ public function create( $request_payload["visionline_metadata"] = $visionline_metadata; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/credentials/create", json: (object) $request_payload, @@ -149,11 +154,9 @@ public function delete(string $acs_credential_id): void { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; - $this->seam->request( + $this->client->request( "POST", "/acs/credentials/delete", json: (object) $request_payload, @@ -170,11 +173,9 @@ public function get(string $acs_credential_id): AcsCredential { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/credentials/get", json: (object) $request_payload, @@ -194,6 +195,7 @@ public function get(string $acs_credential_id): AcsCredential * @param float $limit Number of credentials to return. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned credentials to include all records that satisfy a partial match using `display_name`, `code`, `card_number`, `acs_user_id` or `acs_credential_id`. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -236,7 +238,7 @@ public function list( $request_payload["search"] = $search; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/credentials/list", json: (object) $request_payload, @@ -262,11 +264,9 @@ public function list_accessible_entrances(string $acs_credential_id): array { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/credentials/list_accessible_entrances", json: (object) $request_payload, @@ -293,9 +293,7 @@ public function unassign( ): void { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -303,7 +301,7 @@ public function unassign( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/credentials/unassign", json: (object) $request_payload, @@ -325,9 +323,7 @@ public function update( ): void { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; if ($code !== null) { $request_payload["code"] = $code; } @@ -335,7 +331,7 @@ public function update( $request_payload["ends_at"] = $ends_at; } - $this->seam->request( + $this->client->request( "POST", "/acs/credentials/update", json: (object) $request_payload, diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index d277fdb9..dd12962a 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -2,18 +2,28 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsEncoder; use Seam\Resources\ActionAttempt; -use Seam\SeamClient; class AcsEncodersClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public AcsEncodersSimulateClient $simulate; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->simulate = new AcsEncodersSimulateClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->simulate = new AcsEncodersSimulateClient($client, $defaults); } /** @@ -22,19 +32,18 @@ public function __construct(SeamClient $seam) * @param string $acs_encoder_id ID of the `acs_encoder` to use to encode the `acs_credential`. * @param string $access_method_id ID of the `access_method` to encode onto a card. * @param string $acs_credential_id ID of the `acs_credential` to encode onto a card. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function encode_credential( string $acs_encoder_id, ?string $access_method_id = null, ?string $acs_credential_id = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($access_method_id !== null) { $request_payload["access_method_id"] = $access_method_id; } @@ -42,21 +51,18 @@ public function encode_credential( $request_payload["acs_credential_id"] = $acs_credential_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/encoders/encode_credential", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -69,11 +75,9 @@ public function get(string $acs_encoder_id): AcsEncoder { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/encoders/get", json: (object) $request_payload, @@ -90,6 +94,7 @@ public function get(string $acs_encoder_id): AcsEncoder * @param array $acs_encoder_ids IDs of the encoders that you want to retrieve. * @param float $limit Number of encoders to return. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -118,7 +123,7 @@ public function list( $request_payload["page_cursor"] = $page_cursor; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/encoders/list", json: (object) $request_payload, @@ -139,37 +144,33 @@ public function list( * * @param string $acs_encoder_id ID of the encoder to use for the scan. * @param mixed $salto_ks_metadata Salto KS-specific metadata for the scan action. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function scan_credential( string $acs_encoder_id, mixed $salto_ks_metadata = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($salto_ks_metadata !== null) { $request_payload["salto_ks_metadata"] = $salto_ks_metadata; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/encoders/scan_credential", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -179,6 +180,7 @@ public function scan_credential( * @param string $acs_user_id ID of the `acs_user` to assign the scanned credential to. * @param mixed $salto_ks_metadata Salto KS-specific metadata for the scan action. * @param string $user_identity_id ID of the `user_identity` to assign the scanned credential to. If the ACS system contains an ACS user linked to this user identity, it is used. Otherwise, one is created. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function scan_to_assign_credential( @@ -186,13 +188,11 @@ public function scan_to_assign_credential( ?string $acs_user_id = null, mixed $salto_ks_metadata = null, ?string $user_identity_id = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -203,20 +203,17 @@ public function scan_to_assign_credential( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/encoders/scan_to_assign_credential", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/AcsEncodersSimulateClient.php b/src/Routes/AcsEncodersSimulateClient.php index 7fb9c2b4..54ce9aaf 100644 --- a/src/Routes/AcsEncodersSimulateClient.php +++ b/src/Routes/AcsEncodersSimulateClient.php @@ -2,15 +2,24 @@ namespace Seam\Routes; -use Seam\SeamClient; +use Seam\Http\SeamHttpClient; class AcsEncodersSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -28,9 +37,7 @@ public function next_credential_encode_will_fail( ): void { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($error_code !== null) { $request_payload["error_code"] = $error_code; } @@ -38,7 +45,7 @@ public function next_credential_encode_will_fail( $request_payload["acs_credential_id"] = $acs_credential_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/encoders/simulate/next_credential_encode_will_fail", json: (object) $request_payload, @@ -58,14 +65,12 @@ public function next_credential_encode_will_succeed( ): void { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($scenario !== null) { $request_payload["scenario"] = $scenario; } - $this->seam->request( + $this->client->request( "POST", "/acs/encoders/simulate/next_credential_encode_will_succeed", json: (object) $request_payload, @@ -87,9 +92,7 @@ public function next_credential_scan_will_fail( ): void { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($error_code !== null) { $request_payload["error_code"] = $error_code; } @@ -99,7 +102,7 @@ public function next_credential_scan_will_fail( ] = $acs_credential_id_on_seam; } - $this->seam->request( + $this->client->request( "POST", "/acs/encoders/simulate/next_credential_scan_will_fail", json: (object) $request_payload, @@ -121,9 +124,7 @@ public function next_credential_scan_will_succeed( ): void { $request_payload = []; - if ($acs_encoder_id !== null) { - $request_payload["acs_encoder_id"] = $acs_encoder_id; - } + $request_payload["acs_encoder_id"] = $acs_encoder_id; if ($acs_credential_id_on_seam !== null) { $request_payload[ "acs_credential_id_on_seam" @@ -133,7 +134,7 @@ public function next_credential_scan_will_succeed( $request_payload["scenario"] = $scenario; } - $this->seam->request( + $this->client->request( "POST", "/acs/encoders/simulate/next_credential_scan_will_succeed", json: (object) $request_payload, diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index 583d6e5e..270cfb24 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -2,18 +2,28 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsCredential; use Seam\Resources\AcsEntrance; use Seam\Resources\ActionAttempt; -use Seam\SeamClient; class AcsEntrancesClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -26,11 +36,9 @@ public function get(string $acs_entrance_id): AcsEntrance { $request_payload = []; - if ($acs_entrance_id !== null) { - $request_payload["acs_entrance_id"] = $acs_entrance_id; - } + $request_payload["acs_entrance_id"] = $acs_entrance_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/entrances/get", json: (object) $request_payload, @@ -54,9 +62,7 @@ public function grant_access( ): void { $request_payload = []; - if ($acs_entrance_id !== null) { - $request_payload["acs_entrance_id"] = $acs_entrance_id; - } + $request_payload["acs_entrance_id"] = $acs_entrance_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -64,7 +70,7 @@ public function grant_access( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/entrances/grant_access", json: (object) $request_payload, @@ -85,6 +91,7 @@ public function grant_access( * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned entrances to include all records that satisfy a partial match using `display_name`. * @param string $space_id ID of the space for which you want to list entrances. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -137,7 +144,7 @@ public function list( $request_payload["space_id"] = $space_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/entrances/list", json: (object) $request_payload, @@ -166,14 +173,12 @@ public function list_credentials_with_access( ): array { $request_payload = []; - if ($acs_entrance_id !== null) { - $request_payload["acs_entrance_id"] = $acs_entrance_id; - } + $request_payload["acs_entrance_id"] = $acs_entrance_id; if ($include_if !== null) { $request_payload["include_if"] = $include_if; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/entrances/list_credentials_with_access", json: (object) $request_payload, @@ -190,36 +195,30 @@ public function list_credentials_with_access( * * @param string $acs_credential_id ID of the cloud_key credential to use for the unlock operation. * @param string $acs_entrance_id ID of the entrance to unlock. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function unlock( string $acs_credential_id, string $acs_entrance_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($acs_credential_id !== null) { - $request_payload["acs_credential_id"] = $acs_credential_id; - } - if ($acs_entrance_id !== null) { - $request_payload["acs_entrance_id"] = $acs_entrance_id; - } + $request_payload["acs_credential_id"] = $acs_credential_id; + $request_payload["acs_entrance_id"] = $acs_entrance_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/entrances/unlock", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index 9278fd9f..cc519bf5 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsSystem; -use Seam\SeamClient; class AcsSystemsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -24,11 +33,9 @@ public function get(string $acs_system_id): AcsSystem { $request_payload = []; - if ($acs_system_id !== null) { - $request_payload["acs_system_id"] = $acs_system_id; - } + $request_payload["acs_system_id"] = $acs_system_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/systems/get", json: (object) $request_payload, @@ -64,7 +71,7 @@ public function list( $request_payload["search"] = $search; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/systems/list", json: (object) $request_payload, @@ -86,11 +93,9 @@ public function list_compatible_credential_manager_acs_systems( ): array { $request_payload = []; - if ($acs_system_id !== null) { - $request_payload["acs_system_id"] = $acs_system_id; - } + $request_payload["acs_system_id"] = $acs_system_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/systems/list_compatible_credential_manager_acs_systems", json: (object) $request_payload, @@ -114,9 +119,7 @@ public function report_devices( ): void { $request_payload = []; - if ($acs_system_id !== null) { - $request_payload["acs_system_id"] = $acs_system_id; - } + $request_payload["acs_system_id"] = $acs_system_id; if ($acs_encoders !== null) { $request_payload["acs_encoders"] = $acs_encoders; } @@ -124,7 +127,7 @@ public function report_devices( $request_payload["acs_entrances"] = $acs_entrances; } - $this->seam->request( + $this->client->request( "POST", "/acs/systems/report_devices", json: (object) $request_payload, diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 729709e1..422a0502 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -2,17 +2,26 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsUser; -use Seam\SeamClient; class AcsUsersClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -28,14 +37,10 @@ public function add_to_access_group( ): void { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } - if ($acs_user_id !== null) { - $request_payload["acs_user_id"] = $acs_user_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; + $request_payload["acs_user_id"] = $acs_user_id; - $this->seam->request( + $this->client->request( "POST", "/acs/users/add_to_access_group", json: (object) $request_payload, @@ -67,12 +72,8 @@ public function create( ): AcsUser { $request_payload = []; - if ($acs_system_id !== null) { - $request_payload["acs_system_id"] = $acs_system_id; - } - if ($full_name !== null) { - $request_payload["full_name"] = $full_name; - } + $request_payload["acs_system_id"] = $acs_system_id; + $request_payload["full_name"] = $full_name; if ($access_schedule !== null) { $request_payload["access_schedule"] = $access_schedule; } @@ -92,7 +93,7 @@ public function create( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/users/create", json: (object) $request_payload, @@ -126,7 +127,7 @@ public function delete( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/users/delete", json: (object) $request_payload, @@ -158,7 +159,7 @@ public function get( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/users/get", json: (object) $request_payload, @@ -178,6 +179,7 @@ public function get( * @param string $user_identity_email_address Email address of the user identity for which you want to retrieve all access system users. * @param string $user_identity_id ID of the user identity for which you want to retrieve all access system users. * @param string $user_identity_phone_number Phone number of the user identity for which you want to retrieve all access system users, in [E.164 format](https://www.itu.int/rec/T-REC-E.164/en) (for example, `+15555550100`). + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -222,7 +224,7 @@ public function list( ] = $user_identity_phone_number; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/users/list", json: (object) $request_payload, @@ -260,7 +262,7 @@ public function list_accessible_entrances( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/acs/users/list_accessible_entrances", json: (object) $request_payload, @@ -287,9 +289,7 @@ public function remove_from_access_group( ): void { $request_payload = []; - if ($acs_access_group_id !== null) { - $request_payload["acs_access_group_id"] = $acs_access_group_id; - } + $request_payload["acs_access_group_id"] = $acs_access_group_id; if ($acs_user_id !== null) { $request_payload["acs_user_id"] = $acs_user_id; } @@ -297,7 +297,7 @@ public function remove_from_access_group( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/users/remove_from_access_group", json: (object) $request_payload, @@ -329,7 +329,7 @@ public function revoke_access_to_all_entrances( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/users/revoke_access_to_all_entrances", json: (object) $request_payload, @@ -361,7 +361,7 @@ public function suspend( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/users/suspend", json: (object) $request_payload, @@ -393,7 +393,7 @@ public function unsuspend( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/users/unsuspend", json: (object) $request_payload, @@ -455,7 +455,7 @@ public function update( $request_payload["user_identity_id"] = $user_identity_id; } - $this->seam->request( + $this->client->request( "POST", "/acs/users/update", json: (object) $request_payload, diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index becd80db..971a286e 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -2,41 +2,55 @@ namespace Seam\Routes; -use Seam\ActionAttemptFailedError; -use Seam\ActionAttemptTimeoutError; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; -use Seam\SeamClient; class ActionAttemptsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** * Returns a specified [action attempt](https://docs.seam.co/core-concepts/action-attempts). * * @param string $action_attempt_id ID of the action attempt that you want to get. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ - public function get(string $action_attempt_id): ActionAttempt - { + public function get( + string $action_attempt_id, + bool|array|null $wait_for_action_attempt = null, + ): ActionAttempt { $request_payload = []; - if ($action_attempt_id !== null) { - $request_payload["action_attempt_id"] = $action_attempt_id; - } + $request_payload["action_attempt_id"] = $action_attempt_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/action_attempts/get", json: (object) $request_payload, ); - return ActionAttempt::from_json($res->action_attempt); + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], + ); } /** @@ -46,6 +60,8 @@ public function get(string $action_attempt_id): ActionAttempt * @param string $device_id ID of the device to filter action attempts by. * @param int $limit Maximum number of records to return per page. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -53,6 +69,7 @@ public function list( ?string $device_id = null, ?int $limit = null, ?string $page_cursor = null, + bool|array|null $wait_for_action_attempt = null, ?callable $on_response = null, ): array { $request_payload = []; @@ -70,45 +87,17 @@ public function list( $request_payload["page_cursor"] = $page_cursor; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/action_attempts/list", json: (object) $request_payload, ); - if ($on_response !== null) { - $on_response($res); - } - - return array_map( - fn($r) => ActionAttempt::from_json($r), - $res->action_attempts, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempts), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); } - public function poll_until_ready( - string $action_attempt_id, - float $timeout = 20.0, - ): ActionAttempt { - $seam = $this->seam; - $time_waiting = 0.0; - $polling_interval = 0.4; - $action_attempt = $seam->action_attempts->get($action_attempt_id); - - while ($action_attempt->status == "pending") { - $action_attempt = $seam->action_attempts->get( - $action_attempt->action_attempt_id, - ); - if ($time_waiting > $timeout) { - throw new ActionAttemptTimeoutError($action_attempt, $timeout); - } - $time_waiting += $polling_interval; - usleep($polling_interval * 1000000); - } - - if ($action_attempt->status == "error") { - throw new ActionAttemptFailedError($action_attempt); - } - - return $action_attempt; - } } diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index ebb651de..93b81d8c 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\ClientSession; -use Seam\SeamClient; class ClientSessionsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -64,7 +73,7 @@ public function create( $request_payload["user_identity_ids"] = $user_identity_ids; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/client_sessions/create", json: (object) $request_payload, @@ -83,11 +92,9 @@ public function delete(string $client_session_id): void { $request_payload = []; - if ($client_session_id !== null) { - $request_payload["client_session_id"] = $client_session_id; - } + $request_payload["client_session_id"] = $client_session_id; - $this->seam->request( + $this->client->request( "POST", "/client_sessions/delete", json: (object) $request_payload, @@ -114,7 +121,7 @@ public function get( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/client_sessions/get", json: (object) $request_payload, @@ -163,7 +170,7 @@ public function get_or_create( $request_payload["user_identity_ids"] = $user_identity_ids; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/client_sessions/get_or_create", json: (object) $request_payload, @@ -212,7 +219,7 @@ public function grant_access( $request_payload["user_identity_ids"] = $user_identity_ids; } - $this->seam->request( + $this->client->request( "POST", "/client_sessions/grant_access", json: (object) $request_payload, @@ -256,7 +263,7 @@ public function list( ] = $without_user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/client_sessions/list", json: (object) $request_payload, @@ -280,11 +287,9 @@ public function revoke(string $client_session_id): void { $request_payload = []; - if ($client_session_id !== null) { - $request_payload["client_session_id"] = $client_session_id; - } + $request_payload["client_session_id"] = $client_session_id; - $this->seam->request( + $this->client->request( "POST", "/client_sessions/revoke", json: (object) $request_payload, diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index 4bd32a3e..760d8edb 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\ConnectWebview; -use Seam\SeamClient; class ConnectWebviewsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -86,7 +95,7 @@ public function create( ] = $wait_for_device_creation; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/connect_webviews/create", json: (object) $request_payload, @@ -107,11 +116,9 @@ public function delete(string $connect_webview_id): void { $request_payload = []; - if ($connect_webview_id !== null) { - $request_payload["connect_webview_id"] = $connect_webview_id; - } + $request_payload["connect_webview_id"] = $connect_webview_id; - $this->seam->request( + $this->client->request( "POST", "/connect_webviews/delete", json: (object) $request_payload, @@ -130,11 +137,9 @@ public function get(string $connect_webview_id): ConnectWebview { $request_payload = []; - if ($connect_webview_id !== null) { - $request_payload["connect_webview_id"] = $connect_webview_id; - } + $request_payload["connect_webview_id"] = $connect_webview_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/connect_webviews/get", json: (object) $request_payload, @@ -152,6 +157,7 @@ public function get(string $connect_webview_id): ConnectWebview * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned Connect Webviews to include all records that satisfy a partial match using `connect_webview_id`, `accepted_providers`, `custom_metadata`, or `customer_key`. * @param string $user_identifier_key Your user ID for the user by which you want to filter Connect Webviews. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -184,7 +190,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/connect_webviews/list", json: (object) $request_payload, diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index d3873aa0..9725597a 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -2,17 +2,29 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\ConnectedAccount; -use Seam\SeamClient; class ConnectedAccountsClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public ConnectedAccountsSimulateClient $simulate; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->simulate = new ConnectedAccountsSimulateClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->simulate = new ConnectedAccountsSimulateClient( + $client, + $defaults, + ); } /** @@ -29,11 +41,9 @@ public function delete(string $connected_account_id): void { $request_payload = []; - if ($connected_account_id !== null) { - $request_payload["connected_account_id"] = $connected_account_id; - } + $request_payload["connected_account_id"] = $connected_account_id; - $this->seam->request( + $this->client->request( "POST", "/connected_accounts/delete", json: (object) $request_payload, @@ -60,7 +70,7 @@ public function get( $request_payload["email"] = $email; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/connected_accounts/get", json: (object) $request_payload, @@ -79,6 +89,7 @@ public function get( * @param string $search String for which to search. Filters returned connected accounts to include all records that satisfy a partial match using `connected_account_id`, `account_type`, `customer_key`, `custom_metadata`, `user_identifier.username`, `user_identifier.email` or `user_identifier.phone`. * @param string $space_id ID of the space by which you want to filter connected accounts. * @param string $user_identifier_key Your user ID for the user by which you want to filter connected accounts. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -115,7 +126,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/connected_accounts/list", json: (object) $request_payload, @@ -141,11 +152,9 @@ public function sync(string $connected_account_id): void { $request_payload = []; - if ($connected_account_id !== null) { - $request_payload["connected_account_id"] = $connected_account_id; - } + $request_payload["connected_account_id"] = $connected_account_id; - $this->seam->request( + $this->client->request( "POST", "/connected_accounts/sync", json: (object) $request_payload, @@ -173,9 +182,7 @@ public function update( ): void { $request_payload = []; - if ($connected_account_id !== null) { - $request_payload["connected_account_id"] = $connected_account_id; - } + $request_payload["connected_account_id"] = $connected_account_id; if ($accepted_capabilities !== null) { $request_payload["accepted_capabilities"] = $accepted_capabilities; } @@ -194,7 +201,7 @@ public function update( $request_payload["display_name"] = $display_name; } - $this->seam->request( + $this->client->request( "POST", "/connected_accounts/update", json: (object) $request_payload, diff --git a/src/Routes/ConnectedAccountsSimulateClient.php b/src/Routes/ConnectedAccountsSimulateClient.php index d3a913df..4eef7cb8 100644 --- a/src/Routes/ConnectedAccountsSimulateClient.php +++ b/src/Routes/ConnectedAccountsSimulateClient.php @@ -2,15 +2,24 @@ namespace Seam\Routes; -use Seam\SeamClient; +use Seam\Http\SeamHttpClient; class ConnectedAccountsSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -23,11 +32,9 @@ public function disconnect(string $connected_account_id): void { $request_payload = []; - if ($connected_account_id !== null) { - $request_payload["connected_account_id"] = $connected_account_id; - } + $request_payload["connected_account_id"] = $connected_account_id; - $this->seam->request( + $this->client->request( "POST", "/connected_accounts/simulate/disconnect", json: (object) $request_payload, diff --git a/src/Routes/CustomersClient.php b/src/Routes/CustomersClient.php index f8c97a1f..3fe55788 100644 --- a/src/Routes/CustomersClient.php +++ b/src/Routes/CustomersClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\CustomerPortal; -use Seam\SeamClient; class CustomersClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -83,7 +92,7 @@ public function create_portal( $request_payload["customer_data"] = $customer_data; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/customers/create_portal", json: (object) $request_payload, @@ -198,7 +207,7 @@ public function delete_data( $request_payload["user_keys"] = $user_keys; } - $this->seam->request( + $this->client->request( "POST", "/customers/delete_data", json: (object) $request_payload, @@ -254,9 +263,7 @@ public function push_data( ): void { $request_payload = []; - if ($customer_key !== null) { - $request_payload["customer_key"] = $customer_key; - } + $request_payload["customer_key"] = $customer_key; if ($access_grants !== null) { $request_payload["access_grants"] = $access_grants; } @@ -315,7 +322,7 @@ public function push_data( $request_payload["users"] = $users; } - $this->seam->request( + $this->client->request( "POST", "/customers/push_data", json: (object) $request_payload, diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 9eb2f20c..61ed5303 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -2,20 +2,29 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Device; use Seam\Resources\DeviceProvider; -use Seam\SeamClient; class DevicesClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public DevicesSimulateClient $simulate; public DevicesUnmanagedClient $unmanaged; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->simulate = new DevicesSimulateClient($seam); - $this->unmanaged = new DevicesUnmanagedClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->simulate = new DevicesSimulateClient($client, $defaults); + $this->unmanaged = new DevicesUnmanagedClient($client, $defaults); } /** @@ -38,7 +47,7 @@ public function get(?string $device_id = null, ?string $name = null): Device $request_payload["name"] = $name; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/devices/get", json: (object) $request_payload, @@ -66,6 +75,7 @@ public function get(?string $device_id = null, ?string $name = null): Device * @param string $space_id ID of the space for which you want to list devices. * @param string $unstable_location_id * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -138,7 +148,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/devices/list", json: (object) $request_payload, @@ -170,7 +180,7 @@ public function list_device_providers( $request_payload["provider_category"] = $provider_category; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/devices/list_device_providers", json: (object) $request_payload, @@ -192,11 +202,9 @@ public function report_provider_metadata(array $devices): void { $request_payload = []; - if ($devices !== null) { - $request_payload["devices"] = $devices; - } + $request_payload["devices"] = $devices; - $this->seam->request( + $this->client->request( "POST", "/devices/report_provider_metadata", json: (object) $request_payload, @@ -226,9 +234,7 @@ public function update( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($backup_access_code_pool_enabled !== null) { $request_payload[ "backup_access_code_pool_enabled" @@ -247,7 +253,7 @@ public function update( $request_payload["properties"] = $properties; } - $this->seam->request( + $this->client->request( "POST", "/devices/update", json: (object) $request_payload, diff --git a/src/Routes/DevicesSimulateClient.php b/src/Routes/DevicesSimulateClient.php index 9fb351b3..2a14d57d 100644 --- a/src/Routes/DevicesSimulateClient.php +++ b/src/Routes/DevicesSimulateClient.php @@ -2,15 +2,24 @@ namespace Seam\Routes; -use Seam\SeamClient; +use Seam\Http\SeamHttpClient; class DevicesSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -23,11 +32,9 @@ public function connect(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/devices/simulate/connect", json: (object) $request_payload, @@ -47,11 +54,9 @@ public function connect_to_hub(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/devices/simulate/connect_to_hub", json: (object) $request_payload, @@ -68,11 +73,9 @@ public function disconnect(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/devices/simulate/disconnect", json: (object) $request_payload, @@ -93,11 +96,9 @@ public function disconnect_from_hub(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/devices/simulate/disconnect_from_hub", json: (object) $request_payload, @@ -117,14 +118,10 @@ public function paid_subscription(string $device_id, bool $is_expired): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($is_expired !== null) { - $request_payload["is_expired"] = $is_expired; - } + $request_payload["device_id"] = $device_id; + $request_payload["is_expired"] = $is_expired; - $this->seam->request( + $this->client->request( "POST", "/devices/simulate/paid_subscription", json: (object) $request_payload, @@ -141,11 +138,9 @@ public function remove(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/devices/simulate/remove", json: (object) $request_payload, diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index 171090df..541be58e 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\UnmanagedDevice; -use Seam\SeamClient; class DevicesUnmanagedClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -38,7 +47,7 @@ public function get( $request_payload["name"] = $name; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/devices/unmanaged/get", json: (object) $request_payload, @@ -68,6 +77,7 @@ public function get( * @param string $space_id ID of the space for which you want to list devices. * @param string $unstable_location_id * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -140,7 +150,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/devices/unmanaged/list", json: (object) $request_payload, @@ -173,9 +183,7 @@ public function update( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($custom_metadata !== null) { $request_payload["custom_metadata"] = $custom_metadata; } @@ -183,7 +191,7 @@ public function update( $request_payload["is_managed"] = $is_managed; } - $this->seam->request( + $this->client->request( "POST", "/devices/unmanaged/update", json: (object) $request_payload, diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index a1c37ba1..f2f1f65c 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Event; -use Seam\SeamClient; class EventsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -39,7 +48,7 @@ public function get( $request_payload["event_type"] = $event_type; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/events/get", json: (object) $request_payload, @@ -198,7 +207,7 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/events/list", json: (object) $request_payload, diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index 34815729..e91f4f30 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\InstantKey; -use Seam\SeamClient; class InstantKeysClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -24,11 +33,9 @@ public function delete(string $instant_key_id): void { $request_payload = []; - if ($instant_key_id !== null) { - $request_payload["instant_key_id"] = $instant_key_id; - } + $request_payload["instant_key_id"] = $instant_key_id; - $this->seam->request( + $this->client->request( "POST", "/instant_keys/delete", json: (object) $request_payload, @@ -55,7 +62,7 @@ public function get( $request_payload["instant_key_url"] = $instant_key_url; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/instant_keys/get", json: (object) $request_payload, @@ -78,7 +85,7 @@ public function list(?string $user_identity_id = null): array $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/instant_keys/list", json: (object) $request_payload, diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index 00878fd9..d54afadf 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -2,18 +2,28 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\Device; -use Seam\SeamClient; class LocksClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public LocksSimulateClient $simulate; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->simulate = new LocksSimulateClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->simulate = new LocksSimulateClient($client, $defaults); } /** @@ -22,43 +32,37 @@ public function __construct(SeamClient $seam) * @param bool $auto_lock_enabled Whether to enable or disable auto-lock. * @param string $device_id ID of the lock for which you want to configure the auto-lock. * @param float $auto_lock_delay_seconds Delay in seconds before the lock automatically locks. Required when enabling auto-lock. Must be between 1 and 60. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function configure_auto_lock( bool $auto_lock_enabled, string $device_id, ?float $auto_lock_delay_seconds = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($auto_lock_enabled !== null) { - $request_payload["auto_lock_enabled"] = $auto_lock_enabled; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["auto_lock_enabled"] = $auto_lock_enabled; + $request_payload["device_id"] = $device_id; if ($auto_lock_delay_seconds !== null) { $request_payload[ "auto_lock_delay_seconds" ] = $auto_lock_delay_seconds; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/configure_auto_lock", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -80,7 +84,7 @@ public function get(?string $device_id = null, ?string $name = null): Device $request_payload["name"] = $name; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/get", json: (object) $request_payload, @@ -108,6 +112,7 @@ public function get(?string $device_id = null, ?string $name = null): Device * @param string $space_id ID of the space for which you want to list devices. * @param string $unstable_location_id * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -180,7 +185,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/list", json: (object) $request_payload, @@ -197,65 +202,57 @@ public function list( * Locks a [lock](https://docs.seam.co/low-level-apis/smart-locks). See also [Locking and Unlocking Smart Locks](https://docs.seam.co/low-level-apis/smart-locks/lock-and-unlock). * * @param string $device_id ID of the lock that you want to lock. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function lock_door( string $device_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/lock_door", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** * Unlocks a [lock](https://docs.seam.co/low-level-apis/smart-locks). See also [Locking and Unlocking Smart Locks](https://docs.seam.co/low-level-apis/smart-locks/lock-and-unlock). * * @param string $device_id ID of the lock that you want to unlock. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function unlock_door( string $device_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/unlock_door", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/LocksSimulateClient.php b/src/Routes/LocksSimulateClient.php index 500448d8..ca91968c 100644 --- a/src/Routes/LocksSimulateClient.php +++ b/src/Routes/LocksSimulateClient.php @@ -2,16 +2,26 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; -use Seam\SeamClient; class LocksSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -19,69 +29,59 @@ public function __construct(SeamClient $seam) * * @param string $code Code that you want to simulate entering on a keypad. * @param string $device_id ID of the device for which you want to simulate a keypad code entry. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function keypad_code_entry( string $code, string $device_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($code !== null) { - $request_payload["code"] = $code; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["code"] = $code; + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/simulate/keypad_code_entry", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** * Simulates a manual lock action using a keypad. You can only perform this action for [August](https://docs.seam.co/device-and-system-integration-guides/august-locks) devices within [sandbox workspaces](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces). * * @param string $device_id ID of the device for which you want to simulate a manual lock action using a keypad. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function manual_lock_via_keypad( string $device_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/locks/simulate/manual_lock_via_keypad", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/NoiseSensorsClient.php b/src/Routes/NoiseSensorsClient.php index d34a85b9..3f199835 100644 --- a/src/Routes/NoiseSensorsClient.php +++ b/src/Routes/NoiseSensorsClient.php @@ -2,19 +2,31 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Device; -use Seam\SeamClient; class NoiseSensorsClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public NoiseSensorsNoiseThresholdsClient $noise_thresholds; public NoiseSensorsSimulateClient $simulate; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->noise_thresholds = new NoiseSensorsNoiseThresholdsClient($seam); - $this->simulate = new NoiseSensorsSimulateClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->noise_thresholds = new NoiseSensorsNoiseThresholdsClient( + $client, + $defaults, + ); + $this->simulate = new NoiseSensorsSimulateClient($client, $defaults); } /** @@ -36,6 +48,7 @@ public function __construct(SeamClient $seam) * @param string $space_id ID of the space for which you want to list devices. * @param string $unstable_location_id * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -108,7 +121,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/noise_sensors/list", json: (object) $request_payload, diff --git a/src/Routes/NoiseSensorsNoiseThresholdsClient.php b/src/Routes/NoiseSensorsNoiseThresholdsClient.php index 80492126..af7ad0de 100644 --- a/src/Routes/NoiseSensorsNoiseThresholdsClient.php +++ b/src/Routes/NoiseSensorsNoiseThresholdsClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\NoiseThreshold; -use Seam\SeamClient; class NoiseSensorsNoiseThresholdsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -35,15 +44,9 @@ public function create( ): NoiseThreshold { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($ends_daily_at !== null) { - $request_payload["ends_daily_at"] = $ends_daily_at; - } - if ($starts_daily_at !== null) { - $request_payload["starts_daily_at"] = $starts_daily_at; - } + $request_payload["device_id"] = $device_id; + $request_payload["ends_daily_at"] = $ends_daily_at; + $request_payload["starts_daily_at"] = $starts_daily_at; if ($name !== null) { $request_payload["name"] = $name; } @@ -56,7 +59,7 @@ public function create( $request_payload["noise_threshold_nrs"] = $noise_threshold_nrs; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/noise_sensors/noise_thresholds/create", json: (object) $request_payload, @@ -76,14 +79,10 @@ public function delete(string $device_id, string $noise_threshold_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($noise_threshold_id !== null) { - $request_payload["noise_threshold_id"] = $noise_threshold_id; - } + $request_payload["device_id"] = $device_id; + $request_payload["noise_threshold_id"] = $noise_threshold_id; - $this->seam->request( + $this->client->request( "POST", "/noise_sensors/noise_thresholds/delete", json: (object) $request_payload, @@ -100,11 +99,9 @@ public function get(string $noise_threshold_id): NoiseThreshold { $request_payload = []; - if ($noise_threshold_id !== null) { - $request_payload["noise_threshold_id"] = $noise_threshold_id; - } + $request_payload["noise_threshold_id"] = $noise_threshold_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/noise_sensors/noise_thresholds/get", json: (object) $request_payload, @@ -123,11 +120,9 @@ public function list(string $device_id): array { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/noise_sensors/noise_thresholds/list", json: (object) $request_payload, @@ -162,12 +157,8 @@ public function update( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($noise_threshold_id !== null) { - $request_payload["noise_threshold_id"] = $noise_threshold_id; - } + $request_payload["device_id"] = $device_id; + $request_payload["noise_threshold_id"] = $noise_threshold_id; if ($ends_daily_at !== null) { $request_payload["ends_daily_at"] = $ends_daily_at; } @@ -186,7 +177,7 @@ public function update( $request_payload["starts_daily_at"] = $starts_daily_at; } - $this->seam->request( + $this->client->request( "POST", "/noise_sensors/noise_thresholds/update", json: (object) $request_payload, diff --git a/src/Routes/NoiseSensorsSimulateClient.php b/src/Routes/NoiseSensorsSimulateClient.php index ac6c5891..7388b50b 100644 --- a/src/Routes/NoiseSensorsSimulateClient.php +++ b/src/Routes/NoiseSensorsSimulateClient.php @@ -2,15 +2,24 @@ namespace Seam\Routes; -use Seam\SeamClient; +use Seam\Http\SeamHttpClient; class NoiseSensorsSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -23,11 +32,9 @@ public function trigger_noise_threshold(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/noise_sensors/simulate/trigger_noise_threshold", json: (object) $request_payload, diff --git a/src/Routes/PhonesClient.php b/src/Routes/PhonesClient.php index 922b3f64..b97d5243 100644 --- a/src/Routes/PhonesClient.php +++ b/src/Routes/PhonesClient.php @@ -2,17 +2,26 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Phone; -use Seam\SeamClient; class PhonesClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public PhonesSimulateClient $simulate; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->simulate = new PhonesSimulateClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->simulate = new PhonesSimulateClient($client, $defaults); } /** @@ -25,11 +34,9 @@ public function deactivate(string $device_id): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/phones/deactivate", json: (object) $request_payload, @@ -46,11 +53,9 @@ public function get(string $device_id): Phone { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/phones/get", json: (object) $request_payload, @@ -81,7 +86,7 @@ public function list( ] = $owner_user_identity_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/phones/list", json: (object) $request_payload, diff --git a/src/Routes/PhonesSimulateClient.php b/src/Routes/PhonesSimulateClient.php index e33ee226..85c5e1ff 100644 --- a/src/Routes/PhonesSimulateClient.php +++ b/src/Routes/PhonesSimulateClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Phone; -use Seam\SeamClient; class PhonesSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -31,9 +40,7 @@ public function create_sandbox_phone( ): Phone { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; if ($assa_abloy_metadata !== null) { $request_payload["assa_abloy_metadata"] = $assa_abloy_metadata; } @@ -46,7 +53,7 @@ public function create_sandbox_phone( $request_payload["phone_metadata"] = $phone_metadata; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/phones/simulate/create_sandbox_phone", json: (object) $request_payload, diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index b90f93b9..46db212d 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -2,17 +2,26 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Batch; use Seam\Resources\Space; -use Seam\SeamClient; class SpacesClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -28,14 +37,10 @@ public function add_acs_entrances( ): void { $request_payload = []; - if ($acs_entrance_ids !== null) { - $request_payload["acs_entrance_ids"] = $acs_entrance_ids; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["acs_entrance_ids"] = $acs_entrance_ids; + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/add_acs_entrances", json: (object) $request_payload, @@ -55,14 +60,10 @@ public function add_connected_account( ): void { $request_payload = []; - if ($connected_account_id !== null) { - $request_payload["connected_account_id"] = $connected_account_id; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["connected_account_id"] = $connected_account_id; + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/add_connected_account", json: (object) $request_payload, @@ -80,14 +81,10 @@ public function add_devices(array $device_ids, string $space_id): void { $request_payload = []; - if ($device_ids !== null) { - $request_payload["device_ids"] = $device_ids; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["device_ids"] = $device_ids; + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/add_devices", json: (object) $request_payload, @@ -117,9 +114,7 @@ public function create( ): Space { $request_payload = []; - if ($name !== null) { - $request_payload["name"] = $name; - } + $request_payload["name"] = $name; if ($acs_entrance_ids !== null) { $request_payload["acs_entrance_ids"] = $acs_entrance_ids; } @@ -139,7 +134,7 @@ public function create( $request_payload["space_key"] = $space_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/spaces/create", json: (object) $request_payload, @@ -158,11 +153,9 @@ public function delete(string $space_id): void { $request_payload = []; - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/delete", json: (object) $request_payload, @@ -189,7 +182,7 @@ public function get( $request_payload["space_key"] = $space_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/spaces/get", json: (object) $request_payload, @@ -228,7 +221,7 @@ public function get_related( $request_payload["space_keys"] = $space_keys; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/spaces/get_related", json: (object) $request_payload, @@ -245,6 +238,7 @@ public function get_related( * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned spaces to include all records that satisfy a partial match using `name`, `space_key`, or `customer_key`. * @param string $space_key Filter spaces by space_key. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -273,7 +267,7 @@ public function list( $request_payload["space_key"] = $space_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/spaces/list", json: (object) $request_payload, @@ -299,14 +293,10 @@ public function remove_acs_entrances( ): void { $request_payload = []; - if ($acs_entrance_ids !== null) { - $request_payload["acs_entrance_ids"] = $acs_entrance_ids; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["acs_entrance_ids"] = $acs_entrance_ids; + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/remove_acs_entrances", json: (object) $request_payload, @@ -326,14 +316,10 @@ public function remove_connected_account( ): void { $request_payload = []; - if ($connected_account_id !== null) { - $request_payload["connected_account_id"] = $connected_account_id; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["connected_account_id"] = $connected_account_id; + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/remove_connected_account", json: (object) $request_payload, @@ -351,14 +337,10 @@ public function remove_devices(array $device_ids, string $space_id): void { $request_payload = []; - if ($device_ids !== null) { - $request_payload["device_ids"] = $device_ids; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } + $request_payload["device_ids"] = $device_ids; + $request_payload["space_id"] = $space_id; - $this->seam->request( + $this->client->request( "POST", "/spaces/remove_devices", json: (object) $request_payload, @@ -405,7 +387,7 @@ public function update( $request_payload["space_key"] = $space_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/spaces/update", json: (object) $request_payload, diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index 650aa7d6..7d7c4b4d 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -2,22 +2,35 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\Device; -use Seam\SeamClient; class ThermostatsClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public ThermostatsDailyProgramsClient $daily_programs; public ThermostatsSchedulesClient $schedules; public ThermostatsSimulateClient $simulate; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->daily_programs = new ThermostatsDailyProgramsClient($seam); - $this->schedules = new ThermostatsSchedulesClient($seam); - $this->simulate = new ThermostatsSimulateClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->daily_programs = new ThermostatsDailyProgramsClient( + $client, + $defaults, + ); + $this->schedules = new ThermostatsSchedulesClient($client, $defaults); + $this->simulate = new ThermostatsSimulateClient($client, $defaults); } /** @@ -25,37 +38,31 @@ public function __construct(SeamClient $seam) * * @param string $climate_preset_key Climate preset key of the climate preset that you want to activate. * @param string $device_id ID of the thermostat device for which you want to activate a climate preset. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function activate_climate_preset( string $climate_preset_key, string $device_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($climate_preset_key !== null) { - $request_payload["climate_preset_key"] = $climate_preset_key; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["climate_preset_key"] = $climate_preset_key; + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/activate_climate_preset", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -64,19 +71,18 @@ public function activate_climate_preset( * @param string $device_id ID of the thermostat device that you want to set to cool mode. * @param float $cooling_set_point_celsius [Cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °C that you want to set for the thermostat. You must set one of the `cooling_set_point` parameters. * @param float $cooling_set_point_fahrenheit [Cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °F that you want to set for the thermostat. You must set one of the `cooling_set_point` parameters. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function cool( string $device_id, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($cooling_set_point_celsius !== null) { $request_payload[ "cooling_set_point_celsius" @@ -88,21 +94,18 @@ public function cool( ] = $cooling_set_point_fahrenheit; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/cool", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -138,12 +141,8 @@ public function create_climate_preset( ): void { $request_payload = []; - if ($climate_preset_key !== null) { - $request_payload["climate_preset_key"] = $climate_preset_key; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["climate_preset_key"] = $climate_preset_key; + $request_payload["device_id"] = $device_id; if ($climate_preset_mode !== null) { $request_payload["climate_preset_mode"] = $climate_preset_mode; } @@ -185,7 +184,7 @@ public function create_climate_preset( $request_payload["name"] = $name; } - $this->seam->request( + $this->client->request( "POST", "/thermostats/create_climate_preset", json: (object) $request_payload, @@ -205,14 +204,10 @@ public function delete_climate_preset( ): void { $request_payload = []; - if ($climate_preset_key !== null) { - $request_payload["climate_preset_key"] = $climate_preset_key; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["climate_preset_key"] = $climate_preset_key; + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/thermostats/delete_climate_preset", json: (object) $request_payload, @@ -225,19 +220,18 @@ public function delete_climate_preset( * @param string $device_id ID of the thermostat device that you want to set to heat mode. * @param float $heating_set_point_celsius [Heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °C that you want to set for the thermostat. You must set one of the `heating_set_point` parameters. * @param float $heating_set_point_fahrenheit [Heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °F that you want to set for the thermostat. You must set one of the `heating_set_point` parameters. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function heat( string $device_id, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($heating_set_point_celsius !== null) { $request_payload[ "heating_set_point_celsius" @@ -249,21 +243,18 @@ public function heat( ] = $heating_set_point_fahrenheit; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/heat", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -274,6 +265,7 @@ public function heat( * @param float $cooling_set_point_fahrenheit [Cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °F that you want to set for the thermostat. You must set one of the `cooling_set_point` parameters. * @param float $heating_set_point_celsius [Heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °C that you want to set for the thermostat. You must set one of the `heating_set_point` parameters. * @param float $heating_set_point_fahrenheit [Heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °F that you want to set for the thermostat. You must set one of the `heating_set_point` parameters. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function heat_cool( @@ -282,13 +274,11 @@ public function heat_cool( ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($cooling_set_point_celsius !== null) { $request_payload[ "cooling_set_point_celsius" @@ -310,21 +300,18 @@ public function heat_cool( ] = $heating_set_point_fahrenheit; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/heat_cool", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -346,6 +333,7 @@ public function heat_cool( * @param string $space_id ID of the space for which you want to list devices. * @param string $unstable_location_id * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -418,7 +406,7 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/list", json: (object) $request_payload, @@ -435,33 +423,29 @@ public function list( * Sets a specified [thermostat](https://docs.seam.co/capability-guides/thermostats) to ["off" mode](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings). * * @param string $device_id ID of the thermostat device that you want to set to off mode. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function off( string $device_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/off", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -477,14 +461,10 @@ public function set_fallback_climate_preset( ): void { $request_payload = []; - if ($climate_preset_key !== null) { - $request_payload["climate_preset_key"] = $climate_preset_key; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["climate_preset_key"] = $climate_preset_key; + $request_payload["device_id"] = $device_id; - $this->seam->request( + $this->client->request( "POST", "/thermostats/set_fallback_climate_preset", json: (object) $request_payload, @@ -497,19 +477,18 @@ public function set_fallback_climate_preset( * @param string $device_id ID of the thermostat device for which you want to set the fan mode. * @param string $fan_mode Fan mode setting for the thermostat, such as `auto`, `on`, or `circulate`. * @param string $fan_mode_setting [Fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings) that you want to set for the thermostat. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function set_fan_mode( string $device_id, ?string $fan_mode = null, ?string $fan_mode_setting = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($fan_mode !== null) { $request_payload["fan_mode"] = $fan_mode; } @@ -517,21 +496,18 @@ public function set_fan_mode( $request_payload["fan_mode_setting"] = $fan_mode_setting; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/set_fan_mode", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -543,6 +519,7 @@ public function set_fan_mode( * @param float $cooling_set_point_fahrenheit [Cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °F that you want to set for the thermostat. You must set one of the `cooling_set_point` parameters. * @param float $heating_set_point_celsius [Heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °C that you want to set for the thermostat. You must set one of the `heating_set_point` parameters. * @param float $heating_set_point_fahrenheit [Heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points) in °F that you want to set for the thermostat. You must set one of the `heating_set_point` parameters. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function set_hvac_mode( @@ -552,16 +529,12 @@ public function set_hvac_mode( ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($hvac_mode_setting !== null) { - $request_payload["hvac_mode_setting"] = $hvac_mode_setting; - } + $request_payload["device_id"] = $device_id; + $request_payload["hvac_mode_setting"] = $hvac_mode_setting; if ($cooling_set_point_celsius !== null) { $request_payload[ "cooling_set_point_celsius" @@ -583,21 +556,18 @@ public function set_hvac_mode( ] = $heating_set_point_fahrenheit; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/set_hvac_mode", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -619,9 +589,7 @@ public function set_temperature_threshold( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($lower_limit_celsius !== null) { $request_payload["lower_limit_celsius"] = $lower_limit_celsius; } @@ -639,7 +607,7 @@ public function set_temperature_threshold( ] = $upper_limit_fahrenheit; } - $this->seam->request( + $this->client->request( "POST", "/thermostats/set_temperature_threshold", json: (object) $request_payload, @@ -679,12 +647,8 @@ public function update_climate_preset( ): void { $request_payload = []; - if ($climate_preset_key !== null) { - $request_payload["climate_preset_key"] = $climate_preset_key; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["climate_preset_key"] = $climate_preset_key; + $request_payload["device_id"] = $device_id; if ($climate_preset_mode !== null) { $request_payload["climate_preset_mode"] = $climate_preset_mode; } @@ -726,7 +690,7 @@ public function update_climate_preset( $request_payload["name"] = $name; } - $this->seam->request( + $this->client->request( "POST", "/thermostats/update_climate_preset", json: (object) $request_payload, @@ -744,6 +708,7 @@ public function update_climate_preset( * @param string $thursday_program_id ID of the thermostat daily program to run on Thursdays. * @param string $tuesday_program_id ID of the thermostat daily program to run on Tuesdays. * @param string $wednesday_program_id ID of the thermostat daily program to run on Wednesdays. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function update_weekly_program( @@ -755,13 +720,11 @@ public function update_weekly_program( ?string $thursday_program_id = null, ?string $tuesday_program_id = null, ?string $wednesday_program_id = null, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($friday_program_id !== null) { $request_payload["friday_program_id"] = $friday_program_id; } @@ -784,20 +747,17 @@ public function update_weekly_program( $request_payload["wednesday_program_id"] = $wednesday_program_id; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/update_weekly_program", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index 6d786342..19f8196f 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -2,17 +2,27 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\ThermostatDailyProgram; -use Seam\SeamClient; class ThermostatsDailyProgramsClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -30,17 +40,11 @@ public function create( ): ThermostatDailyProgram { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($name !== null) { - $request_payload["name"] = $name; - } - if ($periods !== null) { - $request_payload["periods"] = $periods; - } - - $res = $this->seam->request( + $request_payload["device_id"] = $device_id; + $request_payload["name"] = $name; + $request_payload["periods"] = $periods; + + $res = $this->client->request( "POST", "/thermostats/daily_programs/create", json: (object) $request_payload, @@ -61,13 +65,11 @@ public function delete(string $thermostat_daily_program_id): void { $request_payload = []; - if ($thermostat_daily_program_id !== null) { - $request_payload[ - "thermostat_daily_program_id" - ] = $thermostat_daily_program_id; - } + $request_payload[ + "thermostat_daily_program_id" + ] = $thermostat_daily_program_id; - $this->seam->request( + $this->client->request( "POST", "/thermostats/daily_programs/delete", json: (object) $request_payload, @@ -80,42 +82,34 @@ public function delete(string $thermostat_daily_program_id): void * @param string $name Name of the thermostat daily program that you want to update. * @param array $periods Array of thermostat daily program periods. The periods that you specify overwrite any existing periods for the daily program. * @param string $thermostat_daily_program_id ID of the thermostat daily program that you want to update. + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function update( string $name, array $periods, string $thermostat_daily_program_id, - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { $request_payload = []; - if ($name !== null) { - $request_payload["name"] = $name; - } - if ($periods !== null) { - $request_payload["periods"] = $periods; - } - if ($thermostat_daily_program_id !== null) { - $request_payload[ - "thermostat_daily_program_id" - ] = $thermostat_daily_program_id; - } - - $res = $this->seam->request( + $request_payload["name"] = $name; + $request_payload["periods"] = $periods; + $request_payload[ + "thermostat_daily_program_id" + ] = $thermostat_daily_program_id; + + $res = $this->client->request( "POST", "/thermostats/daily_programs/update", json: (object) $request_payload, ); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } } diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index fbc504b3..51864c69 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\ThermostatSchedule; -use Seam\SeamClient; class ThermostatsSchedulesClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -37,18 +46,10 @@ public function create( ): ThermostatSchedule { $request_payload = []; - if ($climate_preset_key !== null) { - $request_payload["climate_preset_key"] = $climate_preset_key; - } - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($ends_at !== null) { - $request_payload["ends_at"] = $ends_at; - } - if ($starts_at !== null) { - $request_payload["starts_at"] = $starts_at; - } + $request_payload["climate_preset_key"] = $climate_preset_key; + $request_payload["device_id"] = $device_id; + $request_payload["ends_at"] = $ends_at; + $request_payload["starts_at"] = $starts_at; if ($is_override_allowed !== null) { $request_payload["is_override_allowed"] = $is_override_allowed; } @@ -61,7 +62,7 @@ public function create( $request_payload["name"] = $name; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/schedules/create", json: (object) $request_payload, @@ -80,13 +81,9 @@ public function delete(string $thermostat_schedule_id): void { $request_payload = []; - if ($thermostat_schedule_id !== null) { - $request_payload[ - "thermostat_schedule_id" - ] = $thermostat_schedule_id; - } + $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; - $this->seam->request( + $this->client->request( "POST", "/thermostats/schedules/delete", json: (object) $request_payload, @@ -103,13 +100,9 @@ public function get(string $thermostat_schedule_id): ThermostatSchedule { $request_payload = []; - if ($thermostat_schedule_id !== null) { - $request_payload[ - "thermostat_schedule_id" - ] = $thermostat_schedule_id; - } + $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/schedules/get", json: (object) $request_payload, @@ -131,14 +124,12 @@ public function list( ): array { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($user_identifier_key !== null) { $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/thermostats/schedules/list", json: (object) $request_payload, @@ -173,11 +164,7 @@ public function update( ): void { $request_payload = []; - if ($thermostat_schedule_id !== null) { - $request_payload[ - "thermostat_schedule_id" - ] = $thermostat_schedule_id; - } + $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; if ($climate_preset_key !== null) { $request_payload["climate_preset_key"] = $climate_preset_key; } @@ -199,7 +186,7 @@ public function update( $request_payload["starts_at"] = $starts_at; } - $this->seam->request( + $this->client->request( "POST", "/thermostats/schedules/update", json: (object) $request_payload, diff --git a/src/Routes/ThermostatsSimulateClient.php b/src/Routes/ThermostatsSimulateClient.php index e3cf413f..9b00648f 100644 --- a/src/Routes/ThermostatsSimulateClient.php +++ b/src/Routes/ThermostatsSimulateClient.php @@ -2,15 +2,24 @@ namespace Seam\Routes; -use Seam\SeamClient; +use Seam\Http\SeamHttpClient; class ThermostatsSimulateClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -34,12 +43,8 @@ public function hvac_mode_adjusted( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($hvac_mode !== null) { - $request_payload["hvac_mode"] = $hvac_mode; - } + $request_payload["device_id"] = $device_id; + $request_payload["hvac_mode"] = $hvac_mode; if ($cooling_set_point_celsius !== null) { $request_payload[ "cooling_set_point_celsius" @@ -61,7 +66,7 @@ public function hvac_mode_adjusted( ] = $heating_set_point_fahrenheit; } - $this->seam->request( + $this->client->request( "POST", "/thermostats/simulate/hvac_mode_adjusted", json: (object) $request_payload, @@ -83,9 +88,7 @@ public function temperature_reached( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } + $request_payload["device_id"] = $device_id; if ($temperature_celsius !== null) { $request_payload["temperature_celsius"] = $temperature_celsius; } @@ -95,7 +98,7 @@ public function temperature_reached( ] = $temperature_fahrenheit; } - $this->seam->request( + $this->client->request( "POST", "/thermostats/simulate/temperature_reached", json: (object) $request_payload, diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index 4dca58e2..e70ad961 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -2,22 +2,34 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsSystem; use Seam\Resources\AcsUser; use Seam\Resources\Device; use Seam\Resources\InstantKey; use Seam\Resources\UserIdentity; -use Seam\SeamClient; class UserIdentitiesClient { - private SeamClient $seam; + private SeamHttpClient $client; + + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; public UserIdentitiesUnmanagedClient $unmanaged; - public function __construct(SeamClient $seam) + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; - $this->unmanaged = new UserIdentitiesUnmanagedClient($seam); + $this->client = $client; + $this->defaults = $defaults; + $this->unmanaged = new UserIdentitiesUnmanagedClient( + $client, + $defaults, + ); } /** @@ -39,9 +51,7 @@ public function add_acs_user( ): void { $request_payload = []; - if ($acs_user_id !== null) { - $request_payload["acs_user_id"] = $acs_user_id; - } + $request_payload["acs_user_id"] = $acs_user_id; if ($user_identity_id !== null) { $request_payload["user_identity_id"] = $user_identity_id; } @@ -49,7 +59,7 @@ public function add_acs_user( $request_payload["user_identity_key"] = $user_identity_key; } - $this->seam->request( + $this->client->request( "POST", "/user_identities/add_acs_user", json: (object) $request_payload, @@ -91,7 +101,7 @@ public function create( $request_payload["user_identity_key"] = $user_identity_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/create", json: (object) $request_payload, @@ -110,11 +120,9 @@ public function delete(string $user_identity_id): void { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; - $this->seam->request( + $this->client->request( "POST", "/user_identities/delete", json: (object) $request_payload, @@ -136,9 +144,7 @@ public function generate_instant_key( ): InstantKey { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; if ($customization_profile_id !== null) { $request_payload[ "customization_profile_id" @@ -148,7 +154,7 @@ public function generate_instant_key( $request_payload["max_use_count"] = $max_use_count; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/generate_instant_key", json: (object) $request_payload, @@ -177,7 +183,7 @@ public function get( $request_payload["user_identity_key"] = $user_identity_key; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/get", json: (object) $request_payload, @@ -199,14 +205,10 @@ public function grant_access_to_device( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["device_id"] = $device_id; + $request_payload["user_identity_id"] = $user_identity_id; - $this->seam->request( + $this->client->request( "POST", "/user_identities/grant_access_to_device", json: (object) $request_payload, @@ -222,6 +224,7 @@ public function grant_access_to_device( * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned user identities to include all records that satisfy a partial match using `full_name`, `phone_number`, `email_address` or `user_identity_id`. * @param array $user_identity_ids Array of user identity IDs by which to filter the list of user identities. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -256,7 +259,7 @@ public function list( $request_payload["user_identity_ids"] = $user_identity_ids; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/list", json: (object) $request_payload, @@ -282,11 +285,9 @@ public function list_accessible_devices(string $user_identity_id): array { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/list_accessible_devices", json: (object) $request_payload, @@ -305,11 +306,9 @@ public function list_accessible_entrances(string $user_identity_id): array { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/list_accessible_entrances", json: (object) $request_payload, @@ -331,11 +330,9 @@ public function list_acs_systems(string $user_identity_id): array { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/list_acs_systems", json: (object) $request_payload, @@ -354,11 +351,9 @@ public function list_acs_users(string $user_identity_id): array { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/list_acs_users", json: (object) $request_payload, @@ -380,14 +375,10 @@ public function remove_acs_user( ): void { $request_payload = []; - if ($acs_user_id !== null) { - $request_payload["acs_user_id"] = $acs_user_id; - } - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["acs_user_id"] = $acs_user_id; + $request_payload["user_identity_id"] = $user_identity_id; - $this->seam->request( + $this->client->request( "POST", "/user_identities/remove_acs_user", json: (object) $request_payload, @@ -407,14 +398,10 @@ public function revoke_access_to_device( ): void { $request_payload = []; - if ($device_id !== null) { - $request_payload["device_id"] = $device_id; - } - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["device_id"] = $device_id; + $request_payload["user_identity_id"] = $user_identity_id; - $this->seam->request( + $this->client->request( "POST", "/user_identities/revoke_access_to_device", json: (object) $request_payload, @@ -440,9 +427,7 @@ public function update( ): void { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; if ($email_address !== null) { $request_payload["email_address"] = $email_address; } @@ -456,7 +441,7 @@ public function update( $request_payload["user_identity_key"] = $user_identity_key; } - $this->seam->request( + $this->client->request( "POST", "/user_identities/update", json: (object) $request_payload, diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index e8f40773..6683b8a2 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\UnmanagedUserIdentity; -use Seam\SeamClient; class UserIdentitiesUnmanagedClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -24,11 +33,9 @@ public function get(string $user_identity_id): UnmanagedUserIdentity { $request_payload = []; - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/unmanaged/get", json: (object) $request_payload, @@ -44,6 +51,7 @@ public function get(string $user_identity_id): UnmanagedUserIdentity * @param int $limit Maximum number of records to return per page. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned unmanaged user identities to include all records that satisfy a partial match using `full_name`, `phone_number`, `email_address`, `user_identity_id` or `acs_system_id`. + * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( @@ -68,7 +76,7 @@ public function list( $request_payload["search"] = $search; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/user_identities/unmanaged/list", json: (object) $request_payload, @@ -101,17 +109,13 @@ public function update( ): void { $request_payload = []; - if ($is_managed !== null) { - $request_payload["is_managed"] = $is_managed; - } - if ($user_identity_id !== null) { - $request_payload["user_identity_id"] = $user_identity_id; - } + $request_payload["is_managed"] = $is_managed; + $request_payload["user_identity_id"] = $user_identity_id; if ($user_identity_key !== null) { $request_payload["user_identity_key"] = $user_identity_key; } - $this->seam->request( + $this->client->request( "POST", "/user_identities/unmanaged/update", json: (object) $request_payload, diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index 67fd844c..4d95a1b7 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -2,16 +2,25 @@ namespace Seam\Routes; +use Seam\Http\SeamHttpClient; use Seam\Resources\Webhook; -use Seam\SeamClient; class WebhooksClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -25,14 +34,12 @@ public function create(string $url, ?array $event_types = null): Webhook { $request_payload = []; - if ($url !== null) { - $request_payload["url"] = $url; - } + $request_payload["url"] = $url; if ($event_types !== null) { $request_payload["event_types"] = $event_types; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/webhooks/create", json: (object) $request_payload, @@ -51,11 +58,9 @@ public function delete(string $webhook_id): void { $request_payload = []; - if ($webhook_id !== null) { - $request_payload["webhook_id"] = $webhook_id; - } + $request_payload["webhook_id"] = $webhook_id; - $this->seam->request( + $this->client->request( "POST", "/webhooks/delete", json: (object) $request_payload, @@ -72,11 +77,9 @@ public function get(string $webhook_id): Webhook { $request_payload = []; - if ($webhook_id !== null) { - $request_payload["webhook_id"] = $webhook_id; - } + $request_payload["webhook_id"] = $webhook_id; - $res = $this->seam->request( + $res = $this->client->request( "POST", "/webhooks/get", json: (object) $request_payload, @@ -92,7 +95,7 @@ public function get(string $webhook_id): Webhook */ public function list(): array { - $res = $this->seam->request("POST", "/webhooks/list"); + $res = $this->client->request("POST", "/webhooks/list"); return array_map(fn($r) => Webhook::from_json($r), $res->webhooks); } @@ -108,14 +111,10 @@ public function update(array $event_types, string $webhook_id): void { $request_payload = []; - if ($event_types !== null) { - $request_payload["event_types"] = $event_types; - } - if ($webhook_id !== null) { - $request_payload["webhook_id"] = $webhook_id; - } + $request_payload["event_types"] = $event_types; + $request_payload["webhook_id"] = $webhook_id; - $this->seam->request( + $this->client->request( "POST", "/webhooks/update", json: (object) $request_payload, diff --git a/src/Routes/WorkspacesClient.php b/src/Routes/WorkspacesClient.php index 630f4755..42a52ec5 100644 --- a/src/Routes/WorkspacesClient.php +++ b/src/Routes/WorkspacesClient.php @@ -2,17 +2,27 @@ namespace Seam\Routes; +use Seam\Http\ResolveActionAttempt; +use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\Workspace; -use Seam\SeamClient; class WorkspacesClient { - private SeamClient $seam; + private SeamHttpClient $client; - public function __construct(SeamClient $seam) + /** + * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} + */ + private array $defaults; + + /** + * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults + */ + public function __construct(SeamHttpClient $client, array $defaults) { - $this->seam = $seam; + $this->client = $client; + $this->defaults = $defaults; } /** @@ -44,9 +54,7 @@ public function create( ): Workspace { $request_payload = []; - if ($name !== null) { - $request_payload["name"] = $name; - } + $request_payload["name"] = $name; if ($company_name !== null) { $request_payload["company_name"] = $company_name; } @@ -83,7 +91,7 @@ public function create( ] = $webview_success_message; } - $res = $this->seam->request( + $res = $this->client->request( "POST", "/workspaces/create", json: (object) $request_payload, @@ -99,7 +107,7 @@ public function create( */ public function get(): Workspace { - $res = $this->seam->request("POST", "/workspaces/get"); + $res = $this->client->request("POST", "/workspaces/get"); return Workspace::from_json($res->workspace); } @@ -111,7 +119,7 @@ public function get(): Workspace */ public function list(): array { - $res = $this->seam->request("POST", "/workspaces/list"); + $res = $this->client->request("POST", "/workspaces/list"); return array_map(fn($r) => Workspace::from_json($r), $res->workspaces); } @@ -119,22 +127,20 @@ public function list(): array /** * Resets the [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces) associated with the authentication value. Note that this endpoint is only available for sandbox workspaces. * + * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @return ActionAttempt OK */ public function reset_sandbox( - bool $wait_for_action_attempt = true, + bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - $res = $this->seam->request("POST", "/workspaces/reset_sandbox"); + $res = $this->client->request("POST", "/workspaces/reset_sandbox"); - if (!$wait_for_action_attempt) { - return ActionAttempt::from_json($res->action_attempt); - } - - $action_attempt = $this->seam->action_attempts->poll_until_ready( - $res->action_attempt->action_attempt_id, + return ResolveActionAttempt::resolve_action_attempt( + ActionAttempt::from_json($res->action_attempt), + $this->client, + $wait_for_action_attempt ?? + $this->defaults["wait_for_action_attempt"], ); - - return $action_attempt; } /** @@ -181,7 +187,7 @@ public function update( $request_payload["organization_id"] = $organization_id; } - $this->seam->request( + $this->client->request( "POST", "/workspaces/update", json: (object) $request_payload, diff --git a/src/Seam.php b/src/Seam.php new file mode 100644 index 00000000..588c7617 --- /dev/null +++ b/src/Seam.php @@ -0,0 +1,256 @@ + $guzzle_options Options merged into the underlying Guzzle client, e.g. timeout or headers. + * @param int|null $retries How many times to retry a failed request. Defaults to 2; pass 0 to disable. + * @param ClientInterface|null $client A preconfigured Guzzle client. It carries its own endpoint and authorization, so no other authentication option may be given with it. + */ + public function __construct( + ?string $api_key = null, + ?string $personal_access_token = null, + ?string $workspace_id = null, + ?string $endpoint = null, + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null, + ?ClientInterface $client = null, + ) { + $this->defaults = [ + "wait_for_action_attempt" => $wait_for_action_attempt ?? true, + ]; + + // A client carries its own endpoint and authorization, so the + // authentication options are only read when one has to be built. + $this->client = + $client !== null + ? SeamHttpClient::from_client($client) + : new SeamHttpClient( + Options::get_endpoint($endpoint), + Auth::get_auth_headers( + $api_key, + $personal_access_token, + $workspace_id, + ), + $guzzle_options, + $retries, + ); + + $this->access_codes = new AccessCodesClient( + $this->client, + $this->defaults, + ); + $this->access_grants = new AccessGrantsClient( + $this->client, + $this->defaults, + ); + $this->access_methods = new AccessMethodsClient( + $this->client, + $this->defaults, + ); + $this->acs = new AcsClient($this->client, $this->defaults); + $this->action_attempts = new ActionAttemptsClient( + $this->client, + $this->defaults, + ); + $this->client_sessions = new ClientSessionsClient( + $this->client, + $this->defaults, + ); + $this->connect_webviews = new ConnectWebviewsClient( + $this->client, + $this->defaults, + ); + $this->connected_accounts = new ConnectedAccountsClient( + $this->client, + $this->defaults, + ); + $this->customers = new CustomersClient($this->client, $this->defaults); + $this->devices = new DevicesClient($this->client, $this->defaults); + $this->events = new EventsClient($this->client, $this->defaults); + $this->instant_keys = new InstantKeysClient( + $this->client, + $this->defaults, + ); + $this->locks = new LocksClient($this->client, $this->defaults); + $this->noise_sensors = new NoiseSensorsClient( + $this->client, + $this->defaults, + ); + $this->phones = new PhonesClient($this->client, $this->defaults); + $this->spaces = new SpacesClient($this->client, $this->defaults); + $this->thermostats = new ThermostatsClient( + $this->client, + $this->defaults, + ); + $this->user_identities = new UserIdentitiesClient( + $this->client, + $this->defaults, + ); + $this->webhooks = new WebhooksClient($this->client, $this->defaults); + $this->workspaces = new WorkspacesClient( + $this->client, + $this->defaults, + ); + } + + /** + * Creates a client authorized with an API key. + */ + public static function from_api_key( + string $api_key, + ?string $endpoint = null, + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null, + ): static { + return new static( + api_key: $api_key, + endpoint: $endpoint, + wait_for_action_attempt: $wait_for_action_attempt, + guzzle_options: $guzzle_options, + retries: $retries, + ); + } + + /** + * Creates a client authorized with a personal access token, scoped to the + * given workspace. + */ + public static function from_personal_access_token( + string $personal_access_token, + string $workspace_id, + ?string $endpoint = null, + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null, + ): static { + return new static( + personal_access_token: $personal_access_token, + workspace_id: $workspace_id, + endpoint: $endpoint, + wait_for_action_attempt: $wait_for_action_attempt, + retries: $retries, + guzzle_options: $guzzle_options, + ); + } + + /** + * Creates a client from a preconfigured Guzzle client. + */ + public static function from_client( + ClientInterface $client, + bool|array|null $wait_for_action_attempt = null, + ): static { + return new static( + client: $client, + wait_for_action_attempt: $wait_for_action_attempt, + ); + } + + public function lts_version(): string + { + return self::LTS_VERSION; + } + + /** + * Makes a request against the Seam API with this client's authorization. + * + * @param array|object|null $json + * @param array|null $query + */ + public function request( + string $method, + string $path, + mixed $json = null, + ?array $query = null, + ): mixed { + return $this->client->request($method, $path, $json, $query); + } + + /** + * Creates a paginator for a list endpoint. + * + * @param callable $request Invokes the list method with a params array, e.g. fn($params) => $seam->devices->list(...$params) + * @param array $params + */ + public function createPaginator( + callable $request, + array $params = [], + ): Paginator { + return new Paginator($request, $params); + } +} diff --git a/src/SeamClient.php b/src/SeamClient.php index f2384ab2..c43d7e1b 100644 --- a/src/SeamClient.php +++ b/src/SeamClient.php @@ -2,154 +2,8 @@ namespace Seam; -use Seam\Routes\AccessCodesClient; -use Seam\Routes\AccessGrantsClient; -use Seam\Routes\AccessMethodsClient; -use Seam\Routes\AcsClient; -use Seam\Routes\ActionAttemptsClient; -use Seam\Routes\ClientSessionsClient; -use Seam\Routes\ConnectedAccountsClient; -use Seam\Routes\ConnectWebviewsClient; -use Seam\Routes\CustomersClient; -use Seam\Routes\DevicesClient; -use Seam\Routes\EventsClient; -use Seam\Routes\InstantKeysClient; -use Seam\Routes\LocksClient; -use Seam\Routes\NoiseSensorsClient; -use Seam\Routes\PhonesClient; -use Seam\Routes\SpacesClient; -use Seam\Routes\ThermostatsClient; -use Seam\Routes\UserIdentitiesClient; -use Seam\Routes\WebhooksClient; -use Seam\Routes\WorkspacesClient; -use Seam\Utils\PackageVersion; - -use GuzzleHttp\Client as HTTPClient; -use \Exception as Exception; -use Seam\HttpApiError; -use Seam\HttpUnauthorizedError; -use Seam\HttpInvalidInputError; - -define("LTS_VERSION", "1.0.0"); - -class SeamClient -{ - public AccessCodesClient $access_codes; - public AccessGrantsClient $access_grants; - public AccessMethodsClient $access_methods; - public AcsClient $acs; - public ActionAttemptsClient $action_attempts; - public ClientSessionsClient $client_sessions; - public ConnectWebviewsClient $connect_webviews; - public ConnectedAccountsClient $connected_accounts; - public CustomersClient $customers; - public DevicesClient $devices; - public EventsClient $events; - public InstantKeysClient $instant_keys; - public LocksClient $locks; - public NoiseSensorsClient $noise_sensors; - public PhonesClient $phones; - public SpacesClient $spaces; - public ThermostatsClient $thermostats; - public UserIdentitiesClient $user_identities; - public WebhooksClient $webhooks; - public WorkspacesClient $workspaces; - - public string $api_key; - public HTTPClient $client; - public string $ltsVersion = LTS_VERSION; - - public function __construct( - $api_key = null, - $endpoint = "https://connect.getseam.com", - $throw_http_errors = false, - ) { - $this->api_key = $api_key ?: (getenv("SEAM_API_KEY") ?: null); - $seam_sdk_version = PackageVersion::get(); - $this->client = new HTTPClient([ - "base_uri" => $endpoint, - "timeout" => 60.0, - "headers" => [ - "Authorization" => "Bearer " . $this->api_key, - "User-Agent" => "Seam PHP Client " . $seam_sdk_version, - "seam-sdk-name" => "seamapi/php", - "seam-sdk-version" => $seam_sdk_version, - "seam-lts-version" => $this->ltsVersion, - ], - "http_errors" => $throw_http_errors, - ]); - $this->access_codes = new AccessCodesClient($this); - $this->access_grants = new AccessGrantsClient($this); - $this->access_methods = new AccessMethodsClient($this); - $this->acs = new AcsClient($this); - $this->action_attempts = new ActionAttemptsClient($this); - $this->client_sessions = new ClientSessionsClient($this); - $this->connect_webviews = new ConnectWebviewsClient($this); - $this->connected_accounts = new ConnectedAccountsClient($this); - $this->customers = new CustomersClient($this); - $this->devices = new DevicesClient($this); - $this->events = new EventsClient($this); - $this->instant_keys = new InstantKeysClient($this); - $this->locks = new LocksClient($this); - $this->noise_sensors = new NoiseSensorsClient($this); - $this->phones = new PhonesClient($this); - $this->spaces = new SpacesClient($this); - $this->thermostats = new ThermostatsClient($this); - $this->user_identities = new UserIdentitiesClient($this); - $this->webhooks = new WebhooksClient($this); - $this->workspaces = new WorkspacesClient($this); - } - - public function request($method, $path, $json = null, $query = null) - { - $options = [ - "json" => $json, - "query" => $query, - ]; - $options = array_filter($options, fn($option) => $option !== null); - - $response = $this->client->request($method, $path, $options); - $status_code = $response->getStatusCode(); - $request_id = $response->getHeaderLine("seam-request-id"); - - $res_json = null; - try { - $res_json = json_decode($response->getBody()); - } catch (Exception $ignoreError) { - } - - if ($status_code >= 400) { - if ($status_code === 401) { - throw new HttpUnauthorizedError($request_id); - } - - if (($res_json->error ?? null) != null) { - if ($res_json->error->type === "invalid_input") { - throw new HttpInvalidInputError( - $res_json->error, - $status_code, - $request_id, - ); - } - - throw new HttpApiError( - $res_json->error, - $status_code, - $request_id, - ); - } - - throw \GuzzleHttp\Exception\RequestException::create( - new \GuzzleHttp\Psr7\Request($method, $path), - $response, - ); - } - - return $res_json; - } - - public function createPaginator($request, $params = []) - { - return new Paginator($request, $params); - } -} +/** + * @deprecated Use Seam\Seam instead. This alias will be removed in a future + * major version. + */ +class SeamClient extends Seam {} diff --git a/src/SeamMultiWorkspace.php b/src/SeamMultiWorkspace.php new file mode 100644 index 00000000..315ffd29 --- /dev/null +++ b/src/SeamMultiWorkspace.php @@ -0,0 +1,91 @@ + $guzzle_options + */ + public function __construct( + ?string $personal_access_token = null, + ?string $endpoint = null, + array $guzzle_options = [], + ?int $retries = null, + ?ClientInterface $client = null, + ) { + // A client carries its own endpoint and authorization, so the + // authentication options are only read when one has to be built. + if ($client !== null) { + $this->client = SeamHttpClient::from_client($client); + } else { + if ($personal_access_token === null) { + throw new Exceptions\InvalidOptionsError( + "Must specify a personal_access_token", + ); + } + + $this->client = new SeamHttpClient( + Options::get_endpoint($endpoint), + Auth::get_auth_headers_for_multi_workspace_personal_access_token( + $personal_access_token, + ), + $guzzle_options, + $retries, + ); + } + + $this->workspaces = new WorkspacesProxy( + new WorkspacesClient($this->client, [ + "wait_for_action_attempt" => false, + ]), + ); + } + + /** + * Creates a client authorized with a personal access token, not scoped to + * any workspace. + */ + public static function from_personal_access_token( + string $personal_access_token, + ?string $endpoint = null, + array $guzzle_options = [], + ?int $retries = null, + ): static { + return new static( + personal_access_token: $personal_access_token, + endpoint: $endpoint, + guzzle_options: $guzzle_options, + retries: $retries, + ); + } + + /** + * Creates a client from a preconfigured Guzzle client. + */ + public static function from_client(ClientInterface $client): static + { + return new static(client: $client); + } + + public function lts_version(): string + { + return self::LTS_VERSION; + } +} diff --git a/src/SeamWebhook.php b/src/SeamWebhook.php new file mode 100644 index 00000000..9ab920af --- /dev/null +++ b/src/SeamWebhook.php @@ -0,0 +1,53 @@ +webhook = new Webhook($secret); + } + + /** + * Verifies an incoming webhook request and returns the event it carries. + * + * @param string $payload The raw HTTP request body. + * @param array $headers The HTTP request headers. + * + * @throws \Svix\Exception\WebhookVerificationException When the signature does not match. + */ + public function verify(string $payload, array $headers): Event + { + $normalized_headers = []; + foreach ($headers as $name => $value) { + $normalized_headers[strtolower($name)] = $value; + } + + $this->webhook->verify($payload, $normalized_headers); + + $event = Event::from_json(json_decode($payload)); + + if ($event === null) { + throw new WebhookVerificationException( + "The verified webhook payload did not contain an event", + ); + } + + return $event; + } +} diff --git a/src/Token.php b/src/Token.php new file mode 100644 index 00000000..90349502 --- /dev/null +++ b/src/Token.php @@ -0,0 +1,67 @@ +workspaces = $workspaces; + } + + /** + * @return Workspace[] + */ + public function list(): array + { + return $this->workspaces->list(); + } + + public function create( + string $name, + ?string $company_name = null, + ?string $connect_partner_name = null, + mixed $connect_webview_customization = null, + ?bool $is_sandbox = null, + ?string $organization_id = null, + ?string $webview_logo_shape = null, + ?string $webview_primary_button_color = null, + ?string $webview_primary_button_text_color = null, + ?string $webview_success_message = null, + ): Workspace { + return $this->workspaces->create( + $name, + $company_name, + $connect_partner_name, + $connect_webview_customization, + $is_sandbox, + $organization_id, + $webview_logo_shape, + $webview_primary_button_color, + $webview_primary_button_text_color, + $webview_success_message, + ); + } +} diff --git a/tests/ApiKeyTest.php b/tests/ApiKeyTest.php new file mode 100644 index 00000000..b1b8e9bb --- /dev/null +++ b/tests/ApiKeyTest.php @@ -0,0 +1,89 @@ +seed["seam_apikey1_token"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + $this->assertSame( + $this->seed["seed_workspace_1"], + $device->workspace_id, + ); + } + + public function testConstructorReturnsAnAuthorizedClient(): void + { + $seam = new Seam( + api_key: $this->seed["seam_apikey1_token"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testInvalidApiKeyIsRejectedByTheServer(): void + { + $seam = new Seam( + api_key: "seam_invalid_api_key", + endpoint: $this->endpoint, + ); + + $this->expectException(HttpUnauthorizedError::class); + + $seam->devices->list(); + } + + /** + * @dataProvider unusableTokens + */ + public function testApiKeyFormatIsChecked( + string $token, + string $expected_message, + ): void { + $this->expectException(InvalidTokenError::class); + $this->expectExceptionMessage($expected_message); + + new Seam(api_key: $token, endpoint: $this->endpoint); + } + + public static function unusableTokens(): array + { + return [ + "client session token" => [ + "seam_cst_1234", + "A Client Session Token cannot be used as an api_key", + ], + "jwt" => ["ey_some_jwt", "A JWT cannot be used as an api_key"], + "access token" => [ + "seam_at_1234", + "An Access Token cannot be used as an api_key", + ], + "publishable key" => [ + "seam_pk_1234", + "A Publishable Key cannot be used as an api_key", + ], + "unknown format" => [ + "some-random-token", + "Unknown or invalid api_key format", + ], + ]; + } +} diff --git a/tests/ClientTest.php b/tests/ClientTest.php new file mode 100644 index 00000000..739c263b --- /dev/null +++ b/tests/ClientTest.php @@ -0,0 +1,168 @@ +seam(); + + $res = $seam->client->request( + "POST", + "/devices/get", + (object) [ + "device_id" => $this->seed["august_device_1"], + ], + ); + + $this->assertSame( + $this->seed["august_device_1"], + $res->device->device_id, + ); + $this->assertSame( + $this->seed["seed_workspace_1"], + $res->device->workspace_id, + ); + } + + public function testRequestDelegatesToTheClient(): void + { + $seam = $this->seam(); + + $res = $seam->request( + "POST", + "/devices/get", + (object) [ + "device_id" => $this->seed["august_device_1"], + ], + ); + + $this->assertSame( + $this->seed["august_device_1"], + $res->device->device_id, + ); + } + + /** + * A client carries its own endpoint and authorization, so it has to work + * on its own without any authentication option beside it. + */ + public function testFromClientNeedsNoCredentials(): void + { + $authorized = $this->seam(); + + $seam = Seam::from_client($authorized->client->get_client()); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + $this->assertSame( + $this->seed["seed_workspace_1"], + $device->workspace_id, + ); + } + + public function testClientOptionReusesAnotherInstancesClient(): void + { + $seam = new Seam(client: $this->seam()->client->get_client()); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testGuzzleOptionsAreMergedIntoTheClient(): void + { + $seam = $this->seam( + guzzle_options: [ + "headers" => ["Custom-Header" => "Test-Value"], + "timeout" => 30, + ], + ); + + $config = $seam->client->get_client()->getConfig(); + + $this->assertSame(30, $config["timeout"]); + $this->assertSame("Test-Value", $config["headers"]["Custom-Header"]); + // The custom headers must not displace the authorization or the SDK + // headers. + $this->assertSame( + "Bearer " . $this->seed["seam_apikey1_token"], + $config["headers"]["authorization"], + ); + $this->assertSame("seamapi/php", $config["headers"]["seam-sdk-name"]); + } + + public function testGuzzleOptionsStillAuthorizeRequests(): void + { + $seam = $this->seam( + guzzle_options: ["headers" => ["Custom-Header" => "Test-Value"]], + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testTimeoutDefaultsToSixtySeconds(): void + { + $config = $this->seam()->client->get_client()->getConfig(); + + $this->assertSame(SeamHttpClient::DEFAULT_TIMEOUT, $config["timeout"]); + } + + /** + * Every constructor and factory has to agree on the default, otherwise + * the same call waits or does not depending on how the client was built. + */ + public function testWaitForActionAttemptDefaultsToTrueEverywhere(): void + { + $api_key = $this->seed["seam_apikey1_token"]; + + $clients = [ + "constructor" => new Seam( + api_key: $api_key, + endpoint: $this->endpoint, + ), + "from_api_key" => Seam::from_api_key( + $api_key, + endpoint: $this->endpoint, + ), + "from_personal_access_token" => Seam::from_personal_access_token( + $this->seed["seam_at1_token"], + $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ), + "from_client" => Seam::from_client( + $this->seam()->client->get_client(), + ), + ]; + + foreach ($clients as $name => $seam) { + $this->assertTrue( + $seam->defaults["wait_for_action_attempt"], + "{$name} should wait for action attempts by default", + ); + } + } + + public function testWaitForActionAttemptDefaultCanBeDisabled(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + + $this->assertFalse($seam->defaults["wait_for_action_attempt"]); + } + + public function testLtsVersionIsExposed(): void + { + $this->assertSame("1.0.0", Seam::LTS_VERSION); + $this->assertSame("1.0.0", $this->seam()->lts_version()); + } +} diff --git a/tests/EnvTest.php b/tests/EnvTest.php new file mode 100644 index 00000000..59d4886c --- /dev/null +++ b/tests/EnvTest.php @@ -0,0 +1,143 @@ + */ + private array $saved_env = []; + + protected function setUp(): void + { + parent::setUp(); + + foreach (self::VARIABLES as $name) { + $this->saved_env[$name] = getenv($name); + putenv($name); + } + } + + protected function tearDown(): void + { + foreach ($this->saved_env as $name => $value) { + if ($value === false) { + putenv($name); + } else { + putenv("{$name}={$value}"); + } + } + + parent::tearDown(); + } + + public function testReadsTheApiKeyFromTheEnvironment(): void + { + putenv("SEAM_API_KEY=" . $this->seed["seam_apikey1_token"]); + + $seam = new Seam(endpoint: $this->endpoint); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testReadsTheEndpointFromTheEnvironment(): void + { + putenv("SEAM_ENDPOINT=" . $this->endpoint); + + $seam = new Seam(api_key: $this->seed["seam_apikey1_token"]); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testFallsBackToTheDefaultEndpoint(): void + { + $this->assertSame(Options::DEFAULT_ENDPOINT, Options::get_endpoint()); + } + + public function testEndpointOptionWinsOverTheEnvironment(): void + { + putenv("SEAM_ENDPOINT=https://from-the-environment.example.com"); + + $this->assertSame( + "https://from-the-option.example.com", + Options::get_endpoint("https://from-the-option.example.com"), + ); + } + + public function testSeamEndpointWinsOverTheDeprecatedSeamApiUrl(): void + { + putenv("SEAM_ENDPOINT=https://endpoint.example.com"); + putenv("SEAM_API_URL=https://api-url.example.com"); + + // Both the deprecation and the precedence notice are raised. + $endpoint = @Options::get_endpoint(); + + $this->assertSame("https://endpoint.example.com", $endpoint); + } + + public function testDeprecatedSeamApiUrlIsStillHonored(): void + { + putenv("SEAM_API_URL=https://api-url.example.com"); + + $this->assertSame( + "https://api-url.example.com", + @Options::get_endpoint(), + ); + } + + public function testDeprecatedSeamApiUrlWarns(): void + { + putenv("SEAM_API_URL=https://api-url.example.com"); + + set_error_handler( + static fn( + int $severity, + string $message, + ) => throw new \ErrorException($message), + E_USER_WARNING, + ); + + try { + $this->expectException(\ErrorException::class); + $this->expectExceptionMessage("SEAM_API_URL"); + Options::get_endpoint(); + } finally { + restore_error_handler(); + } + } + + public function testFailsWhenNoCredentialsAreAvailable(): void + { + $this->expectException(InvalidOptionsError::class); + $this->expectExceptionMessage("SEAM_API_KEY is not set"); + + new Seam(endpoint: $this->endpoint); + } + + public function testApiKeyEnvironmentVariableIsIgnoredForAPersonalAccessToken(): void + { + putenv("SEAM_API_KEY=" . $this->seed["seam_apikey1_token"]); + + $seam = new Seam( + personal_access_token: $this->seed["seam_at1_token"], + workspace_id: $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } +} diff --git a/tests/HeadersTest.php b/tests/HeadersTest.php new file mode 100644 index 00000000..3f8cf4c7 --- /dev/null +++ b/tests/HeadersTest.php @@ -0,0 +1,141 @@ + ["device_id" => "d1"]]), + ]); + + $seam = Seam::from_api_key( + "seam_apikey_token", + endpoint: "https://example.com", + guzzle_options: $recorder->guzzle_options(), + ); + + $device = $seam->devices->get("d1"); + + $this->assertSame("d1", $device->device_id); + + $request = $recorder->request(); + + $this->assertSame("/devices/get", $request->getUri()->getPath()); + $this->assertEquals((object) ["device_id" => "d1"], $recorder->body()); + + $this->assertSame( + "Bearer seam_apikey_token", + $request->getHeaderLine("authorization"), + ); + $this->assertSame( + "seamapi/php", + $request->getHeaderLine("seam-sdk-name"), + ); + $this->assertSame( + PackageVersion::get(), + $request->getHeaderLine("seam-sdk-version"), + ); + $this->assertSame( + SeamHttpClient::LTS_VERSION, + $request->getHeaderLine("seam-lts-version"), + ); + $this->assertSame( + "seam-php/" . PackageVersion::get(), + $request->getHeaderLine("User-Agent"), + ); + } + + public function testSendsWorkspaceHeaderWithAPersonalAccessToken(): void + { + $recorder = new RecordingClient([ + RecordingClient::json(200, ["device" => ["device_id" => "d1"]]), + ]); + + $seam = Seam::from_personal_access_token( + "seam_at_token", + "workspace-1", + endpoint: "https://example.com", + guzzle_options: $recorder->guzzle_options(), + ); + + $seam->devices->get("d1"); + + $request = $recorder->request(); + + $this->assertSame( + "Bearer seam_at_token", + $request->getHeaderLine("authorization"), + ); + $this->assertSame( + "workspace-1", + $request->getHeaderLine("seam-workspace"), + ); + } + + public function testCustomHeadersAreSentAlongsideTheSdkHeaders(): void + { + $recorder = new RecordingClient([ + RecordingClient::json(200, ["device" => ["device_id" => "d1"]]), + ]); + + $seam = Seam::from_api_key( + "seam_apikey_token", + endpoint: "https://example.com", + guzzle_options: array_merge($recorder->guzzle_options(), [ + "headers" => ["Custom-Header" => "Test-Value"], + ]), + ); + + $seam->devices->get("d1"); + + $request = $recorder->request(); + + $this->assertSame( + "Test-Value", + $request->getHeaderLine("Custom-Header"), + ); + $this->assertSame( + "seamapi/php", + $request->getHeaderLine("seam-sdk-name"), + ); + } + + /** + * The SDK headers identify the SDK, so a caller cannot displace them. + */ + public function testSdkHeadersCannotBeOverridden(): void + { + $recorder = new RecordingClient([ + RecordingClient::json(200, ["device" => ["device_id" => "d1"]]), + ]); + + $seam = Seam::from_api_key( + "seam_apikey_token", + endpoint: "https://example.com", + guzzle_options: array_merge($recorder->guzzle_options(), [ + "headers" => ["seam-sdk-name" => "not-the-sdk"], + ]), + ); + + $seam->devices->get("d1"); + + $this->assertSame( + "seamapi/php", + $recorder->request()->getHeaderLine("seam-sdk-name"), + ); + } +} diff --git a/tests/HttpErrorTest.php b/tests/HttpErrorTest.php index 648eb882..72a029cb 100644 --- a/tests/HttpErrorTest.php +++ b/tests/HttpErrorTest.php @@ -2,29 +2,118 @@ declare(strict_types=1); -use PHPUnit\Framework\TestCase; +namespace Tests; -final class HttpErrorTest extends TestCase +use Seam\Exceptions\HttpApiError; +use Seam\Exceptions\HttpInvalidInputError; +use Seam\Exceptions\HttpUnauthorizedError; +use Seam\Seam; +use Tests\Support\FakeSeamConnectTestCase; + +final class HttpErrorTest extends FakeSeamConnectTestCase { - public function testNonSeamError(): void + public function testThrowsUnauthorizedError(): void { - $seam = new \Seam\SeamClient( - "seam_apikey1_token", - "https://nonexistent.example.com", + $seam = new Seam( + api_key: "seam_invalid_api_key", + endpoint: $this->endpoint, ); try { $seam->devices->list(); - $this->fail("Expected GuzzleHttp ConnectException"); - } catch (\GuzzleHttp\Exception\ConnectException $e) { - $this->assertInstanceOf( - \GuzzleHttp\Exception\ConnectException::class, - $e, + $this->fail("Expected HttpUnauthorizedError"); + } catch (HttpUnauthorizedError $error) { + $this->assertSame(401, $error->getStatusCode()); + $this->assertSame("unauthorized", $error->getErrorCode()); + $this->assertStringStartsWith( + "request", + (string) $error->getRequestId(), + ); + } + } + + public function testThrowsApiErrorOnStandardErrorResponse(): void + { + try { + $this->seam()->devices->get("unknown-device"); + $this->fail("Expected HttpApiError"); + } catch (HttpApiError $error) { + $this->assertSame(404, $error->getStatusCode()); + $this->assertSame("device_not_found", $error->getErrorCode()); + $this->assertStringStartsWith( + "request", + (string) $error->getRequestId(), + ); + } + } + + public function testThrowsInvalidInputErrorWithValidationMessages(): void + { + try { + $this->seam()->client->request( + "POST", + "/devices/list", + (object) [ + "device_ids" => 4242, + ], + ); + $this->fail("Expected HttpInvalidInputError"); + } catch (HttpInvalidInputError $error) { + $this->assertSame(400, $error->getStatusCode()); + $this->assertSame("invalid_input", $error->getErrorCode()); + $this->assertStringStartsWith( + "request", + (string) $error->getRequestId(), + ); + $this->assertSame( + ["Expected array, received number"], + $error->getValidationErrorMessages("device_ids"), ); - $this->assertStringContainsString( - "Could not resolve host", - $e->getMessage(), + } + } + + public function testValidationMessagesAreEmptyForAnUnknownParam(): void + { + try { + $this->seam()->client->request( + "POST", + "/devices/list", + (object) [ + "device_ids" => 4242, + ], + ); + $this->fail("Expected HttpInvalidInputError"); + } catch (HttpInvalidInputError $error) { + $this->assertSame( + [], + $error->getValidationErrorMessages("non_existent_param"), ); } } + + /** + * A workspace outage answers with a 503 that is not a Seam error + * envelope, so it surfaces as the underlying transport error rather than + * a Seam exception, which is what the other Seam SDKs do too. + */ + public function testWorkspaceOutageSurfacesTheTransportError(): void + { + $seam = $this->seam(retries: 0); + + $seam->client->request( + "POST", + "/_fake/simulate_workspace_outage", + (object) [ + "workspace_id" => $this->seed["seed_workspace_1"], + "routes" => ["/devices/list"], + ], + ); + + try { + $seam->devices->list(); + $this->fail("Expected a Guzzle BadResponseException"); + } catch (\GuzzleHttp\Exception\BadResponseException $error) { + $this->assertSame(503, $error->getResponse()->getStatusCode()); + } + } } diff --git a/tests/MalformedResponseTest.php b/tests/MalformedResponseTest.php new file mode 100644 index 00000000..b9df3dd0 --- /dev/null +++ b/tests/MalformedResponseTest.php @@ -0,0 +1,112 @@ +guzzle_options(), + retries: 0, + ); + } + + /** + * @dataProvider nonSeamErrorResponses + */ + public function testNonSeamErrorResponsesSurfaceTheTransportError( + Response $response, + ): void { + $seam = $this->seam(new RecordingClient([$response])); + + try { + $seam->devices->list(); + $this->fail("Expected a Guzzle BadResponseException"); + } catch (HttpApiError $error) { + $this->fail( + "Expected a transport error, got " . + $error::class . + ": " . + $error->getMessage(), + ); + } catch (BadResponseException $error) { + $this->assertSame(500, $error->getResponse()->getStatusCode()); + } + } + + public static function nonSeamErrorResponses(): array + { + return [ + "plain text body" => [ + RecordingClient::raw(500, "Internal Server Error"), + ], + "html body" => [ + RecordingClient::raw( + 500, + "Gateway", + "text/html", + ), + ], + "malformed json" => [ + RecordingClient::raw(500, "{invalid json", "application/json"), + ], + "json without an error object" => [ + RecordingClient::json(500, ["message" => "Some error"]), + ], + "error without a type and message" => [ + RecordingClient::json(500, ["error" => ["code" => 500]]), + ], + "json that is not an object" => [ + RecordingClient::json(500, [1, 2]), + ], + "error that is not an object" => [ + RecordingClient::json(500, ["error" => "boom"]), + ], + "empty body" => [RecordingClient::raw(500, "", "application/json")], + ]; + } + + /** + * A redirect is not a success, so it must not be handed back to the + * caller as though it were a resource. + */ + public function testRedirectIsNotTreatedAsSuccess(): void + { + $recorder = new RecordingClient([ + new Response(302, ["location" => "https://example.com/elsewhere"]), + ]); + + $seam = Seam::from_api_key( + "seam_apikey_token", + endpoint: "https://example.com", + guzzle_options: array_merge($recorder->guzzle_options(), [ + "allow_redirects" => false, + ]), + retries: 0, + ); + + // A 3xx is not a Seam error envelope either, so it surfaces as the + // transport error rather than being handed back as a resource. + $this->expectException(RequestException::class); + + $seam->devices->list(); + } +} diff --git a/tests/PackageVersionTest.php b/tests/PackageVersionTest.php index d45e970d..7b1d7f9a 100644 --- a/tests/PackageVersionTest.php +++ b/tests/PackageVersionTest.php @@ -28,8 +28,8 @@ public function testVersionMatchesPackageJson(): void public function testVersionIsUsedAsTheSdkVersionHeader(): void { - $seam = new \Seam\SeamClient("seam_apikey1_token"); - $headers = $seam->client->getConfig("headers"); + $seam = new \Seam\Seam("seam_apikey1_token"); + $headers = $seam->client->get_client()->getConfig("headers"); $this->assertSame(PackageVersion::get(), $headers["seam-sdk-version"]); } diff --git a/tests/PaginatorTest.php b/tests/PaginatorTest.php new file mode 100644 index 00000000..8442318a --- /dev/null +++ b/tests/PaginatorTest.php @@ -0,0 +1,123 @@ + 2]): Paginator + { + $seam = $this->seam(); + + return $seam->createPaginator( + fn($p) => $seam->connected_accounts->list(...$p), + $params, + ); + } + + public function testCreatePaginatorReturnsAPaginator(): void + { + $this->assertInstanceOf(Paginator::class, $this->paginator()); + } + + public function testFirstPageReturnsTheFirstPage(): void + { + [$accounts, $pagination] = $this->paginator()->firstPage(); + + $this->assertCount(2, $accounts); + $this->assertInstanceOf(Pagination::class, $pagination); + $this->assertTrue($pagination->has_next_page); + $this->assertNotNull($pagination->next_page_cursor); + } + + public function testNextPageReturnsTheNextPage(): void + { + $pages = $this->paginator(); + + [$first, $pagination] = $pages->firstPage(); + [$second] = $pages->nextPage($pagination->next_page_cursor); + + $this->assertNotEmpty($second); + + $first_ids = array_map( + fn($account) => $account->connected_account_id, + $first, + ); + $second_ids = array_map( + fn($account) => $account->connected_account_id, + $second, + ); + + $this->assertEmpty(array_intersect($first_ids, $second_ids)); + } + + public function testNextPageRequiresACursor(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("next_page_cursor"); + + $this->paginator()->nextPage(null); + } + + public function testNextPageRejectsAnEmptyCursor(): void + { + $this->expectException(\InvalidArgumentException::class); + + $this->paginator()->nextPage(""); + } + + public function testLastPageHasNoNextPage(): void + { + $pages = $this->paginator(["limit" => 100]); + + [, $pagination] = $pages->firstPage(); + + $this->assertFalse($pagination->has_next_page); + $this->assertNull($pagination->next_page_cursor); + } + + public function testFlattenToArrayReturnsEveryResource(): void + { + $all = $this->paginator()->flattenToArray(); + $expected = $this->seam()->connected_accounts->list(); + + $this->assertCount(count($expected), $all); + } + + public function testFlattenIteratesEveryResource(): void + { + $ids = []; + + foreach ($this->paginator()->flatten() as $account) { + $ids[] = $account->connected_account_id; + } + + $expected = $this->seam()->connected_accounts->list(); + + $this->assertCount(count($expected), $ids); + $this->assertSame(array_unique($ids), $ids); + } + + /** + * Not every list endpoint returns pagination metadata, and asking for a + * page of one that does not should not fail. + */ + public function testEndpointWithoutPaginationYieldsAnEmptyPagination(): void + { + $seam = $this->seam(); + + $pages = $seam->createPaginator(fn($p) => $seam->workspaces->list()); + + [$workspaces, $pagination] = $pages->firstPage(); + + $this->assertNotEmpty($workspaces); + $this->assertInstanceOf(Pagination::class, $pagination); + $this->assertFalse($pagination->has_next_page); + $this->assertNull($pagination->next_page_cursor); + } +} diff --git a/tests/PersonalAccessTokenTest.php b/tests/PersonalAccessTokenTest.php new file mode 100644 index 00000000..5abd2d81 --- /dev/null +++ b/tests/PersonalAccessTokenTest.php @@ -0,0 +1,140 @@ +seed["seam_at1_token"], + $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + $this->assertSame( + $this->seed["seed_workspace_1"], + $device->workspace_id, + ); + } + + public function testConstructorReturnsAnAuthorizedClient(): void + { + $seam = new Seam( + personal_access_token: $this->seed["seam_at1_token"], + workspace_id: $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testWorkspaceIdIsRequired(): void + { + $this->expectException(InvalidOptionsError::class); + $this->expectExceptionMessage( + "Must pass a workspace_id when using a personal_access_token", + ); + + new Seam( + personal_access_token: $this->seed["seam_at1_token"], + endpoint: $this->endpoint, + ); + } + + public function testApiKeyCannotBeCombinedWithAPersonalAccessToken(): void + { + $this->expectException(InvalidOptionsError::class); + + new Seam( + api_key: $this->seed["seam_apikey1_token"], + personal_access_token: $this->seed["seam_at1_token"], + workspace_id: $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ); + } + + public function testPersonalAccessTokenFormatIsChecked(): void + { + $this->expectException(InvalidTokenError::class); + + new Seam( + personal_access_token: "seam_cst_1234", + workspace_id: $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ); + } + + public function testMultiWorkspaceClientListsWorkspaces(): void + { + $seam = SeamMultiWorkspace::from_personal_access_token( + $this->seed["seam_at1_token"], + endpoint: $this->endpoint, + ); + + $workspaces = $seam->workspaces->list(); + + $workspace_ids = array_map( + fn($workspace) => $workspace->workspace_id, + $workspaces, + ); + + $this->assertContains($this->seed["seed_workspace_1"], $workspace_ids); + } + + public function testMultiWorkspaceConstructorListsWorkspaces(): void + { + $seam = new SeamMultiWorkspace( + personal_access_token: $this->seed["seam_at1_token"], + endpoint: $this->endpoint, + ); + + $this->assertNotEmpty($seam->workspaces->list()); + } + + public function testMultiWorkspaceClientCreatesAWorkspace(): void + { + $seam = SeamMultiWorkspace::from_personal_access_token( + $this->seed["seam_at1_token"], + endpoint: $this->endpoint, + ); + + $workspace = $seam->workspaces->create( + name: "Test Workspace", + connect_partner_name: "Test Partner", + is_sandbox: true, + ); + + $this->assertSame("Test Workspace", $workspace->name); + } + + public function testMultiWorkspaceClientRequiresAToken(): void + { + $this->expectException(InvalidOptionsError::class); + + new SeamMultiWorkspace(endpoint: $this->endpoint); + } + + public function testMultiWorkspaceClientChecksTheTokenFormat(): void + { + $this->expectException(InvalidTokenError::class); + + SeamMultiWorkspace::from_personal_access_token( + $this->seed["seam_apikey1_token"], + endpoint: $this->endpoint, + ); + } +} diff --git a/tests/RetryTest.php b/tests/RetryTest.php new file mode 100644 index 00000000..322697e7 --- /dev/null +++ b/tests/RetryTest.php @@ -0,0 +1,139 @@ + ["type" => "service_unavailable", "message" => "Down"], + ]); + } + + private static function devices(): \GuzzleHttp\Psr7\Response + { + return RecordingClient::json(200, ["devices" => []]); + } + + private function seam(RecordingClient $recorder, ?int $retries = null): Seam + { + return new Seam( + api_key: self::API_KEY, + endpoint: "https://example.com", + guzzle_options: $recorder->guzzle_options(), + retries: $retries, + ); + } + + /** + * Every Seam endpoint is a POST. Retrying one the server may already have + * processed could duplicate a write, so a status code never triggers a + * retry. The Ruby SDK asserts the same thing. + */ + public function testDoesNotRetryPostOnServiceUnavailable(): void + { + $recorder = RecordingClient::repeating(self::service_unavailable()); + + try { + $this->seam($recorder)->devices->list(); + $this->fail("Expected the 503 to surface"); + } catch (\Throwable) { + // The error mapping is covered in HttpErrorTest. + } + + $this->assertSame(1, $recorder->attempt_count()); + } + + /** + * A connection failure never reached the server, so retrying it cannot + * duplicate anything. + */ + public function testRetriesPostOnConnectionFailure(): void + { + $connect_error = new ConnectException( + "Could not resolve host", + new Request("POST", "/devices/list"), + ); + + $recorder = new RecordingClient([ + $connect_error, + $connect_error, + self::devices(), + ]); + + $devices = $this->seam($recorder)->devices->list(); + + $this->assertSame([], $devices); + $this->assertSame(3, $recorder->attempt_count()); + } + + public function testStopsRetryingOnceRetriesAreExhausted(): void + { + $connect_error = new ConnectException( + "Could not resolve host", + new Request("POST", "/devices/list"), + ); + + $recorder = RecordingClient::repeating_throwable($connect_error); + + $this->expectException(ConnectException::class); + + try { + $this->seam($recorder, retries: 1)->devices->list(); + } finally { + $this->assertSame(2, $recorder->attempt_count()); + } + } + + public function testDoesNotRetryWhenRetriesAreDisabled(): void + { + $connect_error = new ConnectException( + "Could not resolve host", + new Request("POST", "/devices/list"), + ); + + $recorder = RecordingClient::repeating_throwable($connect_error); + + try { + $this->seam($recorder, retries: 0)->devices->list(); + $this->fail("Expected the connection failure to surface"); + } catch (ConnectException) { + // Expected. + } + + $this->assertSame(1, $recorder->attempt_count()); + } + + /** + * The SDK itself only issues POSTs, but a caller reaching for the client + * directly with an idempotent method does get status based retries. + */ + public function testRetriesIdempotentRequestsOnServiceUnavailable(): void + { + $recorder = new RecordingClient([ + self::service_unavailable(), + self::service_unavailable(), + self::devices(), + ]); + + $res = $this->seam($recorder)->client->request("GET", "/devices/list"); + + $this->assertSame([], $res->devices); + $this->assertSame(3, $recorder->attempt_count()); + } +} diff --git a/tests/SeamWebhookTest.php b/tests/SeamWebhookTest.php new file mode 100644 index 00000000..be871513 --- /dev/null +++ b/tests/SeamWebhookTest.php @@ -0,0 +1,103 @@ + "8d7e0b26-5e6c-4a1f-9b3d-1b0f0e5a9c11", + "event_type" => "device.connected", + "workspace_id" => "398d80b7-3f96-47c2-b85a-6f8ba21d07be", + "device_id" => "054765c8-a2fc-4599-b486-14c19f462c45", + "created_at" => "2024-01-01T00:00:00.000Z", + "occurred_at" => "2024-01-01T00:00:00.000Z", + ]); + } + + /** + * @return array + */ + private function signed_headers(string $payload): array + { + $id = "msg_test"; + $timestamp = (string) time(); + + $signature = (new Webhook(self::SECRET))->sign( + $id, + $timestamp, + $payload, + ); + + return [ + "svix-id" => $id, + "svix-timestamp" => $timestamp, + "svix-signature" => $signature, + ]; + } + + public function testVerifyReturnsTheEvent(): void + { + $payload = $this->payload(); + + $event = (new SeamWebhook(self::SECRET))->verify( + $payload, + $this->signed_headers($payload), + ); + + $this->assertSame("device.connected", $event->event_type); + $this->assertSame( + "8d7e0b26-5e6c-4a1f-9b3d-1b0f0e5a9c11", + $event->event_id, + ); + } + + public function testVerifyAcceptsHeadersInAnyCase(): void + { + $payload = $this->payload(); + + $headers = []; + foreach ($this->signed_headers($payload) as $name => $value) { + $headers[strtoupper($name)] = $value; + } + + $event = (new SeamWebhook(self::SECRET))->verify($payload, $headers); + + $this->assertSame("device.connected", $event->event_type); + } + + public function testVerifyRejectsATamperedPayload(): void + { + $payload = $this->payload(); + $headers = $this->signed_headers($payload); + + $this->expectException(WebhookVerificationException::class); + + (new SeamWebhook(self::SECRET))->verify( + str_replace("device.connected", "device.disconnected", $payload), + $headers, + ); + } + + public function testVerifyRejectsTheWrongSecret(): void + { + $payload = $this->payload(); + $headers = $this->signed_headers($payload); + + $this->expectException(WebhookVerificationException::class); + + (new SeamWebhook( + "whsec_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + ))->verify($payload, $headers); + } +} diff --git a/tests/SerializationTest.php b/tests/SerializationTest.php new file mode 100644 index 00000000..83310710 --- /dev/null +++ b/tests/SerializationTest.php @@ -0,0 +1,55 @@ +seam()->devices->list(); + + $this->assertNotEmpty($devices); + } + + public function testNullArrayParameterIsNotSent(): void + { + $devices = $this->seam()->devices->list(device_ids: null); + + $this->assertCount(count($this->seam()->devices->list()), $devices); + } + + public function testEmptyArrayParameterIsSent(): void + { + $devices = $this->seam()->devices->list(device_ids: []); + + $this->assertCount(0, $devices); + } + + public function testPopulatedArrayParameterFiltersTheResults(): void + { + $device_ids = [ + $this->seed["august_device_1"], + $this->seed["ecobee_device_1"], + ]; + + $devices = $this->seam()->devices->list(device_ids: $device_ids); + + $this->assertCount(2, $devices); + + $returned_ids = array_map(fn($device) => $device->device_id, $devices); + + sort($returned_ids); + sort($device_ids); + + $this->assertSame($device_ids, $returned_ids); + } +} diff --git a/tests/Support/FakeSeamConnect.php b/tests/Support/FakeSeamConnect.php new file mode 100644 index 00000000..93976532 --- /dev/null +++ b/tests/Support/FakeSeamConnect.php @@ -0,0 +1,171 @@ + */ + private array $pipes = []; + + public static function start(): self + { + $fake = new self(); + $fake->run(); + + return $fake; + } + + public function endpoint(): string + { + return $this->endpoint; + } + + /** + * The ids and tokens of the seeded records. + */ + public function seed(): array + { + return $this->seed; + } + + private function run(): void + { + $binary = dirname(__DIR__, 2) . "/node_modules/.bin/fake-seam-connect"; + + if (!is_executable($binary)) { + throw new \RuntimeException( + "Could not find {$binary}, run npm install before the tests.", + ); + } + + $port = self::unused_port(); + $this->endpoint = "http://127.0.0.1:{$port}"; + + // The binary is spawned directly rather than through npm so the + // process handle is the server itself and stopping it does not leave + // an orphan behind. PORT goes to the child only, leaving the parent + // environment alone for the tests that read it. + $this->process = proc_open( + [$binary, "--seed"], + [ + 0 => ["file", "/dev/null", "r"], + 1 => ["file", "/dev/null", "w"], + 2 => ["file", "/dev/null", "w"], + ], + $this->pipes, + dirname(__DIR__, 2), + ["PORT" => (string) $port] + getenv(), + ); + + if (!is_resource($this->process)) { + throw new \RuntimeException("Could not start Fake Seam Connect."); + } + + $this->wait_for_health(); + $this->seed = $this->fetch_seed(); + } + + public function stop(): void + { + if (!is_resource($this->process)) { + return; + } + + proc_terminate($this->process, SIGTERM); + + $deadline = microtime(true) + self::SHUTDOWN_TIMEOUT; + while (microtime(true) < $deadline) { + if (!proc_get_status($this->process)["running"]) { + break; + } + usleep(self::POLL_INTERVAL); + } + + if (proc_get_status($this->process)["running"]) { + proc_terminate($this->process, SIGKILL); + } + + proc_close($this->process); + $this->process = null; + } + + private function wait_for_health(): void + { + $deadline = microtime(true) + self::STARTUP_TIMEOUT; + + while (microtime(true) < $deadline) { + if (!proc_get_status($this->process)["running"]) { + throw new \RuntimeException( + "Fake Seam Connect exited before becoming healthy.", + ); + } + + if ($this->get("/health") !== null) { + return; + } + + usleep(self::POLL_INTERVAL); + } + + throw new \RuntimeException( + "Fake Seam Connect did not become healthy within " . + self::STARTUP_TIMEOUT . + "s.", + ); + } + + private function fetch_seed(): array + { + $body = $this->get("/_fake/default_seed"); + + if ($body === null) { + throw new \RuntimeException( + "Could not read the seed from Fake Seam Connect.", + ); + } + + return json_decode($body, true); + } + + private function get(string $path): ?string + { + $context = stream_context_create([ + "http" => ["timeout" => 5, "ignore_errors" => true], + ]); + + $body = @file_get_contents($this->endpoint . $path, false, $context); + + return $body === false ? null : $body; + } + + private static function unused_port(): int + { + $socket = stream_socket_server("tcp://127.0.0.1:0", $errno, $errstr); + + if ($socket === false) { + throw new \RuntimeException( + "Could not find an unused port: {$errstr}", + ); + } + + $name = stream_socket_get_name($socket, false); + fclose($socket); + + return (int) substr($name, strrpos($name, ":") + 1); + } +} diff --git a/tests/Support/FakeSeamConnectTestCase.php b/tests/Support/FakeSeamConnectTestCase.php new file mode 100644 index 00000000..81f91400 --- /dev/null +++ b/tests/Support/FakeSeamConnectTestCase.php @@ -0,0 +1,45 @@ +fake = FakeSeamConnect::start(); + $this->endpoint = $this->fake->endpoint(); + $this->seed = $this->fake->seed(); + } + + protected function tearDown(): void + { + $this->fake->stop(); + } + + /** + * A client authorized against the fake with the seeded API key. + */ + protected function seam( + bool|array|null $wait_for_action_attempt = null, + array $guzzle_options = [], + ?int $retries = null, + ): Seam { + return new Seam( + api_key: $this->seed["seam_apikey1_token"], + endpoint: $this->endpoint, + wait_for_action_attempt: $wait_for_action_attempt, + guzzle_options: $guzzle_options, + retries: $retries, + ); + } +} diff --git a/tests/Support/RecordingClient.php b/tests/Support/RecordingClient.php new file mode 100644 index 00000000..d2494332 --- /dev/null +++ b/tests/Support/RecordingClient.php @@ -0,0 +1,107 @@ + */ + public array $transactions = []; + + private MockHandler $mock; + + /** + * @param array $responses Served in order; the last one repeats. + */ + public function __construct(private array $responses) + { + $this->mock = new MockHandler($responses); + } + + /** + * Builds the Guzzle options to hand to a Seam client so its requests land + * here instead of on the network. + * + * @return array + */ + public function guzzle_options(): array + { + $stack = HandlerStack::create($this->mock); + $stack->push(Middleware::history($this->transactions)); + + return ["handler" => $stack]; + } + + /** + * How many logical calls the SDK made. Retries are invisible here because + * the SDK pushes its retry middleware inside this recorder; use + * attempt_count() to count those. + */ + public function request_count(): int + { + return count($this->transactions); + } + + /** + * How many requests actually reached the handler, retries included. + */ + public function attempt_count(): int + { + return count($this->responses) - $this->mock->count(); + } + + public function request(int $index = 0): RequestInterface + { + return $this->transactions[$index]["request"]; + } + + public function body(int $index = 0): mixed + { + return json_decode((string) $this->request($index)->getBody()); + } + + /** + * A response whose body repeats for every request, so retries keep + * failing the same way. + */ + public static function repeating(Response $response, int $times = 20): self + { + return new self(array_fill(0, $times, $response)); + } + + public static function repeating_throwable( + \Throwable $error, + int $times = 20, + ): self { + return new self(array_fill(0, $times, $error)); + } + + public static function json(int $status, mixed $body): Response + { + return new Response( + $status, + ["content-type" => "application/json"], + json_encode($body), + ); + } + + public static function raw( + int $status, + string $body, + string $content_type = "text/plain", + ): Response { + return new Response($status, ["content-type" => $content_type], $body); + } +} diff --git a/tests/WaitForActionAttemptTest.php b/tests/WaitForActionAttemptTest.php new file mode 100644 index 00000000..d6bbe194 --- /dev/null +++ b/tests/WaitForActionAttemptTest.php @@ -0,0 +1,283 @@ +locks->unlock_door( + $this->seed["august_device_1"], + ); + + $this->assertSame("pending", $action_attempt->status); + + $this->set_status($seam, $action_attempt, "pending"); + + return $action_attempt; + } + + private function set_status( + Seam $seam, + ActionAttempt $action_attempt, + string $status, + ?array $error = null, + ): void { + $seam->client->request( + "POST", + "/_fake/update_action_attempt", + (object) array_filter([ + "action_attempt_id" => $action_attempt->action_attempt_id, + "status" => $status, + "error" => $error, + ]), + ); + } + + public function testWaitsByDefault(): void + { + $action_attempt = $this->seam()->locks->unlock_door( + $this->seed["august_device_1"], + ); + + $this->assertSame("success", $action_attempt->status); + } + + public function testClientDefaultCanDisableWaiting(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + + $action_attempt = $seam->locks->unlock_door( + $this->seed["august_device_1"], + ); + + $this->assertSame("pending", $action_attempt->status); + } + + /** + * The options form of the client default has to wait just like `true` + * does; treating it as "no waiting" would hand back a pending attempt + * with no indication anything was skipped. + */ + public function testClientDefaultCanBeAnOptionsArray(): void + { + $seam = $this->seam( + wait_for_action_attempt: [ + "timeout" => 5.0, + "polling_interval" => 0.05, + ], + ); + + $action_attempt = $seam->locks->unlock_door( + $this->seed["august_device_1"], + ); + + $this->assertSame("success", $action_attempt->status); + } + + public function testPerCallOptionCanDisableWaiting(): void + { + $action_attempt = $this->seam()->locks->unlock_door( + $this->seed["august_device_1"], + wait_for_action_attempt: false, + ); + + $this->assertSame("pending", $action_attempt->status); + } + + public function testPerCallOptionCanEnableWaiting(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + + $action_attempt = $seam->locks->unlock_door( + $this->seed["august_device_1"], + wait_for_action_attempt: true, + ); + + $this->assertSame("success", $action_attempt->status); + } + + public function testReturnsAnAlreadySuccessfulActionAttempt(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + + $action_attempt = $seam->locks->unlock_door( + $this->seed["august_device_1"], + ); + $this->set_status($seam, $action_attempt, "success"); + + $resolved = $seam->action_attempts->get( + $action_attempt->action_attempt_id, + wait_for_action_attempt: true, + ); + + $this->assertSame("success", $resolved->status); + $this->assertSame( + $action_attempt->action_attempt_id, + $resolved->action_attempt_id, + ); + } + + /** + * Proves the resolver really re-reads the action attempt: it starts out + * pending and is moved to success by something outside this process, the + * way the JavaScript and Ruby suites do it. + */ + public function testWaitsForAnActionAttemptResolvedOutOfBand(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + $action_attempt = $this->pending_action_attempt($seam); + + $resolver = $this->resolve_after( + $action_attempt->action_attempt_id, + 0.5, + ); + + try { + $resolved = $seam->action_attempts->get( + $action_attempt->action_attempt_id, + wait_for_action_attempt: [ + "timeout" => 15.0, + "polling_interval" => 0.1, + ], + ); + + $this->assertSame("success", $resolved->status); + } finally { + proc_close($resolver); + } + } + + /** + * @return resource + */ + private function resolve_after(string $action_attempt_id, float $delay) + { + $payload = json_encode([ + "action_attempt_id" => $action_attempt_id, + "status" => "success", + ]); + + $script = sprintf( + "usleep(%d); file_get_contents(%s, false, stream_context_create(%s));", + (int) ($delay * 1000000.0), + var_export($this->endpoint . "/_fake/update_action_attempt", true), + var_export( + [ + "http" => [ + "method" => "POST", + "header" => "Content-Type: application/json", + "content" => $payload, + "ignore_errors" => true, + ], + ], + true, + ), + ); + + $process = proc_open( + [PHP_BINARY, "-r", $script], + [ + 1 => ["file", "/dev/null", "w"], + 2 => ["file", "/dev/null", "w"], + ], + $pipes, + ); + + if (!is_resource($process)) { + $this->fail("Could not start the out of band resolver"); + } + + return $process; + } + + public function testThrowsWhenTheActionAttemptFails(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + + $action_attempt = $seam->locks->unlock_door( + $this->seed["august_device_1"], + ); + $this->set_status($seam, $action_attempt, "error", [ + "type" => "foo", + "message" => "Failed", + ]); + + try { + $seam->action_attempts->get( + $action_attempt->action_attempt_id, + wait_for_action_attempt: true, + ); + $this->fail("Expected ActionAttemptFailedError"); + } catch (ActionAttemptFailedError $error) { + $this->assertSame("Failed", $error->getMessage()); + $this->assertSame("foo", $error->getErrorCode()); + $this->assertSame("error", $error->getActionAttempt()->status); + $this->assertSame( + $action_attempt->action_attempt_id, + $error->getActionAttempt()->action_attempt_id, + ); + $this->assertInstanceOf(ActionAttemptError::class, $error); + } + } + + public function testTimesOutWhileTheActionAttemptIsPending(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + $action_attempt = $this->pending_action_attempt($seam); + + try { + $seam->action_attempts->get( + $action_attempt->action_attempt_id, + wait_for_action_attempt: [ + "timeout" => 0.2, + "polling_interval" => 5.0, + ], + ); + $this->fail("Expected ActionAttemptTimeoutError"); + } catch (ActionAttemptTimeoutError $error) { + $this->assertSame( + $action_attempt->action_attempt_id, + $error->getActionAttempt()->action_attempt_id, + ); + $this->assertStringContainsString( + "Timed out waiting for action attempt", + $error->getMessage(), + ); + } + } + + /** + * Resolving fetches the action attempt through the HTTP client rather + * than the route client, so enabling the option on the route that reads + * action attempts cannot recurse. + */ + public function testActionAttemptsGetDoesNotRecurse(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + + $action_attempt = $seam->locks->unlock_door( + $this->seed["august_device_1"], + ); + $this->set_status($seam, $action_attempt, "success"); + + $resolved = $seam->action_attempts->get( + $action_attempt->action_attempt_id, + wait_for_action_attempt: [ + "timeout" => 1.0, + "polling_interval" => 0.05, + ], + ); + + $this->assertSame("success", $resolved->status); + } +} From e3c87edbc018aa14901c9a3c4c467f47691c1e87 Mon Sep 17 00:00:00 2001 From: Evan Date: Tue, 11 Aug 2026 17:06:12 +0000 Subject: [PATCH 02/35] refactor: keep the exception classes in the Seam namespace The core public API is small enough to read at a glance, so nesting part of it under Seam\Exceptions bought organization it does not need. Keeping the classes where 3.x had them also means existing catch blocks keep working. Sub-namespacing errors is the more common PHP convention, but the Python and JavaScript SDKs both export theirs at the package root, so this is closer to them as well. The new SeamException marker interface, InvalidOptionsError, and InvalidTokenError are all that changes for a caller upgrading from 3.x. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- README.md | 21 ++++++++++--------- src/{Exceptions => }/ActionAttemptError.php | 2 +- .../ActionAttemptFailedError.php | 2 +- .../ActionAttemptTimeoutError.php | 2 +- src/Auth.php | 3 --- src/Http/ResolveActionAttempt.php | 4 ++-- src/Http/SeamHttpClient.php | 6 +++--- src/{Exceptions => }/HttpApiError.php | 2 +- .../HttpInvalidInputError.php | 2 +- .../HttpUnauthorizedError.php | 2 +- src/{Exceptions => }/InvalidOptionsError.php | 2 +- src/{Exceptions => }/InvalidTokenError.php | 2 +- src/Options.php | 2 -- src/{Exceptions => }/SeamException.php | 2 +- src/SeamMultiWorkspace.php | 2 +- tests/ApiKeyTest.php | 4 ++-- tests/EnvTest.php | 2 +- tests/HttpErrorTest.php | 6 +++--- tests/MalformedResponseTest.php | 2 +- tests/PersonalAccessTokenTest.php | 4 ++-- tests/WaitForActionAttemptTest.php | 6 +++--- 21 files changed, 38 insertions(+), 42 deletions(-) rename src/{Exceptions => }/ActionAttemptError.php (95%) rename src/{Exceptions => }/ActionAttemptFailedError.php (96%) rename src/{Exceptions => }/ActionAttemptTimeoutError.php (94%) rename src/{Exceptions => }/HttpApiError.php (97%) rename src/{Exceptions => }/HttpInvalidInputError.php (96%) rename src/{Exceptions => }/HttpUnauthorizedError.php (93%) rename src/{Exceptions => }/InvalidOptionsError.php (93%) rename src/{Exceptions => }/InvalidTokenError.php (92%) rename src/{Exceptions => }/SeamException.php (87%) diff --git a/README.md b/README.md index 35ef6120..9ba25b03 100644 --- a/README.md +++ b/README.md @@ -108,18 +108,18 @@ By default the SDK waits for the action attempt to finish: - It polls up to a timeout at a polling interval. - It returns a fresh copy of the successful action attempt. -- It throws `Seam\Exceptions\ActionAttemptFailedError` if the action failed. -- It throws `Seam\Exceptions\ActionAttemptTimeoutError` if the timeout +- It throws `Seam\ActionAttemptFailedError` if the action failed. +- It throws `Seam\ActionAttemptTimeoutError` if the timeout elapses first. -Both errors extend `Seam\Exceptions\ActionAttemptError` and expose the action +Both errors extend `Seam\ActionAttemptError` and expose the action attempt with `getActionAttempt()`. [action attempt]: https://docs.seam.co/latest/core-concepts/action-attempts ```php -use Seam\Exceptions\ActionAttemptFailedError; -use Seam\Exceptions\ActionAttemptTimeoutError; +use Seam\ActionAttemptFailedError; +use Seam\ActionAttemptTimeoutError; try { $seam->locks->unlock_door($device_id); @@ -355,7 +355,7 @@ $seam = Seam\Seam::from_client($client); #### Errors -Every exception the SDK raises implements `Seam\Exceptions\SeamException`. +Every exception the SDK raises implements `Seam\SeamException`. | Error | Raised when | | --------------------------- | -------------------------------------------------- | @@ -373,8 +373,8 @@ BadResponseException` instead. Transport failures surface as the corresponding Guzzle exception. ```php -use Seam\Exceptions\HttpApiError; -use Seam\Exceptions\HttpInvalidInputError; +use Seam\HttpApiError; +use Seam\HttpInvalidInputError; try { $seam->devices->get($device_id); @@ -397,8 +397,9 @@ Version 4 brings this SDK in line with the Seam SDKs for other languages. - The constructor takes named options. `$endpoint` is no longer the second positional argument, and `$throw_http_errors` is gone; API errors always raise a Seam exception. -- Exceptions moved to the `Seam\Exceptions` namespace, e.g. - `Seam\HttpApiError` is now `Seam\Exceptions\HttpApiError`. +- The exception classes keep their `Seam\` namespace. They now all implement + a shared `Seam\SeamException` interface, and `Seam\InvalidOptionsError` and + `Seam\InvalidTokenError` are new. - `$seam->action_attempts->poll_until_ready()` was removed. Use `wait_for_action_attempt`, which now also accepts a `timeout` and `polling_interval`. The defaults changed from 20s/0.4s to 10s/1s. diff --git a/src/Exceptions/ActionAttemptError.php b/src/ActionAttemptError.php similarity index 95% rename from src/Exceptions/ActionAttemptError.php rename to src/ActionAttemptError.php index 7368770b..776ee6fe 100644 --- a/src/Exceptions/ActionAttemptError.php +++ b/src/ActionAttemptError.php @@ -1,6 +1,6 @@ client = SeamHttpClient::from_client($client); } else { if ($personal_access_token === null) { - throw new Exceptions\InvalidOptionsError( + throw new InvalidOptionsError( "Must specify a personal_access_token", ); } diff --git a/tests/ApiKeyTest.php b/tests/ApiKeyTest.php index b1b8e9bb..838fa24a 100644 --- a/tests/ApiKeyTest.php +++ b/tests/ApiKeyTest.php @@ -4,8 +4,8 @@ namespace Tests; -use Seam\Exceptions\HttpUnauthorizedError; -use Seam\Exceptions\InvalidTokenError; +use Seam\HttpUnauthorizedError; +use Seam\InvalidTokenError; use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; diff --git a/tests/EnvTest.php b/tests/EnvTest.php index 59d4886c..3c0f92a7 100644 --- a/tests/EnvTest.php +++ b/tests/EnvTest.php @@ -4,7 +4,7 @@ namespace Tests; -use Seam\Exceptions\InvalidOptionsError; +use Seam\InvalidOptionsError; use Seam\Options; use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; diff --git a/tests/HttpErrorTest.php b/tests/HttpErrorTest.php index 72a029cb..d445defc 100644 --- a/tests/HttpErrorTest.php +++ b/tests/HttpErrorTest.php @@ -4,9 +4,9 @@ namespace Tests; -use Seam\Exceptions\HttpApiError; -use Seam\Exceptions\HttpInvalidInputError; -use Seam\Exceptions\HttpUnauthorizedError; +use Seam\HttpApiError; +use Seam\HttpInvalidInputError; +use Seam\HttpUnauthorizedError; use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; diff --git a/tests/MalformedResponseTest.php b/tests/MalformedResponseTest.php index b9df3dd0..e132c10a 100644 --- a/tests/MalformedResponseTest.php +++ b/tests/MalformedResponseTest.php @@ -8,7 +8,7 @@ use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Psr7\Response; use PHPUnit\Framework\TestCase; -use Seam\Exceptions\HttpApiError; +use Seam\HttpApiError; use Seam\Seam; use Tests\Support\RecordingClient; diff --git a/tests/PersonalAccessTokenTest.php b/tests/PersonalAccessTokenTest.php index 5abd2d81..bce47735 100644 --- a/tests/PersonalAccessTokenTest.php +++ b/tests/PersonalAccessTokenTest.php @@ -4,8 +4,8 @@ namespace Tests; -use Seam\Exceptions\InvalidOptionsError; -use Seam\Exceptions\InvalidTokenError; +use Seam\InvalidOptionsError; +use Seam\InvalidTokenError; use Seam\Seam; use Seam\SeamMultiWorkspace; use Tests\Support\FakeSeamConnectTestCase; diff --git a/tests/WaitForActionAttemptTest.php b/tests/WaitForActionAttemptTest.php index d6bbe194..32dae081 100644 --- a/tests/WaitForActionAttemptTest.php +++ b/tests/WaitForActionAttemptTest.php @@ -4,9 +4,9 @@ namespace Tests; -use Seam\Exceptions\ActionAttemptError; -use Seam\Exceptions\ActionAttemptFailedError; -use Seam\Exceptions\ActionAttemptTimeoutError; +use Seam\ActionAttemptError; +use Seam\ActionAttemptFailedError; +use Seam\ActionAttemptTimeoutError; use Seam\Resources\ActionAttempt; use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; From 1236f7bf746a9fc49860449824c9ac25ac668be7 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 18:08:31 +0000 Subject: [PATCH 03/35] fix: namespace the generated nested resource classes A nested class was named after the root resource plus the property, but the base name was never replaced as the recursion descended, so every shape below the first level competed for one name and addClass silently kept whichever was generated first. device.properties.battery and device.properties.accessory_keypad.battery both resolved to DeviceBattery. The keypad won, so the device battery was generated with only level and $device->properties->battery->status did not exist. The same collapse hit the climate preset metadata, which took the shape of properties.ecobee_metadata and lost climate_ref, is_optimized and owner, and the phone_session credential and entrance metadata pairs. The flat map is now a recursive tree. A nested class is named after its property alone and declared in the namespace of the class that owns it, so the two batteries are Device\Properties\Battery and Device\Properties\AccessoryKeypad\Battery. Properties reference their nested classes relatively, letting PHP resolve them from the owning namespace. This also keeps Seam\Resources free of the hundreds of names that existed only to type a property. Where the old code silently overwrote, codegen now throws: on two siblings producing the same class name, on a name PHP reserves as a type, and on nesting deeper than 16 levels, which means a cyclic schema rather than a real shape. Merging a discriminated union moves to codegen/lib/merge-properties.ts, which unions by name recursively rather than taking the first occurrence, so a merged class keeps every variant's fields. A merged property keeps its description only when every variant that documents it agrees, because each variant documents the property for its own case and that text is not necessarily true of the class the variants collapse into. Deprecation is now deprecate-if-any, since first wins could undeprecate a field depending on blueprint ordering. This drops the description on is_device_error, which claimed the error is not a device error on every variant including the ones where it is, while is_bridge_error keeps the text all variants share. Resource classes are emitted as one braced namespace block per namespace in a single file per resource, which works because src/Resources is autoloaded by classmap rather than PSR-4. The resource and property docblock helpers are indented one level deeper to match. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- codegen/layouts/resource.hbs | 33 +- codegen/lib/handlebars-helpers.ts | 6 +- codegen/lib/layouts/resource.ts | 42 +- codegen/lib/merge-properties.ts | 148 + codegen/lib/resource-model.ts | 331 +- src/Resources/AccessCode.php | 964 +-- src/Resources/AccessGrant.php | 804 ++- src/Resources/AccessMethod.php | 538 +- src/Resources/AcsAccessGroup.php | 645 +- src/Resources/AcsCredential.php | 722 +- src/Resources/AcsEncoder.php | 190 +- src/Resources/AcsEntrance.php | 1386 ++-- src/Resources/AcsSystem.php | 504 +- src/Resources/AcsUser.php | 870 ++- src/Resources/ActionAttempt.php | 1890 ++--- src/Resources/Batch.php | 425 +- src/Resources/ClientSession.php | 170 +- src/Resources/ConnectWebview.php | 245 +- src/Resources/ConnectedAccount.php | 677 +- src/Resources/CustomerPortal.php | 88 +- src/Resources/Device.php | 8277 +++++++++++----------- src/Resources/DeviceProvider.php | 284 +- src/Resources/Event.php | 1710 +++-- src/Resources/InstantKey.php | 187 +- src/Resources/NoiseThreshold.php | 101 +- src/Resources/Phone.php | 482 +- src/Resources/Space.php | 264 +- src/Resources/ThermostatDailyProgram.php | 150 +- src/Resources/ThermostatSchedule.php | 206 +- src/Resources/UnmanagedAccessCode.php | 667 +- src/Resources/UnmanagedAccessGrant.php | 782 +- src/Resources/UnmanagedAccessMethod.php | 521 +- src/Resources/UnmanagedDevice.php | 1002 +-- src/Resources/UnmanagedUserIdentity.php | 285 +- src/Resources/UserIdentity.php | 288 +- src/Resources/Webhook.php | 70 +- src/Resources/Workspace.php | 213 +- tests/ResourceTest.php | 161 + 38 files changed, 13669 insertions(+), 12659 deletions(-) create mode 100644 codegen/lib/merge-properties.ts create mode 100644 tests/ResourceTest.php diff --git a/codegen/layouts/resource.hbs b/codegen/layouts/resource.hbs index a7d7f4ed..271e4355 100644 --- a/codegen/layouts/resource.hbs +++ b/codegen/layouts/resource.hbs @@ -1,34 +1,37 @@ } +// Resource classes and their properties are emitted inside a braced namespace +// block, so both sit one level deeper than a docblock at file scope would. export const resourcePhpDoc = (context: DeprecatedPhpDocContext): string => - createPhpDoc(context.description, deprecatedTag(context)) + createPhpDoc(context.description, deprecatedTag(context), ' ') export const hasPhpDoc = (context: DeprecatedPhpDocContext): boolean => context.description.trim() !== '' || context.isDeprecated export const propertyPhpDoc = (context: DeprecatedPhpDocContext): string => - createPhpDoc(context.description, deprecatedTag(context), ' ') + createPhpDoc(context.description, deprecatedTag(context), ' ') export const methodPhpDoc = (context: MethodPhpDocContext): string => createPhpDoc( diff --git a/codegen/lib/layouts/resource.ts b/codegen/lib/layouts/resource.ts index d0400061..6ebd0ee6 100644 --- a/codegen/lib/layouts/resource.ts +++ b/codegen/lib/layouts/resource.ts @@ -1,7 +1,7 @@ // Builds the template context for resource files (src/Resources/{Name}.php): -// the resource class followed by the local classes for its object properties. -// Each class contributes its from_json body lines and constructor parameter -// lines. +// the resource class and the nested classes for its object properties, grouped +// into one braced namespace block per namespace. Each class contributes its +// from_json body lines and constructor parameter lines. // // The blueprint does not track which resource properties are required, so // every property is optional: from_json falls back to null for missing values @@ -29,10 +29,15 @@ export interface ConstructorParamLayoutContext { deprecationMessage: string } -export interface ResourceLayoutContext { +export interface NamespaceLayoutContext { + namespace: string classes: ClassLayoutContext[] } +export interface ResourceLayoutContext { + namespaces: NamespaceLayoutContext[] +} + const generateFromJsonProp = (property: ResourceClassProperty): string => { const { name } = property @@ -96,8 +101,27 @@ const getClassLayoutContext = ( export const setResourceLayoutContext = ( resource: ResourceSchema, -): ResourceLayoutContext => ({ - classes: [resource.resourceClass, ...resource.localClasses].map( - getClassLayoutContext, - ), -}) +): ResourceLayoutContext => { + // First appearance order, so the resource class leads the file and every + // owning namespace precedes the namespaces nested inside it. + const namespaces = new Map() + + for (const schema of resource.classes) { + const classes = namespaces.get(schema.namespace) + const context = getClassLayoutContext(schema) + + if (classes == null) { + namespaces.set(schema.namespace, [context]) + continue + } + + classes.push(context) + } + + return { + namespaces: [...namespaces.entries()].map(([namespace, classes]) => ({ + namespace, + classes, + })), + } +} diff --git a/codegen/lib/merge-properties.ts b/codegen/lib/merge-properties.ts new file mode 100644 index 00000000..43a28523 --- /dev/null +++ b/codegen/lib/merge-properties.ts @@ -0,0 +1,148 @@ +// Merges the property lists of a discriminated union into one list. +// +// Two things in the blueprint are unions the SDK collapses into a single +// class: the event and action attempt resources, which the blueprint models as +// one resource per variant, and any property that is a list of discriminated +// objects. +// +// This logic is shared with the Ruby and Python SDKs. Changing the semantics +// here means changing them there too, or the SDKs drift. + +import type { Property } from '@seamapi/blueprint' + +/** + * Unions property lists by name, recursively. + * + * A name carried by one variant is taken as is. A name carried by several is + * merged: objects and lists of objects recurse, lists of discriminated objects + * concatenate their variants for whoever consumes the list to merge in turn, + * and scalars collapse onto one another because they map to the same PHP type. + */ +export const mergeProperties = ( + propertyLists: Property[][], + path = '', +): Property[] => { + const occurrencesByName = new Map() + + for (const properties of propertyLists) { + for (const property of properties) { + const occurrences = occurrencesByName.get(property.name) + if (occurrences == null) { + occurrencesByName.set(property.name, [property]) + continue + } + occurrences.push(property) + } + } + + return [...occurrencesByName.entries()].map(([name, occurrences]) => + mergeOccurrences(occurrences, path === '' ? name : `${path}.${name}`), + ) +} + +const mergeOccurrences = (occurrences: Property[], path: string): Property => { + const [first] = occurrences + + if (first == null) { + throw new Error(`Cannot merge ${path}: no occurrences`) + } + + if (occurrences.length === 1) return first + + // Each variant documents the property for its own case, which is accurate + // there and not necessarily on the class the variants collapse into. Keep + // the text only when every variant that documents it agrees, since no + // documentation beats wrong documentation. + const docs = mergeDocs(occurrences) + + const formatKeys = new Set(occurrences.map(getFormatKey)) + + if (formatKeys.size > 1) { + if (occurrences.every(isScalar)) { + // Differing scalar formats map to the same PHP type, so any one will do. + return { ...first, ...docs } + } + + throw new Error( + `Cannot merge ${path}: variants disagree on its shape (${[...formatKeys].sort().join(', ')})`, + ) + } + + if (first.format === 'object') { + return { + ...first, + ...docs, + properties: mergeProperties( + occurrences.map((occurrence) => + occurrence.format === 'object' ? occurrence.properties : [], + ), + path, + ), + } + } + + if (first.format === 'list' && first.itemFormat === 'object') { + return { + ...first, + ...docs, + itemProperties: mergeProperties( + occurrences.map((occurrence) => + occurrence.format === 'list' && occurrence.itemFormat === 'object' + ? occurrence.itemProperties + : [], + ), + path, + ), + } + } + + if (first.format === 'list' && first.itemFormat === 'discriminated_object') { + return { + ...first, + ...docs, + variants: occurrences.flatMap((occurrence) => + occurrence.format === 'list' && + occurrence.itemFormat === 'discriminated_object' + ? occurrence.variants + : [], + ), + } + } + + return { ...first, ...docs } +} + +interface MergedDocs { + description: string + isDeprecated: boolean + deprecationMessage: string +} + +const mergeDocs = (occurrences: Property[]): MergedDocs => { + const descriptions = [ + ...new Set( + occurrences + .map((occurrence) => occurrence.description.trim()) + .filter((description) => description !== ''), + ), + ] + + // Deprecating a property in any variant deprecates it on the merged class: + // first wins could silently undeprecate a field depending on the order the + // blueprint happens to list the variants in. + const deprecated = occurrences.find((occurrence) => occurrence.isDeprecated) + + return { + description: descriptions.length === 1 ? (descriptions[0] ?? '') : '', + isDeprecated: deprecated != null, + deprecationMessage: deprecated?.deprecationMessage ?? '', + } +} + +// A list of objects and a list of scalars are different shapes, so the item +// format is part of the key. +const getFormatKey = (property: Property): string => + property.format === 'list' ? `list<${property.itemFormat}>` : property.format + +const isScalar = (property: Property): boolean => + property.format !== 'list' && property.format !== 'object' diff --git a/codegen/lib/resource-model.ts b/codegen/lib/resource-model.ts index e29a192f..2d9bccd3 100644 --- a/codegen/lib/resource-model.ts +++ b/codegen/lib/resource-model.ts @@ -1,18 +1,22 @@ // Builds the resource class model for src/Resources from the blueprint. // -// Each blueprint resource becomes a PHP class in its own file. Nested object -// properties and lists of objects are split into their own classes, named -// after the base resource and the property, e.g. the device battery property -// becomes DeviceBattery. Those classes only exist to type a resource -// property, so they are emitted as local classes in the file of the resource -// that introduced them. Discriminated unions (events, action attempts, and -// discriminated object lists) are flattened into a single class with the -// union of the variant properties. - -import type { Blueprint, Property } from '@seamapi/blueprint' +// Each blueprint resource becomes a PHP class in its own file. A nested object +// property, or a list of objects, becomes its own class named after the +// property alone and declared in the namespace of the class that owns it, so +// the device battery is Seam\Resources\Device\Properties\Battery. Nesting the +// classes keeps Seam\Resources free of the hundreds of names that exist only +// to type a property, and lets two properties of the same name at different +// depths keep their own shapes. +// +// Discriminated unions (events, action attempts, and discriminated object +// lists) are flattened into a single class holding the union of the variant +// properties. + +import type { Blueprint, Property, Resource } from '@seamapi/blueprint' import { pascalCase } from 'change-case' import { getPhpType } from './map-php-type.js' +import { mergeProperties } from './merge-properties.js' export type ResourceClassProperty = | ({ kind: 'value'; phpType: string } & ResourceClassPropertyMetadata) @@ -34,6 +38,7 @@ interface ResourceClassPropertyMetadata { export interface ResourceClassSchema { name: string + namespace: string description: string isDeprecated: boolean deprecationMessage: string @@ -42,8 +47,7 @@ export interface ResourceClassSchema { export interface ResourceSchema { name: string - resourceClass: ResourceClassSchema - localClasses: ResourceClassSchema[] + classes: ResourceClassSchema[] } export interface ResourceModel { @@ -51,6 +55,46 @@ export interface ResourceModel { resources: ResourceSchema[] } +const rootNamespace = 'Seam\\Resources' + +// A cyclic schema would recurse forever. Real shapes are nowhere near this +// deep, so exceeding it means the blueprint is cyclic rather than nested. +const maxDepth = 16 + +// PHP reserves these as type names, so a class cannot be called any of them. +// Class names are case insensitive, so the check has to be too. +const reservedClassNames = new Set([ + 'array', + 'bool', + 'callable', + 'enum', + 'false', + 'float', + 'int', + 'iterable', + 'mixed', + 'never', + 'null', + 'object', + 'parent', + 'self', + 'static', + 'string', + 'true', + 'void', +]) + +interface ClassDocs { + description: string + isDeprecated: boolean + deprecationMessage: string +} + +interface BuiltClass { + schema: ResourceClassSchema + nestedClasses: BuiltClass[] +} + export const createResourceModel = (blueprint: Blueprint): ResourceModel => { const baseResources = new Map() @@ -65,7 +109,10 @@ export const createResourceModel = (blueprint: Blueprint): ResourceModel => { if (events.length > 0) { baseResources.set( 'event', - mergeProperties(events.map((event) => event.properties)), + mergeProperties( + events.map((event) => event.properties), + 'event', + ), ) } @@ -75,164 +122,172 @@ export const createResourceModel = (blueprint: Blueprint): ResourceModel => { 'action_attempt', mergeProperties( actionAttempts.map((actionAttempt) => actionAttempt.properties), + 'action_attempt', ), ) } - const classes = new Map() - const localClassNames = new Map() - - let currentResourceName = '' - - const addClass = ( - name: string, - properties: Property[], - baseName: string, - description = '', - isDeprecated = false, - deprecationMessage = '', - ): void => { - if (classes.has(name)) return - const schema: ResourceClassSchema = { - name, - description, - isDeprecated, - deprecationMessage, - properties: [], - } - classes.set(name, schema) - if (name !== currentResourceName) { - localClassNames.get(currentResourceName)?.push(name) - } - schema.properties = properties.map((property) => - createResourceClassProperty(property, baseName, addClass), - ) - } - const baseResourceTypes = [...baseResources.keys()].sort() + const resources = baseResourceTypes.map((resourceType) => { const name = pascalCase(resourceType) - currentResourceName = name - localClassNames.set(name, []) - const sourceResource = - blueprint.resources.find( - (resource) => resource.resourceType === resourceType, - ) ?? - (resourceType === 'event' - ? blueprint.events[0] - : blueprint.actionAttempts[0]) - addClass( + const sourceResource = getSourceResource(blueprint, resourceType) + + const built = buildClass( name, + rootNamespace, baseResources.get(resourceType) ?? [], resourceType, - sourceResource?.description, - sourceResource?.isDeprecated, - sourceResource?.deprecationMessage, + 0, + { + description: sourceResource?.description ?? '', + isDeprecated: sourceResource?.isDeprecated ?? false, + deprecationMessage: sourceResource?.deprecationMessage ?? '', + }, + ) + + return { name, classes: flattenClasses(built) } + }) + + return { + resourceNames: resources.map((resource) => resource.name), + resources, + } +} + +const getSourceResource = ( + blueprint: Blueprint, + resourceType: string, +): Resource | undefined => + blueprint.resources.find( + (resource) => resource.resourceType === resourceType, + ) ?? + (resourceType === 'event' + ? blueprint.events[0] + : resourceType === 'action_attempt' + ? blueprint.actionAttempts[0] + : undefined) + +const buildClass = ( + className: string, + namespace: string, + classProperties: Property[], + path: string, + depth: number, + docs: ClassDocs, +): BuiltClass => { + if (depth > maxDepth) { + throw new Error( + `Cannot generate ${path}: nesting exceeded a depth of ${maxDepth}, which means the schema is cyclic`, + ) + } + + // A class at namespace N with short name S owns its children at N\S. + const nestedNamespace = `${namespace}\\${className}` + const nestedClasses: BuiltClass[] = [] + const takenClassNames = new Set() + + const properties = classProperties.map((property): ResourceClassProperty => { + const metadata = { + name: property.name, + description: property.description, + isDeprecated: property.isDeprecated, + deprecationMessage: property.deprecationMessage, + } + + const nestedProperties = getNestedProperties( + property, + `${path}.${property.name}`, ) - const resourceClass = classes.get(name) - if (resourceClass == null) { + if (nestedProperties == null) { + return { ...metadata, kind: 'value', phpType: getPhpType(property) } + } + + const nestedClassName = pascalCase(property.name) + const nestedPath = `${path}.${property.name}` + + if (reservedClassNames.has(nestedClassName.toLowerCase())) { + throw new Error( + `Cannot generate ${nestedPath}: ${nestedClassName} is a reserved PHP type name`, + ) + } + + if (takenClassNames.has(nestedClassName)) { throw new Error( - `Missing class for resource ${resourceType}: ${name} is already used by a property class of another resource`, + `Cannot generate ${nestedPath}: ${nestedClassName} is already used by a sibling property in ${nestedNamespace}`, ) } + takenClassNames.add(nestedClassName) + + nestedClasses.push( + buildClass( + nestedClassName, + nestedNamespace, + nestedProperties, + nestedPath, + depth + 1, + { + description: property.description, + isDeprecated: property.isDeprecated, + deprecationMessage: property.deprecationMessage, + }, + ), + ) + + // Referenced relative to the namespace the owning class is declared in, + // so PHP resolves Device\Properties from within Seam\Resources. return { - name, - resourceClass, - localClasses: (localClassNames.get(name) ?? []) - .map((localClassName) => { - const localClass = classes.get(localClassName) - if (localClass == null) { - throw new Error(`Missing local class ${localClassName}`) - } - return localClass - }) - .sort((a, b) => a.name.localeCompare(b.name)), + ...metadata, + kind: property.format === 'list' ? 'listReference' : 'objectReference', + referenceName: `${className}\\${nestedClassName}`, } }) return { - resourceNames: resources.map((resource) => resource.name), - resources, + schema: { + name: className, + namespace, + ...docs, + properties, + }, + nestedClasses, } } -const createResourceClassProperty = ( +// Decides whether a property earns a class of its own. An object with no +// documented properties stays a plain value rather than becoming an empty +// class. +const getNestedProperties = ( property: Property, - baseName: string, - addClass: ( - name: string, - properties: Property[], - baseName: string, - description?: string, - isDeprecated?: boolean, - deprecationMessage?: string, - ) => void, -): ResourceClassProperty => { - const referenceName = pascalCase(`${baseName}_${property.name}`) - const metadata = { - name: property.name, - description: property.description, - isDeprecated: property.isDeprecated, - deprecationMessage: property.deprecationMessage, - } - + path: string, +): Property[] | undefined => { if (property.format === 'object') { - const { properties } = property - - if (properties.length > 0) { - addClass(referenceName, properties, baseName, property.description) - return { ...metadata, kind: 'objectReference', referenceName } - } + return property.properties.length > 0 ? property.properties : undefined } if (property.format === 'list') { - const itemProperties = - property.itemFormat === 'object' + if (property.itemFormat === 'object') { + return property.itemProperties.length > 0 ? property.itemProperties - : property.itemFormat === 'discriminated_object' - ? mergeProperties( - property.variants.map((variant) => variant.properties), - ) - : [] - - if (itemProperties.length > 0) { - addClass(referenceName, itemProperties, baseName, property.description) - return { ...metadata, kind: 'listReference', referenceName } + : undefined } - } - - return { - ...metadata, - kind: 'value', - phpType: getPhpType(property), - } -} -const mergeProperties = (propertyLists: Property[][]): Property[] => { - const merged = new Map() - - for (const properties of propertyLists) { - for (const property of properties) { - const existing = merged.get(property.name) - - if (existing == null) { - merged.set(property.name, property) - continue - } - - if (existing.format === 'object' && property.format === 'object') { - merged.set(property.name, { - ...existing, - properties: mergeProperties([ - existing.properties, - property.properties, - ]), - }) - } + if (property.itemFormat === 'discriminated_object') { + const itemProperties = mergeProperties( + property.variants.map((variant) => variant.properties), + path, + ) + return itemProperties.length > 0 ? itemProperties : undefined } } - return [...merged.values()] + return undefined } + +// Depth first, so an owning class always precedes the children it references. +const flattenClasses = (built: BuiltClass): ResourceClassSchema[] => [ + built.schema, + ...built.nestedClasses.flatMap(flattenClasses), +] diff --git a/src/Resources/AccessCode.php b/src/Resources/AccessCode.php index 3fe2aa9c..d2c1a118 100644 --- a/src/Resources/AccessCode.php +++ b/src/Resources/AccessCode.php @@ -1,508 +1,532 @@ access_code_id ?? null, - code: $json->code ?? null, - common_code_key: $json->common_code_key ?? null, - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - dormakaba_oracode_metadata: isset($json->dormakaba_oracode_metadata) - ? AccessCodeDormakabaOracodeMetadata::from_json( + public static function from_json(mixed $json): AccessCode|null + { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + code: $json->code ?? null, + common_code_key: $json->common_code_key ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + dormakaba_oracode_metadata: isset( $json->dormakaba_oracode_metadata, ) - : null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => AccessCodeErrors::from_json($e), - $json->errors ?? [], - ), - is_backup: $json->is_backup ?? null, - is_backup_access_code_available: $json->is_backup_access_code_available ?? - null, - is_external_modification_allowed: $json->is_external_modification_allowed ?? - null, - is_managed: $json->is_managed ?? null, - is_offline_access_code: $json->is_offline_access_code ?? null, - is_one_time_use: $json->is_one_time_use ?? null, - is_scheduled_on_device: $json->is_scheduled_on_device ?? null, - is_waiting_for_code_assignment: $json->is_waiting_for_code_assignment ?? - null, - name: $json->name ?? null, - pending_mutations: array_map( - fn($p) => AccessCodePendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - pulled_backup_access_code_id: $json->pulled_backup_access_code_id ?? - null, - starts_at: $json->starts_at ?? null, - status: $json->status ?? null, - type: $json->type ?? null, - warnings: array_map( - fn($w) => AccessCodeWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } + ? AccessCode\DormakabaOracodeMetadata::from_json( + $json->dormakaba_oracode_metadata, + ) + : null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => AccessCode\Errors::from_json($e), + $json->errors ?? [], + ), + is_backup: $json->is_backup ?? null, + is_backup_access_code_available: $json->is_backup_access_code_available ?? + null, + is_external_modification_allowed: $json->is_external_modification_allowed ?? + null, + is_managed: $json->is_managed ?? null, + is_offline_access_code: $json->is_offline_access_code ?? null, + is_one_time_use: $json->is_one_time_use ?? null, + is_scheduled_on_device: $json->is_scheduled_on_device ?? null, + is_waiting_for_code_assignment: $json->is_waiting_for_code_assignment ?? + null, + name: $json->name ?? null, + pending_mutations: array_map( + fn($p) => AccessCode\PendingMutations::from_json($p), + $json->pending_mutations ?? [], + ), + pulled_backup_access_code_id: $json->pulled_backup_access_code_id ?? + null, + starts_at: $json->starts_at ?? null, + status: $json->status ?? null, + type: $json->type ?? null, + warnings: array_map( + fn($w) => AccessCode\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); + } - public function __construct( - /** - * Unique identifier for the access code. - */ - public string|null $access_code_id, - /** - * Code used for access. Typically, a numeric or alphanumeric string. - */ - public string|null $code, - /** - * Unique identifier for a group of access codes that share the same code. - */ - public string|null $common_code_key, - /** - * Date and time at which the access code was created. - */ - public string|null $created_at, - /** - * Unique identifier for the device associated with the access code. - */ - public string|null $device_id, - /** - * Metadata for a dormakaba Oracode managed access code. Only present for access codes from dormakaba Oracode devices. - */ - public AccessCodeDormakabaOracodeMetadata|null $dormakaba_oracode_metadata, - /** - * Date and time after which the time-bound access code becomes inactive. - */ - public string|null $ends_at, - /** - * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ - public array $errors, - /** - * Indicates whether the access code is a backup code. - */ - public bool|null $is_backup, - /** - * Indicates whether a backup access code is available for use if the primary access code is lost or compromised. - */ - public bool|null $is_backup_access_code_available, - /** - * Indicates whether changes to the access code from external sources are permitted. - */ - public bool|null $is_external_modification_allowed, - /** - * Indicates whether Seam manages the access code. - */ - public bool|null $is_managed, - /** - * Indicates whether the access code is intended for use in offline scenarios. If `true`, this code can be created on a device without a network connection. - */ - public bool|null $is_offline_access_code, - /** - * Indicates whether the access code can only be used once. If `true`, the code becomes invalid after the first use. - */ - public bool|null $is_one_time_use, - /** - * Indicates whether the code is set on the device according to a preconfigured schedule. - */ - public bool|null $is_scheduled_on_device, - /** - * Indicates whether the access code is waiting for a code assignment. - */ - public bool|null $is_waiting_for_code_assignment, - /** - * Name of the access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). - */ - public string|null $name, - /** - * Collection of pending mutations for the access code. Indicates changes that Seam is in the process of pushing to the device. - */ - public array $pending_mutations, - /** - * Identifier of the pulled backup access code. Used to associate the pulled backup access code with the original access code. - */ - public string|null $pulled_backup_access_code_id, - /** - * Date and time at which the time-bound access code becomes active. - */ - public string|null $starts_at, - /** - * Current status of the access code within the operational lifecycle. Values are `setting`, a transitional phase that indicates that the code is being configured or activated; `set`, which indicates that the code is active and operational; `unset`, which indicates a deactivated or unused state, either before activation or after deliberate deactivation; `removing`, which indicates a transitional period in which the code is being deleted or made inactive; and `unknown`, which indicates an indeterminate state, due to reasons such as system errors or incomplete data, that highlights a potential need for system review or troubleshooting. See also [Lifecycle of Access Codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes/lifecycle-of-access-codes). - */ - public string|null $status, - /** - * Type of the access code. `ongoing` access codes are active continuously until deactivated manually. `time_bound` access codes have a specific duration. - */ - public string|null $type, - /** - * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ - public array $warnings, - /** - * Unique identifier for the Seam workspace associated with the access code. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Unique identifier for the access code. + */ + public string|null $access_code_id, + /** + * Code used for access. Typically, a numeric or alphanumeric string. + */ + public string|null $code, + /** + * Unique identifier for a group of access codes that share the same code. + */ + public string|null $common_code_key, + /** + * Date and time at which the access code was created. + */ + public string|null $created_at, + /** + * Unique identifier for the device associated with the access code. + */ + public string|null $device_id, + /** + * Metadata for a dormakaba Oracode managed access code. Only present for access codes from dormakaba Oracode devices. + */ + public AccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata, + /** + * Date and time after which the time-bound access code becomes inactive. + */ + public string|null $ends_at, + /** + * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + public array $errors, + /** + * Indicates whether the access code is a backup code. + */ + public bool|null $is_backup, + /** + * Indicates whether a backup access code is available for use if the primary access code is lost or compromised. + */ + public bool|null $is_backup_access_code_available, + /** + * Indicates whether changes to the access code from external sources are permitted. + */ + public bool|null $is_external_modification_allowed, + /** + * Indicates whether Seam manages the access code. + */ + public bool|null $is_managed, + /** + * Indicates whether the access code is intended for use in offline scenarios. If `true`, this code can be created on a device without a network connection. + */ + public bool|null $is_offline_access_code, + /** + * Indicates whether the access code can only be used once. If `true`, the code becomes invalid after the first use. + */ + public bool|null $is_one_time_use, + /** + * Indicates whether the code is set on the device according to a preconfigured schedule. + */ + public bool|null $is_scheduled_on_device, + /** + * Indicates whether the access code is waiting for a code assignment. + */ + public bool|null $is_waiting_for_code_assignment, + /** + * Name of the access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). + */ + public string|null $name, + /** + * Collection of pending mutations for the access code. Indicates changes that Seam is in the process of pushing to the device. + */ + public array $pending_mutations, + /** + * Identifier of the pulled backup access code. Used to associate the pulled backup access code with the original access code. + */ + public string|null $pulled_backup_access_code_id, + /** + * Date and time at which the time-bound access code becomes active. + */ + public string|null $starts_at, + /** + * Current status of the access code within the operational lifecycle. Values are `setting`, a transitional phase that indicates that the code is being configured or activated; `set`, which indicates that the code is active and operational; `unset`, which indicates a deactivated or unused state, either before activation or after deliberate deactivation; `removing`, which indicates a transitional period in which the code is being deleted or made inactive; and `unknown`, which indicates an indeterminate state, due to reasons such as system errors or incomplete data, that highlights a potential need for system review or troubleshooting. See also [Lifecycle of Access Codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes/lifecycle-of-access-codes). + */ + public string|null $status, + /** + * Type of the access code. `ongoing` access codes are active continuously until deactivated manually. `time_bound` access codes have a specific duration. + */ + public string|null $type, + /** + * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + public array $warnings, + /** + * Unique identifier for the Seam workspace associated with the access code. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Metadata for a dormakaba Oracode managed access code. Only present for access codes from dormakaba Oracode devices. - */ -class AccessCodeDormakabaOracodeMetadata -{ - public static function from_json( - mixed $json, - ): AccessCodeDormakabaOracodeMetadata|null { - if (!$json) { - return null; +namespace Seam\Resources\AccessCode { + /** + * Metadata for a dormakaba Oracode managed access code. Only present for access codes from dormakaba Oracode devices. + */ + class DormakabaOracodeMetadata + { + public static function from_json( + mixed $json, + ): DormakabaOracodeMetadata|null { + if (!$json) { + return null; + } + return new self( + is_cancellable: $json->is_cancellable ?? null, + is_early_checkin_able: $json->is_early_checkin_able ?? null, + is_extendable: $json->is_extendable ?? null, + is_overridable: $json->is_overridable ?? null, + site_name: $json->site_name ?? null, + stay_id: $json->stay_id ?? null, + user_level_id: $json->user_level_id ?? null, + user_level_name: $json->user_level_name ?? null, + ); } - return new self( - is_cancellable: $json->is_cancellable ?? null, - is_early_checkin_able: $json->is_early_checkin_able ?? null, - is_extendable: $json->is_extendable ?? null, - is_overridable: $json->is_overridable ?? null, - site_name: $json->site_name ?? null, - stay_id: $json->stay_id ?? null, - user_level_id: $json->user_level_id ?? null, - user_level_name: $json->user_level_name ?? null, - ); - } - public function __construct( - /** - * Indicates whether the stay can be cancelled via the Dormakaba Oracode API. - */ - public bool|null $is_cancellable, - /** - * Indicates whether early check-in is available for this stay. - */ - public bool|null $is_early_checkin_able, - /** - * Indicates whether the stay can be extended via the Dormakaba Oracode API. - */ - public bool|null $is_extendable, - /** - * Indicates whether the access code can be overridden. When false, the maximum number of overrides has been reached. - */ - public bool|null $is_overridable, - /** - * Dormakaba Oracode site name associated with this access code. - */ - public string|null $site_name, - /** - * Dormakaba Oracode stay ID associated with this access code. - */ - public float|null $stay_id, - /** - * Dormakaba Oracode user level ID associated with this access code. - */ - public string|null $user_level_id, - /** - * Dormakaba Oracode user level name associated with this access code. - */ - public string|null $user_level_name, - ) {} -} + public function __construct( + /** + * Indicates whether the stay can be cancelled via the Dormakaba Oracode API. + */ + public bool|null $is_cancellable, + /** + * Indicates whether early check-in is available for this stay. + */ + public bool|null $is_early_checkin_able, + /** + * Indicates whether the stay can be extended via the Dormakaba Oracode API. + */ + public bool|null $is_extendable, + /** + * Indicates whether the access code can be overridden. When false, the maximum number of overrides has been reached. + */ + public bool|null $is_overridable, + /** + * Dormakaba Oracode site name associated with this access code. + */ + public string|null $site_name, + /** + * Dormakaba Oracode stay ID associated with this access code. + */ + public float|null $stay_id, + /** + * Dormakaba Oracode user level ID associated with this access code. + */ + public string|null $user_level_id, + /** + * Dormakaba Oracode user level name associated with this access code. + */ + public string|null $user_level_name, + ) {} + } -/** - * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ -class AccessCodeErrors -{ - public static function from_json(mixed $json): AccessCodeErrors|null + /** + * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + change_type: $json->change_type ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + managed_access_code_id: $json->managed_access_code_id ?? null, + message: $json->message ?? null, + modified_fields: array_map( + fn($m) => Errors\ModifiedFields::from_json($m), + $json->modified_fields ?? [], + ), + unmanaged_access_code_id: $json->unmanaged_access_code_id ?? + null, + ); } - return new self( - change_type: $json->change_type ?? null, - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_access_code_error: $json->is_access_code_error ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - is_device_error: $json->is_device_error ?? null, - managed_access_code_id: $json->managed_access_code_id ?? null, - message: $json->message ?? null, - modified_fields: array_map( - fn($m) => AccessCodeModifiedFields::from_json($m), - $json->modified_fields ?? [], - ), - unmanaged_access_code_id: $json->unmanaged_access_code_id ?? null, - ); - } - public function __construct( - /** - * Indicates the type of external modification. `modified` means the code's PIN or schedule was changed. `removed` means the code was deleted from the device. - */ - public string|null $change_type, - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Indicates that this is an access code error. - */ - public bool|null $is_access_code_error, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error, - /** - * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. - */ - public bool|null $is_connected_account_error, - /** - * Indicates that the error is not a device error. - */ - public bool|null $is_device_error, - /** - * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. - */ - public string|null $managed_access_code_id, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * List of fields that were changed externally, with their previous and new values. - */ - public array $modified_fields, - /** - * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. - */ - public string|null $unmanaged_access_code_id, - ) {} -} + public function __construct( + /** + * Indicates the type of external modification. `modified` means the code's PIN or schedule was changed. `removed` means the code was deleted from the device. + */ + public string|null $change_type, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Indicates that this is an access code error. + */ + public bool|null $is_access_code_error, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error, + public bool|null $is_connected_account_error, + public bool|null $is_device_error, + /** + * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. + */ + public string|null $managed_access_code_id, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * List of fields that were changed externally, with their previous and new values. + */ + public array $modified_fields, + /** + * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. + */ + public string|null $unmanaged_access_code_id, + ) {} + } -/** - * Previous code configuration. - */ -class AccessCodeFrom -{ - public static function from_json(mixed $json): AccessCodeFrom|null + /** + * Collection of pending mutations for the access code. Indicates changes that Seam is in the process of pushing to the device. + */ + class PendingMutations { - if (!$json) { - return null; + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + scheduled_at: $json->scheduled_at ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); } - return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Previous PIN code. - */ - public string|null $code, - /** - * Previous end time for the access code. - */ - public string|null $ends_at, - /** - * Previous access code name. - */ - public string|null $name, - /** - * Previous start time for the access code. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + /** + * Date and time at which Seam will attempt to program this access code on the device. + */ + public string|null $scheduled_at, + public PendingMutations\To|null $to, + ) {} + } -/** - * List of fields that were changed externally, with their previous and new values. - */ -class AccessCodeModifiedFields -{ - public static function from_json(mixed $json): AccessCodeModifiedFields|null + /** + * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + class Warnings { - if (!$json) { - return null; + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + change_type: $json->change_type ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + modified_fields: array_map( + fn($m) => Warnings\ModifiedFields::from_json($m), + $json->modified_fields ?? [], + ), + warning_code: $json->warning_code ?? null, + ); } - return new self( - field: $json->field ?? null, - from: $json->from ?? null, - to: $json->to ?? null, - ); - } - public function __construct( - /** - * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). - */ - public string|null $field, - /** - * The previous value of the field. - */ - public string|null $from, - /** - * The new value of the field. - */ - public string|null $to, - ) {} + public function __construct( + /** + * Indicates the type of external modification. `modified` means the code's PIN or schedule was changed. `removed` means the code was deleted from the device. + */ + public string|null $change_type, + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * List of fields that were changed externally, with their previous and new values. + */ + public array $modified_fields, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Collection of pending mutations for the access code. Indicates changes that Seam is in the process of pushing to the device. - */ -class AccessCodePendingMutations -{ - public static function from_json( - mixed $json, - ): AccessCodePendingMutations|null { - if (!$json) { - return null; +namespace Seam\Resources\AccessCode\Errors { + /** + * List of fields that were changed externally, with their previous and new values. + */ + class ModifiedFields + { + public static function from_json(mixed $json): ModifiedFields|null + { + if (!$json) { + return null; + } + return new self( + field: $json->field ?? null, + from: $json->from ?? null, + to: $json->to ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - from: isset($json->from) - ? AccessCodeFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - scheduled_at: $json->scheduled_at ?? null, - to: isset($json->to) ? AccessCodeTo::from_json($json->to) : null, - ); - } - public function __construct( - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Previous code configuration. - */ - public AccessCodeFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of setting an access code on the device. - */ - public string|null $mutation_code, - /** - * Date and time at which Seam will attempt to program this access code on the device. - */ - public string|null $scheduled_at, - /** - * New code configuration. - */ - public AccessCodeTo|null $to, - ) {} + public function __construct( + /** + * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + */ + public string|null $field, + /** + * The previous value of the field. + */ + public string|null $from, + /** + * The new value of the field. + */ + public string|null $to, + ) {} + } } -/** - * New code configuration. - */ -class AccessCodeTo -{ - public static function from_json(mixed $json): AccessCodeTo|null +namespace Seam\Resources\AccessCode\PendingMutations { + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - ); + + public function __construct( + /** + * Previous PIN code. + */ + public string|null $code, + /** + * Previous end time for the access code. + */ + public string|null $ends_at, + /** + * Previous access code name. + */ + public string|null $name, + /** + * Previous start time for the access code. + */ + public string|null $starts_at, + ) {} } - public function __construct( - /** - * New PIN code. - */ - public string|null $code, - /** - * New end time for the access code. - */ - public string|null $ends_at, - /** - * New access code name. - */ - public string|null $name, - /** - * New start time for the access code. - */ - public string|null $starts_at, - ) {} + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( + /** + * New PIN code. + */ + public string|null $code, + /** + * New end time for the access code. + */ + public string|null $ends_at, + /** + * New access code name. + */ + public string|null $name, + /** + * New start time for the access code. + */ + public string|null $starts_at, + ) {} + } } -/** - * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ -class AccessCodeWarnings -{ - public static function from_json(mixed $json): AccessCodeWarnings|null +namespace Seam\Resources\AccessCode\Warnings { + /** + * List of fields that were changed externally, with their previous and new values. + */ + class ModifiedFields { - if (!$json) { - return null; + public static function from_json(mixed $json): ModifiedFields|null + { + if (!$json) { + return null; + } + return new self( + field: $json->field ?? null, + from: $json->from ?? null, + to: $json->to ?? null, + ); } - return new self( - change_type: $json->change_type ?? null, - created_at: $json->created_at ?? null, - message: $json->message ?? null, - modified_fields: array_map( - fn($m) => AccessCodeModifiedFields::from_json($m), - $json->modified_fields ?? [], - ), - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Indicates the type of external modification. `modified` means the code's PIN or schedule was changed. `removed` means the code was deleted from the device. - */ - public string|null $change_type, - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * List of fields that were changed externally, with their previous and new values. - */ - public array $modified_fields, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + */ + public string|null $field, + /** + * The previous value of the field. + */ + public string|null $from, + /** + * The new value of the field. + */ + public string|null $to, + ) {} + } } diff --git a/src/Resources/AccessGrant.php b/src/Resources/AccessGrant.php index a4ff5cca..6df3876a 100644 --- a/src/Resources/AccessGrant.php +++ b/src/Resources/AccessGrant.php @@ -1,441 +1,433 @@ access_grant_id ?? null, + access_grant_key: $json->access_grant_key ?? null, + access_method_ids: $json->access_method_ids ?? null, + client_session_token: $json->client_session_token ?? null, + created_at: $json->created_at ?? null, + customization_profile_id: $json->customization_profile_id ?? + null, + display_name: $json->display_name ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => AccessGrant\Errors::from_json($e), + $json->errors ?? [], + ), + instant_key_url: $json->instant_key_url ?? null, + location_ids: $json->location_ids ?? null, + name: $json->name ?? null, + pending_mutations: array_map( + fn($p) => AccessGrant\PendingMutations::from_json($p), + $json->pending_mutations ?? [], + ), + requested_access_methods: array_map( + fn($r) => AccessGrant\RequestedAccessMethods::from_json($r), + $json->requested_access_methods ?? [], + ), + reservation_key: $json->reservation_key ?? null, + space_ids: $json->space_ids ?? null, + starts_at: $json->starts_at ?? null, + user_identity_id: $json->user_identity_id ?? null, + warnings: array_map( + fn($w) => AccessGrant\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - access_grant_id: $json->access_grant_id ?? null, - access_grant_key: $json->access_grant_key ?? null, - access_method_ids: $json->access_method_ids ?? null, - client_session_token: $json->client_session_token ?? null, - created_at: $json->created_at ?? null, - customization_profile_id: $json->customization_profile_id ?? null, - display_name: $json->display_name ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => AccessGrantErrors::from_json($e), - $json->errors ?? [], - ), - instant_key_url: $json->instant_key_url ?? null, - location_ids: $json->location_ids ?? null, - name: $json->name ?? null, - pending_mutations: array_map( - fn($p) => AccessGrantPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - requested_access_methods: array_map( - fn($r) => AccessGrantRequestedAccessMethods::from_json($r), - $json->requested_access_methods ?? [], - ), - reservation_key: $json->reservation_key ?? null, - space_ids: $json->space_ids ?? null, - starts_at: $json->starts_at ?? null, - user_identity_id: $json->user_identity_id ?? null, - warnings: array_map( - fn($w) => AccessGrantWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * ID of the Access Grant. - */ - public string|null $access_grant_id, - /** - * Unique key for the access grant within the workspace. - */ - public string|null $access_grant_key, - /** - * IDs of the access methods created for the Access Grant. - */ - public array|null $access_method_ids, - /** - * Client Session Token. Only returned if the Access Grant has a mobile_key access method. - */ - public string|null $client_session_token, - /** - * Date and time at which the Access Grant was created. - */ - public string|null $created_at, - /** - * ID of the customization profile associated with the Access Grant. - */ - public string|null $customization_profile_id, - /** - * Display name of the Access Grant. - */ - public string|null $display_name, - /** - * Date and time at which the Access Grant ends. - */ - public string|null $ends_at, - /** - * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ - public array $errors, - /** - * Instant Key URL. Only returned if the Access Grant has a single mobile_key access_method. - */ - public string|null $instant_key_url, - /** - * @deprecated Use `space_ids`. - */ - public array|null $location_ids, - /** - * Name of the Access Grant. If not provided, the display name will be computed. - */ - public string|null $name, - /** - * List of pending mutations for the access grant. This shows updates that are in progress. - */ - public array $pending_mutations, - /** - * Access methods that the user requested for the Access Grant. - */ - public array $requested_access_methods, - /** - * Reservation key for the access grant. - */ - public string|null $reservation_key, - /** - * IDs of the spaces to which the Access Grant gives access. - */ - public array|null $space_ids, - /** - * Date and time at which the Access Grant starts. - */ - public string|null $starts_at, - /** - * ID of user identity to which the Access Grant gives access. - */ - public string|null $user_identity_id, - /** - * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ - public array $warnings, - /** - * ID of the Seam workspace associated with the Access Grant. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the Access Grant. + */ + public string|null $access_grant_id, + /** + * Unique key for the access grant within the workspace. + */ + public string|null $access_grant_key, + /** + * IDs of the access methods created for the Access Grant. + */ + public array|null $access_method_ids, + /** + * Client Session Token. Only returned if the Access Grant has a mobile_key access method. + */ + public string|null $client_session_token, + /** + * Date and time at which the Access Grant was created. + */ + public string|null $created_at, + /** + * ID of the customization profile associated with the Access Grant. + */ + public string|null $customization_profile_id, + /** + * Display name of the Access Grant. + */ + public string|null $display_name, + /** + * Date and time at which the Access Grant ends. + */ + public string|null $ends_at, + /** + * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + public array $errors, + /** + * Instant Key URL. Only returned if the Access Grant has a single mobile_key access_method. + */ + public string|null $instant_key_url, + /** + * @deprecated Use `space_ids`. + */ + public array|null $location_ids, + /** + * Name of the Access Grant. If not provided, the display name will be computed. + */ + public string|null $name, + /** + * List of pending mutations for the access grant. This shows updates that are in progress. + */ + public array $pending_mutations, + /** + * Access methods that the user requested for the Access Grant. + */ + public array $requested_access_methods, + /** + * Reservation key for the access grant. + */ + public string|null $reservation_key, + /** + * IDs of the spaces to which the Access Grant gives access. + */ + public array|null $space_ids, + /** + * Date and time at which the Access Grant starts. + */ + public string|null $starts_at, + /** + * ID of user identity to which the Access Grant gives access. + */ + public string|null $user_identity_id, + /** + * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + public array $warnings, + /** + * ID of the Seam workspace associated with the Access Grant. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ -class AccessGrantErrors -{ - public static function from_json(mixed $json): AccessGrantErrors|null +namespace Seam\Resources\AccessGrant { + /** + * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + missing_device_ids: $json->missing_device_ids ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - missing_device_ids: $json->missing_device_ids ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. - */ - public array|null $missing_device_ids, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. + */ + public array|null $missing_device_ids, + ) {} + } -/** - * Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). - */ -class AccessGrantFailedDevices -{ - public static function from_json(mixed $json): AccessGrantFailedDevices|null + /** + * List of pending mutations for the access grant. This shows updates that are in progress. + */ + class PendingMutations { - if (!$json) { - return null; + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + access_method_ids: $json->access_method_ids ?? null, + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); } - return new self( - device_id: $json->device_id ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Device whose access code could not be revoked. - */ - public string|null $device_id, - /** - * Reason the access code could not be revoked (e.g. `offline_access_code_not_revocable`). - */ - public string|null $error_code, - /** - * Human-readable description of why revocation failed. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * IDs of the access methods being updated. + */ + public array|null $access_method_ids, + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + public PendingMutations\To|null $to, + ) {} + } -/** - * Previous location configuration. - */ -class AccessGrantFrom -{ - public static function from_json(mixed $json): AccessGrantFrom|null + /** + * Access methods that the user requested for the Access Grant. + */ + class RequestedAccessMethods { - if (!$json) { - return null; + public static function from_json( + mixed $json, + ): RequestedAccessMethods|null { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + created_access_method_ids: $json->created_access_method_ids ?? + null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + instant_key_max_use_count: $json->instant_key_max_use_count ?? + null, + mode: $json->mode ?? null, + ); } - return new self( - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Previous device IDs where access codes existed. - */ - public array|null $device_ids, - /** - * Previous end time for access. - */ - public string|null $ends_at, - /** - * Previous start time for access. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Specific PIN code to use for this access method. Only applicable when mode is 'code'. + */ + public string|null $code, + /** + * IDs of the access methods created for the requested access method. + */ + public array|null $created_access_method_ids, + /** + * Date and time at which the requested access method was added to the Access Grant. + */ + public string|null $created_at, + /** + * Display name of the access method. + */ + public string|null $display_name, + /** + * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. + */ + public int|null $instant_key_max_use_count, + /** + * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $mode, + ) {} + } -/** - * List of pending mutations for the access grant. This shows updates that are in progress. - */ -class AccessGrantPendingMutations -{ - public static function from_json( - mixed $json, - ): AccessGrantPendingMutations|null { - if (!$json) { - return null; + /** + * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + access_method_ids: $json->access_method_ids ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + failed_devices: array_map( + fn($f) => Warnings\FailedDevices::from_json($f), + $json->failed_devices ?? [], + ), + message: $json->message ?? null, + new_code: $json->new_code ?? null, + original_code: $json->original_code ?? null, + reason: $json->reason ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - access_method_ids: $json->access_method_ids ?? null, - created_at: $json->created_at ?? null, - from: isset($json->from) - ? AccessGrantFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) ? AccessGrantTo::from_json($json->to) : null, - ); - } - public function __construct( - /** - * IDs of the access methods being updated. - */ - public array|null $access_method_ids, - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Previous location configuration. - */ - public AccessGrantFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of updating the spaces (devices) associated with this access grant. - */ - public string|null $mutation_code, - /** - * New location configuration. - */ - public AccessGrantTo|null $to, - ) {} + public function __construct( + /** + * IDs of the access methods being updated. + */ + public array|null $access_method_ids, + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + public string|null $device_id, + /** + * Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). + */ + public array $failed_devices, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * The new PIN code that was assigned instead. + */ + public string|null $new_code, + /** + * The originally requested PIN code that was unavailable. + */ + public string|null $original_code, + /** + * Specific reason why the grant's times are not programmable on the device. + */ + public string|null $reason, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Access methods that the user requested for the Access Grant. - */ -class AccessGrantRequestedAccessMethods -{ - public static function from_json( - mixed $json, - ): AccessGrantRequestedAccessMethods|null { - if (!$json) { - return null; +namespace Seam\Resources\AccessGrant\PendingMutations { + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - code: $json->code ?? null, - created_access_method_ids: $json->created_access_method_ids ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - instant_key_max_use_count: $json->instant_key_max_use_count ?? null, - mode: $json->mode ?? null, - ); - } - public function __construct( - /** - * Specific PIN code to use for this access method. Only applicable when mode is 'code'. - */ - public string|null $code, - /** - * IDs of the access methods created for the requested access method. - */ - public array|null $created_access_method_ids, - /** - * Date and time at which the requested access method was added to the Access Grant. - */ - public string|null $created_at, - /** - * Display name of the access method. - */ - public string|null $display_name, - /** - * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. - */ - public int|null $instant_key_max_use_count, - /** - * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $mode, - ) {} -} + public function __construct( + /** + * Previous device IDs where access codes existed. + */ + public array|null $device_ids, + /** + * Previous end time for access. + */ + public string|null $ends_at, + /** + * Previous start time for access. + */ + public string|null $starts_at, + ) {} + } -/** - * New location configuration. - */ -class AccessGrantTo -{ - public static function from_json(mixed $json): AccessGrantTo|null + class To { - if (!$json) { - return null; + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + common_code_key: $json->common_code_key ?? null, + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - common_code_key: $json->common_code_key ?? null, - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Common code key to ensure PIN code reuse across devices. - */ - public string|null $common_code_key, - /** - * New device IDs where access codes should be created. - */ - public array|null $device_ids, - /** - * New end time for access. - */ - public string|null $ends_at, - /** - * New start time for access. - */ - public string|null $starts_at, - ) {} + public function __construct( + /** + * Common code key to ensure PIN code reuse across devices. + */ + public string|null $common_code_key, + /** + * New device IDs where access codes should be created. + */ + public array|null $device_ids, + /** + * New end time for access. + */ + public string|null $ends_at, + /** + * New start time for access. + */ + public string|null $starts_at, + ) {} + } } -/** - * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ -class AccessGrantWarnings -{ - public static function from_json(mixed $json): AccessGrantWarnings|null +namespace Seam\Resources\AccessGrant\Warnings { + /** + * Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). + */ + class FailedDevices { - if (!$json) { - return null; + public static function from_json(mixed $json): FailedDevices|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - access_method_ids: $json->access_method_ids ?? null, - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - failed_devices: array_map( - fn($f) => AccessGrantFailedDevices::from_json($f), - $json->failed_devices ?? [], - ), - message: $json->message ?? null, - new_code: $json->new_code ?? null, - original_code: $json->original_code ?? null, - reason: $json->reason ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * IDs of the access methods being updated. - */ - public array|null $access_method_ids, - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * ID of the device where the requested code was unavailable. - */ - public string|null $device_id, - /** - * Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). - */ - public array $failed_devices, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * The new PIN code that was assigned instead. - */ - public string|null $new_code, - /** - * The originally requested PIN code that was unavailable. - */ - public string|null $original_code, - /** - * Specific reason why the grant's times are not programmable on the device. - */ - public string|null $reason, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Device whose access code could not be revoked. + */ + public string|null $device_id, + /** + * Reason the access code could not be revoked (e.g. `offline_access_code_not_revocable`). + */ + public string|null $error_code, + /** + * Human-readable description of why revocation failed. + */ + public string|null $message, + ) {} + } } diff --git a/src/Resources/AccessMethod.php b/src/Resources/AccessMethod.php index c4be2ae1..7745b2f6 100644 --- a/src/Resources/AccessMethod.php +++ b/src/Resources/AccessMethod.php @@ -1,303 +1,289 @@ access_method_id ?? null, + client_session_token: $json->client_session_token ?? null, + code: $json->code ?? null, + created_at: $json->created_at ?? null, + customization_profile_id: $json->customization_profile_id ?? + null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => AccessMethod\Errors::from_json($e), + $json->errors ?? [], + ), + instant_key_url: $json->instant_key_url ?? null, + is_assignment_required: $json->is_assignment_required ?? null, + is_encoding_required: $json->is_encoding_required ?? null, + is_issued: $json->is_issued ?? null, + is_ready_for_assignment: $json->is_ready_for_assignment ?? null, + is_ready_for_encoding: $json->is_ready_for_encoding ?? null, + issued_at: $json->issued_at ?? null, + mode: $json->mode ?? null, + pending_mutations: array_map( + fn($p) => AccessMethod\PendingMutations::from_json($p), + $json->pending_mutations ?? [], + ), + warnings: array_map( + fn($w) => AccessMethod\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - access_method_id: $json->access_method_id ?? null, - client_session_token: $json->client_session_token ?? null, - code: $json->code ?? null, - created_at: $json->created_at ?? null, - customization_profile_id: $json->customization_profile_id ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => AccessMethodErrors::from_json($e), - $json->errors ?? [], - ), - instant_key_url: $json->instant_key_url ?? null, - is_assignment_required: $json->is_assignment_required ?? null, - is_encoding_required: $json->is_encoding_required ?? null, - is_issued: $json->is_issued ?? null, - is_ready_for_assignment: $json->is_ready_for_assignment ?? null, - is_ready_for_encoding: $json->is_ready_for_encoding ?? null, - issued_at: $json->issued_at ?? null, - mode: $json->mode ?? null, - pending_mutations: array_map( - fn($p) => AccessMethodPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - warnings: array_map( - fn($w) => AccessMethodWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * ID of the access method. - */ - public string|null $access_method_id, - /** - * Token of the client session associated with the access method. - */ - public string|null $client_session_token, - /** - * The actual PIN code for code access methods. - */ - public string|null $code, - /** - * Date and time at which the access method was created. - */ - public string|null $created_at, - /** - * ID of the customization profile associated with the access method. - */ - public string|null $customization_profile_id, - /** - * Display name of the access method. - */ - public string|null $display_name, - /** - * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ - public array $errors, - /** - * URL of the Instant Key for mobile key access methods. - */ - public string|null $instant_key_url, - /** - * Indicates whether an existing card credential must be assigned to this access method before it can be issued. Only applies to card-mode access methods on systems that support credential assignment. - */ - public bool|null $is_assignment_required, - /** - * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. - */ - public bool|null $is_encoding_required, - /** - * Indicates whether the access method has been issued. - */ - public bool|null $is_issued, - /** - * Indicates whether the access method is ready for card assignment. This is true when the access method is in card mode, has not yet been issued, and the system supports credential assignment. - */ - public bool|null $is_ready_for_assignment, - /** - * Indicates whether the access method is ready to be encoded. This is true when the credential has been created and the card has not yet been issued. - */ - public bool|null $is_ready_for_encoding, - /** - * Date and time at which the access method was issued. - */ - public string|null $issued_at, - /** - * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $mode, - /** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ - public array $pending_mutations, - /** - * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ - public array $warnings, - /** - * ID of the Seam workspace associated with the access method. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the access method. + */ + public string|null $access_method_id, + /** + * Token of the client session associated with the access method. + */ + public string|null $client_session_token, + /** + * The actual PIN code for code access methods. + */ + public string|null $code, + /** + * Date and time at which the access method was created. + */ + public string|null $created_at, + /** + * ID of the customization profile associated with the access method. + */ + public string|null $customization_profile_id, + /** + * Display name of the access method. + */ + public string|null $display_name, + /** + * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + public array $errors, + /** + * URL of the Instant Key for mobile key access methods. + */ + public string|null $instant_key_url, + /** + * Indicates whether an existing card credential must be assigned to this access method before it can be issued. Only applies to card-mode access methods on systems that support credential assignment. + */ + public bool|null $is_assignment_required, + /** + * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. + */ + public bool|null $is_encoding_required, + /** + * Indicates whether the access method has been issued. + */ + public bool|null $is_issued, + /** + * Indicates whether the access method is ready for card assignment. This is true when the access method is in card mode, has not yet been issued, and the system supports credential assignment. + */ + public bool|null $is_ready_for_assignment, + /** + * Indicates whether the access method is ready to be encoded. This is true when the credential has been created and the card has not yet been issued. + */ + public bool|null $is_ready_for_encoding, + /** + * Date and time at which the access method was issued. + */ + public string|null $issued_at, + /** + * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $mode, + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + public array $pending_mutations, + /** + * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + public array $warnings, + /** + * ID of the Seam workspace associated with the access method. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ -class AccessMethodErrors -{ - public static function from_json(mixed $json): AccessMethodErrors|null +namespace Seam\Resources\AccessMethod { + /** + * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Previous device configuration. - */ -class AccessMethodFrom -{ - public static function from_json(mixed $json): AccessMethodFrom|null + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + class PendingMutations { - if (!$json) { - return null; + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); } - return new self( - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Previous device IDs where access was provisioned. - */ - public array|null $device_ids, - /** - * Previous end time for access. - */ - public string|null $ends_at, - /** - * Previous start time for access. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + public PendingMutations\To|null $to, + ) {} + } -/** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ -class AccessMethodPendingMutations -{ - public static function from_json( - mixed $json, - ): AccessMethodPendingMutations|null { - if (!$json) { - return null; + /** + * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + original_access_method_id: $json->original_access_method_id ?? + null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - from: isset($json->from) - ? AccessMethodFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) ? AccessMethodTo::from_json($json->to) : null, - ); - } - public function __construct( - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Previous device configuration. - */ - public AccessMethodFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. - */ - public string|null $mutation_code, - /** - * New device configuration. - */ - public AccessMethodTo|null $to, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * ID of the original access method from which this backup access method was split, if applicable. + */ + public string|null $original_access_method_id, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * New device configuration. - */ -class AccessMethodTo -{ - public static function from_json(mixed $json): AccessMethodTo|null +namespace Seam\Resources\AccessMethod\PendingMutations { + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * New device IDs where access is being provisioned. - */ - public array|null $device_ids, - /** - * New end time for access. - */ - public string|null $ends_at, - /** - * New start time for access. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + public array|null $device_ids, + /** + * Previous end time for access. + */ + public string|null $ends_at, + /** + * Previous start time for access. + */ + public string|null $starts_at, + ) {} + } -/** - * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ -class AccessMethodWarnings -{ - public static function from_json(mixed $json): AccessMethodWarnings|null + class To { - if (!$json) { - return null; + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - original_access_method_id: $json->original_access_method_id ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * ID of the original access method from which this backup access method was split, if applicable. - */ - public string|null $original_access_method_id, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + public array|null $device_ids, + /** + * New end time for access. + */ + public string|null $ends_at, + /** + * New start time for access. + */ + public string|null $starts_at, + ) {} + } } diff --git a/src/Resources/AcsAccessGroup.php b/src/Resources/AcsAccessGroup.php index 9d8d7bc4..b51f042d 100644 --- a/src/Resources/AcsAccessGroup.php +++ b/src/Resources/AcsAccessGroup.php @@ -1,359 +1,346 @@ access_group_type ?? null, + access_group_type_display_name: $json->access_group_type_display_name ?? + null, + access_schedule: isset($json->access_schedule) + ? AcsAccessGroup\AccessSchedule::from_json( + $json->access_schedule, + ) + : null, + acs_access_group_id: $json->acs_access_group_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => 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( + fn($p) => AcsAccessGroup\PendingMutations::from_json($p), + $json->pending_mutations ?? [], + ), + warnings: array_map( + fn($w) => AcsAccessGroup\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - access_group_type: $json->access_group_type ?? null, - access_group_type_display_name: $json->access_group_type_display_name ?? - null, - access_schedule: isset($json->access_schedule) - ? AcsAccessGroupAccessSchedule::from_json( - $json->access_schedule, - ) - : null, - acs_access_group_id: $json->acs_access_group_id ?? null, - acs_system_id: $json->acs_system_id ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => AcsAccessGroupErrors::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( - fn($p) => AcsAccessGroupPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - warnings: array_map( - fn($w) => AcsAccessGroupWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * @deprecated Use `external_type`. - */ - public string|null $access_group_type, - /** - * @deprecated Use `external_type_display_name`. - */ - public string|null $access_group_type_display_name, - /** - * `starts_at` and `ends_at` timestamps for the access group's access. - */ - public AcsAccessGroupAccessSchedule|null $access_schedule, - /** - * ID of the access group. - */ - public string|null $acs_access_group_id, - /** - * ID of the access control system that contains the access group. - */ - public string|null $acs_system_id, - /** - * ID of the connected account that contains the access group. - */ - public string|null $connected_account_id, - /** - * Date and time at which the access group was created. - */ - public string|null $created_at, - /** - * Display name for the access group. - */ - public string|null $display_name, - /** - * Errors associated with the `acs_access_group`. - */ - public array $errors, - /** - * Brand-specific terminology for the access group type. - */ - public string|null $external_type, - /** - * Display name that corresponds to the brand-specific terminology for the access group type. - */ - public string|null $external_type_display_name, - /** - * Indicates whether Seam manages the access group. - */ - public bool|null $is_managed, - /** - * Name of the access group. - */ - public string|null $name, - /** - * Collection of pending mutations for the access group. Represents operations that have been requested but not yet completed on the integrated access system. - */ - public array $pending_mutations, - /** - * Warnings associated with the `acs_access_group`. - */ - public array $warnings, - /** - * ID of the workspace that contains the access group. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * @deprecated Use `external_type`. + */ + public string|null $access_group_type, + /** + * @deprecated Use `external_type_display_name`. + */ + public string|null $access_group_type_display_name, + /** + * `starts_at` and `ends_at` timestamps for the access group's access. + */ + public AcsAccessGroup\AccessSchedule|null $access_schedule, + /** + * ID of the access group. + */ + public string|null $acs_access_group_id, + /** + * ID of the access control system that contains the access group. + */ + public string|null $acs_system_id, + /** + * ID of the connected account that contains the access group. + */ + public string|null $connected_account_id, + /** + * Date and time at which the access group was created. + */ + public string|null $created_at, + /** + * Display name for the access group. + */ + public string|null $display_name, + /** + * Errors associated with the `acs_access_group`. + */ + public array $errors, + /** + * Brand-specific terminology for the access group type. + */ + public string|null $external_type, + /** + * Display name that corresponds to the brand-specific terminology for the access group type. + */ + public string|null $external_type_display_name, + /** + * Indicates whether Seam manages the access group. + */ + public bool|null $is_managed, + /** + * Name of the access group. + */ + public string|null $name, + /** + * Collection of pending mutations for the access group. Represents operations that have been requested but not yet completed on the integrated access system. + */ + public array $pending_mutations, + /** + * Warnings associated with the `acs_access_group`. + */ + public array $warnings, + /** + * ID of the workspace that contains the access group. + */ + public string|null $workspace_id, + ) {} + } } -/** - * `starts_at` and `ends_at` timestamps for the access group's access. - */ -class AcsAccessGroupAccessSchedule -{ - public static function from_json( - mixed $json, - ): AcsAccessGroupAccessSchedule|null { - if (!$json) { - return null; +namespace Seam\Resources\AcsAccessGroup { + /** + * `starts_at` and `ends_at` timestamps for the access group's access. + */ + class AccessSchedule + { + public static function from_json(mixed $json): AccessSchedule|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Date and time at which the user's access ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $ends_at, - /** - * Date and time at which the user's access starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Date and time at which the user's access ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $ends_at, + /** + * Date and time at which the user's access starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + ) {} + } -/** - * Errors associated with the `acs_access_group`. - */ -class AcsAccessGroupErrors -{ - public static function from_json(mixed $json): AcsAccessGroupErrors|null + /** + * Errors associated with the `acs_access_group`. + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Old access group information. - */ -class AcsAccessGroupFrom -{ - public static function from_json(mixed $json): AcsAccessGroupFrom|null + /** + * Collection of pending mutations for the access group. Represents operations that have been requested but not yet completed on the integrated access system. + */ + class PendingMutations { - if (!$json) { - return null; + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + acs_user_id: $json->acs_user_id ?? null, + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + variant: $json->variant ?? null, + ); } - return new self( - acs_entrance_id: $json->acs_entrance_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Old entrance ID. - */ - public string|null $acs_entrance_id, - /** - * Old user ID. - */ - public string|null $acs_user_id, - /** - * Ending time for the access schedule. - */ - public string|null $ends_at, - /** - * Name of the access group. - */ - public string|null $name, - /** - * Starting time for the access schedule. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * ID of the user involved in the scheduled change. + */ + public string|null $acs_user_id, + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + public PendingMutations\To|null $to, + /** + * Whether the user is scheduled to be added to or removed from this access group. + */ + public string|null $variant, + ) {} + } -/** - * Collection of pending mutations for the access group. Represents operations that have been requested but not yet completed on the integrated access system. - */ -class AcsAccessGroupPendingMutations -{ - public static function from_json( - mixed $json, - ): AcsAccessGroupPendingMutations|null { - if (!$json) { - return null; + /** + * Warnings associated with the `acs_access_group`. + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - acs_user_id: $json->acs_user_id ?? null, - created_at: $json->created_at ?? null, - from: isset($json->from) - ? AcsAccessGroupFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) - ? AcsAccessGroupTo::from_json($json->to) - : null, - variant: $json->variant ?? null, - ); - } - public function __construct( - /** - * ID of the user involved in the scheduled change. - */ - public string|null $acs_user_id, - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Old access group information. - */ - public AcsAccessGroupFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of pushing an access group creation to the integrated access system. - */ - public string|null $mutation_code, - /** - * New access group information. - */ - public AcsAccessGroupTo|null $to, - /** - * Whether the user is scheduled to be added to or removed from this access group. - */ - public string|null $variant, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * New access group information. - */ -class AcsAccessGroupTo -{ - public static function from_json(mixed $json): AcsAccessGroupTo|null +namespace Seam\Resources\AcsAccessGroup\PendingMutations { + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + acs_entrance_id: $json->acs_entrance_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - acs_entrance_id: $json->acs_entrance_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * New entrance ID. - */ - public string|null $acs_entrance_id, - /** - * New user ID. - */ - public string|null $acs_user_id, - /** - * Ending time for the access schedule. - */ - public string|null $ends_at, - /** - * Name of the access group. - */ - public string|null $name, - /** - * Starting time for the access schedule. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Old entrance ID. + */ + public string|null $acs_entrance_id, + /** + * Old user ID. + */ + public string|null $acs_user_id, + /** + * Ending time for the access schedule. + */ + public string|null $ends_at, + /** + * Name of the access group. + */ + public string|null $name, + /** + * Starting time for the access schedule. + */ + public string|null $starts_at, + ) {} + } -/** - * Warnings associated with the `acs_access_group`. - */ -class AcsAccessGroupWarnings -{ - public static function from_json(mixed $json): AcsAccessGroupWarnings|null + class To { - if (!$json) { - return null; + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + acs_entrance_id: $json->acs_entrance_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * New entrance ID. + */ + public string|null $acs_entrance_id, + /** + * New user ID. + */ + public string|null $acs_user_id, + /** + * Ending time for the access schedule. + */ + public string|null $ends_at, + /** + * Name of the access group. + */ + public string|null $name, + /** + * Starting time for the access schedule. + */ + public string|null $starts_at, + ) {} + } } diff --git a/src/Resources/AcsCredential.php b/src/Resources/AcsCredential.php index 6ec383e7..f62fceda 100644 --- a/src/Resources/AcsCredential.php +++ b/src/Resources/AcsCredential.php @@ -1,383 +1,389 @@ access_method ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - acs_credential_pool_id: $json->acs_credential_pool_id ?? null, - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - akiles_metadata: isset($json->akiles_metadata) - ? AcsCredentialAkilesMetadata::from_json($json->akiles_metadata) - : null, - assa_abloy_vostio_metadata: isset($json->assa_abloy_vostio_metadata) - ? AcsCredentialAssaAbloyVostioMetadata::from_json( + public static function from_json(mixed $json): AcsCredential|null + { + if (!$json) { + return null; + } + return new self( + access_method: $json->access_method ?? null, + acs_credential_id: $json->acs_credential_id ?? null, + acs_credential_pool_id: $json->acs_credential_pool_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + akiles_metadata: isset($json->akiles_metadata) + ? AcsCredential\AkilesMetadata::from_json( + $json->akiles_metadata, + ) + : null, + assa_abloy_vostio_metadata: isset( $json->assa_abloy_vostio_metadata, ) - : null, - card_number: $json->card_number ?? null, - code: $json->code ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => AcsCredentialErrors::from_json($e), - $json->errors ?? [], - ), - external_type: $json->external_type ?? null, - external_type_display_name: $json->external_type_display_name ?? - null, - is_issued: $json->is_issued ?? null, - is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? - null, - is_managed: $json->is_managed ?? null, - is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? - null, - is_one_time_use: $json->is_one_time_use ?? null, - issued_at: $json->issued_at ?? null, - latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? - null, - parent_acs_credential_id: $json->parent_acs_credential_id ?? null, - starts_at: $json->starts_at ?? null, - user_identity_id: $json->user_identity_id ?? null, - visionline_metadata: isset($json->visionline_metadata) - ? AcsCredentialVisionlineMetadata::from_json( - $json->visionline_metadata, - ) - : null, - warnings: array_map( - fn($w) => AcsCredentialWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } + ? AcsCredential\AssaAbloyVostioMetadata::from_json( + $json->assa_abloy_vostio_metadata, + ) + : null, + card_number: $json->card_number ?? null, + code: $json->code ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => AcsCredential\Errors::from_json($e), + $json->errors ?? [], + ), + external_type: $json->external_type ?? null, + external_type_display_name: $json->external_type_display_name ?? + null, + is_issued: $json->is_issued ?? null, + is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? + null, + is_managed: $json->is_managed ?? null, + is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? + null, + is_one_time_use: $json->is_one_time_use ?? null, + issued_at: $json->issued_at ?? null, + latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? + null, + parent_acs_credential_id: $json->parent_acs_credential_id ?? + null, + starts_at: $json->starts_at ?? null, + user_identity_id: $json->user_identity_id ?? null, + visionline_metadata: isset($json->visionline_metadata) + ? AcsCredential\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + warnings: array_map( + fn($w) => AcsCredential\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); + } - public function __construct( - /** - * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $access_method, - /** - * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $acs_credential_id, - /** - * ID of the credential pool to which the credential belongs. - */ - public string|null $acs_credential_pool_id, - /** - * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $acs_system_id, - /** - * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $acs_user_id, - /** - * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public AcsCredentialAkilesMetadata|null $akiles_metadata, - /** - * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public AcsCredentialAssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, - /** - * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_number, - /** - * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $code, - /** - * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $connected_account_id, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. - */ - public string|null $created_at, - /** - * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $display_name, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. - */ - public string|null $ends_at, - /** - * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $errors, - /** - * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. - */ - public string|null $external_type, - /** - * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $external_type_display_name, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. - */ - public bool|null $is_issued, - /** - * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. - */ - public bool|null $is_latest_desired_state_synced_with_provider, - /** - * Indicates whether Seam manages the credential. - */ - public bool|null $is_managed, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). - */ - public bool|null $is_multi_phone_sync_credential, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. - */ - public bool|null $is_one_time_use, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. - */ - public string|null $issued_at, - /** - * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. - */ - public string|null $latest_desired_state_synced_with_provider_at, - /** - * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $parent_acs_credential_id, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - /** - * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $user_identity_id, - /** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public AcsCredentialVisionlineMetadata|null $visionline_metadata, - /** - * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $warnings, - /** - * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $access_method, + /** + * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_credential_id, + /** + * ID of the credential pool to which the credential belongs. + */ + public string|null $acs_credential_pool_id, + /** + * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_system_id, + /** + * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $acs_user_id, + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredential\AkilesMetadata|null $akiles_metadata, + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredential\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + /** + * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_number, + /** + * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $code, + /** + * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $connected_account_id, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. + */ + public string|null $created_at, + /** + * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $display_name, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. + */ + public string|null $ends_at, + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array $errors, + /** + * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. + */ + public string|null $external_type, + /** + * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $external_type_display_name, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. + */ + public bool|null $is_issued, + /** + * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. + */ + public bool|null $is_latest_desired_state_synced_with_provider, + /** + * Indicates whether Seam manages the credential. + */ + public bool|null $is_managed, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). + */ + public bool|null $is_multi_phone_sync_credential, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. + */ + public bool|null $is_one_time_use, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. + */ + public string|null $issued_at, + /** + * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. + */ + public string|null $latest_desired_state_synced_with_provider_at, + /** + * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $parent_acs_credential_id, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + /** + * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $user_identity_id, + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredential\VisionlineMetadata|null $visionline_metadata, + /** + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array $warnings, + /** + * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $workspace_id, + ) {} + } } -/** - * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class AcsCredentialAkilesMetadata -{ - public static function from_json( - mixed $json, - ): AcsCredentialAkilesMetadata|null { - if (!$json) { - return null; +namespace Seam\Resources\AcsCredential { + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AkilesMetadata + { + public static function from_json(mixed $json): AkilesMetadata|null + { + if (!$json) { + return null; + } + return new self(member_pin_id: $json->member_pin_id ?? null); } - return new self(member_pin_id: $json->member_pin_id ?? null); - } - public function __construct( - /** - * ID of the Akiles member PIN. - */ - public string|null $member_pin_id, - ) {} -} + public function __construct( + /** + * ID of the Akiles member PIN. + */ + public string|null $member_pin_id, + ) {} + } -/** - * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class AcsCredentialAssaAbloyVostioMetadata -{ - public static function from_json( - mixed $json, - ): AcsCredentialAssaAbloyVostioMetadata|null { - if (!$json) { - return null; + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AssaAbloyVostioMetadata + { + public static function from_json( + mixed $json, + ): AssaAbloyVostioMetadata|null { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + door_names: $json->door_names ?? null, + endpoint_id: $json->endpoint_id ?? null, + key_id: $json->key_id ?? null, + key_issuing_request_id: $json->key_issuing_request_id ?? null, + override_guest_acs_entrance_ids: $json->override_guest_acs_entrance_ids ?? + null, + ); } - return new self( - auto_join: $json->auto_join ?? null, - door_names: $json->door_names ?? null, - endpoint_id: $json->endpoint_id ?? null, - key_id: $json->key_id ?? null, - key_issuing_request_id: $json->key_issuing_request_id ?? null, - override_guest_acs_entrance_ids: $json->override_guest_acs_entrance_ids ?? - null, - ); - } - public function __construct( - /** - * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. - */ - public bool|null $auto_join, - /** - * Names of the doors to which to grant access in the Vostio access system. - */ - public array|null $door_names, - /** - * Endpoint ID in the Vostio access system. - */ - public string|null $endpoint_id, - /** - * Key ID in the Vostio access system. - */ - public string|null $key_id, - /** - * Key issuing request ID in the Vostio access system. - */ - public string|null $key_issuing_request_id, - /** - * IDs of the guest entrances to override in the Vostio access system. - */ - public array|null $override_guest_acs_entrance_ids, - ) {} -} + public function __construct( + /** + * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. + */ + public bool|null $auto_join, + /** + * Names of the doors to which to grant access in the Vostio access system. + */ + public array|null $door_names, + /** + * Endpoint ID in the Vostio access system. + */ + public string|null $endpoint_id, + /** + * Key ID in the Vostio access system. + */ + public string|null $key_id, + /** + * Key issuing request ID in the Vostio access system. + */ + public string|null $key_issuing_request_id, + /** + * IDs of the guest entrances to override in the Vostio access system. + */ + public array|null $override_guest_acs_entrance_ids, + ) {} + } -/** - * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class AcsCredentialErrors -{ - public static function from_json(mixed $json): AcsCredentialErrors|null + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - public string|null $error_code, - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + public string|null $error_code, + public string|null $message, + ) {} + } -/** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class AcsCredentialVisionlineMetadata -{ - public static function from_json( - mixed $json, - ): AcsCredentialVisionlineMetadata|null { - if (!$json) { - return null; + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + card_function_type: $json->card_function_type ?? null, + card_id: $json->card_id ?? null, + common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, + credential_id: $json->credential_id ?? null, + guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, + is_valid: $json->is_valid ?? null, + joiner_acs_credential_ids: $json->joiner_acs_credential_ids ?? + null, + ); } - return new self( - auto_join: $json->auto_join ?? null, - card_function_type: $json->card_function_type ?? null, - card_id: $json->card_id ?? null, - common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, - credential_id: $json->credential_id ?? null, - guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, - is_valid: $json->is_valid ?? null, - joiner_acs_credential_ids: $json->joiner_acs_credential_ids ?? null, - ); - } - public function __construct( - /** - * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. - */ - public bool|null $auto_join, - /** - * Card function type in the Visionline access system. - */ - public string|null $card_function_type, - /** - * ID of the card in the Visionline access system. - */ - public string|null $card_id, - /** - * Common entrance IDs in the Visionline access system. - */ - public array|null $common_acs_entrance_ids, - /** - * ID of the credential in the Visionline access system. - */ - public string|null $credential_id, - /** - * Guest entrance IDs in the Visionline access system. - */ - public array|null $guest_acs_entrance_ids, - /** - * Indicates whether the credential is valid. - */ - public bool|null $is_valid, - /** - * IDs of the credentials to which you want to join. - */ - public array|null $joiner_acs_credential_ids, - ) {} -} + public function __construct( + /** + * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. + */ + public bool|null $auto_join, + /** + * Card function type in the Visionline access system. + */ + public string|null $card_function_type, + /** + * ID of the card in the Visionline access system. + */ + public string|null $card_id, + /** + * Common entrance IDs in the Visionline access system. + */ + public array|null $common_acs_entrance_ids, + /** + * ID of the credential in the Visionline access system. + */ + public string|null $credential_id, + /** + * Guest entrance IDs in the Visionline access system. + */ + public array|null $guest_acs_entrance_ids, + /** + * Indicates whether the credential is valid. + */ + public bool|null $is_valid, + /** + * IDs of the credentials to which you want to join. + */ + public array|null $joiner_acs_credential_ids, + ) {} + } -/** - * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class AcsCredentialWarnings -{ - public static function from_json(mixed $json): AcsCredentialWarnings|null + /** + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class Warnings { - if (!$json) { - return null; + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } diff --git a/src/Resources/AcsEncoder.php b/src/Resources/AcsEncoder.php index 2375f65d..fdb6ee77 100644 --- a/src/Resources/AcsEncoder.php +++ b/src/Resources/AcsEncoder.php @@ -1,105 +1,107 @@ acs_encoder_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => AcsEncoder\Errors::from_json($e), + $json->errors ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - acs_encoder_id: $json->acs_encoder_id ?? null, - acs_system_id: $json->acs_system_id ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => AcsEncoderErrors::from_json($e), - $json->errors ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * ID of the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ - public string|null $acs_encoder_id, - /** - * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ - public string|null $acs_system_id, - /** - * ID of the connected account that contains the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ - public string|null $connected_account_id, - /** - * Date and time at which the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners) was created. - */ - public string|null $created_at, - /** - * Display name for the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ - public string|null $display_name, - /** - * Errors associated with the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ - public array $errors, - /** - * ID of the workspace that contains the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + public string|null $acs_encoder_id, + /** + * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + public string|null $acs_system_id, + /** + * ID of the connected account that contains the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + public string|null $connected_account_id, + /** + * Date and time at which the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners) was created. + */ + public string|null $created_at, + /** + * Display name for the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + public string|null $display_name, + /** + * Errors associated with the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + public array $errors, + /** + * ID of the workspace that contains the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - */ -class AcsEncoderErrors -{ - public static function from_json(mixed $json): AcsEncoderErrors|null +namespace Seam\Resources\AcsEncoder { + /** + * Errors associated with the [encoder](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } } diff --git a/src/Resources/AcsEntrance.php b/src/Resources/AcsEntrance.php index 870e7b6a..59fdad0d 100644 --- a/src/Resources/AcsEntrance.php +++ b/src/Resources/AcsEntrance.php @@ -1,751 +1,767 @@ acs_entrance_id ?? null, - acs_system_id: $json->acs_system_id ?? null, - akiles_metadata: isset($json->akiles_metadata) - ? AcsEntranceAkilesMetadata::from_json($json->akiles_metadata) - : null, - assa_abloy_vostio_metadata: isset($json->assa_abloy_vostio_metadata) - ? AcsEntranceAssaAbloyVostioMetadata::from_json( + public static function from_json(mixed $json): AcsEntrance|null + { + if (!$json) { + return null; + } + return new self( + acs_entrance_id: $json->acs_entrance_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + akiles_metadata: isset($json->akiles_metadata) + ? AcsEntrance\AkilesMetadata::from_json( + $json->akiles_metadata, + ) + : null, + assa_abloy_vostio_metadata: isset( $json->assa_abloy_vostio_metadata, ) - : null, - avigilon_alta_metadata: isset($json->avigilon_alta_metadata) - ? AcsEntranceAvigilonAltaMetadata::from_json( - $json->avigilon_alta_metadata, - ) - : null, - brivo_metadata: isset($json->brivo_metadata) - ? AcsEntranceBrivoMetadata::from_json($json->brivo_metadata) - : null, - can_belong_to_reservation: $json->can_belong_to_reservation ?? null, - can_unlock_with_card: $json->can_unlock_with_card ?? null, - can_unlock_with_cloud_key: $json->can_unlock_with_cloud_key ?? null, - can_unlock_with_code: $json->can_unlock_with_code ?? null, - can_unlock_with_mobile_key: $json->can_unlock_with_mobile_key ?? - null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - dormakaba_ambiance_metadata: isset( - $json->dormakaba_ambiance_metadata, - ) - ? AcsEntranceDormakabaAmbianceMetadata::from_json( + ? AcsEntrance\AssaAbloyVostioMetadata::from_json( + $json->assa_abloy_vostio_metadata, + ) + : null, + avigilon_alta_metadata: isset($json->avigilon_alta_metadata) + ? AcsEntrance\AvigilonAltaMetadata::from_json( + $json->avigilon_alta_metadata, + ) + : null, + brivo_metadata: isset($json->brivo_metadata) + ? AcsEntrance\BrivoMetadata::from_json( + $json->brivo_metadata, + ) + : null, + can_belong_to_reservation: $json->can_belong_to_reservation ?? + null, + can_unlock_with_card: $json->can_unlock_with_card ?? null, + can_unlock_with_cloud_key: $json->can_unlock_with_cloud_key ?? + null, + can_unlock_with_code: $json->can_unlock_with_code ?? null, + can_unlock_with_mobile_key: $json->can_unlock_with_mobile_key ?? + null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + dormakaba_ambiance_metadata: isset( $json->dormakaba_ambiance_metadata, ) - : null, - dormakaba_community_metadata: isset( - $json->dormakaba_community_metadata, - ) - ? AcsEntranceDormakabaCommunityMetadata::from_json( + ? AcsEntrance\DormakabaAmbianceMetadata::from_json( + $json->dormakaba_ambiance_metadata, + ) + : null, + dormakaba_community_metadata: isset( $json->dormakaba_community_metadata, ) - : null, - errors: array_map( - fn($e) => AcsEntranceErrors::from_json($e), - $json->errors ?? [], - ), - hotek_metadata: isset($json->hotek_metadata) - ? AcsEntranceHotekMetadata::from_json($json->hotek_metadata) - : null, - is_locked: $json->is_locked ?? null, - latch_metadata: isset($json->latch_metadata) - ? AcsEntranceLatchMetadata::from_json($json->latch_metadata) - : null, - salto_ks_metadata: isset($json->salto_ks_metadata) - ? AcsEntranceSaltoKsMetadata::from_json( - $json->salto_ks_metadata, - ) - : null, - salto_space_metadata: isset($json->salto_space_metadata) - ? AcsEntranceSaltoSpaceMetadata::from_json( - $json->salto_space_metadata, - ) - : null, - space_ids: $json->space_ids ?? null, - visionline_metadata: isset($json->visionline_metadata) - ? AcsEntranceVisionlineMetadata::from_json( - $json->visionline_metadata, - ) - : null, - warnings: array_map( - fn($w) => AcsEntranceWarnings::from_json($w), - $json->warnings ?? [], - ), - ); - } + ? AcsEntrance\DormakabaCommunityMetadata::from_json( + $json->dormakaba_community_metadata, + ) + : null, + errors: array_map( + fn($e) => AcsEntrance\Errors::from_json($e), + $json->errors ?? [], + ), + hotek_metadata: isset($json->hotek_metadata) + ? AcsEntrance\HotekMetadata::from_json( + $json->hotek_metadata, + ) + : null, + is_locked: $json->is_locked ?? null, + latch_metadata: isset($json->latch_metadata) + ? AcsEntrance\LatchMetadata::from_json( + $json->latch_metadata, + ) + : null, + salto_ks_metadata: isset($json->salto_ks_metadata) + ? AcsEntrance\SaltoKsMetadata::from_json( + $json->salto_ks_metadata, + ) + : null, + salto_space_metadata: isset($json->salto_space_metadata) + ? AcsEntrance\SaltoSpaceMetadata::from_json( + $json->salto_space_metadata, + ) + : null, + space_ids: $json->space_ids ?? null, + visionline_metadata: isset($json->visionline_metadata) + ? AcsEntrance\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + warnings: array_map( + fn($w) => AcsEntrance\Warnings::from_json($w), + $json->warnings ?? [], + ), + ); + } - public function __construct( - /** - * ID of the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public string|null $acs_entrance_id, - /** - * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public string|null $acs_system_id, - /** - * Akiles-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceAkilesMetadata|null $akiles_metadata, - /** - * ASSA ABLOY Vostio-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceAssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, - /** - * Avigilon Alta-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceAvigilonAltaMetadata|null $avigilon_alta_metadata, - /** - * Brivo-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceBrivoMetadata|null $brivo_metadata, - /** - * Indicates whether the ACS entrance can belong to a reservation via an access_grant.reservation_key. - */ - public bool|null $can_belong_to_reservation, - /** - * Indicates whether the ACS entrance can be unlocked with card credentials. - */ - public bool|null $can_unlock_with_card, - /** - * Indicates whether the ACS entrance can be unlocked with cloud key credentials. - */ - public bool|null $can_unlock_with_cloud_key, - /** - * Indicates whether the ACS entrance can be unlocked with pin codes. - */ - public bool|null $can_unlock_with_code, - /** - * Indicates whether the ACS entrance can be unlocked with mobile key credentials. - */ - public bool|null $can_unlock_with_mobile_key, - /** - * ID of the [connected account](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public string|null $connected_account_id, - /** - * Date and time at which the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) was created. - */ - public string|null $created_at, - /** - * Display name for the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public string|null $display_name, - /** - * dormakaba Ambiance-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceDormakabaAmbianceMetadata|null $dormakaba_ambiance_metadata, - /** - * dormakaba Community-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceDormakabaCommunityMetadata|null $dormakaba_community_metadata, - /** - * Errors associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public array $errors, - /** - * Hotek-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceHotekMetadata|null $hotek_metadata, - /** - * Indicates whether the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) is currently locked. - */ - public bool|null $is_locked, - /** - * Latch-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceLatchMetadata|null $latch_metadata, - /** - * Salto KS-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceSaltoKsMetadata|null $salto_ks_metadata, - /** - * Salto Space-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceSaltoSpaceMetadata|null $salto_space_metadata, - /** - * IDs of the spaces that the entrance is in. - */ - public array|null $space_ids, - /** - * Visionline-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public AcsEntranceVisionlineMetadata|null $visionline_metadata, - /** - * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public array $warnings, - ) {} + public function __construct( + /** + * ID of the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public string|null $acs_entrance_id, + /** + * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public string|null $acs_system_id, + /** + * Akiles-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\AkilesMetadata|null $akiles_metadata, + /** + * ASSA ABLOY Vostio-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + /** + * Avigilon Alta-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\AvigilonAltaMetadata|null $avigilon_alta_metadata, + /** + * Brivo-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\BrivoMetadata|null $brivo_metadata, + /** + * Indicates whether the ACS entrance can belong to a reservation via an access_grant.reservation_key. + */ + public bool|null $can_belong_to_reservation, + /** + * Indicates whether the ACS entrance can be unlocked with card credentials. + */ + public bool|null $can_unlock_with_card, + /** + * Indicates whether the ACS entrance can be unlocked with cloud key credentials. + */ + public bool|null $can_unlock_with_cloud_key, + /** + * Indicates whether the ACS entrance can be unlocked with pin codes. + */ + public bool|null $can_unlock_with_code, + /** + * Indicates whether the ACS entrance can be unlocked with mobile key credentials. + */ + public bool|null $can_unlock_with_mobile_key, + /** + * ID of the [connected account](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public string|null $connected_account_id, + /** + * Date and time at which the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) was created. + */ + public string|null $created_at, + /** + * Display name for the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public string|null $display_name, + /** + * dormakaba Ambiance-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\DormakabaAmbianceMetadata|null $dormakaba_ambiance_metadata, + /** + * dormakaba Community-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\DormakabaCommunityMetadata|null $dormakaba_community_metadata, + /** + * Errors associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public array $errors, + /** + * Hotek-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\HotekMetadata|null $hotek_metadata, + /** + * Indicates whether the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) is currently locked. + */ + public bool|null $is_locked, + /** + * Latch-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\LatchMetadata|null $latch_metadata, + /** + * Salto KS-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\SaltoKsMetadata|null $salto_ks_metadata, + /** + * Salto Space-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\SaltoSpaceMetadata|null $salto_space_metadata, + /** + * IDs of the spaces that the entrance is in. + */ + public array|null $space_ids, + /** + * Visionline-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public AcsEntrance\VisionlineMetadata|null $visionline_metadata, + /** + * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + public array $warnings, + ) {} + } } -/** - * Actions the gadget exposes (for example, open). - */ -class AcsEntranceActions -{ - public static function from_json(mixed $json): AcsEntranceActions|null +namespace Seam\Resources\AcsEntrance { + /** + * Akiles-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class AkilesMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): AkilesMetadata|null + { + if (!$json) { + return null; + } + return new self( + actions: array_map( + fn($a) => AkilesMetadata\Actions::from_json($a), + $json->actions ?? [], + ), + gadget_id: $json->gadget_id ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + ); } - return new self(id: $json->id ?? null, name: $json->name ?? null); - } - public function __construct( - /** - * ID of the gadget action. - */ - public string|null $id, - /** - * Name of the gadget action. - */ - public string|null $name, - ) {} -} + public function __construct( + /** + * Actions the gadget exposes (for example, open). + */ + public array $actions, + /** + * ID of the Akiles gadget. + */ + public string|null $gadget_id, + /** + * ID of the Akiles site the gadget belongs to. + */ + public string|null $site_id, + /** + * Name of the Akiles site the gadget belongs to. + */ + public string|null $site_name, + ) {} + } -/** - * Akiles-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceAkilesMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceAkilesMetadata|null { - if (!$json) { - return null; + /** + * ASSA ABLOY Vostio-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class AssaAbloyVostioMetadata + { + public static function from_json( + mixed $json, + ): AssaAbloyVostioMetadata|null { + if (!$json) { + return null; + } + return new self( + door_name: $json->door_name ?? null, + door_number: $json->door_number ?? null, + door_type: $json->door_type ?? null, + pms_id: $json->pms_id ?? null, + stand_open: $json->stand_open ?? null, + ); } - return new self( - actions: array_map( - fn($a) => AcsEntranceActions::from_json($a), - $json->actions ?? [], - ), - gadget_id: $json->gadget_id ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - ); - } - public function __construct( - /** - * Actions the gadget exposes (for example, open). - */ - public array $actions, - /** - * ID of the Akiles gadget. - */ - public string|null $gadget_id, - /** - * ID of the Akiles site the gadget belongs to. - */ - public string|null $site_id, - /** - * Name of the Akiles site the gadget belongs to. - */ - public string|null $site_name, - ) {} -} + public function __construct( + /** + * Name of the door in the Vostio access system. + */ + public string|null $door_name, + /** + * Number of the door in the Vostio access system. + */ + public float|null $door_number, + /** + * Type of the door in the Vostio access system. + */ + public string|null $door_type, + /** + * PMS ID of the door in the Vostio access system. + */ + public string|null $pms_id, + /** + * Indicates whether keys are allowed to set the door in stand open mode in the Vostio access system. + */ + public bool|null $stand_open, + ) {} + } -/** - * ASSA ABLOY Vostio-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceAssaAbloyVostioMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceAssaAbloyVostioMetadata|null { - if (!$json) { - return null; + /** + * Avigilon Alta-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class AvigilonAltaMetadata + { + public static function from_json(mixed $json): AvigilonAltaMetadata|null + { + if (!$json) { + return null; + } + return new self( + entry_name: $json->entry_name ?? null, + entry_relays_total_count: $json->entry_relays_total_count ?? + null, + org_name: $json->org_name ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + zone_id: $json->zone_id ?? null, + zone_name: $json->zone_name ?? null, + ); } - return new self( - door_name: $json->door_name ?? null, - door_number: $json->door_number ?? null, - door_type: $json->door_type ?? null, - pms_id: $json->pms_id ?? null, - stand_open: $json->stand_open ?? null, - ); - } - public function __construct( - /** - * Name of the door in the Vostio access system. - */ - public string|null $door_name, - /** - * Number of the door in the Vostio access system. - */ - public float|null $door_number, - /** - * Type of the door in the Vostio access system. - */ - public string|null $door_type, - /** - * PMS ID of the door in the Vostio access system. - */ - public string|null $pms_id, - /** - * Indicates whether keys are allowed to set the door in stand open mode in the Vostio access system. - */ - public bool|null $stand_open, - ) {} -} + public function __construct( + /** + * Entry name for an Avigilon Alta system. + */ + public string|null $entry_name, + /** + * Total count of entry relays for an Avigilon Alta system. + */ + public float|null $entry_relays_total_count, + /** + * Organization name for an Avigilon Alta system. + */ + public string|null $org_name, + /** + * Site ID for an Avigilon Alta system. + */ + public float|null $site_id, + /** + * Site name for an Avigilon Alta system. + */ + public string|null $site_name, + /** + * Zone ID for an Avigilon Alta system. + */ + public float|null $zone_id, + /** + * Zone name for an Avigilon Alta system. + */ + public string|null $zone_name, + ) {} + } -/** - * Avigilon Alta-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceAvigilonAltaMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceAvigilonAltaMetadata|null { - if (!$json) { - return null; + /** + * Brivo-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class BrivoMetadata + { + public static function from_json(mixed $json): BrivoMetadata|null + { + if (!$json) { + return null; + } + return new self( + access_point_id: $json->access_point_id ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + ); } - return new self( - entry_name: $json->entry_name ?? null, - entry_relays_total_count: $json->entry_relays_total_count ?? null, - org_name: $json->org_name ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - zone_id: $json->zone_id ?? null, - zone_name: $json->zone_name ?? null, - ); - } - public function __construct( - /** - * Entry name for an Avigilon Alta system. - */ - public string|null $entry_name, - /** - * Total count of entry relays for an Avigilon Alta system. - */ - public float|null $entry_relays_total_count, - /** - * Organization name for an Avigilon Alta system. - */ - public string|null $org_name, - /** - * Site ID for an Avigilon Alta system. - */ - public float|null $site_id, - /** - * Site name for an Avigilon Alta system. - */ - public string|null $site_name, - /** - * Zone ID for an Avigilon Alta system. - */ - public float|null $zone_id, - /** - * Zone name for an Avigilon Alta system. - */ - public string|null $zone_name, - ) {} -} + public function __construct( + /** + * ID of the access point in the Brivo access system. + */ + public string|null $access_point_id, + /** + * ID of the site that the access point belongs to. + */ + public float|null $site_id, + /** + * Name of the site that the access point belongs to. + */ + public string|null $site_name, + ) {} + } -/** - * Brivo-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceBrivoMetadata -{ - public static function from_json(mixed $json): AcsEntranceBrivoMetadata|null + /** + * dormakaba Ambiance-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class DormakabaAmbianceMetadata { - if (!$json) { - return null; + public static function from_json( + mixed $json, + ): DormakabaAmbianceMetadata|null { + if (!$json) { + return null; + } + return new self( + access_point_name: $json->access_point_name ?? null, + ); } - return new self( - access_point_id: $json->access_point_id ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - ); - } - public function __construct( - /** - * ID of the access point in the Brivo access system. - */ - public string|null $access_point_id, - /** - * ID of the site that the access point belongs to. - */ - public float|null $site_id, - /** - * Name of the site that the access point belongs to. - */ - public string|null $site_name, - ) {} -} + public function __construct( + /** + * Name of the access point in the dormakaba Ambiance access system. + */ + public string|null $access_point_name, + ) {} + } -/** - * dormakaba Ambiance-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceDormakabaAmbianceMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceDormakabaAmbianceMetadata|null { - if (!$json) { - return null; + /** + * dormakaba Community-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class DormakabaCommunityMetadata + { + public static function from_json( + mixed $json, + ): DormakabaCommunityMetadata|null { + if (!$json) { + return null; + } + return new self( + access_point_profile: $json->access_point_profile ?? null, + ); } - return new self(access_point_name: $json->access_point_name ?? null); - } - public function __construct( - /** - * Name of the access point in the dormakaba Ambiance access system. - */ - public string|null $access_point_name, - ) {} -} + public function __construct( + /** + * Type of access point profile in the dormakaba Community access system. + */ + public string|null $access_point_profile, + ) {} + } -/** - * dormakaba Community-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceDormakabaCommunityMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceDormakabaCommunityMetadata|null { - if (!$json) { - return null; + /** + * Errors associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - access_point_profile: $json->access_point_profile ?? null, - ); - } - public function __construct( - /** - * Type of access point profile in the dormakaba Community access system. - */ - public string|null $access_point_profile, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Errors associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceErrors -{ - public static function from_json(mixed $json): AcsEntranceErrors|null + /** + * Hotek-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class HotekMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): HotekMetadata|null + { + if (!$json) { + return null; + } + return new self( + common_area_name: $json->common_area_name ?? null, + common_area_number: $json->common_area_number ?? null, + room_number: $json->room_number ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Display name of the entrance. + */ + public string|null $common_area_name, + /** + * Display name of the entrance. + */ + public string|null $common_area_number, + /** + * Room number of the entrance. + */ + public string|null $room_number, + ) {} + } -/** - * Hotek-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceHotekMetadata -{ - public static function from_json(mixed $json): AcsEntranceHotekMetadata|null + /** + * Latch-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class LatchMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): LatchMetadata|null + { + if (!$json) { + return null; + } + return new self( + accessibility_type: $json->accessibility_type ?? null, + door_name: $json->door_name ?? null, + door_type: $json->door_type ?? null, + is_connected: $json->is_connected ?? null, + ); } - return new self( - common_area_name: $json->common_area_name ?? null, - common_area_number: $json->common_area_number ?? null, - room_number: $json->room_number ?? null, - ); - } - public function __construct( - /** - * Display name of the entrance. - */ - public string|null $common_area_name, - /** - * Display name of the entrance. - */ - public string|null $common_area_number, - /** - * Room number of the entrance. - */ - public string|null $room_number, - ) {} -} + public function __construct( + /** + * Accessibility type in the Latch access system. + */ + public string|null $accessibility_type, + /** + * Name of the door in the Latch access system. + */ + public string|null $door_name, + /** + * Type of the door in the Latch access system. + */ + public string|null $door_type, + /** + * Indicates whether the entrance is connected. + */ + public bool|null $is_connected, + ) {} + } -/** - * Latch-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceLatchMetadata -{ - public static function from_json(mixed $json): AcsEntranceLatchMetadata|null + /** + * Salto KS-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class SaltoKsMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): SaltoKsMetadata|null + { + if (!$json) { + return null; + } + return new self( + battery_level: $json->battery_level ?? null, + door_name: $json->door_name ?? null, + intrusion_alarm: $json->intrusion_alarm ?? null, + left_open_alarm: $json->left_open_alarm ?? null, + lock_type: $json->lock_type ?? null, + locked_state: $json->locked_state ?? null, + online: $json->online ?? null, + privacy_mode: $json->privacy_mode ?? null, + ); } - return new self( - accessibility_type: $json->accessibility_type ?? null, - door_name: $json->door_name ?? null, - door_type: $json->door_type ?? null, - is_connected: $json->is_connected ?? null, - ); - } - public function __construct( - /** - * Accessibility type in the Latch access system. - */ - public string|null $accessibility_type, - /** - * Name of the door in the Latch access system. - */ - public string|null $door_name, - /** - * Type of the door in the Latch access system. - */ - public string|null $door_type, - /** - * Indicates whether the entrance is connected. - */ - public bool|null $is_connected, - ) {} -} + public function __construct( + /** + * Battery level of the door access device. + */ + public string|null $battery_level, + /** + * Name of the door in the Salto KS access system. + */ + public string|null $door_name, + /** + * Indicates whether an intrusion alarm is active on the door. + */ + public bool|null $intrusion_alarm, + /** + * Indicates whether the door is left open. + */ + public bool|null $left_open_alarm, + /** + * Type of the lock in the Salto KS access system. + */ + public string|null $lock_type, + /** + * Locked state of the door in the Salto KS access system. + */ + public string|null $locked_state, + /** + * Indicates whether the door access device is online. + */ + public bool|null $online, + /** + * Indicates whether privacy mode is enabled for the lock. + */ + public bool|null $privacy_mode, + ) {} + } -/** - * Profile for the door in the Visionline access system. - */ -class AcsEntranceProfiles -{ - public static function from_json(mixed $json): AcsEntranceProfiles|null + /** + * Salto Space-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class SaltoSpaceMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): SaltoSpaceMetadata|null + { + if (!$json) { + return null; + } + return new self( + audit_on_keys: $json->audit_on_keys ?? null, + door_description: $json->door_description ?? null, + door_id: $json->door_id ?? null, + door_name: $json->door_name ?? null, + room_description: $json->room_description ?? null, + room_name: $json->room_name ?? null, + ); } - return new self( - visionline_door_profile_id: $json->visionline_door_profile_id ?? - null, - visionline_door_profile_type: $json->visionline_door_profile_type ?? - null, - ); - } - public function __construct( - /** - * Door profile ID in the Visionline access system. - */ - public string|null $visionline_door_profile_id, - /** - * Door profile type in the Visionline access system. - */ - public string|null $visionline_door_profile_type, - ) {} -} + public function __construct( + /** + * Indicates whether AuditOnKeys is enabled for the door in the Salto Space access system. + */ + public bool|null $audit_on_keys, + /** + * Description of the door in the Salto Space access system. + */ + public string|null $door_description, + /** + * Door ID in the Salto Space access system. + */ + public string|null $door_id, + /** + * Name of the door in the Salto Space access system. + */ + public string|null $door_name, + /** + * Description of the room in the Salto Space access system. + */ + public string|null $room_description, + /** + * Name of the room in the Salto Space access system. + */ + public string|null $room_name, + ) {} + } -/** - * Salto KS-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceSaltoKsMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceSaltoKsMetadata|null { - if (!$json) { - return null; + /** + * Visionline-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + door_category: $json->door_category ?? null, + door_name: $json->door_name ?? null, + profiles: array_map( + fn($p) => VisionlineMetadata\Profiles::from_json($p), + $json->profiles ?? [], + ), + ); } - return new self( - battery_level: $json->battery_level ?? null, - door_name: $json->door_name ?? null, - intrusion_alarm: $json->intrusion_alarm ?? null, - left_open_alarm: $json->left_open_alarm ?? null, - lock_type: $json->lock_type ?? null, - locked_state: $json->locked_state ?? null, - online: $json->online ?? null, - privacy_mode: $json->privacy_mode ?? null, - ); - } - public function __construct( - /** - * Battery level of the door access device. - */ - public string|null $battery_level, - /** - * Name of the door in the Salto KS access system. - */ - public string|null $door_name, - /** - * Indicates whether an intrusion alarm is active on the door. - */ - public bool|null $intrusion_alarm, - /** - * Indicates whether the door is left open. - */ - public bool|null $left_open_alarm, - /** - * Type of the lock in the Salto KS access system. - */ - public string|null $lock_type, - /** - * Locked state of the door in the Salto KS access system. - */ - public string|null $locked_state, - /** - * Indicates whether the door access device is online. - */ - public bool|null $online, - /** - * Indicates whether privacy mode is enabled for the lock. - */ - public bool|null $privacy_mode, - ) {} -} + public function __construct( + /** + * Category of the door in the Visionline access system. + */ + public string|null $door_category, + /** + * Name of the door in the Visionline access system. + */ + public string|null $door_name, + /** + * Profile for the door in the Visionline access system. + */ + public array $profiles, + ) {} + } -/** - * Salto Space-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceSaltoSpaceMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceSaltoSpaceMetadata|null { - if (!$json) { - return null; + /** + * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - audit_on_keys: $json->audit_on_keys ?? null, - door_description: $json->door_description ?? null, - door_id: $json->door_id ?? null, - door_name: $json->door_name ?? null, - room_description: $json->room_description ?? null, - room_name: $json->room_name ?? null, - ); - } - public function __construct( - /** - * Indicates whether AuditOnKeys is enabled for the door in the Salto Space access system. - */ - public bool|null $audit_on_keys, - /** - * Description of the door in the Salto Space access system. - */ - public string|null $door_description, - /** - * Door ID in the Salto Space access system. - */ - public string|null $door_id, - /** - * Name of the door in the Salto Space access system. - */ - public string|null $door_name, - /** - * Description of the room in the Salto Space access system. - */ - public string|null $room_description, - /** - * Name of the room in the Salto Space access system. - */ - public string|null $room_name, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Visionline-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceVisionlineMetadata -{ - public static function from_json( - mixed $json, - ): AcsEntranceVisionlineMetadata|null { - if (!$json) { - return null; +namespace Seam\Resources\AcsEntrance\AkilesMetadata { + /** + * Actions the gadget exposes (for example, open). + */ + class Actions + { + public static function from_json(mixed $json): Actions|null + { + if (!$json) { + return null; + } + return new self(id: $json->id ?? null, name: $json->name ?? null); } - return new self( - door_category: $json->door_category ?? null, - door_name: $json->door_name ?? null, - profiles: array_map( - fn($p) => AcsEntranceProfiles::from_json($p), - $json->profiles ?? [], - ), - ); - } - public function __construct( - /** - * Category of the door in the Visionline access system. - */ - public string|null $door_category, - /** - * Name of the door in the Visionline access system. - */ - public string|null $door_name, - /** - * Profile for the door in the Visionline access system. - */ - public array $profiles, - ) {} + public function __construct( + /** + * ID of the gadget action. + */ + public string|null $id, + /** + * Name of the gadget action. + */ + public string|null $name, + ) {} + } } -/** - * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ -class AcsEntranceWarnings -{ - public static function from_json(mixed $json): AcsEntranceWarnings|null +namespace Seam\Resources\AcsEntrance\VisionlineMetadata { + /** + * Profile for the door in the Visionline access system. + */ + class Profiles { - if (!$json) { - return null; + public static function from_json(mixed $json): Profiles|null + { + if (!$json) { + return null; + } + return new self( + visionline_door_profile_id: $json->visionline_door_profile_id ?? + null, + visionline_door_profile_type: $json->visionline_door_profile_type ?? + null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Door profile ID in the Visionline access system. + */ + public string|null $visionline_door_profile_id, + /** + * Door profile type in the Visionline access system. + */ + public string|null $visionline_door_profile_type, + ) {} + } } diff --git a/src/Resources/AcsSystem.php b/src/Resources/AcsSystem.php index 76b4f601..b00ec137 100644 --- a/src/Resources/AcsSystem.php +++ b/src/Resources/AcsSystem.php @@ -1,273 +1,275 @@ acs_access_group_count ?? null, + acs_system_id: $json->acs_system_id ?? null, + acs_user_count: $json->acs_user_count ?? null, + connected_account_id: $json->connected_account_id ?? null, + connected_account_ids: $json->connected_account_ids ?? null, + created_at: $json->created_at ?? null, + default_credential_manager_acs_system_id: $json->default_credential_manager_acs_system_id ?? + null, + errors: array_map( + fn($e) => AcsSystem\Errors::from_json($e), + $json->errors ?? [], + ), + external_type: $json->external_type ?? null, + external_type_display_name: $json->external_type_display_name ?? + null, + image_alt_text: $json->image_alt_text ?? null, + image_url: $json->image_url ?? null, + is_credential_manager: $json->is_credential_manager ?? null, + location: isset($json->location) + ? AcsSystem\Location::from_json($json->location) + : null, + name: $json->name ?? null, + system_type: $json->system_type ?? null, + system_type_display_name: $json->system_type_display_name ?? + null, + visionline_metadata: isset($json->visionline_metadata) + ? AcsSystem\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + warnings: array_map( + fn($w) => AcsSystem\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - acs_access_group_count: $json->acs_access_group_count ?? null, - acs_system_id: $json->acs_system_id ?? null, - acs_user_count: $json->acs_user_count ?? null, - connected_account_id: $json->connected_account_id ?? null, - connected_account_ids: $json->connected_account_ids ?? null, - created_at: $json->created_at ?? null, - default_credential_manager_acs_system_id: $json->default_credential_manager_acs_system_id ?? - null, - errors: array_map( - fn($e) => AcsSystemErrors::from_json($e), - $json->errors ?? [], - ), - external_type: $json->external_type ?? null, - external_type_display_name: $json->external_type_display_name ?? - null, - image_alt_text: $json->image_alt_text ?? null, - image_url: $json->image_url ?? null, - is_credential_manager: $json->is_credential_manager ?? null, - location: isset($json->location) - ? AcsSystemLocation::from_json($json->location) - : null, - name: $json->name ?? null, - system_type: $json->system_type ?? null, - system_type_display_name: $json->system_type_display_name ?? null, - visionline_metadata: isset($json->visionline_metadata) - ? AcsSystemVisionlineMetadata::from_json( - $json->visionline_metadata, - ) - : null, - warnings: array_map( - fn($w) => AcsSystemWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Number of access groups in the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public float|null $acs_access_group_count, - /** - * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $acs_system_id, - /** - * Number of users in the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public float|null $acs_user_count, - /** - * ID of the connected account associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $connected_account_id, - /** - * IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). - * - * @deprecated Use `connected_account_id`. - */ - public array|null $connected_account_ids, - /** - * Date and time at which the [access control system](https://docs.seam.co/low-level-apis/access-systems) was created. - */ - public string|null $created_at, - /** - * ID of the default credential manager `acs_system` for this [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $default_credential_manager_acs_system_id, - /** - * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public array $errors, - /** - * Brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. - */ - public string|null $external_type, - /** - * Display name that corresponds to the brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. - */ - public string|null $external_type_display_name, - /** - * Alternative text for the [access control system](https://docs.seam.co/low-level-apis/access-systems) image. - */ - public string|null $image_alt_text, - /** - * URL for the image that represents the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $image_url, - /** - * Indicates whether the `acs_system` is a credential manager. - */ - public bool|null $is_credential_manager, - /** - * Location information for the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public AcsSystemLocation|null $location, - /** - * Name of the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $name, - /** - * @deprecated Use `external_type`. - */ - public string|null $system_type, - /** - * @deprecated Use `external_type_display_name`. - */ - public string|null $system_type_display_name, - /** - * Visionline-specific metadata for the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public AcsSystemVisionlineMetadata|null $visionline_metadata, - /** - * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public array $warnings, - /** - * ID of the workspace that contains the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Number of access groups in the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public float|null $acs_access_group_count, + /** + * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $acs_system_id, + /** + * Number of users in the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public float|null $acs_user_count, + /** + * ID of the connected account associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $connected_account_id, + /** + * IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + * + * @deprecated Use `connected_account_id`. + */ + public array|null $connected_account_ids, + /** + * Date and time at which the [access control system](https://docs.seam.co/low-level-apis/access-systems) was created. + */ + public string|null $created_at, + /** + * ID of the default credential manager `acs_system` for this [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $default_credential_manager_acs_system_id, + /** + * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public array $errors, + /** + * Brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. + */ + public string|null $external_type, + /** + * Display name that corresponds to the brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. + */ + public string|null $external_type_display_name, + /** + * Alternative text for the [access control system](https://docs.seam.co/low-level-apis/access-systems) image. + */ + public string|null $image_alt_text, + /** + * URL for the image that represents the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $image_url, + /** + * Indicates whether the `acs_system` is a credential manager. + */ + public bool|null $is_credential_manager, + /** + * Location information for the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public AcsSystem\Location|null $location, + /** + * Name of the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $name, + /** + * @deprecated Use `external_type`. + */ + public string|null $system_type, + /** + * @deprecated Use `external_type_display_name`. + */ + public string|null $system_type_display_name, + /** + * Visionline-specific metadata for the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public AcsSystem\VisionlineMetadata|null $visionline_metadata, + /** + * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public array $warnings, + /** + * ID of the workspace that contains the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ -class AcsSystemErrors -{ - public static function from_json(mixed $json): AcsSystemErrors|null +namespace Seam\Resources\AcsSystem { + /** + * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Indicates whether the error is related to the [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Indicates whether the error is related to the [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Location information for the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ -class AcsSystemLocation -{ - public static function from_json(mixed $json): AcsSystemLocation|null + /** + * Location information for the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + class Location { - if (!$json) { - return null; + public static function from_json(mixed $json): Location|null + { + if (!$json) { + return null; + } + return new self(time_zone: $json->time_zone ?? null); } - return new self(time_zone: $json->time_zone ?? null); - } - public function __construct( - /** - * Time zone in which the [access control system](https://docs.seam.co/low-level-apis/access-systems) is located. - */ - public string|null $time_zone, - ) {} -} + public function __construct( + /** + * Time zone in which the [access control system](https://docs.seam.co/low-level-apis/access-systems) is located. + */ + public string|null $time_zone, + ) {} + } -/** - * Visionline-specific metadata for the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ -class AcsSystemVisionlineMetadata -{ - public static function from_json( - mixed $json, - ): AcsSystemVisionlineMetadata|null { - if (!$json) { - return null; + /** + * Visionline-specific metadata for the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + lan_address: $json->lan_address ?? null, + mobile_access_uuid: $json->mobile_access_uuid ?? null, + system_id: $json->system_id ?? null, + ); } - return new self( - lan_address: $json->lan_address ?? null, - mobile_access_uuid: $json->mobile_access_uuid ?? null, - system_id: $json->system_id ?? null, - ); - } - public function __construct( - /** - * IP address or hostname of the main Visionline server relative to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge) on the local network. - */ - public string|null $lan_address, - /** - * Keyset loaded into a reader. Mobile keys and reader administration tools securely authenticate only with readers programmed with a matching keyset. - */ - public string|null $mobile_access_uuid, - /** - * Unique ID assigned by the ASSA ABLOY licensing team that identifies each hotel in your credential manager. - */ - public string|null $system_id, - ) {} -} + public function __construct( + /** + * IP address or hostname of the main Visionline server relative to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge) on the local network. + */ + public string|null $lan_address, + /** + * Keyset loaded into a reader. Mobile keys and reader administration tools securely authenticate only with readers programmed with a matching keyset. + */ + public string|null $mobile_access_uuid, + /** + * Unique ID assigned by the ASSA ABLOY licensing team that identifies each hotel in your credential manager. + */ + public string|null $system_id, + ) {} + } -/** - * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). - */ -class AcsSystemWarnings -{ - public static function from_json(mixed $json): AcsSystemWarnings|null + /** + * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). + */ + class Warnings { - if (!$json) { - return null; + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + misconfigured_acs_entrance_ids: $json->misconfigured_acs_entrance_ids ?? + null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - misconfigured_acs_entrance_ids: $json->misconfigured_acs_entrance_ids ?? - null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * @deprecated this field is deprecated. - */ - public array|null $misconfigured_acs_entrance_ids, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * @deprecated this field is deprecated. + */ + public array|null $misconfigured_acs_entrance_ids, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } diff --git a/src/Resources/AcsUser.php b/src/Resources/AcsUser.php index b8df0217..e2bc4fbf 100644 --- a/src/Resources/AcsUser.php +++ b/src/Resources/AcsUser.php @@ -1,478 +1,470 @@ access_schedule) + ? AcsUser\AccessSchedule::from_json($json->access_schedule) + : null, + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + email: $json->email ?? null, + email_address: $json->email_address ?? null, + errors: array_map( + fn($e) => AcsUser\Errors::from_json($e), + $json->errors ?? [], + ), + external_type: $json->external_type ?? null, + external_type_display_name: $json->external_type_display_name ?? + null, + full_name: $json->full_name ?? null, + hid_acs_system_id: $json->hid_acs_system_id ?? null, + is_managed: $json->is_managed ?? null, + is_suspended: $json->is_suspended ?? null, + pending_mutations: array_map( + fn($p) => AcsUser\PendingMutations::from_json($p), + $json->pending_mutations ?? [], + ), + phone_number: $json->phone_number ?? null, + salto_ks_metadata: isset($json->salto_ks_metadata) + ? AcsUser\SaltoKsMetadata::from_json( + $json->salto_ks_metadata, + ) + : null, + salto_space_metadata: isset($json->salto_space_metadata) + ? AcsUser\SaltoSpaceMetadata::from_json( + $json->salto_space_metadata, + ) + : null, + user_identity_email_address: $json->user_identity_email_address ?? + null, + user_identity_full_name: $json->user_identity_full_name ?? null, + user_identity_id: $json->user_identity_id ?? null, + user_identity_phone_number: $json->user_identity_phone_number ?? + null, + warnings: array_map( + fn($w) => AcsUser\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - access_schedule: isset($json->access_schedule) - ? AcsUserAccessSchedule::from_json($json->access_schedule) - : null, - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - email: $json->email ?? null, - email_address: $json->email_address ?? null, - errors: array_map( - fn($e) => AcsUserErrors::from_json($e), - $json->errors ?? [], - ), - external_type: $json->external_type ?? null, - external_type_display_name: $json->external_type_display_name ?? - null, - full_name: $json->full_name ?? null, - hid_acs_system_id: $json->hid_acs_system_id ?? null, - is_managed: $json->is_managed ?? null, - is_suspended: $json->is_suspended ?? null, - pending_mutations: array_map( - fn($p) => AcsUserPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - phone_number: $json->phone_number ?? null, - salto_ks_metadata: isset($json->salto_ks_metadata) - ? AcsUserSaltoKsMetadata::from_json($json->salto_ks_metadata) - : null, - salto_space_metadata: isset($json->salto_space_metadata) - ? AcsUserSaltoSpaceMetadata::from_json( - $json->salto_space_metadata, - ) - : null, - user_identity_email_address: $json->user_identity_email_address ?? - null, - user_identity_full_name: $json->user_identity_full_name ?? null, - user_identity_id: $json->user_identity_id ?? null, - user_identity_phone_number: $json->user_identity_phone_number ?? - null, - warnings: array_map( - fn($w) => AcsUserWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * `starts_at` and `ends_at` timestamps for the [access system user's](https://docs.seam.co/low-level-apis/access-systems/user-management) access. - */ - public AcsUserAccessSchedule|null $access_schedule, - /** - * ID of the [access system](https://docs.seam.co/low-level-apis/access-systems) that contains the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $acs_system_id, - /** - * ID of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $acs_user_id, - /** - * The ID of the connected account that is associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $connected_account_id, - /** - * Date and time at which the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) was created. - */ - public string|null $created_at, - /** - * Display name for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $display_name, - /** - * @deprecated use email_address. - */ - public string|null $email, - /** - * Email address of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $email_address, - /** - * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public array $errors, - /** - * Brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. - */ - public string|null $external_type, - /** - * Display name that corresponds to the brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. - */ - public string|null $external_type_display_name, - /** - * Full name of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $full_name, - /** - * ID of the HID access control system associated with the user. - */ - public string|null $hid_acs_system_id, - /** - * Indicates whether Seam manages the access system user. - */ - public bool|null $is_managed, - /** - * Indicates whether the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) is currently [suspended](https://docs.seam.co/low-level-apis/access-systems/user-management/suspending-and-unsuspending-users). - */ - public bool|null $is_suspended, - /** - * Pending mutations associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Seam is in the process of pushing these mutations to the integrated access system. - */ - public array $pending_mutations, - /** - * Phone number of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). - */ - public string|null $phone_number, - /** - * Salto KS-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public AcsUserSaltoKsMetadata|null $salto_ks_metadata, - /** - * Salto Space-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public AcsUserSaltoSpaceMetadata|null $salto_space_metadata, - /** - * Email address of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $user_identity_email_address, - /** - * Full name of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $user_identity_full_name, - /** - * ID of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $user_identity_id, - /** - * Phone number of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). - */ - public string|null $user_identity_phone_number, - /** - * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public array $warnings, - /** - * ID of the workspace that contains the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * `starts_at` and `ends_at` timestamps for the [access system user's](https://docs.seam.co/low-level-apis/access-systems/user-management) access. + */ + public AcsUser\AccessSchedule|null $access_schedule, + /** + * ID of the [access system](https://docs.seam.co/low-level-apis/access-systems) that contains the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $acs_system_id, + /** + * ID of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $acs_user_id, + /** + * The ID of the connected account that is associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $connected_account_id, + /** + * Date and time at which the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) was created. + */ + public string|null $created_at, + /** + * Display name for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $display_name, + /** + * @deprecated use email_address. + */ + public string|null $email, + /** + * Email address of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $email_address, + /** + * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public array $errors, + /** + * Brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. + */ + public string|null $external_type, + /** + * Display name that corresponds to the brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. + */ + public string|null $external_type_display_name, + /** + * Full name of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $full_name, + /** + * ID of the HID access control system associated with the user. + */ + public string|null $hid_acs_system_id, + /** + * Indicates whether Seam manages the access system user. + */ + public bool|null $is_managed, + /** + * Indicates whether the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) is currently [suspended](https://docs.seam.co/low-level-apis/access-systems/user-management/suspending-and-unsuspending-users). + */ + public bool|null $is_suspended, + /** + * Pending mutations associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Seam is in the process of pushing these mutations to the integrated access system. + */ + public array $pending_mutations, + /** + * Phone number of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). + */ + public string|null $phone_number, + /** + * Salto KS-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public AcsUser\SaltoKsMetadata|null $salto_ks_metadata, + /** + * Salto Space-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public AcsUser\SaltoSpaceMetadata|null $salto_space_metadata, + /** + * Email address of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $user_identity_email_address, + /** + * Full name of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $user_identity_full_name, + /** + * ID of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $user_identity_id, + /** + * Phone number of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). + */ + public string|null $user_identity_phone_number, + /** + * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public array $warnings, + /** + * ID of the workspace that contains the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + public string|null $workspace_id, + ) {} + } } -/** - * `starts_at` and `ends_at` timestamps for the [access system user's](https://docs.seam.co/low-level-apis/access-systems/user-management) access. - */ -class AcsUserAccessSchedule -{ - public static function from_json(mixed $json): AcsUserAccessSchedule|null +namespace Seam\Resources\AcsUser { + /** + * `starts_at` and `ends_at` timestamps for the [access system user's](https://docs.seam.co/low-level-apis/access-systems/user-management) access. + */ + class AccessSchedule { - if (!$json) { - return null; + public static function from_json(mixed $json): AccessSchedule|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Date and time at which the user's access ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $ends_at, - /** - * Date and time at which the user's access starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Date and time at which the user's access ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $ends_at, + /** + * Date and time at which the user's access starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + ) {} + } -/** - * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ -class AcsUserErrors -{ - public static function from_json(mixed $json): AcsUserErrors|null + /** + * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Old access system user information. - */ -class AcsUserFrom -{ - public static function from_json(mixed $json): AcsUserFrom|null + /** + * Pending mutations associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Seam is in the process of pushing these mutations to the integrated access system. + */ + class PendingMutations { - if (!$json) { - return null; + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + acs_access_group_id: $json->acs_access_group_id ?? null, + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + scheduled_at: $json->scheduled_at ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + variant: $json->variant ?? null, + ); } - return new self( - acs_access_group_id: $json->acs_access_group_id ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - email_address: $json->email_address ?? null, - ends_at: $json->ends_at ?? null, - full_name: $json->full_name ?? null, - is_suspended: $json->is_suspended ?? null, - phone_number: $json->phone_number ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Old access group ID. - */ - public string|null $acs_access_group_id, - /** - * Previous credential ID. - */ - public string|null $acs_credential_id, - /** - * Email address of the access system user. - */ - public string|null $email_address, - /** - * Starting time for the access schedule. - */ - public string|null $ends_at, - /** - * Full name of the access system user. - */ - public string|null $full_name, - public bool|null $is_suspended, - /** - * Phone number of the access system user. - */ - public string|null $phone_number, - /** - * Starting time for the access schedule. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * ID of the access group involved in the scheduled change. + */ + public string|null $acs_access_group_id, + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + /** + * Optional: When the user creation is scheduled to occur. + */ + public string|null $scheduled_at, + public PendingMutations\To|null $to, + /** + * Whether the user is scheduled to be added to or removed from the access group. + */ + public string|null $variant, + ) {} + } -/** - * Pending mutations associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Seam is in the process of pushing these mutations to the integrated access system. - */ -class AcsUserPendingMutations -{ - public static function from_json(mixed $json): AcsUserPendingMutations|null + /** + * Salto KS-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + class SaltoKsMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): SaltoKsMetadata|null + { + if (!$json) { + return null; + } + return new self(is_subscribed: $json->is_subscribed ?? null); } - return new self( - acs_access_group_id: $json->acs_access_group_id ?? null, - created_at: $json->created_at ?? null, - from: isset($json->from) - ? AcsUserFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - scheduled_at: $json->scheduled_at ?? null, - to: isset($json->to) ? AcsUserTo::from_json($json->to) : null, - variant: $json->variant ?? null, - ); - } - public function __construct( - /** - * ID of the access group involved in the scheduled change. - */ - public string|null $acs_access_group_id, - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Old access system user information. - */ - public AcsUserFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of pushing a user creation to the integrated access system. - */ - public string|null $mutation_code, - /** - * Optional: When the user creation is scheduled to occur. - */ - public string|null $scheduled_at, - /** - * New access system user information. - */ - public AcsUserTo|null $to, - /** - * Whether the user is scheduled to be added to or removed from the access group. - */ - public string|null $variant, - ) {} -} + public function __construct( + /** + * Indicates whether the user holds an active subscription slot on the Salto KS site. Only subscribed users can unlock doors and count against the site's user-subscription limit. A user may not be subscribed because their access schedule has not started or has ended, the site has reached its subscription limit, or they were manually unsubscribed. This is distinct from `is_suspended`, which reflects whether the user has been explicitly blocked. + */ + public bool|null $is_subscribed, + ) {} + } -/** - * Salto KS-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ -class AcsUserSaltoKsMetadata -{ - public static function from_json(mixed $json): AcsUserSaltoKsMetadata|null + /** + * Salto Space-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + class SaltoSpaceMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): SaltoSpaceMetadata|null + { + if (!$json) { + return null; + } + return new self( + audit_openings: $json->audit_openings ?? null, + user_id: $json->user_id ?? null, + ); } - return new self(is_subscribed: $json->is_subscribed ?? null); - } - public function __construct( - /** - * Indicates whether the user holds an active subscription slot on the Salto KS site. Only subscribed users can unlock doors and count against the site's user-subscription limit. A user may not be subscribed because their access schedule has not started or has ended, the site has reached its subscription limit, or they were manually unsubscribed. This is distinct from `is_suspended`, which reflects whether the user has been explicitly blocked. - */ - public bool|null $is_subscribed, - ) {} -} + public function __construct( + /** + * Indicates whether AuditOpenings is enabled for the user in the Salto Space access system. + */ + public bool|null $audit_openings, + /** + * User ID in the Salto Space access system. + */ + public string|null $user_id, + ) {} + } -/** - * Salto Space-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ -class AcsUserSaltoSpaceMetadata -{ - public static function from_json( - mixed $json, - ): AcsUserSaltoSpaceMetadata|null { - if (!$json) { - return null; + /** + * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - audit_openings: $json->audit_openings ?? null, - user_id: $json->user_id ?? null, - ); - } - public function __construct( - /** - * Indicates whether AuditOpenings is enabled for the user in the Salto Space access system. - */ - public bool|null $audit_openings, - /** - * User ID in the Salto Space access system. - */ - public string|null $user_id, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + public string|null $warning_code, + ) {} + } } -/** - * New access system user information. - */ -class AcsUserTo -{ - public static function from_json(mixed $json): AcsUserTo|null +namespace Seam\Resources\AcsUser\PendingMutations { + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + acs_access_group_id: $json->acs_access_group_id ?? null, + acs_credential_id: $json->acs_credential_id ?? null, + email_address: $json->email_address ?? null, + ends_at: $json->ends_at ?? null, + full_name: $json->full_name ?? null, + is_suspended: $json->is_suspended ?? null, + phone_number: $json->phone_number ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - acs_access_group_id: $json->acs_access_group_id ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - email_address: $json->email_address ?? null, - ends_at: $json->ends_at ?? null, - full_name: $json->full_name ?? null, - is_suspended: $json->is_suspended ?? null, - phone_number: $json->phone_number ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * New access group ID. - */ - public string|null $acs_access_group_id, - /** - * New credential ID. - */ - public string|null $acs_credential_id, - /** - * Email address of the access system user. - */ - public string|null $email_address, - /** - * Starting time for the access schedule. - */ - public string|null $ends_at, - /** - * Full name of the access system user. - */ - public string|null $full_name, - public bool|null $is_suspended, - /** - * Phone number of the access system user. - */ - public string|null $phone_number, - /** - * Starting time for the access schedule. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Old access group ID. + */ + public string|null $acs_access_group_id, + /** + * Previous credential ID. + */ + public string|null $acs_credential_id, + /** + * Email address of the access system user. + */ + public string|null $email_address, + /** + * Starting time for the access schedule. + */ + public string|null $ends_at, + /** + * Full name of the access system user. + */ + public string|null $full_name, + public bool|null $is_suspended, + /** + * Phone number of the access system user. + */ + public string|null $phone_number, + /** + * Starting time for the access schedule. + */ + public string|null $starts_at, + ) {} + } -/** - * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). - */ -class AcsUserWarnings -{ - public static function from_json(mixed $json): AcsUserWarnings|null + class To { - if (!$json) { - return null; + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + acs_access_group_id: $json->acs_access_group_id ?? null, + acs_credential_id: $json->acs_credential_id ?? null, + email_address: $json->email_address ?? null, + ends_at: $json->ends_at ?? null, + full_name: $json->full_name ?? null, + is_suspended: $json->is_suspended ?? null, + phone_number: $json->phone_number ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - public string|null $warning_code, - ) {} + public function __construct( + /** + * New access group ID. + */ + public string|null $acs_access_group_id, + /** + * New credential ID. + */ + public string|null $acs_credential_id, + /** + * Email address of the access system user. + */ + public string|null $email_address, + /** + * Starting time for the access schedule. + */ + public string|null $ends_at, + /** + * Full name of the access system user. + */ + public string|null $full_name, + public bool|null $is_suspended, + /** + * Phone number of the access system user. + */ + public string|null $phone_number, + /** + * Starting time for the access schedule. + */ + public string|null $starts_at, + ) {} + } } diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 3448d210..3a471b9d 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -1,888 +1,1122 @@ action_attempt_id ?? null, + action_type: $json->action_type ?? null, + error: isset($json->error) + ? ActionAttempt\Error::from_json($json->error) + : null, + result: isset($json->result) + ? ActionAttempt\Result::from_json($json->result) + : null, + status: $json->status ?? null, + ); } - return new self( - action_attempt_id: $json->action_attempt_id ?? null, - action_type: $json->action_type ?? null, - error: isset($json->error) - ? ActionAttemptError::from_json($json->error) - : null, - result: isset($json->result) - ? ActionAttemptResult::from_json($json->result) - : null, - status: $json->status ?? null, - ); - } - public function __construct( - /** - * ID of the action attempt. - */ - public string|null $action_attempt_id, - /** - * Action attempt to track the status of locking a door. - */ - public string|null $action_type, - /** - * Error associated with the action. - */ - public ActionAttemptError|null $error, - /** - * Result of the action. - */ - public ActionAttemptResult|null $result, - public string|null $status, - ) {} + public function __construct( + /** + * ID of the action attempt. + */ + public string|null $action_attempt_id, + public string|null $action_type, + /** + * Error associated with the action. + */ + public ActionAttempt\Error|null $error, + public ActionAttempt\Result|null $result, + public string|null $status, + ) {} + } } -/** - * Snapshot of credential data read from the physical encoder. - */ -class ActionAttemptAcsCredentialOnEncoder -{ - public static function from_json( - mixed $json, - ): ActionAttemptAcsCredentialOnEncoder|null { - if (!$json) { - return null; +namespace Seam\Resources\ActionAttempt { + /** + * Error associated with the action. + */ + class Error + { + public static function from_json(mixed $json): Error|null + { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + type: $json->type ?? null, + ); } - return new self( - card_number: $json->card_number ?? null, - created_at: $json->created_at ?? null, - ends_at: $json->ends_at ?? null, - is_issued: $json->is_issued ?? null, - starts_at: $json->starts_at ?? null, - visionline_metadata: isset($json->visionline_metadata) - ? ActionAttemptVisionlineMetadata::from_json( - $json->visionline_metadata, - ) - : null, - ); - } - public function __construct( - /** - * A number or string that physically identifies the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_number, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. - */ - public string|null $created_at, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) will stop being usable. - */ - public string|null $ends_at, - /** - * Indicates whether the credential has been issued (encoded onto a card). - */ - public bool|null $is_issued, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) becomes usable. - */ - public string|null $starts_at, - /** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptVisionlineMetadata|null $visionline_metadata, - ) {} -} + public function __construct( + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + public string|null $type, + ) {} + } -/** - * Corresponding credential data as stored on Seam and the access system. - */ -class ActionAttemptAcsCredentialOnSeam -{ - public static function from_json( - mixed $json, - ): ActionAttemptAcsCredentialOnSeam|null { - if (!$json) { - return null; - } - return new self( - access_method: $json->access_method ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - acs_credential_pool_id: $json->acs_credential_pool_id ?? null, - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - akiles_metadata: isset($json->akiles_metadata) - ? ActionAttemptAkilesMetadata::from_json($json->akiles_metadata) - : null, - assa_abloy_vostio_metadata: isset($json->assa_abloy_vostio_metadata) - ? ActionAttemptAssaAbloyVostioMetadata::from_json( - $json->assa_abloy_vostio_metadata, + class Result + { + public static function from_json(mixed $json): Result|null + { + if (!$json) { + return null; + } + return new self( + access_code: $json->access_code ?? null, + access_method: $json->access_method ?? null, + access_method_id: $json->access_method_id ?? null, + acs_credential_id: $json->acs_credential_id ?? null, + acs_credential_on_encoder: isset( + $json->acs_credential_on_encoder, ) - : null, - card_number: $json->card_number ?? null, - code: $json->code ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => ActionAttemptErrors::from_json($e), - $json->errors ?? [], - ), - external_type: $json->external_type ?? null, - external_type_display_name: $json->external_type_display_name ?? - null, - is_issued: $json->is_issued ?? null, - is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? - null, - is_managed: $json->is_managed ?? null, - is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? - null, - is_one_time_use: $json->is_one_time_use ?? null, - issued_at: $json->issued_at ?? null, - latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? - null, - parent_acs_credential_id: $json->parent_acs_credential_id ?? null, - starts_at: $json->starts_at ?? null, - user_identity_id: $json->user_identity_id ?? null, - visionline_metadata: isset($json->visionline_metadata) - ? ActionAttemptVisionlineMetadata::from_json( - $json->visionline_metadata, + ? Result\AcsCredentialOnEncoder::from_json( + $json->acs_credential_on_encoder, + ) + : null, + acs_credential_on_seam: isset($json->acs_credential_on_seam) + ? Result\AcsCredentialOnSeam::from_json( + $json->acs_credential_on_seam, + ) + : null, + acs_credential_pool_id: $json->acs_credential_pool_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + akiles_metadata: isset($json->akiles_metadata) + ? Result\AkilesMetadata::from_json($json->akiles_metadata) + : null, + assa_abloy_vostio_metadata: isset( + $json->assa_abloy_vostio_metadata, ) - : null, - warnings: array_map( - fn($w) => ActionAttemptWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } + ? Result\AssaAbloyVostioMetadata::from_json( + $json->assa_abloy_vostio_metadata, + ) + : null, + card_number: $json->card_number ?? null, + client_session_token: $json->client_session_token ?? null, + code: $json->code ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + customization_profile_id: $json->customization_profile_id ?? + null, + display_name: $json->display_name ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => Result\Errors::from_json($e), + $json->errors ?? [], + ), + external_type: $json->external_type ?? null, + external_type_display_name: $json->external_type_display_name ?? + null, + instant_key_url: $json->instant_key_url ?? null, + is_assignment_required: $json->is_assignment_required ?? null, + is_encoding_required: $json->is_encoding_required ?? null, + is_issued: $json->is_issued ?? null, + is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? + null, + is_managed: $json->is_managed ?? null, + is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? + null, + is_one_time_use: $json->is_one_time_use ?? null, + is_ready_for_assignment: $json->is_ready_for_assignment ?? null, + is_ready_for_encoding: $json->is_ready_for_encoding ?? null, + issued_at: $json->issued_at ?? null, + latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? + null, + mode: $json->mode ?? null, + noise_threshold: $json->noise_threshold ?? null, + parent_acs_credential_id: $json->parent_acs_credential_id ?? + null, + pending_mutations: array_map( + fn($p) => Result\PendingMutations::from_json($p), + $json->pending_mutations ?? [], + ), + starts_at: $json->starts_at ?? null, + user_identity_id: $json->user_identity_id ?? null, + visionline_metadata: isset($json->visionline_metadata) + ? Result\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + warnings: array_map( + fn($w) => Result\Warnings::from_json($w), + $json->warnings ?? [], + ), + was_confirmed_by_device: $json->was_confirmed_by_device ?? null, + workspace_id: $json->workspace_id ?? null, + ); + } - public function __construct( - /** - * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $access_method, - /** - * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $acs_credential_id, - /** - * ID of the credential pool to which the credential belongs. - */ - public string|null $acs_credential_pool_id, - /** - * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $acs_system_id, - /** - * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $acs_user_id, - /** - * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptAkilesMetadata|null $akiles_metadata, - /** - * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptAssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, - /** - * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_number, - /** - * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $code, - /** - * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $connected_account_id, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. - */ - public string|null $created_at, - /** - * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $display_name, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. - */ - public string|null $ends_at, - /** - * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $errors, - /** - * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. - */ - public string|null $external_type, - /** - * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $external_type_display_name, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. - */ - public bool|null $is_issued, - /** - * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. - */ - public bool|null $is_latest_desired_state_synced_with_provider, - public bool|null $is_managed, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). - */ - public bool|null $is_multi_phone_sync_credential, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. - */ - public bool|null $is_one_time_use, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. - */ - public string|null $issued_at, - /** - * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. - */ - public string|null $latest_desired_state_synced_with_provider_at, - /** - * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $parent_acs_credential_id, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - /** - * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $user_identity_id, - /** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptVisionlineMetadata|null $visionline_metadata, - /** - * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $warnings, - /** - * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $workspace_id, - ) {} + public function __construct( + public mixed $access_code, + /** + * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $access_method, + /** + * ID of the access method. + */ + public string|null $access_method_id, + /** + * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_credential_id, + /** + * Snapshot of credential data read from the physical encoder. + */ + public Result\AcsCredentialOnEncoder|null $acs_credential_on_encoder, + /** + * Corresponding credential data as stored on Seam and the access system. + */ + public Result\AcsCredentialOnSeam|null $acs_credential_on_seam, + /** + * ID of the credential pool to which the credential belongs. + */ + public string|null $acs_credential_pool_id, + /** + * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_system_id, + /** + * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $acs_user_id, + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public Result\AkilesMetadata|null $akiles_metadata, + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public Result\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + /** + * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_number, + /** + * Token of the client session associated with the access method. + */ + public string|null $client_session_token, + public string|null $code, + /** + * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $connected_account_id, + public string|null $created_at, + /** + * ID of the customization profile associated with the access method. + */ + public string|null $customization_profile_id, + public string|null $display_name, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. + */ + public string|null $ends_at, + public array $errors, + /** + * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. + */ + public string|null $external_type, + /** + * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $external_type_display_name, + /** + * URL of the Instant Key for mobile key access methods. + */ + public string|null $instant_key_url, + /** + * Indicates whether an existing card credential must be assigned to this access method before it can be issued. Only applies to card-mode access methods on systems that support credential assignment. + */ + public bool|null $is_assignment_required, + /** + * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. + */ + public bool|null $is_encoding_required, + public bool|null $is_issued, + /** + * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. + */ + public bool|null $is_latest_desired_state_synced_with_provider, + /** + * Indicates whether Seam manages the credential. + */ + public bool|null $is_managed, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). + */ + public bool|null $is_multi_phone_sync_credential, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. + */ + public bool|null $is_one_time_use, + /** + * Indicates whether the access method is ready for card assignment. This is true when the access method is in card mode, has not yet been issued, and the system supports credential assignment. + */ + public bool|null $is_ready_for_assignment, + /** + * Indicates whether the access method is ready to be encoded. This is true when the credential has been created and the card has not yet been issued. + */ + public bool|null $is_ready_for_encoding, + public string|null $issued_at, + /** + * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. + */ + public string|null $latest_desired_state_synced_with_provider_at, + /** + * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $mode, + public mixed $noise_threshold, + /** + * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $parent_acs_credential_id, + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + public array $pending_mutations, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + /** + * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $user_identity_id, + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public Result\VisionlineMetadata|null $visionline_metadata, + public array $warnings, + public bool|null $was_confirmed_by_device, + public string|null $workspace_id, + ) {} + } } -/** - * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class ActionAttemptAkilesMetadata -{ - public static function from_json( - mixed $json, - ): ActionAttemptAkilesMetadata|null { - if (!$json) { - return null; +namespace Seam\Resources\ActionAttempt\Result { + /** + * Snapshot of credential data read from the physical encoder. + */ + class AcsCredentialOnEncoder + { + public static function from_json( + mixed $json, + ): AcsCredentialOnEncoder|null { + if (!$json) { + return null; + } + return new self( + card_number: $json->card_number ?? null, + created_at: $json->created_at ?? null, + ends_at: $json->ends_at ?? null, + is_issued: $json->is_issued ?? null, + starts_at: $json->starts_at ?? null, + visionline_metadata: isset($json->visionline_metadata) + ? AcsCredentialOnEncoder\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + ); } - return new self(member_pin_id: $json->member_pin_id ?? null); - } - public function __construct( - /** - * ID of the Akiles member PIN. - */ - public string|null $member_pin_id, - ) {} -} + public function __construct( + /** + * A number or string that physically identifies the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_number, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. + */ + public string|null $created_at, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) will stop being usable. + */ + public string|null $ends_at, + /** + * Indicates whether the credential has been issued (encoded onto a card). + */ + public bool|null $is_issued, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) becomes usable. + */ + public string|null $starts_at, + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredentialOnEncoder\VisionlineMetadata|null $visionline_metadata, + ) {} + } -/** - * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class ActionAttemptAssaAbloyVostioMetadata -{ - public static function from_json( - mixed $json, - ): ActionAttemptAssaAbloyVostioMetadata|null { - if (!$json) { - return null; + /** + * Corresponding credential data as stored on Seam and the access system. + */ + class AcsCredentialOnSeam + { + public static function from_json(mixed $json): AcsCredentialOnSeam|null + { + if (!$json) { + return null; + } + return new self( + access_method: $json->access_method ?? null, + acs_credential_id: $json->acs_credential_id ?? null, + acs_credential_pool_id: $json->acs_credential_pool_id ?? null, + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + akiles_metadata: isset($json->akiles_metadata) + ? AcsCredentialOnSeam\AkilesMetadata::from_json( + $json->akiles_metadata, + ) + : null, + assa_abloy_vostio_metadata: isset( + $json->assa_abloy_vostio_metadata, + ) + ? AcsCredentialOnSeam\AssaAbloyVostioMetadata::from_json( + $json->assa_abloy_vostio_metadata, + ) + : null, + card_number: $json->card_number ?? null, + code: $json->code ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => AcsCredentialOnSeam\Errors::from_json($e), + $json->errors ?? [], + ), + external_type: $json->external_type ?? null, + external_type_display_name: $json->external_type_display_name ?? + null, + is_issued: $json->is_issued ?? null, + is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? + null, + is_managed: $json->is_managed ?? null, + is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? + null, + is_one_time_use: $json->is_one_time_use ?? null, + issued_at: $json->issued_at ?? null, + latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? + null, + parent_acs_credential_id: $json->parent_acs_credential_id ?? + null, + starts_at: $json->starts_at ?? null, + user_identity_id: $json->user_identity_id ?? null, + visionline_metadata: isset($json->visionline_metadata) + ? AcsCredentialOnSeam\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + warnings: array_map( + fn($w) => AcsCredentialOnSeam\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - auto_join: $json->auto_join ?? null, - door_names: $json->door_names ?? null, - endpoint_id: $json->endpoint_id ?? null, - key_id: $json->key_id ?? null, - key_issuing_request_id: $json->key_issuing_request_id ?? null, - override_guest_acs_entrance_ids: $json->override_guest_acs_entrance_ids ?? - null, - ); - } - public function __construct( - /** - * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. - */ - public bool|null $auto_join, - /** - * Names of the doors to which to grant access in the Vostio access system. - */ - public array|null $door_names, - /** - * Endpoint ID in the Vostio access system. - */ - public string|null $endpoint_id, - /** - * Key ID in the Vostio access system. - */ - public string|null $key_id, - /** - * Key issuing request ID in the Vostio access system. - */ - public string|null $key_issuing_request_id, - /** - * IDs of the guest entrances to override in the Vostio access system. - */ - public array|null $override_guest_acs_entrance_ids, - ) {} -} + public function __construct( + /** + * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $access_method, + /** + * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_credential_id, + /** + * ID of the credential pool to which the credential belongs. + */ + public string|null $acs_credential_pool_id, + /** + * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $acs_system_id, + /** + * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $acs_user_id, + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredentialOnSeam\AkilesMetadata|null $akiles_metadata, + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredentialOnSeam\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + /** + * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_number, + /** + * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $code, + /** + * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $connected_account_id, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. + */ + public string|null $created_at, + /** + * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $display_name, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. + */ + public string|null $ends_at, + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array $errors, + /** + * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. + */ + public string|null $external_type, + /** + * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. + */ + public string|null $external_type_display_name, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. + */ + public bool|null $is_issued, + /** + * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. + */ + public bool|null $is_latest_desired_state_synced_with_provider, + public bool|null $is_managed, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). + */ + public bool|null $is_multi_phone_sync_credential, + /** + * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. + */ + public bool|null $is_one_time_use, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. + */ + public string|null $issued_at, + /** + * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. + */ + public string|null $latest_desired_state_synced_with_provider_at, + /** + * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $parent_acs_credential_id, + /** + * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + /** + * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. + */ + public string|null $user_identity_id, + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public AcsCredentialOnSeam\VisionlineMetadata|null $visionline_metadata, + /** + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array $warnings, + /** + * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $workspace_id, + ) {} + } -/** - * Error associated with the action. - */ -class ActionAttemptError -{ - public static function from_json(mixed $json): ActionAttemptError|null + class Warnings { - if (!$json) { - return null; + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + original_access_method_id: $json->original_access_method_id ?? + null, + warning_code: $json->warning_code ?? null, + warning_message: $json->warning_message ?? null, + ); } - return new self( - message: $json->message ?? null, - type: $json->type ?? null, - ); + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * ID of the original access method from which this backup access method was split, if applicable. + */ + public string|null $original_access_method_id, + public string|null $warning_code, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $warning_message, + ) {} } - public function __construct( - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Type of the error. - */ - public string|null $type, - ) {} -} + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AkilesMetadata + { + public static function from_json(mixed $json): AkilesMetadata|null + { + if (!$json) { + return null; + } + return new self(member_pin_id: $json->member_pin_id ?? null); + } -/** - * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class ActionAttemptErrors -{ - public static function from_json(mixed $json): ActionAttemptErrors|null + public function __construct( + /** + * ID of the Akiles member PIN. + */ + public string|null $member_pin_id, + ) {} + } + + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AssaAbloyVostioMetadata { - if (!$json) { - return null; + public static function from_json( + mixed $json, + ): AssaAbloyVostioMetadata|null { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + door_names: $json->door_names ?? null, + endpoint_id: $json->endpoint_id ?? null, + key_id: $json->key_id ?? null, + key_issuing_request_id: $json->key_issuing_request_id ?? null, + override_guest_acs_entrance_ids: $json->override_guest_acs_entrance_ids ?? + null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); + + public function __construct( + /** + * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. + */ + public bool|null $auto_join, + /** + * Names of the doors to which to grant access in the Vostio access system. + */ + public array|null $door_names, + /** + * Endpoint ID in the Vostio access system. + */ + public string|null $endpoint_id, + /** + * Key ID in the Vostio access system. + */ + public string|null $key_id, + /** + * Key issuing request ID in the Vostio access system. + */ + public string|null $key_issuing_request_id, + /** + * IDs of the guest entrances to override in the Vostio access system. + */ + public array|null $override_guest_acs_entrance_ids, + ) {} } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - public string|null $error_code, - public string|null $message, - ) {} -} + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } -/** - * Previous access time configuration. - */ -class ActionAttemptFrom -{ - public static function from_json(mixed $json): ActionAttemptFrom|null + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } + + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class VisionlineMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + card_function_type: $json->card_function_type ?? null, + card_id: $json->card_id ?? null, + common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, + credential_id: $json->credential_id ?? null, + guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, + is_valid: $json->is_valid ?? null, + joiner_acs_credential_ids: $json->joiner_acs_credential_ids ?? + null, + ); } - return new self( - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); + + public function __construct( + /** + * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. + */ + public bool|null $auto_join, + /** + * Card function type in the Visionline access system. + */ + public string|null $card_function_type, + /** + * ID of the card in the Visionline access system. + */ + public string|null $card_id, + /** + * Common entrance IDs in the Visionline access system. + */ + public array|null $common_acs_entrance_ids, + /** + * ID of the credential in the Visionline access system. + */ + public string|null $credential_id, + /** + * Guest entrance IDs in the Visionline access system. + */ + public array|null $guest_acs_entrance_ids, + /** + * Indicates whether the credential is valid. + */ + public bool|null $is_valid, + /** + * IDs of the credentials to which you want to join. + */ + public array|null $joiner_acs_credential_ids, + ) {} } - public function __construct( - /** - * Previous end time for access. - */ - public string|null $ends_at, - /** - * Previous start time for access. - */ - public string|null $starts_at, - ) {} + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + class PendingMutations + { + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + /** + * Previous access time configuration. + */ + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + /** + * Mutation code to indicate that Seam is in the process of updating the access times for this access method. + */ + public string|null $mutation_code, + /** + * New access time configuration. + */ + public PendingMutations\To|null $to, + ) {} + } } -/** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ -class ActionAttemptPendingMutations -{ - public static function from_json( - mixed $json, - ): ActionAttemptPendingMutations|null { - if (!$json) { - return null; +namespace Seam\Resources\ActionAttempt\Result\AcsCredentialOnEncoder { + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + cancelled: $json->cancelled ?? null, + card_format: $json->card_format ?? null, + card_holder: $json->card_holder ?? null, + card_id: $json->card_id ?? null, + common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, + discarded: $json->discarded ?? null, + expired: $json->expired ?? null, + guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, + number_of_issued_cards: $json->number_of_issued_cards ?? null, + overridden: $json->overridden ?? null, + overwritten: $json->overwritten ?? null, + pending_auto_update: $json->pending_auto_update ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - from: isset($json->from) - ? ActionAttemptFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) ? ActionAttemptTo::from_json($json->to) : null, - ); - } - public function __construct( - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Previous access time configuration. - */ - public ActionAttemptFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of updating the access times for this access method. - */ - public string|null $mutation_code, - /** - * New access time configuration. - */ - public ActionAttemptTo|null $to, - ) {} + public function __construct( + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is cancelled. + */ + public bool|null $cancelled, + /** + * Format of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_format, + /** + * Holder of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_holder, + /** + * Card ID for the Visionline card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public string|null $card_id, + /** + * IDs of the common [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array|null $common_acs_entrance_ids, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is discarded. + */ + public bool|null $discarded, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is expired. + */ + public bool|null $expired, + /** + * IDs of the guest [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public array|null $guest_acs_entrance_ids, + /** + * Number of issued cards associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + public float|null $number_of_issued_cards, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overridden. + */ + public bool|null $overridden, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overwritten. + */ + public bool|null $overwritten, + /** + * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is pending auto-update. + */ + public bool|null $pending_auto_update, + ) {} + } } -/** - * Result of the action. - */ -class ActionAttemptResult -{ - public static function from_json(mixed $json): ActionAttemptResult|null +namespace Seam\Resources\ActionAttempt\Result\AcsCredentialOnSeam { + /** + * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AkilesMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): AkilesMetadata|null + { + if (!$json) { + return null; + } + return new self(member_pin_id: $json->member_pin_id ?? null); } - return new self( - access_code: $json->access_code ?? null, - access_method: $json->access_method ?? null, - access_method_id: $json->access_method_id ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - acs_credential_on_encoder: isset($json->acs_credential_on_encoder) - ? ActionAttemptAcsCredentialOnEncoder::from_json( - $json->acs_credential_on_encoder, - ) - : null, - acs_credential_on_seam: isset($json->acs_credential_on_seam) - ? ActionAttemptAcsCredentialOnSeam::from_json( - $json->acs_credential_on_seam, - ) - : null, - acs_credential_pool_id: $json->acs_credential_pool_id ?? null, - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - akiles_metadata: isset($json->akiles_metadata) - ? ActionAttemptAkilesMetadata::from_json($json->akiles_metadata) - : null, - assa_abloy_vostio_metadata: isset($json->assa_abloy_vostio_metadata) - ? ActionAttemptAssaAbloyVostioMetadata::from_json( - $json->assa_abloy_vostio_metadata, - ) - : null, - card_number: $json->card_number ?? null, - client_session_token: $json->client_session_token ?? null, - code: $json->code ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - customization_profile_id: $json->customization_profile_id ?? null, - display_name: $json->display_name ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => ActionAttemptErrors::from_json($e), - $json->errors ?? [], - ), - external_type: $json->external_type ?? null, - external_type_display_name: $json->external_type_display_name ?? - null, - instant_key_url: $json->instant_key_url ?? null, - is_assignment_required: $json->is_assignment_required ?? null, - is_encoding_required: $json->is_encoding_required ?? null, - is_issued: $json->is_issued ?? null, - is_latest_desired_state_synced_with_provider: $json->is_latest_desired_state_synced_with_provider ?? - null, - is_managed: $json->is_managed ?? null, - is_multi_phone_sync_credential: $json->is_multi_phone_sync_credential ?? - null, - is_one_time_use: $json->is_one_time_use ?? null, - is_ready_for_assignment: $json->is_ready_for_assignment ?? null, - is_ready_for_encoding: $json->is_ready_for_encoding ?? null, - issued_at: $json->issued_at ?? null, - latest_desired_state_synced_with_provider_at: $json->latest_desired_state_synced_with_provider_at ?? - null, - mode: $json->mode ?? null, - noise_threshold: $json->noise_threshold ?? null, - parent_acs_credential_id: $json->parent_acs_credential_id ?? null, - pending_mutations: array_map( - fn($p) => ActionAttemptPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - starts_at: $json->starts_at ?? null, - user_identity_id: $json->user_identity_id ?? null, - visionline_metadata: isset($json->visionline_metadata) - ? ActionAttemptVisionlineMetadata::from_json( - $json->visionline_metadata, - ) - : null, - warnings: array_map( - fn($w) => ActionAttemptWarnings::from_json($w), - $json->warnings ?? [], - ), - was_confirmed_by_device: $json->was_confirmed_by_device ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Created access code. - */ - public mixed $access_code, - /** - * Access method for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $access_method, - /** - * ID of the access method. - */ - public string|null $access_method_id, - /** - * ID of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $acs_credential_id, - /** - * Snapshot of credential data read from the physical encoder. - */ - public ActionAttemptAcsCredentialOnEncoder|null $acs_credential_on_encoder, - /** - * Corresponding credential data as stored on Seam and the access system. - */ - public ActionAttemptAcsCredentialOnSeam|null $acs_credential_on_seam, - /** - * ID of the credential pool to which the credential belongs. - */ - public string|null $acs_credential_pool_id, - /** - * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $acs_system_id, - /** - * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $acs_user_id, - /** - * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptAkilesMetadata|null $akiles_metadata, - /** - * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptAssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, - /** - * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_number, - /** - * Token of the client session associated with the access method. - */ - public string|null $client_session_token, - /** - * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $code, - /** - * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $connected_account_id, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was created. - */ - public string|null $created_at, - /** - * ID of the customization profile associated with the access method. - */ - public string|null $customization_profile_id, - /** - * Display name that corresponds to the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $display_name, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. - */ - public string|null $ends_at, - /** - * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array $errors, - /** - * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. - */ - public string|null $external_type, - /** - * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. - */ - public string|null $external_type_display_name, - /** - * URL of the Instant Key for mobile key access methods. - */ - public string|null $instant_key_url, - /** - * Indicates whether an existing card credential must be assigned to this access method before it can be issued. Only applies to card-mode access methods on systems that support credential assignment. - */ - public bool|null $is_assignment_required, - /** - * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. - */ - public bool|null $is_encoding_required, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. - */ - public bool|null $is_issued, - /** - * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. - */ - public bool|null $is_latest_desired_state_synced_with_provider, - public bool|null $is_managed, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). - */ - public bool|null $is_multi_phone_sync_credential, - /** - * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. - */ - public bool|null $is_one_time_use, - /** - * Indicates whether the access method is ready for card assignment. This is true when the access method is in card mode, has not yet been issued, and the system supports credential assignment. - */ - public bool|null $is_ready_for_assignment, - /** - * Indicates whether the access method is ready to be encoded. This is true when the credential has been created and the card has not yet been issued. - */ - public bool|null $is_ready_for_encoding, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. - */ - public string|null $issued_at, - /** - * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. - */ - public string|null $latest_desired_state_synced_with_provider_at, - /** - * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $mode, - /** - * Created noise threshold. - */ - public mixed $noise_threshold, - /** - * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $parent_acs_credential_id, - /** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ - public array $pending_mutations, - /** - * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - /** - * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. - */ - public string|null $user_identity_id, - /** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public ActionAttemptVisionlineMetadata|null $visionline_metadata, - /** - * Warnings related to scanning the credential, such as mismatches between the credential data currently encoded on the card and the corresponding data stored on Seam and the access system. - */ - public array $warnings, - /** - * Indicates whether the device confirmed that the lock action occurred. - */ - public bool|null $was_confirmed_by_device, - /** - * ID of the workspace that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $workspace_id, - ) {} -} + public function __construct( + /** + * ID of the Akiles member PIN. + */ + public string|null $member_pin_id, + ) {} + } -/** - * New access time configuration. - */ -class ActionAttemptTo -{ - public static function from_json(mixed $json): ActionAttemptTo|null + /** + * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class AssaAbloyVostioMetadata { - if (!$json) { - return null; + public static function from_json( + mixed $json, + ): AssaAbloyVostioMetadata|null { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + door_names: $json->door_names ?? null, + endpoint_id: $json->endpoint_id ?? null, + key_id: $json->key_id ?? null, + key_issuing_request_id: $json->key_issuing_request_id ?? null, + override_guest_acs_entrance_ids: $json->override_guest_acs_entrance_ids ?? + null, + ); } - return new self( - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); + + public function __construct( + /** + * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. + */ + public bool|null $auto_join, + /** + * Names of the doors to which to grant access in the Vostio access system. + */ + public array|null $door_names, + /** + * Endpoint ID in the Vostio access system. + */ + public string|null $endpoint_id, + /** + * Key ID in the Vostio access system. + */ + public string|null $key_id, + /** + * Key issuing request ID in the Vostio access system. + */ + public string|null $key_issuing_request_id, + /** + * IDs of the guest entrances to override in the Vostio access system. + */ + public array|null $override_guest_acs_entrance_ids, + ) {} } - public function __construct( - /** - * New end time for access. - */ - public string|null $ends_at, - /** - * New start time for access. - */ - public string|null $starts_at, - ) {} -} + /** + * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); + } -/** - * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class ActionAttemptVisionlineMetadata -{ - public static function from_json( - mixed $json, - ): ActionAttemptVisionlineMetadata|null { - if (!$json) { - return null; + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + public string|null $error_code, + public string|null $message, + ) {} + } + + /** + * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self( + auto_join: $json->auto_join ?? null, + card_function_type: $json->card_function_type ?? null, + card_id: $json->card_id ?? null, + common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, + credential_id: $json->credential_id ?? null, + guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, + is_valid: $json->is_valid ?? null, + joiner_acs_credential_ids: $json->joiner_acs_credential_ids ?? + null, + ); } - return new self( - cancelled: $json->cancelled ?? null, - card_format: $json->card_format ?? null, - card_holder: $json->card_holder ?? null, - card_id: $json->card_id ?? null, - common_acs_entrance_ids: $json->common_acs_entrance_ids ?? null, - discarded: $json->discarded ?? null, - expired: $json->expired ?? null, - guest_acs_entrance_ids: $json->guest_acs_entrance_ids ?? null, - number_of_issued_cards: $json->number_of_issued_cards ?? null, - overridden: $json->overridden ?? null, - overwritten: $json->overwritten ?? null, - pending_auto_update: $json->pending_auto_update ?? null, - ); + + public function __construct( + /** + * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. + */ + public bool|null $auto_join, + /** + * Card function type in the Visionline access system. + */ + public string|null $card_function_type, + /** + * ID of the card in the Visionline access system. + */ + public string|null $card_id, + /** + * Common entrance IDs in the Visionline access system. + */ + public array|null $common_acs_entrance_ids, + /** + * ID of the credential in the Visionline access system. + */ + public string|null $credential_id, + /** + * Guest entrance IDs in the Visionline access system. + */ + public array|null $guest_acs_entrance_ids, + /** + * Indicates whether the credential is valid. + */ + public bool|null $is_valid, + /** + * IDs of the credentials to which you want to join. + */ + public array|null $joiner_acs_credential_ids, + ) {} } - public function __construct( - /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is cancelled. - */ - public bool|null $cancelled, - /** - * Format of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_format, - /** - * Holder of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_holder, - /** - * Card ID for the Visionline card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public string|null $card_id, - /** - * IDs of the common [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array|null $common_acs_entrance_ids, - /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is discarded. - */ - public bool|null $discarded, - /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is expired. - */ - public bool|null $expired, - /** - * IDs of the guest [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public array|null $guest_acs_entrance_ids, - /** - * Number of issued cards associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ - public float|null $number_of_issued_cards, - /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overridden. - */ - public bool|null $overridden, - /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overwritten. - */ - public bool|null $overwritten, - /** - * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is pending auto-update. - */ - public bool|null $pending_auto_update, - ) {} + /** + * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). - */ -class ActionAttemptWarnings -{ - public static function from_json(mixed $json): ActionAttemptWarnings|null +namespace Seam\Resources\ActionAttempt\Result\PendingMutations { + /** + * Previous access time configuration. + */ + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); + + public function __construct( + /** + * Previous end time for access. + */ + public string|null $ends_at, + /** + * Previous start time for access. + */ + public string|null $starts_at, + ) {} } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + /** + * New access time configuration. + */ + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( + /** + * New end time for access. + */ + public string|null $ends_at, + /** + * New start time for access. + */ + public string|null $starts_at, + ) {} + } } diff --git a/src/Resources/Batch.php b/src/Resources/Batch.php index 1b32ca77..29a7b2da 100644 --- a/src/Resources/Batch.php +++ b/src/Resources/Batch.php @@ -1,218 +1,219 @@ access_codes ?? null, + access_grants: $json->access_grants ?? null, + access_methods: $json->access_methods ?? null, + acs_access_groups: $json->acs_access_groups ?? null, + acs_credentials: $json->acs_credentials ?? null, + acs_encoders: $json->acs_encoders ?? null, + acs_entrances: $json->acs_entrances ?? null, + acs_systems: $json->acs_systems ?? null, + acs_users: $json->acs_users ?? null, + action_attempts: $json->action_attempts ?? null, + client_sessions: $json->client_sessions ?? null, + connect_webviews: $json->connect_webviews ?? null, + connected_accounts: $json->connected_accounts ?? null, + devices: $json->devices ?? null, + events: $json->events ?? null, + instant_keys: $json->instant_keys ?? null, + noise_thresholds: $json->noise_thresholds ?? null, + spaces: $json->spaces ?? null, + thermostat_daily_programs: $json->thermostat_daily_programs ?? + null, + thermostat_schedules: $json->thermostat_schedules ?? null, + unmanaged_access_codes: $json->unmanaged_access_codes ?? null, + unmanaged_devices: $json->unmanaged_devices ?? null, + user_identities: $json->user_identities ?? null, + workspaces: $json->workspaces ?? null, + ); } - return new self( - access_codes: $json->access_codes ?? null, - access_grants: $json->access_grants ?? null, - access_methods: $json->access_methods ?? null, - acs_access_groups: $json->acs_access_groups ?? null, - acs_credentials: $json->acs_credentials ?? null, - acs_encoders: $json->acs_encoders ?? null, - acs_entrances: $json->acs_entrances ?? null, - acs_systems: $json->acs_systems ?? null, - acs_users: $json->acs_users ?? null, - action_attempts: $json->action_attempts ?? null, - client_sessions: $json->client_sessions ?? null, - connect_webviews: $json->connect_webviews ?? null, - connected_accounts: $json->connected_accounts ?? null, - devices: $json->devices ?? null, - events: $json->events ?? null, - instant_keys: $json->instant_keys ?? null, - noise_thresholds: $json->noise_thresholds ?? null, - spaces: $json->spaces ?? null, - thermostat_daily_programs: $json->thermostat_daily_programs ?? null, - thermostat_schedules: $json->thermostat_schedules ?? null, - unmanaged_access_codes: $json->unmanaged_access_codes ?? null, - unmanaged_devices: $json->unmanaged_devices ?? null, - user_identities: $json->user_identities ?? null, - workspaces: $json->workspaces ?? null, - ); - } - public function __construct( - /** - * Represents a smart lock [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - * - * An access code is a code used for a keypad or pinpad device. Unlike physical keys, which can easily be lost or duplicated, PIN codes can be customized, tracked, and altered on the fly. Using the Seam Access Code API, you can easily generate access codes on the hundreds of door lock models with which we integrate. - * - * Seam supports programming two types of access codes: [ongoing](https://docs.seam.co/low-level-apis/smart-locks/access-codes#ongoing-access-codes) and [time-bound](https://docs.seam.co/low-level-apis/smart-locks/access-codes#time-bound-access-codes). To differentiate between the two, refer to the `type` property of the access code. Ongoing codes display as `ongoing`, whereas time-bound codes are labeled `time_bound`. An ongoing access code is active, until it has been removed from the device. To specify an ongoing access code, leave both `starts_at` and `ends_at` empty. A time-bound access code will be programmed at the `starts_at` time and removed at the `ends_at` time. - * - * In addition, for certain devices, Seam also supports [offline access codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes#offline-access-codes). Offline access (PIN) codes are designed for door locks that might not always maintain an internet connection. For this type of access code, the device manufacturer uses encryption keys (tokens) to create server-based registries of algorithmically-generated offline PIN codes. Because the tokens remain synchronized with the managed devices, the locks do not require an active internet connection—and you do not need to be near the locks—to create an offline access code. Then, owners or managers can share these offline codes with users through a variety of mechanisms, such as messaging applications. That is, lock users do not need to install a smartphone application to receive an offline access code. - * - * For granting a person access to a space, [Access Grants](https://docs.seam.co/use-cases/granting-access) are the default and recommended approach and work across both standalone smart locks and access systems. Use the lower-level Access Codes API directly only when you specifically need to manage individual PIN codes. - */ - public mixed $access_codes, - /** - * Represents an Access Grant. Access Grants enable you to grant a user identity access to spaces, entrances, and devices through one or more access methods, such as mobile keys, plastic cards, and PIN codes. You can create an Access Grant for an existing user identity, or you can create a new user identity *while* creating the new Access Grant. - */ - public mixed $access_grants, - /** - * Represents an access method for an Access Grant. Access methods describe the modes of access, such as PIN codes, plastic cards, and mobile keys. For a mobile key, the access method also stores the URL for the associated Instant Key. - */ - public mixed $access_methods, - /** - * Group that defines the entrances to which a set of users has access and, in some cases, the access schedule for these entrances and users. - * - * Some access control systems use [access group](https://docs.seam.co/low-level-apis/access-systems/user-management/assigning-users-to-access-groups), which are sets of users, combined with sets of permissions. These permissions include both the set of areas or assets that the users can access and the schedule during which the users can access these areas or assets. Instead of assigning access rights individually to each access control system user, which can be time-consuming and error-prone, administrators can assign users to an access group, thereby ensuring that the users inherit all the permissions associated with the access group. Using access groups streamlines the process of managing large numbers of access control system users, especially in bigger organizations or complexes. - * - * To learn whether your access control system supports access groups, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). - */ - public mixed $acs_access_groups, - /** - * Means by which an [access control system user](https://docs.seam.co/low-level-apis/access-systems/user-management) gains access at an [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). The `acs_credential` object represents a [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) that provides an ACS user access within an [access control system](https://docs.seam.co/low-level-apis/access-systems). - * - * An access control system generally uses digital means of access to authorize a user trying to get through a specific entrance. Examples of credentials include plastic key cards, mobile keys, biometric identifiers, and PIN codes. The electronic nature of these credentials, as well as the fact that access is centralized, enables both the rapid provisioning and rescinding of access and the ability to compile access audit logs. - * - * For each `acs_credential`, you define the access method. You can also specify additional properties, such as a PIN code, depending on the credential type. - * - * For granting a person access to a space, [Access Grants](https://docs.seam.co/use-cases/granting-access) are the default and recommended approach. Use the lower-level ACS credential API directly only when you specifically need to manage individual credentials. - */ - public mixed $acs_credentials, - /** - * Represents a hardware device that encodes [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) data onto physical cards within an [access control system](https://docs.seam.co/low-level-apis/access-systems). - * - * Some access control systems require credentials to be encoded onto plastic key cards using a card encoder. This process involves the following two key steps: - * - * 1. Credential creation - * Configure the access parameters for the credential. - * 2. Card encoding - * Write the credential data onto the card using a compatible card encoder. - * - * Separately, the Seam API also supports card scanning, which enables you to scan and read the encoded data on a card. You can use this action to confirm consistency with access control system records or diagnose discrepancies if needed. - * - * See [Working with Card Encoders and Scanners](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). - * - * To verify if your access control system requires a card encoder, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). - */ - public mixed $acs_encoders, - /** - * Represents an [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) within an [access control system](https://docs.seam.co/low-level-apis/access-systems). - * - * In an access control system, an entrance is a secured door, gate, zone, or other method of entry. You can list details for all the `acs_entrance` resources in your workspace or get these details for a specific `acs_entrance`. You can also list all entrances associated with a specific credential, and you can list all credentials associated with a specific entrance. - */ - public mixed $acs_entrances, - /** - * Represents an [access control system](https://docs.seam.co/low-level-apis/access-systems). - * - * Within an `acs_system`, create [`acs_user`s](https://docs.seam.co/api/acs/users/object) and [`acs_credential`s](https://docs.seam.co/api/acs/credentials/object) to grant access to the `acs_user`s. - * - * For details about the resources associated with an access control system, see the [access control systems namespace](https://docs.seam.co/api/acs). - */ - public mixed $acs_systems, - /** - * Represents a [user](https://docs.seam.co/low-level-apis/access-systems/user-management) in an [access system](https://docs.seam.co/low-level-apis/access-systems). - * - * An access system user typically refers to an individual who requires access, like an employee or resident. Each user can possess multiple credentials that serve as their keys or identifiers for access. The type of credential can vary widely. For example, in the Salto system, a user can have a PIN code, a mobile app account, and a fob. In other platforms, it is not uncommon for a user to have more than one of the same credential type, such as multiple key cards. Additionally, these credentials can have a schedule or validity period. - * - * For details about how to configure users in your access system, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). - */ - public mixed $acs_users, - /** - * Represents an action attempt that enables you to keep track of the progress of your action that affects a physical device or system.actions against a device. Action attempts are useful because the physical world is intrinsically asynchronous. - * - * When you request for a device to perform an action, the Seam API immediately returns an action attempt object. In the background, the Seam API performs the action. - * - * See also [Action Attempts](https://docs.seam.co/core-concepts/action-attempts). - */ - public mixed $action_attempts, - /** - * Represents a [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). If you want to restrict your users' access to their own devices, use client sessions. - * - * You create each client session with a custom `user_identifier_key`. Normally, the `user_identifier_key` is a user ID that your application provides. - * - * When calling the Seam API from your backend using an API key, you can pass the `user_identifier_key` as a parameter to limit results to the associated client session. For example, `/devices/list?user_identifier_key=123` only returns devices associated with the client session created with the `user_identifier_key` `123`. - * - * A client session has a token that you can use with the Seam JavaScript SDK to make requests from the client (browser) directly to the Seam API. The token restricts the user's access to only the devices that they own. - * - * See also [Get Started with React](https://docs.seam.co/ui-components/overview/getting-started-with-seam-components/get-started-with-react-components-and-client-session-tokens). - */ - public mixed $client_sessions, - /** - * Represents a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews). - * - * Connect Webviews are fully-embedded client-side components that you add to your app. Your users interact with your embedded Connect Webviews to link their IoT device or system accounts to Seam. That is, Connect Webviews walk your users through the process of logging in to their device or system accounts. Seam handles all the authentication steps, and—once your user has completed the authorization through your app—you can access and control their devices or systems using the Seam API. - * - * Connect Webviews perform credential validation, multifactor authentication (when applicable), and error handling for each brand that Seam supports. Further, Connect Webviews work across all modern browsers and platforms, including Chrome, Safari, and Firefox. - * - * To enable a user to connect their device or system account to Seam through your app, first create a `connect_webview`. Once created, this `connect_webview` includes a URL that you can use to open an [iframe](https://www.w3schools.com/html/html_iframe.asp) or new window containing the Connect Webview for your user. - * - * When you create a Connect Webview, specify the desired provider category key in the `provider_category` parameter. Alternately, to specify a list of providers explicitly, use the `accepted_providers` parameter with a list of device provider keys. - * - * To list all providers within a category, use `/devices/list_device_providers` with the desired `provider_category` filter. To list all provider keys, use `/devices/list_device_providers` with no filters. - */ - public mixed $connect_webviews, - /** - * Represents a [connected account](https://docs.seam.co/core-concepts/connected-accounts). A connected account is an external third-party account to which your user has authorized Seam to get access, for example, an August account with a list of door locks. - */ - public mixed $connected_accounts, - /** - * Represents a [device](https://docs.seam.co/core-concepts/devices) that has been connected to Seam. - */ - public mixed $devices, - /** - * Represents an event. Events let you know when something interesting happens in your workspace. For example, when a lock is unlocked, Seam creates a `lock.unlocked` event. When a device's battery level is low, Seam creates a `device.battery_low` event. - * - * As with other API resources, you can retrieve an individual event or a list of events. Seam also provides a separate webhook system for sending the event objects directly to an endpoint on your sever. Manage webhooks through [Seam Console](https://console.seam.co). You can also use the webhooks sandbox in Seam Console to see the different payloads for each event and test them against your own endpoints. - */ - public mixed $events, - /** - * Represents a Seam Instant Key. For issuing Bluetooth mobile keys, Instant Keys are the fastest way to share access. With a single API call, you can create a mobile key and send it through text or email or embed it in your own app. - * - * There’s no app to install, nor account to create. Your user just taps a link and gets a lightweight, native-feeling experience using iOS App Clip or Instant Apps on Android. Further, Instant Keys work offline, so even in areas with poor cellular or Wi-Fi, like elevator banks or concrete-walled hallways, the Instant Keys still work. - */ - public mixed $instant_keys, - /** - * Represents a [noise threshold](https://docs.seam.co/capability-guides/noise-sensors/configure-noise-threshold-settings) for a [noise sensor](https://docs.seam.co/capability-guides/noise-sensors). Thresholds represent the limits of noise tolerated at a property, which can be customized for each hour of the day. Each device has its own default thresholds, but you can use the Seam API to modify them. - */ - public mixed $noise_thresholds, - /** - * Represents a space that is a logical grouping of devices and entrances. You can assign access to an entire space, thereby making granting access more efficient. - */ - public mixed $spaces, - /** - * Represents a thermostat daily program, consisting of a set of periods, each of which has a starting time and the key that identifies the climate preset to apply at the starting time. - */ - public mixed $thermostat_daily_programs, - /** - * Represents a [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) that activates a configured [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) on a [thermostat](https://docs.seam.co/capability-guides/thermostats) at a specified starting time and deactivates the climate preset at a specified ending time. - */ - public mixed $thermostat_schedules, - /** - * Represents an [unmanaged smart lock access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes). - * - * An access code is a code used for a keypad or pinpad device. Unlike physical keys, which can easily be lost or duplicated, PIN codes can be customized, tracked, and altered on the fly. - * - * When you create an access code on a device in Seam, it is created as a managed access code. Access codes that exist on a device that were not created through Seam are considered unmanaged codes. We strictly limit the operations that can be performed on unmanaged codes. - * - * Prior to using Seam to manage your devices, you may have used another lock management system to manage the access codes on your devices. Where possible, we help you keep any existing access codes on devices and transition those codes to ones managed by your Seam workspace. - * - * Not all providers support unmanaged access codes. The following providers do not support unmanaged access codes: - * - * - [Kwikset](https://docs.seam.co/device-and-system-integration-guides/kwikset-locks) - */ - public mixed $unmanaged_access_codes, - /** - * Represents an [unmanaged device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). An unmanaged device has a limited set of visible properties and a subset of supported events. You cannot control an unmanaged device. Any [access codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes) on an unmanaged device are unmanaged. To control an unmanaged device with Seam, [convert it to a managed device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices#convert-an-unmanaged-device-to-managed). - */ - public mixed $unmanaged_devices, - /** - * Represents a [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with an application user account. - */ - public mixed $user_identities, - /** - * Represents a Seam [workspace](https://docs.seam.co/core-concepts/workspaces). A workspace is a top-level entity that encompasses all other resources below it, such as devices, connected accounts, and Connect Webviews. Seam provides two types of workspaces. A [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces) is a special type of workspace designed for testing code. Sandbox workspaces offer test device accounts and virtual devices that you can connect and control. This ability to work with virtual devices is quite handy because it removes the need to own physical devices from multiple brands. To connect real devices and systems to Seam, use a [production workspace](https://docs.seam.co/core-concepts/workspaces#production-workspaces). - */ - public mixed $workspaces, - ) {} + public function __construct( + /** + * Represents a smart lock [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + * + * An access code is a code used for a keypad or pinpad device. Unlike physical keys, which can easily be lost or duplicated, PIN codes can be customized, tracked, and altered on the fly. Using the Seam Access Code API, you can easily generate access codes on the hundreds of door lock models with which we integrate. + * + * Seam supports programming two types of access codes: [ongoing](https://docs.seam.co/low-level-apis/smart-locks/access-codes#ongoing-access-codes) and [time-bound](https://docs.seam.co/low-level-apis/smart-locks/access-codes#time-bound-access-codes). To differentiate between the two, refer to the `type` property of the access code. Ongoing codes display as `ongoing`, whereas time-bound codes are labeled `time_bound`. An ongoing access code is active, until it has been removed from the device. To specify an ongoing access code, leave both `starts_at` and `ends_at` empty. A time-bound access code will be programmed at the `starts_at` time and removed at the `ends_at` time. + * + * In addition, for certain devices, Seam also supports [offline access codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes#offline-access-codes). Offline access (PIN) codes are designed for door locks that might not always maintain an internet connection. For this type of access code, the device manufacturer uses encryption keys (tokens) to create server-based registries of algorithmically-generated offline PIN codes. Because the tokens remain synchronized with the managed devices, the locks do not require an active internet connection—and you do not need to be near the locks—to create an offline access code. Then, owners or managers can share these offline codes with users through a variety of mechanisms, such as messaging applications. That is, lock users do not need to install a smartphone application to receive an offline access code. + * + * For granting a person access to a space, [Access Grants](https://docs.seam.co/use-cases/granting-access) are the default and recommended approach and work across both standalone smart locks and access systems. Use the lower-level Access Codes API directly only when you specifically need to manage individual PIN codes. + */ + public mixed $access_codes, + /** + * Represents an Access Grant. Access Grants enable you to grant a user identity access to spaces, entrances, and devices through one or more access methods, such as mobile keys, plastic cards, and PIN codes. You can create an Access Grant for an existing user identity, or you can create a new user identity *while* creating the new Access Grant. + */ + public mixed $access_grants, + /** + * Represents an access method for an Access Grant. Access methods describe the modes of access, such as PIN codes, plastic cards, and mobile keys. For a mobile key, the access method also stores the URL for the associated Instant Key. + */ + public mixed $access_methods, + /** + * Group that defines the entrances to which a set of users has access and, in some cases, the access schedule for these entrances and users. + * + * Some access control systems use [access group](https://docs.seam.co/low-level-apis/access-systems/user-management/assigning-users-to-access-groups), which are sets of users, combined with sets of permissions. These permissions include both the set of areas or assets that the users can access and the schedule during which the users can access these areas or assets. Instead of assigning access rights individually to each access control system user, which can be time-consuming and error-prone, administrators can assign users to an access group, thereby ensuring that the users inherit all the permissions associated with the access group. Using access groups streamlines the process of managing large numbers of access control system users, especially in bigger organizations or complexes. + * + * To learn whether your access control system supports access groups, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). + */ + public mixed $acs_access_groups, + /** + * Means by which an [access control system user](https://docs.seam.co/low-level-apis/access-systems/user-management) gains access at an [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). The `acs_credential` object represents a [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) that provides an ACS user access within an [access control system](https://docs.seam.co/low-level-apis/access-systems). + * + * An access control system generally uses digital means of access to authorize a user trying to get through a specific entrance. Examples of credentials include plastic key cards, mobile keys, biometric identifiers, and PIN codes. The electronic nature of these credentials, as well as the fact that access is centralized, enables both the rapid provisioning and rescinding of access and the ability to compile access audit logs. + * + * For each `acs_credential`, you define the access method. You can also specify additional properties, such as a PIN code, depending on the credential type. + * + * For granting a person access to a space, [Access Grants](https://docs.seam.co/use-cases/granting-access) are the default and recommended approach. Use the lower-level ACS credential API directly only when you specifically need to manage individual credentials. + */ + public mixed $acs_credentials, + /** + * Represents a hardware device that encodes [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) data onto physical cards within an [access control system](https://docs.seam.co/low-level-apis/access-systems). + * + * Some access control systems require credentials to be encoded onto plastic key cards using a card encoder. This process involves the following two key steps: + * + * 1. Credential creation + * Configure the access parameters for the credential. + * 2. Card encoding + * Write the credential data onto the card using a compatible card encoder. + * + * Separately, the Seam API also supports card scanning, which enables you to scan and read the encoded data on a card. You can use this action to confirm consistency with access control system records or diagnose discrepancies if needed. + * + * See [Working with Card Encoders and Scanners](https://docs.seam.co/low-level-apis/access-systems/working-with-card-encoders-and-scanners). + * + * To verify if your access control system requires a card encoder, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). + */ + public mixed $acs_encoders, + /** + * Represents an [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) within an [access control system](https://docs.seam.co/low-level-apis/access-systems). + * + * In an access control system, an entrance is a secured door, gate, zone, or other method of entry. You can list details for all the `acs_entrance` resources in your workspace or get these details for a specific `acs_entrance`. You can also list all entrances associated with a specific credential, and you can list all credentials associated with a specific entrance. + */ + public mixed $acs_entrances, + /** + * Represents an [access control system](https://docs.seam.co/low-level-apis/access-systems). + * + * Within an `acs_system`, create [`acs_user`s](https://docs.seam.co/api/acs/users/object) and [`acs_credential`s](https://docs.seam.co/api/acs/credentials/object) to grant access to the `acs_user`s. + * + * For details about the resources associated with an access control system, see the [access control systems namespace](https://docs.seam.co/api/acs). + */ + public mixed $acs_systems, + /** + * Represents a [user](https://docs.seam.co/low-level-apis/access-systems/user-management) in an [access system](https://docs.seam.co/low-level-apis/access-systems). + * + * An access system user typically refers to an individual who requires access, like an employee or resident. Each user can possess multiple credentials that serve as their keys or identifiers for access. The type of credential can vary widely. For example, in the Salto system, a user can have a PIN code, a mobile app account, and a fob. In other platforms, it is not uncommon for a user to have more than one of the same credential type, such as multiple key cards. Additionally, these credentials can have a schedule or validity period. + * + * For details about how to configure users in your access system, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). + */ + public mixed $acs_users, + /** + * Represents an action attempt that enables you to keep track of the progress of your action that affects a physical device or system.actions against a device. Action attempts are useful because the physical world is intrinsically asynchronous. + * + * When you request for a device to perform an action, the Seam API immediately returns an action attempt object. In the background, the Seam API performs the action. + * + * See also [Action Attempts](https://docs.seam.co/core-concepts/action-attempts). + */ + public mixed $action_attempts, + /** + * Represents a [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). If you want to restrict your users' access to their own devices, use client sessions. + * + * You create each client session with a custom `user_identifier_key`. Normally, the `user_identifier_key` is a user ID that your application provides. + * + * When calling the Seam API from your backend using an API key, you can pass the `user_identifier_key` as a parameter to limit results to the associated client session. For example, `/devices/list?user_identifier_key=123` only returns devices associated with the client session created with the `user_identifier_key` `123`. + * + * A client session has a token that you can use with the Seam JavaScript SDK to make requests from the client (browser) directly to the Seam API. The token restricts the user's access to only the devices that they own. + * + * See also [Get Started with React](https://docs.seam.co/ui-components/overview/getting-started-with-seam-components/get-started-with-react-components-and-client-session-tokens). + */ + public mixed $client_sessions, + /** + * Represents a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews). + * + * Connect Webviews are fully-embedded client-side components that you add to your app. Your users interact with your embedded Connect Webviews to link their IoT device or system accounts to Seam. That is, Connect Webviews walk your users through the process of logging in to their device or system accounts. Seam handles all the authentication steps, and—once your user has completed the authorization through your app—you can access and control their devices or systems using the Seam API. + * + * Connect Webviews perform credential validation, multifactor authentication (when applicable), and error handling for each brand that Seam supports. Further, Connect Webviews work across all modern browsers and platforms, including Chrome, Safari, and Firefox. + * + * To enable a user to connect their device or system account to Seam through your app, first create a `connect_webview`. Once created, this `connect_webview` includes a URL that you can use to open an [iframe](https://www.w3schools.com/html/html_iframe.asp) or new window containing the Connect Webview for your user. + * + * When you create a Connect Webview, specify the desired provider category key in the `provider_category` parameter. Alternately, to specify a list of providers explicitly, use the `accepted_providers` parameter with a list of device provider keys. + * + * To list all providers within a category, use `/devices/list_device_providers` with the desired `provider_category` filter. To list all provider keys, use `/devices/list_device_providers` with no filters. + */ + public mixed $connect_webviews, + /** + * Represents a [connected account](https://docs.seam.co/core-concepts/connected-accounts). A connected account is an external third-party account to which your user has authorized Seam to get access, for example, an August account with a list of door locks. + */ + public mixed $connected_accounts, + /** + * Represents a [device](https://docs.seam.co/core-concepts/devices) that has been connected to Seam. + */ + public mixed $devices, + /** + * Represents an event. Events let you know when something interesting happens in your workspace. For example, when a lock is unlocked, Seam creates a `lock.unlocked` event. When a device's battery level is low, Seam creates a `device.battery_low` event. + * + * As with other API resources, you can retrieve an individual event or a list of events. Seam also provides a separate webhook system for sending the event objects directly to an endpoint on your sever. Manage webhooks through [Seam Console](https://console.seam.co). You can also use the webhooks sandbox in Seam Console to see the different payloads for each event and test them against your own endpoints. + */ + public mixed $events, + /** + * Represents a Seam Instant Key. For issuing Bluetooth mobile keys, Instant Keys are the fastest way to share access. With a single API call, you can create a mobile key and send it through text or email or embed it in your own app. + * + * There’s no app to install, nor account to create. Your user just taps a link and gets a lightweight, native-feeling experience using iOS App Clip or Instant Apps on Android. Further, Instant Keys work offline, so even in areas with poor cellular or Wi-Fi, like elevator banks or concrete-walled hallways, the Instant Keys still work. + */ + public mixed $instant_keys, + /** + * Represents a [noise threshold](https://docs.seam.co/capability-guides/noise-sensors/configure-noise-threshold-settings) for a [noise sensor](https://docs.seam.co/capability-guides/noise-sensors). Thresholds represent the limits of noise tolerated at a property, which can be customized for each hour of the day. Each device has its own default thresholds, but you can use the Seam API to modify them. + */ + public mixed $noise_thresholds, + /** + * Represents a space that is a logical grouping of devices and entrances. You can assign access to an entire space, thereby making granting access more efficient. + */ + public mixed $spaces, + /** + * Represents a thermostat daily program, consisting of a set of periods, each of which has a starting time and the key that identifies the climate preset to apply at the starting time. + */ + public mixed $thermostat_daily_programs, + /** + * Represents a [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) that activates a configured [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) on a [thermostat](https://docs.seam.co/capability-guides/thermostats) at a specified starting time and deactivates the climate preset at a specified ending time. + */ + public mixed $thermostat_schedules, + /** + * Represents an [unmanaged smart lock access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes). + * + * An access code is a code used for a keypad or pinpad device. Unlike physical keys, which can easily be lost or duplicated, PIN codes can be customized, tracked, and altered on the fly. + * + * When you create an access code on a device in Seam, it is created as a managed access code. Access codes that exist on a device that were not created through Seam are considered unmanaged codes. We strictly limit the operations that can be performed on unmanaged codes. + * + * Prior to using Seam to manage your devices, you may have used another lock management system to manage the access codes on your devices. Where possible, we help you keep any existing access codes on devices and transition those codes to ones managed by your Seam workspace. + * + * Not all providers support unmanaged access codes. The following providers do not support unmanaged access codes: + * + * - [Kwikset](https://docs.seam.co/device-and-system-integration-guides/kwikset-locks) + */ + public mixed $unmanaged_access_codes, + /** + * Represents an [unmanaged device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). An unmanaged device has a limited set of visible properties and a subset of supported events. You cannot control an unmanaged device. Any [access codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes) on an unmanaged device are unmanaged. To control an unmanaged device with Seam, [convert it to a managed device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices#convert-an-unmanaged-device-to-managed). + */ + public mixed $unmanaged_devices, + /** + * Represents a [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with an application user account. + */ + public mixed $user_identities, + /** + * Represents a Seam [workspace](https://docs.seam.co/core-concepts/workspaces). A workspace is a top-level entity that encompasses all other resources below it, such as devices, connected accounts, and Connect Webviews. Seam provides two types of workspaces. A [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces) is a special type of workspace designed for testing code. Sandbox workspaces offer test device accounts and virtual devices that you can connect and control. This ability to work with virtual devices is quite handy because it removes the need to own physical devices from multiple brands. To connect real devices and systems to Seam, use a [production workspace](https://docs.seam.co/core-concepts/workspaces#production-workspaces). + */ + public mixed $workspaces, + ) {} + } } diff --git a/src/Resources/ClientSession.php b/src/Resources/ClientSession.php index 92c77b9d..53ec9f7e 100644 --- a/src/Resources/ClientSession.php +++ b/src/Resources/ClientSession.php @@ -1,91 +1,91 @@ client_session_id ?? null, + connect_webview_ids: $json->connect_webview_ids ?? null, + connected_account_ids: $json->connected_account_ids ?? null, + created_at: $json->created_at ?? null, + customer_key: $json->customer_key ?? null, + device_count: $json->device_count ?? null, + expires_at: $json->expires_at ?? null, + token: $json->token ?? null, + user_identifier_key: $json->user_identifier_key ?? null, + user_identity_id: $json->user_identity_id ?? null, + user_identity_ids: $json->user_identity_ids ?? null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - client_session_id: $json->client_session_id ?? null, - connect_webview_ids: $json->connect_webview_ids ?? null, - connected_account_ids: $json->connected_account_ids ?? null, - created_at: $json->created_at ?? null, - customer_key: $json->customer_key ?? null, - device_count: $json->device_count ?? null, - expires_at: $json->expires_at ?? null, - token: $json->token ?? null, - user_identifier_key: $json->user_identifier_key ?? null, - user_identity_id: $json->user_identity_id ?? null, - user_identity_ids: $json->user_identity_ids ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * ID of the client session. - */ - public string|null $client_session_id, - /** - * IDs of the [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). - */ - public array|null $connect_webview_ids, - /** - * IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). - */ - public array|null $connected_account_ids, - /** - * Date and time at which the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens) was created. - */ - public string|null $created_at, - /** - * Customer key associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). - */ - public string|null $customer_key, - /** - * Number of devices associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). - */ - public float|null $device_count, - /** - * Date and time at which the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens) expires. - */ - public string|null $expires_at, - /** - * Client session token associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). - */ - public string|null $token, - /** - * Your user ID for the user associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). - */ - public string|null $user_identifier_key, - /** - * ID of the [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. - */ - public string|null $user_identity_id, - /** - * IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. - * - * @deprecated Use `user_identity_id` instead. - */ - public array|null $user_identity_ids, - /** - * ID of the workspace associated with the client session. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the client session. + */ + public string|null $client_session_id, + /** + * IDs of the [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + */ + public array|null $connect_webview_ids, + /** + * IDs of the [connected accounts](https://docs.seam.co/core-concepts/connected-accounts) associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + */ + public array|null $connected_account_ids, + /** + * Date and time at which the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens) was created. + */ + public string|null $created_at, + /** + * Customer key associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + */ + public string|null $customer_key, + /** + * Number of devices associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + */ + public float|null $device_count, + /** + * Date and time at which the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens) expires. + */ + public string|null $expires_at, + /** + * Client session token associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + */ + public string|null $token, + /** + * Your user ID for the user associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). + */ + public string|null $user_identifier_key, + /** + * ID of the [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. + */ + public string|null $user_identity_id, + /** + * IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. + * + * @deprecated Use `user_identity_id` instead. + */ + public array|null $user_identity_ids, + /** + * ID of the workspace associated with the client session. + */ + public string|null $workspace_id, + ) {} + } } diff --git a/src/Resources/ConnectWebview.php b/src/Resources/ConnectWebview.php index 55a02bd2..224eb740 100644 --- a/src/Resources/ConnectWebview.php +++ b/src/Resources/ConnectWebview.php @@ -1,128 +1,129 @@ accepted_capabilities ?? null, + accepted_providers: $json->accepted_providers ?? null, + any_provider_allowed: $json->any_provider_allowed ?? null, + authorized_at: $json->authorized_at ?? null, + automatically_manage_new_devices: $json->automatically_manage_new_devices ?? + null, + connect_webview_id: $json->connect_webview_id ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + custom_metadata: $json->custom_metadata ?? null, + custom_redirect_failure_url: $json->custom_redirect_failure_url ?? + null, + custom_redirect_url: $json->custom_redirect_url ?? null, + customer_key: $json->customer_key ?? null, + device_selection_mode: $json->device_selection_mode ?? null, + login_successful: $json->login_successful ?? null, + selected_provider: $json->selected_provider ?? null, + status: $json->status ?? null, + url: $json->url ?? null, + wait_for_device_creation: $json->wait_for_device_creation ?? + null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - accepted_capabilities: $json->accepted_capabilities ?? null, - accepted_providers: $json->accepted_providers ?? null, - any_provider_allowed: $json->any_provider_allowed ?? null, - authorized_at: $json->authorized_at ?? null, - automatically_manage_new_devices: $json->automatically_manage_new_devices ?? - null, - connect_webview_id: $json->connect_webview_id ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - custom_metadata: $json->custom_metadata ?? null, - custom_redirect_failure_url: $json->custom_redirect_failure_url ?? - null, - custom_redirect_url: $json->custom_redirect_url ?? null, - customer_key: $json->customer_key ?? null, - device_selection_mode: $json->device_selection_mode ?? null, - login_successful: $json->login_successful ?? null, - selected_provider: $json->selected_provider ?? null, - status: $json->status ?? null, - url: $json->url ?? null, - wait_for_device_creation: $json->wait_for_device_creation ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * High-level device capabilities that the Connect Webview can accept. When creating a Connect Webview, you can specify the types of devices that it can connect to Seam. If you do not set custom `accepted_capabilities`, Seam uses a default set of `accepted_capabilities` for each provider. For example, if you create a Connect Webview that accepts SmartThing devices, without specifying `accepted_capabilities`, Seam accepts only SmartThings locks. To connect SmartThings thermostats and locks to Seam, create a Connect Webview and include both `thermostat` and `lock` in the `accepted_capabilities`. - */ - public array|null $accepted_capabilities, - /** - * List of accepted [provider keys](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-brands-to-display-in-your-connect-webviews). - */ - public array|null $accepted_providers, - /** - * Indicates whether any provider is allowed. - */ - public bool|null $any_provider_allowed, - /** - * Date and time at which the user authorized (through the Connect Webview) the management of their devices. - */ - public string|null $authorized_at, - /** - * Indicates whether Seam should [import all new devices](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#automatically_manage_new_devices) for the connected account to make these devices available for use and management by the Seam API. - */ - public bool|null $automatically_manage_new_devices, - /** - * ID of the Connect Webview. - */ - public string|null $connect_webview_id, - /** - * ID of the connected account associated with the Connect Webview. - */ - public string|null $connected_account_id, - /** - * Date and time at which the Connect Webview was created. - */ - public string|null $created_at, - /** - * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. - */ - public mixed $custom_metadata, - /** - * URL to which the Connect Webview should redirect when an unexpected error occurs. - */ - public string|null $custom_redirect_failure_url, - /** - * URL to which the Connect Webview should redirect when the user successfully pairs a device or system. If you do not set the `custom_redirect_failure_url`, the Connect Webview redirects to the `custom_redirect_url` when an unexpected error occurs. - */ - public string|null $custom_redirect_url, - /** - * The customer key associated with this webview, if any. - */ - public string|null $customer_key, - /** - * Device selection mode of the Connect Webview. Supported values: `none`, `single`, `multiple`. - */ - public string|null $device_selection_mode, - /** - * Indicates whether the user logged in successfully using the Connect Webview. - */ - public bool|null $login_successful, - /** - * Selected provider of the Connect Webview, one of the [provider keys](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-brands-to-display-in-your-connect-webviews). - */ - public string|null $selected_provider, - /** - * Status of the Connect Webview. `authorized` indicates that the user has successfully logged into their device or system account, thereby completing the Connect Webview. - */ - public string|null $status, - /** - * URL for the Connect Webview. You use the URL to display the Connect Webview flow to your user. - */ - public string|null $url, - /** - * Indicates whether Seam should [finish syncing all devices](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#wait_for_device_creation) in a newly-connected account before completing the associated Connect Webview. - */ - public bool|null $wait_for_device_creation, - /** - * ID of the workspace that contains the Connect Webview. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * High-level device capabilities that the Connect Webview can accept. When creating a Connect Webview, you can specify the types of devices that it can connect to Seam. If you do not set custom `accepted_capabilities`, Seam uses a default set of `accepted_capabilities` for each provider. For example, if you create a Connect Webview that accepts SmartThing devices, without specifying `accepted_capabilities`, Seam accepts only SmartThings locks. To connect SmartThings thermostats and locks to Seam, create a Connect Webview and include both `thermostat` and `lock` in the `accepted_capabilities`. + */ + public array|null $accepted_capabilities, + /** + * List of accepted [provider keys](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-brands-to-display-in-your-connect-webviews). + */ + public array|null $accepted_providers, + /** + * Indicates whether any provider is allowed. + */ + public bool|null $any_provider_allowed, + /** + * Date and time at which the user authorized (through the Connect Webview) the management of their devices. + */ + public string|null $authorized_at, + /** + * Indicates whether Seam should [import all new devices](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#automatically_manage_new_devices) for the connected account to make these devices available for use and management by the Seam API. + */ + public bool|null $automatically_manage_new_devices, + /** + * ID of the Connect Webview. + */ + public string|null $connect_webview_id, + /** + * ID of the connected account associated with the Connect Webview. + */ + public string|null $connected_account_id, + /** + * Date and time at which the Connect Webview was created. + */ + public string|null $created_at, + /** + * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + */ + public mixed $custom_metadata, + /** + * URL to which the Connect Webview should redirect when an unexpected error occurs. + */ + public string|null $custom_redirect_failure_url, + /** + * URL to which the Connect Webview should redirect when the user successfully pairs a device or system. If you do not set the `custom_redirect_failure_url`, the Connect Webview redirects to the `custom_redirect_url` when an unexpected error occurs. + */ + public string|null $custom_redirect_url, + /** + * The customer key associated with this webview, if any. + */ + public string|null $customer_key, + /** + * Device selection mode of the Connect Webview. Supported values: `none`, `single`, `multiple`. + */ + public string|null $device_selection_mode, + /** + * Indicates whether the user logged in successfully using the Connect Webview. + */ + public bool|null $login_successful, + /** + * Selected provider of the Connect Webview, one of the [provider keys](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-brands-to-display-in-your-connect-webviews). + */ + public string|null $selected_provider, + /** + * Status of the Connect Webview. `authorized` indicates that the user has successfully logged into their device or system account, thereby completing the Connect Webview. + */ + public string|null $status, + /** + * URL for the Connect Webview. You use the URL to display the Connect Webview flow to your user. + */ + public string|null $url, + /** + * Indicates whether Seam should [finish syncing all devices](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#wait_for_device_creation) in a newly-connected account before completing the associated Connect Webview. + */ + public bool|null $wait_for_device_creation, + /** + * ID of the workspace that contains the Connect Webview. + */ + public string|null $workspace_id, + ) {} + } } diff --git a/src/Resources/ConnectedAccount.php b/src/Resources/ConnectedAccount.php index 245d3c25..3f6e237b 100644 --- a/src/Resources/ConnectedAccount.php +++ b/src/Resources/ConnectedAccount.php @@ -1,330 +1,407 @@ accepted_capabilities ?? null, + account_type: $json->account_type ?? null, + account_type_display_name: $json->account_type_display_name ?? + null, + automatically_manage_new_devices: $json->automatically_manage_new_devices ?? + null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + custom_metadata: $json->custom_metadata ?? null, + customer_key: $json->customer_key ?? null, + default_checkin_time: $json->default_checkin_time ?? null, + default_checkout_time: $json->default_checkout_time ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => ConnectedAccount\Errors::from_json($e), + $json->errors ?? [], + ), + ical_feed_origin: $json->ical_feed_origin ?? null, + ical_url: $json->ical_url ?? null, + image_url: $json->image_url ?? null, + time_zone: $json->time_zone ?? null, + user_identifier: isset($json->user_identifier) + ? ConnectedAccount\UserIdentifier::from_json( + $json->user_identifier, + ) + : null, + warnings: array_map( + fn($w) => ConnectedAccount\Warnings::from_json($w), + $json->warnings ?? [], + ), + ); } - return new self( - accepted_capabilities: $json->accepted_capabilities ?? null, - account_type: $json->account_type ?? null, - account_type_display_name: $json->account_type_display_name ?? null, - automatically_manage_new_devices: $json->automatically_manage_new_devices ?? - null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - custom_metadata: $json->custom_metadata ?? null, - customer_key: $json->customer_key ?? null, - default_checkin_time: $json->default_checkin_time ?? null, - default_checkout_time: $json->default_checkout_time ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => ConnectedAccountErrors::from_json($e), - $json->errors ?? [], - ), - ical_feed_origin: $json->ical_feed_origin ?? null, - ical_url: $json->ical_url ?? null, - image_url: $json->image_url ?? null, - time_zone: $json->time_zone ?? null, - user_identifier: isset($json->user_identifier) - ? ConnectedAccountUserIdentifier::from_json( - $json->user_identifier, - ) - : null, - warnings: array_map( - fn($w) => ConnectedAccountWarnings::from_json($w), - $json->warnings ?? [], - ), - ); - } - public function __construct( - /** - * List of capabilities that were accepted during the account connection process. - */ - public array|null $accepted_capabilities, - /** - * Type of connected account. - */ - public string|null $account_type, - /** - * Display name for the connected account type. - */ - public string|null $account_type_display_name, - /** - * Indicates whether Seam should [import all new devices](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#automatically_manage_new_devices) for the connected account to make these devices available for management by the Seam API. - */ - public bool|null $automatically_manage_new_devices, - /** - * ID of the connected account. - */ - public string|null $connected_account_id, - /** - * Date and time at which the connected account was created. - */ - public string|null $created_at, - /** - * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. - */ - public mixed $custom_metadata, - /** - * Your unique key for the customer associated with this connected account. - */ - public string|null $customer_key, - /** - * Default reservation check-in time for this connected account, as `HH:mm` (24-hour). Sourced from the connector configuration — set during the connect_webview for providers like Lodgify whose API does not expose check-in times. - */ - public string|null $default_checkin_time, - /** - * Default reservation check-out time for this connected account, as `HH:mm` (24-hour). Sourced from the connector configuration. - */ - public string|null $default_checkout_time, - /** - * Display name for the connected account. - */ - public string|null $display_name, - /** - * Errors associated with the connected account. - */ - public array $errors, - /** - * For iCal connected accounts, the platform that produced the feed (for example, `airbnb`, `vrbo`, or `booking`), or `unknown` when it could not be determined. Intended for rendering the source platform's logo. - */ - public string|null $ical_feed_origin, - /** - * For iCal connected accounts, the feed URL for the connection. Sourced from the connector configuration. - */ - public string|null $ical_url, - /** - * Logo URL for the connected account provider. - */ - public string|null $image_url, - /** - * IANA time zone (e.g. America/Los_Angeles) for this connected account. Sourced from the connector configuration. - */ - public string|null $time_zone, - /** - * User identifier associated with the connected account. - * - * @deprecated Use `display_name` instead. - */ - public ConnectedAccountUserIdentifier|null $user_identifier, - /** - * Warnings associated with the connected account. - */ - public array $warnings, - ) {} + public function __construct( + /** + * List of capabilities that were accepted during the account connection process. + */ + public array|null $accepted_capabilities, + /** + * Type of connected account. + */ + public string|null $account_type, + /** + * Display name for the connected account type. + */ + public string|null $account_type_display_name, + /** + * Indicates whether Seam should [import all new devices](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#automatically_manage_new_devices) for the connected account to make these devices available for management by the Seam API. + */ + public bool|null $automatically_manage_new_devices, + /** + * ID of the connected account. + */ + public string|null $connected_account_id, + /** + * Date and time at which the connected account was created. + */ + public string|null $created_at, + /** + * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + */ + public mixed $custom_metadata, + /** + * Your unique key for the customer associated with this connected account. + */ + public string|null $customer_key, + /** + * Default reservation check-in time for this connected account, as `HH:mm` (24-hour). Sourced from the connector configuration — set during the connect_webview for providers like Lodgify whose API does not expose check-in times. + */ + public string|null $default_checkin_time, + /** + * Default reservation check-out time for this connected account, as `HH:mm` (24-hour). Sourced from the connector configuration. + */ + public string|null $default_checkout_time, + /** + * Display name for the connected account. + */ + public string|null $display_name, + /** + * Errors associated with the connected account. + */ + public array $errors, + /** + * For iCal connected accounts, the platform that produced the feed (for example, `airbnb`, `vrbo`, or `booking`), or `unknown` when it could not be determined. Intended for rendering the source platform's logo. + */ + public string|null $ical_feed_origin, + /** + * For iCal connected accounts, the feed URL for the connection. Sourced from the connector configuration. + */ + public string|null $ical_url, + /** + * Logo URL for the connected account provider. + */ + public string|null $image_url, + /** + * IANA time zone (e.g. America/Los_Angeles) for this connected account. Sourced from the connector configuration. + */ + public string|null $time_zone, + /** + * User identifier associated with the connected account. + * + * @deprecated Use `display_name` instead. + */ + public ConnectedAccount\UserIdentifier|null $user_identifier, + /** + * Warnings associated with the connected account. + */ + public array $warnings, + ) {} + } } -/** - * Errors associated with the connected account. - */ -class ConnectedAccountErrors -{ - public static function from_json(mixed $json): ConnectedAccountErrors|null +namespace Seam\Resources\ConnectedAccount { + /** + * Errors associated with the connected account. + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + message: $json->message ?? null, + salto_ks_metadata: isset($json->salto_ks_metadata) + ? Errors\SaltoKsMetadata::from_json( + $json->salto_ks_metadata, + ) + : null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - message: $json->message ?? null, - salto_ks_metadata: isset($json->salto_ks_metadata) - ? ConnectedAccountSaltoKsMetadata::from_json( - $json->salto_ks_metadata, - ) - : null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error, - /** - * Indicates whether the error is related specifically to the connected account. - */ - public bool|null $is_connected_account_error, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Salto KS metadata associated with the connected account that has an error. - */ - public ConnectedAccountSaltoKsMetadata|null $salto_ks_metadata, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error, + /** + * Indicates whether the error is related specifically to the connected account. + */ + public bool|null $is_connected_account_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Salto KS metadata associated with the connected account that has an error. + */ + public Errors\SaltoKsMetadata|null $salto_ks_metadata, + ) {} + } -/** - * Salto KS metadata associated with the connected account that has an error. - */ -class ConnectedAccountSaltoKsMetadata -{ - public static function from_json( - mixed $json, - ): ConnectedAccountSaltoKsMetadata|null { - if (!$json) { - return null; + /** + * User identifier associated with the connected account. + * + * @deprecated Use `display_name` instead. + */ + class UserIdentifier + { + public static function from_json(mixed $json): UserIdentifier|null + { + if (!$json) { + return null; + } + return new self( + api_url: $json->api_url ?? null, + email: $json->email ?? null, + exclusive: $json->exclusive ?? null, + phone: $json->phone ?? null, + username: $json->username ?? null, + ); } - return new self( - sites: array_map( - fn($s) => ConnectedAccountSites::from_json($s), - $json->sites ?? [], - ), - ); + + public function __construct( + /** + * API URL for the user identifier associated with the connected account. + */ + public string|null $api_url, + /** + * Email address of the user identifier associated with the connected account. + */ + public string|null $email, + /** + * Indicates whether the user identifier associated with the connected account is exclusive. + */ + public bool|null $exclusive, + /** + * Phone number of the user identifier associated with the connected account. + */ + public string|null $phone, + /** + * Username of the user identifier associated with the connected account. + */ + public string|null $username, + ) {} } - public function __construct( - /** - * Salto sites associated with the connected account that has an error. - */ - public array $sites, - ) {} + /** + * Warnings associated with the connected account. + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + salto_ks_metadata: isset($json->salto_ks_metadata) + ? Warnings\SaltoKsMetadata::from_json( + $json->salto_ks_metadata, + ) + : null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Salto KS metadata associated with the connected account that has a warning. + */ + public Warnings\SaltoKsMetadata|null $salto_ks_metadata, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Salto sites associated with the connected account that has an error. - */ -class ConnectedAccountSites -{ - public static function from_json(mixed $json): ConnectedAccountSites|null +namespace Seam\Resources\ConnectedAccount\Errors { + /** + * Salto KS metadata associated with the connected account that has an error. + */ + class SaltoKsMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): SaltoKsMetadata|null + { + if (!$json) { + return null; + } + return new self( + sites: array_map( + fn($s) => SaltoKsMetadata\Sites::from_json($s), + $json->sites ?? [], + ), + ); } - return new self( - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - site_user_subscription_limit: $json->site_user_subscription_limit ?? - null, - subscribed_site_user_count: $json->subscribed_site_user_count ?? - null, - ); - } - public function __construct( - /** - * ID of a Salto site associated with the connected account that has an error. - */ - public string|null $site_id, - /** - * Name of a Salto site associated with the connected account that has an error. - */ - public string|null $site_name, - /** - * Subscription limit of site users for a Salto site associated with the connected account that has an error. - */ - public int|null $site_user_subscription_limit, - /** - * Count of subscribed site users for a Salto site associated with the connected account that has an error. - */ - public int|null $subscribed_site_user_count, - ) {} + public function __construct( + /** + * Salto sites associated with the connected account that has an error. + */ + public array $sites, + ) {} + } } -/** - * User identifier associated with the connected account. - */ -class ConnectedAccountUserIdentifier -{ - public static function from_json( - mixed $json, - ): ConnectedAccountUserIdentifier|null { - if (!$json) { - return null; +namespace Seam\Resources\ConnectedAccount\Errors\SaltoKsMetadata { + /** + * Salto sites associated with the connected account that has an error. + */ + class Sites + { + public static function from_json(mixed $json): Sites|null + { + if (!$json) { + return null; + } + return new self( + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + site_user_subscription_limit: $json->site_user_subscription_limit ?? + null, + subscribed_site_user_count: $json->subscribed_site_user_count ?? + null, + ); } - return new self( - api_url: $json->api_url ?? null, - email: $json->email ?? null, - exclusive: $json->exclusive ?? null, - phone: $json->phone ?? null, - username: $json->username ?? null, - ); - } - public function __construct( - /** - * API URL for the user identifier associated with the connected account. - */ - public string|null $api_url, - /** - * Email address of the user identifier associated with the connected account. - */ - public string|null $email, - /** - * Indicates whether the user identifier associated with the connected account is exclusive. - */ - public bool|null $exclusive, - /** - * Phone number of the user identifier associated with the connected account. - */ - public string|null $phone, - /** - * Username of the user identifier associated with the connected account. - */ - public string|null $username, - ) {} + public function __construct( + /** + * ID of a Salto site associated with the connected account that has an error. + */ + public string|null $site_id, + /** + * Name of a Salto site associated with the connected account that has an error. + */ + public string|null $site_name, + /** + * Subscription limit of site users for a Salto site associated with the connected account that has an error. + */ + public int|null $site_user_subscription_limit, + /** + * Count of subscribed site users for a Salto site associated with the connected account that has an error. + */ + public int|null $subscribed_site_user_count, + ) {} + } } -/** - * Warnings associated with the connected account. - */ -class ConnectedAccountWarnings -{ - public static function from_json(mixed $json): ConnectedAccountWarnings|null +namespace Seam\Resources\ConnectedAccount\Warnings { + /** + * Salto KS metadata associated with the connected account that has a warning. + */ + class SaltoKsMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): SaltoKsMetadata|null + { + if (!$json) { + return null; + } + return new self( + sites: array_map( + fn($s) => SaltoKsMetadata\Sites::from_json($s), + $json->sites ?? [], + ), + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - salto_ks_metadata: isset($json->salto_ks_metadata) - ? ConnectedAccountSaltoKsMetadata::from_json( - $json->salto_ks_metadata, - ) - : null, - warning_code: $json->warning_code ?? null, - ); + + public function __construct( + /** + * Salto sites associated with the connected account that has a warning. + */ + public array $sites, + ) {} } +} - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Salto KS metadata associated with the connected account that has a warning. - */ - public ConnectedAccountSaltoKsMetadata|null $salto_ks_metadata, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} +namespace Seam\Resources\ConnectedAccount\Warnings\SaltoKsMetadata { + /** + * Salto sites associated with the connected account that has a warning. + */ + class Sites + { + public static function from_json(mixed $json): Sites|null + { + if (!$json) { + return null; + } + return new self( + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + site_user_subscription_limit: $json->site_user_subscription_limit ?? + null, + subscribed_site_user_count: $json->subscribed_site_user_count ?? + null, + ); + } + + public function __construct( + /** + * ID of a Salto site associated with the connected account that has a warning. + */ + public string|null $site_id, + /** + * Name of a Salto site associated with the connected account that has a warning. + */ + public string|null $site_name, + /** + * Subscription limit of site users for a Salto site associated with the connected account that has a warning. + */ + public int|null $site_user_subscription_limit, + /** + * Count of subscribed site users for a Salto site associated with the connected account that has a warning. + */ + public int|null $subscribed_site_user_count, + ) {} + } } diff --git a/src/Resources/CustomerPortal.php b/src/Resources/CustomerPortal.php index 89029d27..0822f0ff 100644 --- a/src/Resources/CustomerPortal.php +++ b/src/Resources/CustomerPortal.php @@ -1,50 +1,50 @@ created_at ?? null, + customer_key: $json->customer_key ?? null, + expires_at: $json->expires_at ?? null, + url: $json->url ?? null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - customer_key: $json->customer_key ?? null, - expires_at: $json->expires_at ?? null, - url: $json->url ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Date and time at which the customer portal link was created. - */ - public string|null $created_at, - /** - * Customer key for the customer portal. - */ - public string|null $customer_key, - /** - * Date and time at which the customer portal link expires. - */ - public string|null $expires_at, - /** - * URL for the customer portal. - */ - public string|null $url, - /** - * ID of the workspace associated with the customer portal. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Date and time at which the customer portal link was created. + */ + public string|null $created_at, + /** + * Customer key for the customer portal. + */ + public string|null $customer_key, + /** + * Date and time at which the customer portal link expires. + */ + public string|null $expires_at, + /** + * URL for the customer portal. + */ + public string|null $url, + /** + * ID of the workspace associated with the customer portal. + */ + public string|null $workspace_id, + ) {} + } } diff --git a/src/Resources/Device.php b/src/Resources/Device.php index 2b6d428c..9b01f398 100644 --- a/src/Resources/Device.php +++ b/src/Resources/Device.php @@ -1,4092 +1,4397 @@ can_configure_auto_lock ?? null, - can_hvac_cool: $json->can_hvac_cool ?? null, - can_hvac_heat: $json->can_hvac_heat ?? null, - can_hvac_heat_cool: $json->can_hvac_heat_cool ?? null, - can_program_offline_access_codes: $json->can_program_offline_access_codes ?? - null, - can_program_online_access_codes: $json->can_program_online_access_codes ?? - null, - can_program_thermostat_programs_as_different_each_day: $json->can_program_thermostat_programs_as_different_each_day ?? - null, - can_program_thermostat_programs_as_same_each_day: $json->can_program_thermostat_programs_as_same_each_day ?? - null, - can_program_thermostat_programs_as_weekday_weekend: $json->can_program_thermostat_programs_as_weekday_weekend ?? - null, - can_remotely_lock: $json->can_remotely_lock ?? null, - can_remotely_unlock: $json->can_remotely_unlock ?? null, - can_run_thermostat_programs: $json->can_run_thermostat_programs ?? - null, - can_simulate_connection: $json->can_simulate_connection ?? null, - can_simulate_disconnection: $json->can_simulate_disconnection ?? - null, - can_simulate_hub_connection: $json->can_simulate_hub_connection ?? - null, - can_simulate_hub_disconnection: $json->can_simulate_hub_disconnection ?? - null, - can_simulate_paid_subscription: $json->can_simulate_paid_subscription ?? - null, - can_simulate_removal: $json->can_simulate_removal ?? null, - can_turn_off_hvac: $json->can_turn_off_hvac ?? null, - can_unlock_with_code: $json->can_unlock_with_code ?? null, - capabilities_supported: $json->capabilities_supported ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - custom_metadata: $json->custom_metadata ?? null, - device_id: $json->device_id ?? null, - device_manufacturer: isset($json->device_manufacturer) - ? DeviceDeviceManufacturer::from_json( - $json->device_manufacturer, - ) - : null, - device_provider: isset($json->device_provider) - ? DeviceDeviceProvider::from_json($json->device_provider) - : null, - device_type: $json->device_type ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => DeviceErrors::from_json($e), - $json->errors ?? [], - ), - is_managed: $json->is_managed ?? null, - location: isset($json->location) - ? DeviceLocation::from_json($json->location) - : null, - nickname: $json->nickname ?? null, - properties: isset($json->properties) - ? DeviceProperties::from_json($json->properties) - : null, - space_ids: $json->space_ids ?? null, - warnings: array_map( - fn($w) => DeviceWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - - public function __construct( - /** - * Indicates whether the lock supports configuring automatic locking. - */ - public bool|null $can_configure_auto_lock, - /** - * Indicates whether the thermostat supports cooling. - */ - public bool|null $can_hvac_cool, - /** - * Indicates whether the thermostat supports heating. - */ - public bool|null $can_hvac_heat, - /** - * Indicates whether the thermostat supports simultaneous heating and cooling. - */ - public bool|null $can_hvac_heat_cool, - /** - * Indicates whether the device supports programming offline access codes. - */ - public bool|null $can_program_offline_access_codes, - /** - * Indicates whether the device supports programming online access codes. - */ - public bool|null $can_program_online_access_codes, - /** - * Indicates whether the thermostat supports different climate programs for each day of the week. - */ - public bool|null $can_program_thermostat_programs_as_different_each_day, - /** - * Indicates whether the thermostat supports a single climate program applied to every day. - */ - public bool|null $can_program_thermostat_programs_as_same_each_day, - /** - * Indicates whether the thermostat supports weekday/weekend climate programs. - */ - public bool|null $can_program_thermostat_programs_as_weekday_weekend, - /** - * Indicates whether the device supports remote locking. - */ - public bool|null $can_remotely_lock, - /** - * Indicates whether the device supports remote unlocking. - */ - public bool|null $can_remotely_unlock, - /** - * Indicates whether the thermostat supports running climate programs. - */ - public bool|null $can_run_thermostat_programs, - /** - * Indicates whether the device supports simulating connection in a sandbox. - */ - public bool|null $can_simulate_connection, - /** - * Indicates whether the device supports simulating disconnection in a sandbox. - */ - public bool|null $can_simulate_disconnection, - /** - * Indicates whether the hub supports simulating connection in a sandbox. - */ - public bool|null $can_simulate_hub_connection, - /** - * Indicates whether the hub supports simulating disconnection in a sandbox. - */ - public bool|null $can_simulate_hub_disconnection, - /** - * Indicates whether the device supports simulating a paid subscription in a sandbox. - */ - public bool|null $can_simulate_paid_subscription, - /** - * Indicates whether the device supports simulating removal in a sandbox. - */ - public bool|null $can_simulate_removal, - /** - * Indicates whether the thermostat can be turned off. - */ - public bool|null $can_turn_off_hvac, - /** - * Indicates whether the lock supports unlocking with an access code. - */ - public bool|null $can_unlock_with_code, - /** - * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). - */ - public array|null $capabilities_supported, - /** - * Unique identifier for the account associated with the device. - */ - public string|null $connected_account_id, - /** - * Date and time at which the device object was created. - */ - public string|null $created_at, - /** - * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. - */ - public mixed $custom_metadata, - /** - * ID of the device. - */ - public string|null $device_id, - /** - * Manufacturer of the device. Represents the hardware brand, which may differ from the provider. - */ - public DeviceDeviceManufacturer|null $device_manufacturer, - /** - * Provider of the device. Represents the third-party service through which the device is controlled. - */ - public DeviceDeviceProvider|null $device_provider, - /** - * Type of the device. - */ - public string|null $device_type, - /** - * Display name of the device, defaults to nickname (if it is set) or `properties.appearance.name`, otherwise. Enables administrators and users to identify the device easily, especially when there are numerous devices. - */ - public string|null $display_name, - /** - * Array of errors associated with the device. Each error object within the array contains two fields: `error_code` and `message`. `error_code` is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. - */ - public array $errors, - /** - * Indicates whether Seam manages the device. See also [Managed and Unmanaged Devices](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). - */ - public bool|null $is_managed, - /** - * Location information for the device. - */ - public DeviceLocation|null $location, - /** - * Optional nickname to describe the device, settable through Seam. - */ - public string|null $nickname, - /** - * Properties of the device. - */ - public DeviceProperties|null $properties, - /** - * IDs of the spaces the device is in. - */ - public array|null $space_ids, - /** - * Array of warnings associated with the device. Each warning object within the array contains two fields: `warning_code` and `message`. `warning_code` is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. - */ - public array $warnings, - /** - * Unique identifier for the Seam workspace associated with the device. - */ - public string|null $workspace_id, - ) {} -} +namespace Seam\Resources { + /** + * Represents a [device](https://docs.seam.co/core-concepts/devices) that has been connected to Seam. + */ + class Device + { + public static function from_json(mixed $json): Device|null + { + if (!$json) { + return null; + } + return new self( + can_configure_auto_lock: $json->can_configure_auto_lock ?? null, + can_hvac_cool: $json->can_hvac_cool ?? null, + can_hvac_heat: $json->can_hvac_heat ?? null, + can_hvac_heat_cool: $json->can_hvac_heat_cool ?? null, + can_program_offline_access_codes: $json->can_program_offline_access_codes ?? + null, + can_program_online_access_codes: $json->can_program_online_access_codes ?? + null, + can_program_thermostat_programs_as_different_each_day: $json->can_program_thermostat_programs_as_different_each_day ?? + null, + can_program_thermostat_programs_as_same_each_day: $json->can_program_thermostat_programs_as_same_each_day ?? + null, + can_program_thermostat_programs_as_weekday_weekend: $json->can_program_thermostat_programs_as_weekday_weekend ?? + null, + can_remotely_lock: $json->can_remotely_lock ?? null, + can_remotely_unlock: $json->can_remotely_unlock ?? null, + can_run_thermostat_programs: $json->can_run_thermostat_programs ?? + null, + can_simulate_connection: $json->can_simulate_connection ?? null, + can_simulate_disconnection: $json->can_simulate_disconnection ?? + null, + can_simulate_hub_connection: $json->can_simulate_hub_connection ?? + null, + can_simulate_hub_disconnection: $json->can_simulate_hub_disconnection ?? + null, + can_simulate_paid_subscription: $json->can_simulate_paid_subscription ?? + null, + can_simulate_removal: $json->can_simulate_removal ?? null, + can_turn_off_hvac: $json->can_turn_off_hvac ?? null, + can_unlock_with_code: $json->can_unlock_with_code ?? null, + capabilities_supported: $json->capabilities_supported ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + custom_metadata: $json->custom_metadata ?? null, + device_id: $json->device_id ?? null, + device_manufacturer: isset($json->device_manufacturer) + ? Device\DeviceManufacturer::from_json( + $json->device_manufacturer, + ) + : null, + device_provider: isset($json->device_provider) + ? Device\DeviceProvider::from_json($json->device_provider) + : null, + device_type: $json->device_type ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => Device\Errors::from_json($e), + $json->errors ?? [], + ), + is_managed: $json->is_managed ?? null, + location: isset($json->location) + ? Device\Location::from_json($json->location) + : null, + nickname: $json->nickname ?? null, + properties: isset($json->properties) + ? Device\Properties::from_json($json->properties) + : null, + space_ids: $json->space_ids ?? null, + warnings: array_map( + fn($w) => Device\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); + } -/** - * Latest accelerometer Z-axis reading for a Minut device. - */ -class DeviceAccelerometerZ -{ - public static function from_json(mixed $json): DeviceAccelerometerZ|null - { - if (!$json) { - return null; - } - return new self(time: $json->time ?? null, value: $json->value ?? null); - } - - public function __construct( - /** - * Time of latest accelerometer Z-axis reading for a Minut device. - */ - public string|null $time, - /** - * Value of latest accelerometer Z-axis reading for a Minut device. - */ - public float|null $value, - ) {} + public function __construct( + /** + * Indicates whether the lock supports configuring automatic locking. + */ + public bool|null $can_configure_auto_lock, + /** + * Indicates whether the thermostat supports cooling. + */ + public bool|null $can_hvac_cool, + /** + * Indicates whether the thermostat supports heating. + */ + public bool|null $can_hvac_heat, + /** + * Indicates whether the thermostat supports simultaneous heating and cooling. + */ + public bool|null $can_hvac_heat_cool, + /** + * Indicates whether the device supports programming offline access codes. + */ + public bool|null $can_program_offline_access_codes, + /** + * Indicates whether the device supports programming online access codes. + */ + public bool|null $can_program_online_access_codes, + /** + * Indicates whether the thermostat supports different climate programs for each day of the week. + */ + public bool|null $can_program_thermostat_programs_as_different_each_day, + /** + * Indicates whether the thermostat supports a single climate program applied to every day. + */ + public bool|null $can_program_thermostat_programs_as_same_each_day, + /** + * Indicates whether the thermostat supports weekday/weekend climate programs. + */ + public bool|null $can_program_thermostat_programs_as_weekday_weekend, + /** + * Indicates whether the device supports remote locking. + */ + public bool|null $can_remotely_lock, + /** + * Indicates whether the device supports remote unlocking. + */ + public bool|null $can_remotely_unlock, + /** + * Indicates whether the thermostat supports running climate programs. + */ + public bool|null $can_run_thermostat_programs, + /** + * Indicates whether the device supports simulating connection in a sandbox. + */ + public bool|null $can_simulate_connection, + /** + * Indicates whether the device supports simulating disconnection in a sandbox. + */ + public bool|null $can_simulate_disconnection, + /** + * Indicates whether the hub supports simulating connection in a sandbox. + */ + public bool|null $can_simulate_hub_connection, + /** + * Indicates whether the hub supports simulating disconnection in a sandbox. + */ + public bool|null $can_simulate_hub_disconnection, + /** + * Indicates whether the device supports simulating a paid subscription in a sandbox. + */ + public bool|null $can_simulate_paid_subscription, + /** + * Indicates whether the device supports simulating removal in a sandbox. + */ + public bool|null $can_simulate_removal, + /** + * Indicates whether the thermostat can be turned off. + */ + public bool|null $can_turn_off_hvac, + /** + * Indicates whether the lock supports unlocking with an access code. + */ + public bool|null $can_unlock_with_code, + /** + * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). + */ + public array|null $capabilities_supported, + /** + * Unique identifier for the account associated with the device. + */ + public string|null $connected_account_id, + /** + * Date and time at which the device object was created. + */ + public string|null $created_at, + /** + * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + */ + public mixed $custom_metadata, + /** + * ID of the device. + */ + public string|null $device_id, + /** + * Manufacturer of the device. Represents the hardware brand, which may differ from the provider. + */ + public Device\DeviceManufacturer|null $device_manufacturer, + /** + * Provider of the device. Represents the third-party service through which the device is controlled. + */ + public Device\DeviceProvider|null $device_provider, + /** + * Type of the device. + */ + public string|null $device_type, + /** + * Display name of the device, defaults to nickname (if it is set) or `properties.appearance.name`, otherwise. Enables administrators and users to identify the device easily, especially when there are numerous devices. + */ + public string|null $display_name, + /** + * Array of errors associated with the device. Each error object within the array contains two fields: `error_code` and `message`. `error_code` is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. + */ + public array $errors, + /** + * Indicates whether Seam manages the device. See also [Managed and Unmanaged Devices](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). + */ + public bool|null $is_managed, + /** + * Location information for the device. + */ + public Device\Location|null $location, + /** + * Optional nickname to describe the device, settable through Seam. + */ + public string|null $nickname, + /** + * Properties of the device. + */ + public Device\Properties|null $properties, + /** + * IDs of the spaces the device is in. + */ + public array|null $space_ids, + /** + * Array of warnings associated with the device. Each warning object within the array contains two fields: `warning_code` and `message`. `warning_code` is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. + */ + public array $warnings, + /** + * Unique identifier for the Seam workspace associated with the device. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Accessory keypad properties and state. - */ -class DeviceAccessoryKeypad -{ - public static function from_json(mixed $json): DeviceAccessoryKeypad|null - { - if (!$json) { - return null; - } - return new self( - battery: isset($json->battery) - ? DeviceBattery::from_json($json->battery) - : null, - is_connected: $json->is_connected ?? null, - ); - } - - public function __construct( - /** - * Keypad battery properties. - */ - public DeviceBattery|null $battery, - /** - * Indicates if an accessory keypad is connected to the device. - */ - public bool|null $is_connected, - ) {} -} +namespace Seam\Resources\Device { + /** + * Manufacturer of the device. Represents the hardware brand, which may differ from the provider. + */ + class DeviceManufacturer + { + public static function from_json(mixed $json): DeviceManufacturer|null + { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + image_url: $json->image_url ?? null, + manufacturer: $json->manufacturer ?? null, + ); + } -/** - * Active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ -class DeviceActiveThermostatSchedule -{ - public static function from_json( - mixed $json, - ): DeviceActiveThermostatSchedule|null { - if (!$json) { - return null; - } - return new self( - climate_preset_key: $json->climate_preset_key ?? null, - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => DeviceErrors::from_json($e), - $json->errors ?? [], - ), - is_override_allowed: $json->is_override_allowed ?? null, - max_override_period_minutes: $json->max_override_period_minutes ?? - null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - thermostat_schedule_id: $json->thermostat_schedule_id ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - - public function __construct( - /** - * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to use for the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $climate_preset_key, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) was created. - */ - public string|null $created_at, - /** - * ID of the desired [thermostat](https://docs.seam.co/capability-guides/thermostats) device. - */ - public string|null $device_id, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $ends_at, - /** - * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public array $errors, - /** - * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. - */ - public bool|null $is_override_allowed, - /** - * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - */ - public int|null $max_override_period_minutes, - /** - * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $name, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - /** - * ID of the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $thermostat_schedule_id, - /** - * ID of the workspace that contains the thermostat schedule. - */ - public string|null $workspace_id, - ) {} -} + public function __construct( + /** + * Display name for the manufacturer, such as `August`, `Yale`, `Salto`, and so on. + */ + public string|null $display_name, + /** + * Image URL for the manufacturer logo. + */ + public string|null $image_url, + /** + * Manufacturer identifier, such as `august`, `yale`, `salto`, and so on. + */ + public string|null $manufacturer, + ) {} + } -/** - * Metadata for an Akiles device. - */ -class DeviceAkilesMetadata -{ - public static function from_json(mixed $json): DeviceAkilesMetadata|null - { - if (!$json) { - return null; - } - return new self( - _member_group_id: $json->_member_group_id ?? null, - gadget_id: $json->gadget_id ?? null, - gadget_name: $json->gadget_name ?? null, - product_name: $json->product_name ?? null, - ); - } - - public function __construct( - /** - * Group ID to which to add users for an Akiles device. - */ - public string|null $_member_group_id, - /** - * Gadget ID for an Akiles device. - */ - public string|null $gadget_id, - /** - * Gadget name for an Akiles device. - */ - public string|null $gadget_name, - /** - * Product name for an Akiles device. - */ - public string|null $product_name, - ) {} -} + /** + * Provider of the device. Represents the third-party service through which the device is controlled. + */ + class DeviceProvider + { + public static function from_json(mixed $json): DeviceProvider|null + { + if (!$json) { + return null; + } + return new self( + device_provider_name: $json->device_provider_name ?? null, + display_name: $json->display_name ?? null, + image_url: $json->image_url ?? null, + provider_category: $json->provider_category ?? null, + ); + } -/** - * Appearance-related properties, as reported by the device. - */ -class DeviceAppearance -{ - public static function from_json(mixed $json): DeviceAppearance|null + public function __construct( + /** + * Device provider name. Corresponds to the integration type, such as `august`, `schlage`, `yale_access`, and so on. + */ + public string|null $device_provider_name, + /** + * Display name for the device provider type. + */ + public string|null $display_name, + /** + * Image URL for the device provider. + */ + public string|null $image_url, + /** + * Provider category. Indicates the third-party provider type, such as `stable`, for stable integrations, or `internal`, for internal integrations. + */ + public string|null $provider_category, + ) {} + } + + /** + * Array of errors associated with the device. Each error object within the array contains two fields: `error_code` and `message`. `error_code` is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); } - return new self(name: $json->name ?? null); + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error, + public bool|null $is_connected_account_error, + public bool|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} } - public function __construct( - /** - * Name of the device as seen from the provider API and application, not settable through Seam. - */ - public string|null $name, - ) {} -} + /** + * Location information for the device. + */ + class Location + { + public static function from_json(mixed $json): Location|null + { + if (!$json) { + return null; + } + return new self( + location_name: $json->location_name ?? null, + time_zone: $json->time_zone ?? null, + timezone: $json->timezone ?? null, + ); + } -/** - * Metadata for an Aqara device. - */ -class DeviceAqaraMetadata -{ - public static function from_json(mixed $json): DeviceAqaraMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_name: $json->device_name ?? null, - did: $json->did ?? null, - firmware_version: $json->firmware_version ?? null, - model: $json->model ?? null, - model_type: $json->model_type ?? null, - parent_did: $json->parent_did ?? null, - position_id: $json->position_id ?? null, - time_zone: $json->time_zone ?? null, - ); - } - - public function __construct( - /** - * Device name for an Aqara device. - */ - public string|null $device_name, - /** - * Device ID (did) for an Aqara device. - */ - public string|null $did, - /** - * Firmware version for an Aqara device. - */ - public string|null $firmware_version, - /** - * Model identifier for an Aqara device. - */ - public string|null $model, - /** - * Model type for an Aqara device. - */ - public float|null $model_type, - /** - * Parent gateway device ID for an Aqara device. - */ - public string|null $parent_did, - /** - * Position (room) ID for an Aqara device. - */ - public string|null $position_id, - /** - * Time zone reported for an Aqara device (e.g. GMT-07:00). - */ - public string|null $time_zone, - ) {} -} + public function __construct( + /** + * Name of the device location. + */ + public string|null $location_name, + /** + * Time zone of the device location. + */ + public string|null $time_zone, + /** + * Time zone of the device location. + * + * @deprecated Use `time_zone` instead. + */ + public string|null $timezone, + ) {} + } -/** - * ASSA ABLOY Credential Service metadata for the phone. - */ -class DeviceAssaAbloyCredentialServiceMetadata -{ - public static function from_json( - mixed $json, - ): DeviceAssaAbloyCredentialServiceMetadata|null { - if (!$json) { - return null; - } - return new self( - endpoints: array_map( - fn($e) => DeviceEndpoints::from_json($e), - $json->endpoints ?? [], - ), - has_active_endpoint: $json->has_active_endpoint ?? null, - ); - } - - public function __construct( - /** - * Endpoints associated with the phone. - */ - public array $endpoints, - /** - * Indicates whether the credential service has active endpoints associated with the phone. - */ - public bool|null $has_active_endpoint, - ) {} -} + /** + * Properties of the device. + */ + class Properties + { + public static function from_json(mixed $json): Properties|null + { + if (!$json) { + return null; + } + return new self( + accessory_keypad: isset($json->accessory_keypad) + ? Properties\AccessoryKeypad::from_json( + $json->accessory_keypad, + ) + : null, + active_thermostat_schedule: isset( + $json->active_thermostat_schedule, + ) + ? Properties\ActiveThermostatSchedule::from_json( + $json->active_thermostat_schedule, + ) + : null, + active_thermostat_schedule_id: $json->active_thermostat_schedule_id ?? + null, + akiles_metadata: isset($json->akiles_metadata) + ? Properties\AkilesMetadata::from_json( + $json->akiles_metadata, + ) + : null, + appearance: isset($json->appearance) + ? Properties\Appearance::from_json($json->appearance) + : null, + aqara_metadata: isset($json->aqara_metadata) + ? Properties\AqaraMetadata::from_json($json->aqara_metadata) + : null, + assa_abloy_credential_service_metadata: isset( + $json->assa_abloy_credential_service_metadata, + ) + ? Properties\AssaAbloyCredentialServiceMetadata::from_json( + $json->assa_abloy_credential_service_metadata, + ) + : null, + assa_abloy_vostio_metadata: isset( + $json->assa_abloy_vostio_metadata, + ) + ? Properties\AssaAbloyVostioMetadata::from_json( + $json->assa_abloy_vostio_metadata, + ) + : null, + august_metadata: isset($json->august_metadata) + ? Properties\AugustMetadata::from_json( + $json->august_metadata, + ) + : null, + auto_lock_delay_seconds: $json->auto_lock_delay_seconds ?? null, + auto_lock_enabled: $json->auto_lock_enabled ?? null, + available_climate_preset_modes: $json->available_climate_preset_modes ?? + null, + available_climate_presets: array_map( + fn($a) => Properties\AvailableClimatePresets::from_json($a), + $json->available_climate_presets ?? [], + ), + available_fan_mode_settings: $json->available_fan_mode_settings ?? + null, + available_hvac_mode_settings: $json->available_hvac_mode_settings ?? + null, + avigilon_alta_metadata: isset($json->avigilon_alta_metadata) + ? Properties\AvigilonAltaMetadata::from_json( + $json->avigilon_alta_metadata, + ) + : null, + backup_access_code_pool_enabled: $json->backup_access_code_pool_enabled ?? + null, + battery: isset($json->battery) + ? Properties\Battery::from_json($json->battery) + : null, + battery_level: $json->battery_level ?? null, + brivo_metadata: isset($json->brivo_metadata) + ? Properties\BrivoMetadata::from_json($json->brivo_metadata) + : null, + code_constraints: array_map( + fn($c) => Properties\CodeConstraints::from_json($c), + $json->code_constraints ?? [], + ), + controlbyweb_metadata: isset($json->controlbyweb_metadata) + ? Properties\ControlbywebMetadata::from_json( + $json->controlbyweb_metadata, + ) + : null, + current_climate_setting: isset($json->current_climate_setting) + ? Properties\CurrentClimateSetting::from_json( + $json->current_climate_setting, + ) + : null, + currently_triggering_noise_threshold_ids: $json->currently_triggering_noise_threshold_ids ?? + null, + default_climate_setting: isset($json->default_climate_setting) + ? Properties\DefaultClimateSetting::from_json( + $json->default_climate_setting, + ) + : null, + door_open: $json->door_open ?? null, + dormakaba_oracode_metadata: isset( + $json->dormakaba_oracode_metadata, + ) + ? Properties\DormakabaOracodeMetadata::from_json( + $json->dormakaba_oracode_metadata, + ) + : null, + ecobee_metadata: isset($json->ecobee_metadata) + ? Properties\EcobeeMetadata::from_json( + $json->ecobee_metadata, + ) + : null, + fallback_climate_preset_key: $json->fallback_climate_preset_key ?? + null, + fan_mode_setting: $json->fan_mode_setting ?? null, + four_suites_metadata: isset($json->four_suites_metadata) + ? Properties\FourSuitesMetadata::from_json( + $json->four_suites_metadata, + ) + : null, + genie_metadata: isset($json->genie_metadata) + ? Properties\GenieMetadata::from_json($json->genie_metadata) + : null, + has_direct_power: $json->has_direct_power ?? null, + has_native_entry_events: $json->has_native_entry_events ?? null, + honeywell_resideo_metadata: isset( + $json->honeywell_resideo_metadata, + ) + ? Properties\HoneywellResideoMetadata::from_json( + $json->honeywell_resideo_metadata, + ) + : null, + igloo_metadata: isset($json->igloo_metadata) + ? Properties\IglooMetadata::from_json($json->igloo_metadata) + : null, + igloohome_metadata: isset($json->igloohome_metadata) + ? Properties\IgloohomeMetadata::from_json( + $json->igloohome_metadata, + ) + : null, + image_alt_text: $json->image_alt_text ?? null, + image_url: $json->image_url ?? null, + is_cooling: $json->is_cooling ?? null, + is_fan_running: $json->is_fan_running ?? null, + is_heating: $json->is_heating ?? null, + is_temporary_manual_override_active: $json->is_temporary_manual_override_active ?? + null, + keynest_metadata: isset($json->keynest_metadata) + ? Properties\KeynestMetadata::from_json( + $json->keynest_metadata, + ) + : null, + keypad_battery: isset($json->keypad_battery) + ? Properties\KeypadBattery::from_json($json->keypad_battery) + : null, + kisi_metadata: isset($json->kisi_metadata) + ? Properties\KisiMetadata::from_json($json->kisi_metadata) + : null, + korelock_metadata: isset($json->korelock_metadata) + ? Properties\KorelockMetadata::from_json( + $json->korelock_metadata, + ) + : null, + kwikset_metadata: isset($json->kwikset_metadata) + ? Properties\KwiksetMetadata::from_json( + $json->kwikset_metadata, + ) + : null, + locked: $json->locked ?? null, + lockly_metadata: isset($json->lockly_metadata) + ? Properties\LocklyMetadata::from_json( + $json->lockly_metadata, + ) + : null, + manufacturer: $json->manufacturer ?? null, + max_active_codes_supported: $json->max_active_codes_supported ?? + null, + max_cooling_set_point_celsius: $json->max_cooling_set_point_celsius ?? + null, + max_cooling_set_point_fahrenheit: $json->max_cooling_set_point_fahrenheit ?? + null, + max_heating_set_point_celsius: $json->max_heating_set_point_celsius ?? + null, + max_heating_set_point_fahrenheit: $json->max_heating_set_point_fahrenheit ?? + null, + max_thermostat_daily_program_periods_per_day: $json->max_thermostat_daily_program_periods_per_day ?? + null, + max_unique_climate_presets_per_thermostat_weekly_program: $json->max_unique_climate_presets_per_thermostat_weekly_program ?? + null, + min_cooling_set_point_celsius: $json->min_cooling_set_point_celsius ?? + null, + min_cooling_set_point_fahrenheit: $json->min_cooling_set_point_fahrenheit ?? + null, + min_heating_cooling_delta_celsius: $json->min_heating_cooling_delta_celsius ?? + null, + min_heating_cooling_delta_fahrenheit: $json->min_heating_cooling_delta_fahrenheit ?? + null, + min_heating_set_point_celsius: $json->min_heating_set_point_celsius ?? + null, + min_heating_set_point_fahrenheit: $json->min_heating_set_point_fahrenheit ?? + null, + minut_metadata: isset($json->minut_metadata) + ? Properties\MinutMetadata::from_json($json->minut_metadata) + : null, + model: isset($json->model) + ? Properties\Model::from_json($json->model) + : null, + name: $json->name ?? null, + nest_metadata: isset($json->nest_metadata) + ? Properties\NestMetadata::from_json($json->nest_metadata) + : null, + noise_level_decibels: $json->noise_level_decibels ?? null, + noiseaware_metadata: isset($json->noiseaware_metadata) + ? Properties\NoiseawareMetadata::from_json( + $json->noiseaware_metadata, + ) + : null, + nuki_metadata: isset($json->nuki_metadata) + ? Properties\NukiMetadata::from_json($json->nuki_metadata) + : null, + offline_access_codes_enabled: $json->offline_access_codes_enabled ?? + null, + offline_time_frame_options: array_map( + fn($o) => Properties\OfflineTimeFrameOptions::from_json($o), + $json->offline_time_frame_options ?? [], + ), + omnitec_metadata: isset($json->omnitec_metadata) + ? Properties\OmnitecMetadata::from_json( + $json->omnitec_metadata, + ) + : null, + online: $json->online ?? null, + online_access_codes_enabled: $json->online_access_codes_enabled ?? + null, + online_time_frame_options: array_map( + fn($o) => Properties\OnlineTimeFrameOptions::from_json($o), + $json->online_time_frame_options ?? [], + ), + relative_humidity: $json->relative_humidity ?? null, + ring_metadata: isset($json->ring_metadata) + ? Properties\RingMetadata::from_json($json->ring_metadata) + : null, + salto_ks_metadata: isset($json->salto_ks_metadata) + ? Properties\SaltoKsMetadata::from_json( + $json->salto_ks_metadata, + ) + : null, + salto_metadata: isset($json->salto_metadata) + ? Properties\SaltoMetadata::from_json($json->salto_metadata) + : null, + salto_space_credential_service_metadata: isset( + $json->salto_space_credential_service_metadata, + ) + ? Properties\SaltoSpaceCredentialServiceMetadata::from_json( + $json->salto_space_credential_service_metadata, + ) + : null, + schlage_metadata: isset($json->schlage_metadata) + ? Properties\SchlageMetadata::from_json( + $json->schlage_metadata, + ) + : null, + seam_bridge_metadata: isset($json->seam_bridge_metadata) + ? Properties\SeamBridgeMetadata::from_json( + $json->seam_bridge_metadata, + ) + : null, + sensi_metadata: isset($json->sensi_metadata) + ? Properties\SensiMetadata::from_json($json->sensi_metadata) + : null, + serial_number: $json->serial_number ?? null, + smartthings_metadata: isset($json->smartthings_metadata) + ? Properties\SmartthingsMetadata::from_json( + $json->smartthings_metadata, + ) + : null, + supported_code_lengths: $json->supported_code_lengths ?? null, + supports_accessory_keypad: $json->supports_accessory_keypad ?? + null, + supports_backup_access_code_pool: $json->supports_backup_access_code_pool ?? + null, + supports_offline_access_codes: $json->supports_offline_access_codes ?? + null, + tado_metadata: isset($json->tado_metadata) + ? Properties\TadoMetadata::from_json($json->tado_metadata) + : null, + tedee_metadata: isset($json->tedee_metadata) + ? Properties\TedeeMetadata::from_json($json->tedee_metadata) + : null, + temperature_celsius: $json->temperature_celsius ?? null, + temperature_fahrenheit: $json->temperature_fahrenheit ?? null, + temperature_threshold: isset($json->temperature_threshold) + ? Properties\TemperatureThreshold::from_json( + $json->temperature_threshold, + ) + : null, + thermostat_daily_program_period_precision_minutes: $json->thermostat_daily_program_period_precision_minutes ?? + null, + thermostat_daily_programs: array_map( + fn($t) => Properties\ThermostatDailyPrograms::from_json($t), + $json->thermostat_daily_programs ?? [], + ), + thermostat_weekly_program: isset( + $json->thermostat_weekly_program, + ) + ? Properties\ThermostatWeeklyProgram::from_json( + $json->thermostat_weekly_program, + ) + : null, + ttlock_metadata: isset($json->ttlock_metadata) + ? Properties\TtlockMetadata::from_json( + $json->ttlock_metadata, + ) + : null, + two_n_metadata: isset($json->two_n_metadata) + ? Properties\TwoNMetadata::from_json($json->two_n_metadata) + : null, + ultraloq_metadata: isset($json->ultraloq_metadata) + ? Properties\UltraloqMetadata::from_json( + $json->ultraloq_metadata, + ) + : null, + visionline_metadata: isset($json->visionline_metadata) + ? Properties\VisionlineMetadata::from_json( + $json->visionline_metadata, + ) + : null, + wyze_metadata: isset($json->wyze_metadata) + ? Properties\WyzeMetadata::from_json($json->wyze_metadata) + : null, + ); + } -/** - * Metadata for an ASSA ABLOY Vostio system. - */ -class DeviceAssaAbloyVostioMetadata -{ - public static function from_json( - mixed $json, - ): DeviceAssaAbloyVostioMetadata|null { - if (!$json) { - return null; - } - return new self(encoder_name: $json->encoder_name ?? null); - } - - public function __construct( - /** - * Encoder name for an ASSA ABLOY Vostio system. - */ - public string|null $encoder_name, - ) {} -} + public function __construct( + /** + * Accessory keypad properties and state. + */ + public Properties\AccessoryKeypad|null $accessory_keypad, + /** + * Active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + * + * @deprecated Use `active_thermostat_schedule_id` with `/thermostats/schedules/get` instead. + */ + public Properties\ActiveThermostatSchedule|null $active_thermostat_schedule, + /** + * ID of the active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $active_thermostat_schedule_id, + /** + * Metadata for an Akiles device. + */ + public Properties\AkilesMetadata|null $akiles_metadata, + /** + * Appearance-related properties, as reported by the device. + */ + public Properties\Appearance|null $appearance, + /** + * Metadata for an Aqara device. + */ + public Properties\AqaraMetadata|null $aqara_metadata, + /** + * ASSA ABLOY Credential Service metadata for the phone. + */ + public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata, + /** + * Metadata for an ASSA ABLOY Vostio system. + */ + public Properties\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + /** + * Metadata for an August device. + */ + public Properties\AugustMetadata|null $august_metadata, + /** + * The delay in seconds before the lock automatically locks after being unlocked. + */ + public float|null $auto_lock_delay_seconds, + /** + * Indicates whether automatic locking is enabled. + */ + public bool|null $auto_lock_enabled, + /** + * Climate preset modes that the thermostat supports, such as "home", "away", "wake", "sleep", "occupied", and "unoccupied". + */ + public array|null $available_climate_preset_modes, + /** + * Available [climate presets](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) for the thermostat. + */ + public array $available_climate_presets, + /** + * Fan mode settings that the thermostat supports. + */ + public array|null $available_fan_mode_settings, + /** + * HVAC mode settings that the thermostat supports. + */ + public array|null $available_hvac_mode_settings, + /** + * Metadata for an Avigilon Alta system. + */ + public Properties\AvigilonAltaMetadata|null $avigilon_alta_metadata, + /** + * Indicates whether the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is currently enabled for the device. To disable it, set this to `false` using [/devices/update](https://docs.seam.co/api/devices/update). + */ + public bool|null $backup_access_code_pool_enabled, + /** + * Represents the current status of the battery charge level. + */ + public Properties\Battery|null $battery, + /** + * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. + */ + public float|null $battery_level, + /** + * Metadata for a Brivo device. + */ + public Properties\BrivoMetadata|null $brivo_metadata, + /** + * Constraints on access codes for the device. Seam represents each constraint as an object with a `constraint_type` property. Depending on the constraint type, there may also be additional properties. Note that some constraints are manufacturer- or device-specific. + */ + public array $code_constraints, + /** + * Metadata for a ControlByWeb device. + */ + public Properties\ControlbywebMetadata|null $controlbyweb_metadata, + /** + * Current climate setting. + */ + public Properties\CurrentClimateSetting|null $current_climate_setting, + /** + * Array of noise threshold IDs that are currently triggering. + */ + public array|null $currently_triggering_noise_threshold_ids, + /** + * @deprecated use fallback_climate_preset_key to specify a fallback climate preset instead. + */ + public Properties\DefaultClimateSetting|null $default_climate_setting, + /** + * Indicates whether the door is open. + */ + public bool|null $door_open, + /** + * Metadata for a dormakaba Oracode device. + */ + public Properties\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata, + /** + * Metadata for an ecobee device. + */ + public Properties\EcobeeMetadata|null $ecobee_metadata, + /** + * Key of the [fallback climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets/setting-the-fallback-climate-preset) for the thermostat. + */ + public string|null $fallback_climate_preset_key, + /** + * @deprecated Use `current_climate_setting.fan_mode_setting` instead. + */ + public string|null $fan_mode_setting, + /** + * Metadata for a 4SUITES device. + */ + public Properties\FourSuitesMetadata|null $four_suites_metadata, + /** + * Metadata for a Genie device. + */ + public Properties\GenieMetadata|null $genie_metadata, + /** + * Indicates whether the device has direct power. + */ + public bool|null $has_direct_power, + /** + * Indicates whether the device supports native entry events. + */ + public bool|null $has_native_entry_events, + /** + * Metadata for a Honeywell Resideo device. + */ + public Properties\HoneywellResideoMetadata|null $honeywell_resideo_metadata, + /** + * Metadata for an igloo device. + */ + public Properties\IglooMetadata|null $igloo_metadata, + /** + * Metadata for an igloohome device. + */ + public Properties\IgloohomeMetadata|null $igloohome_metadata, + /** + * Alt text for the device image. + */ + public string|null $image_alt_text, + /** + * Image URL for the device. + */ + public string|null $image_url, + /** + * Indicates whether the connected HVAC system is currently cooling, as reported by the thermostat. + */ + public bool|null $is_cooling, + /** + * Indicates whether the fan in the connected HVAC system is currently running, as reported by the thermostat. + */ + public bool|null $is_fan_running, + /** + * Indicates whether the connected HVAC system is currently heating, as reported by the thermostat. + */ + public bool|null $is_heating, + /** + * Indicates whether the current thermostat settings differ from the most recent active program or schedule that Seam activated. For this condition to occur, `current_climate_setting.manual_override_allowed` must also be `true`. + */ + public bool|null $is_temporary_manual_override_active, + /** + * Metadata for a KeyNest device. + */ + public Properties\KeynestMetadata|null $keynest_metadata, + /** + * Keypad battery status. + */ + public Properties\KeypadBattery|null $keypad_battery, + /** + * Metadata for a Kisi device. + */ + public Properties\KisiMetadata|null $kisi_metadata, + /** + * Metadata for a Korelock device. + */ + public Properties\KorelockMetadata|null $korelock_metadata, + /** + * Metadata for a Kwikset device. + */ + public Properties\KwiksetMetadata|null $kwikset_metadata, + /** + * Indicates whether the lock is locked. + */ + public bool|null $locked, + /** + * Metadata for a Lockly device. + */ + public Properties\LocklyMetadata|null $lockly_metadata, + /** + * Manufacturer of the device. When a device, such as a smart lock, is connected through a smart hub, the manufacturer of the device might be different from that of the smart hub. + */ + public string|null $manufacturer, + /** + * Maximum number of active access codes that the device supports. + */ + public float|null $max_active_codes_supported, + /** + * Maximum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °C. + */ + public float|null $max_cooling_set_point_celsius, + /** + * Maximum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °F. + */ + public float|null $max_cooling_set_point_fahrenheit, + /** + * Maximum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °C. + */ + public float|null $max_heating_set_point_celsius, + /** + * Maximum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °F. + */ + public float|null $max_heating_set_point_fahrenheit, + /** + * Maximum number of periods that the thermostat can support per day. For example, if the thermostat supports 4 periods per day, this value is 4. + */ + public float|null $max_thermostat_daily_program_periods_per_day, + /** + * Maximum number of climate presets that the thermostat can support for weekly programming. + */ + public float|null $max_unique_climate_presets_per_thermostat_weekly_program, + /** + * Minimum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °C. + */ + public float|null $min_cooling_set_point_celsius, + /** + * Minimum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °F. + */ + public float|null $min_cooling_set_point_fahrenheit, + /** + * Minimum [temperature difference](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#minimum-heating-cooling-temperature-delta) in °C between the cooling and heating set points when in heat-cool (auto) mode. + */ + public float|null $min_heating_cooling_delta_celsius, + /** + * Minimum [temperature difference](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#minimum-heating-cooling-temperature-delta) in °F between the cooling and heating set points when in heat-cool (auto) mode. + */ + public float|null $min_heating_cooling_delta_fahrenheit, + /** + * Minimum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °C. + */ + public float|null $min_heating_set_point_celsius, + /** + * Minimum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °F. + */ + public float|null $min_heating_set_point_fahrenheit, + /** + * Metadata for a Minut device. + */ + public Properties\MinutMetadata|null $minut_metadata, + /** + * Device model-related properties. + */ + public Properties\Model|null $model, + /** + * Name of the device. + * + * @deprecated use device.display_name instead + */ + public string|null $name, + /** + * Metadata for a Google Nest device. + */ + public Properties\NestMetadata|null $nest_metadata, + /** + * Indicates current noise level in decibels, if the device supports noise detection. + */ + public float|null $noise_level_decibels, + /** + * Metadata for a NoiseAware device. + */ + public Properties\NoiseawareMetadata|null $noiseaware_metadata, + /** + * Metadata for a Nuki device. + */ + public Properties\NukiMetadata|null $nuki_metadata, + /** + * Indicates whether it is currently possible to use offline access codes for the device. + * + * @deprecated use device.can_program_offline_access_codes + */ + public bool|null $offline_access_codes_enabled, + /** + * Time frames that may be requested when creating an offline access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by `display_name` when they do) and satisfies that one option's rules. When `undefined`, any time frame works. + */ + public array $offline_time_frame_options, + /** + * Metadata for an Omnitec device. + */ + public Properties\OmnitecMetadata|null $omnitec_metadata, + /** + * Indicates whether the device is online. + */ + public bool|null $online, + /** + * Indicates whether it is currently possible to use online access codes for the device. + * + * @deprecated use device.can_program_online_access_codes + */ + public bool|null $online_access_codes_enabled, + /** + * Time frames that may be requested when creating an online access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by `display_name` when they do) and satisfies that one option's rules. When `undefined`, any time frame works. + */ + public array $online_time_frame_options, + /** + * Reported relative humidity, as a value between 0 and 1, inclusive. + */ + public float|null $relative_humidity, + /** + * Metadata for a Ring device. + */ + public Properties\RingMetadata|null $ring_metadata, + /** + * Metadata for a Salto KS device. + */ + public Properties\SaltoKsMetadata|null $salto_ks_metadata, + /** + * Metada for a Salto device. + * + * @deprecated Use `salto_ks_metadata ` instead. + */ + public Properties\SaltoMetadata|null $salto_metadata, + /** + * Salto Space credential service metadata for the phone. + */ + public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata, + /** + * Metadata for a Schlage device. + */ + public Properties\SchlageMetadata|null $schlage_metadata, + /** + * Metadata for Seam Bridge. + */ + public Properties\SeamBridgeMetadata|null $seam_bridge_metadata, + /** + * Metadata for a Sensi device. + */ + public Properties\SensiMetadata|null $sensi_metadata, + /** + * Serial number of the device. + */ + public string|null $serial_number, + /** + * Metadata for a SmartThings device. + */ + public Properties\SmartthingsMetadata|null $smartthings_metadata, + /** + * Supported code lengths for access codes. + */ + public array|null $supported_code_lengths, + /** + * @deprecated use device.properties.model.can_connect_accessory_keypad + */ + public bool|null $supports_accessory_keypad, + /** + * Indicates whether the device supports a [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes). + */ + public bool|null $supports_backup_access_code_pool, + /** + * @deprecated use offline_access_codes_enabled + */ + public bool|null $supports_offline_access_codes, + /** + * Metadata for a tado° device. + */ + public Properties\TadoMetadata|null $tado_metadata, + /** + * Metadata for a Tedee device. + */ + public Properties\TedeeMetadata|null $tedee_metadata, + /** + * Reported temperature in °C. + */ + public float|null $temperature_celsius, + /** + * Reported temperature in °F. + */ + public float|null $temperature_fahrenheit, + /** + * Current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public Properties\TemperatureThreshold|null $temperature_threshold, + /** + * Precision of the thermostat's period in minutes. For example, if the thermostat supports 15-minute periods, this value is 15. All values are relative to the top of the hour, so for 15 minutes, the periods would be 0, 15, 30, and 45 minutes past the hour. + */ + public float|null $thermostat_daily_program_period_precision_minutes, + /** + * Configured [daily programs](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. + */ + public array $thermostat_daily_programs, + /** + * Current [weekly program](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. + */ + public Properties\ThermostatWeeklyProgram|null $thermostat_weekly_program, + /** + * Metadata for a TTLock device. + */ + public Properties\TtlockMetadata|null $ttlock_metadata, + /** + * Metadata for a 2N device. + */ + public Properties\TwoNMetadata|null $two_n_metadata, + /** + * Metadata for an Ultraloq device. + */ + public Properties\UltraloqMetadata|null $ultraloq_metadata, + /** + * Metadata for an ASSA ABLOY Visionline system. + */ + public Properties\VisionlineMetadata|null $visionline_metadata, + /** + * Metadata for a Wyze device. + */ + public Properties\WyzeMetadata|null $wyze_metadata, + ) {} + } -/** - * Metadata for an August device. - */ -class DeviceAugustMetadata -{ - public static function from_json(mixed $json): DeviceAugustMetadata|null - { - if (!$json) { - return null; - } - return new self( - has_keypad: $json->has_keypad ?? null, - house_id: $json->house_id ?? null, - house_name: $json->house_name ?? null, - keypad_battery_level: $json->keypad_battery_level ?? null, - lock_id: $json->lock_id ?? null, - lock_name: $json->lock_name ?? null, - model: $json->model ?? null, - ); - } - - public function __construct( - /** - * Indicates whether an August device has a keypad. - */ - public bool|null $has_keypad, - /** - * House ID for an August device. - */ - public string|null $house_id, - /** - * House name for an August device. - */ - public string|null $house_name, - /** - * Keypad battery level for an August device. - */ - public string|null $keypad_battery_level, - /** - * Lock ID for an August device. - */ - public string|null $lock_id, - /** - * Lock name for an August device. - */ - public string|null $lock_name, - /** - * Model for an August device. - */ - public string|null $model, - ) {} -} + /** + * Array of warnings associated with the device. Each warning object within the array contains two fields: `warning_code` and `message`. `warning_code` is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + active_access_code_count: $json->active_access_code_count ?? + null, + created_at: $json->created_at ?? null, + max_active_access_code_count: $json->max_active_access_code_count ?? + null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } -/** - * Available [climate presets](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) for the thermostat. - */ -class DeviceAvailableClimatePresets -{ - public static function from_json( - mixed $json, - ): DeviceAvailableClimatePresets|null { - if (!$json) { - return null; - } - return new self( - can_delete: $json->can_delete ?? null, - can_edit: $json->can_edit ?? null, - can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? - null, - climate_preset_key: $json->climate_preset_key ?? null, - climate_preset_mode: $json->climate_preset_mode ?? null, - cooling_set_point_celsius: $json->cooling_set_point_celsius ?? null, - cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? - null, - display_name: $json->display_name ?? null, - ecobee_metadata: isset($json->ecobee_metadata) - ? DeviceEcobeeMetadata::from_json($json->ecobee_metadata) - : null, - fan_mode_setting: $json->fan_mode_setting ?? null, - heating_set_point_celsius: $json->heating_set_point_celsius ?? null, - heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? - null, - hvac_mode_setting: $json->hvac_mode_setting ?? null, - manual_override_allowed: $json->manual_override_allowed ?? null, - name: $json->name ?? null, - ); - } - - public function __construct( - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. - */ - public bool|null $can_delete, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. - */ - public bool|null $can_edit, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. - */ - public bool|null $can_use_with_thermostat_daily_programs, - /** - * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $climate_preset_key, - /** - * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. - */ - public string|null $climate_preset_mode, - /** - * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_celsius, - /** - * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_fahrenheit, - /** - * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $display_name, - /** - * Metadata specific to the Ecobee climate, if applicable. - */ - public DeviceEcobeeMetadata|null $ecobee_metadata, - /** - * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. - */ - public string|null $fan_mode_setting, - /** - * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_celsius, - /** - * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_fahrenheit, - /** - * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. - */ - public string|null $hvac_mode_setting, - /** - * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - * - * @deprecated Use 'thermostat_schedule.is_override_allowed' - */ - public bool|null $manual_override_allowed, - /** - * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $name, - ) {} + public function __construct( + /** + * Number of active access codes on the device when the warning was set. + */ + public int|null $active_access_code_count, + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Maximum number of active access codes supported by the device. + */ + public int|null $max_active_access_code_count, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Metadata for an Avigilon Alta system. - */ -class DeviceAvigilonAltaMetadata -{ - public static function from_json( - mixed $json, - ): DeviceAvigilonAltaMetadata|null { - if (!$json) { - return null; - } - return new self( - entry_name: $json->entry_name ?? null, - entry_relays_total_count: $json->entry_relays_total_count ?? null, - org_name: $json->org_name ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - zone_id: $json->zone_id ?? null, - zone_name: $json->zone_name ?? null, - ); - } - - public function __construct( - /** - * Entry name for an Avigilon Alta system. - */ - public string|null $entry_name, - /** - * Total count of entry relays for an Avigilon Alta system. - */ - public float|null $entry_relays_total_count, - /** - * Organization name for an Avigilon Alta system. - */ - public string|null $org_name, - /** - * Site ID for an Avigilon Alta system. - */ - public float|null $site_id, - /** - * Site name for an Avigilon Alta system. - */ - public string|null $site_name, - /** - * Zone ID for an Avigilon Alta system. - */ - public float|null $zone_id, - /** - * Zone name for an Avigilon Alta system. - */ - public string|null $zone_name, - ) {} -} +namespace Seam\Resources\Device\Properties { + /** + * Accessory keypad properties and state. + */ + class AccessoryKeypad + { + public static function from_json(mixed $json): AccessoryKeypad|null + { + if (!$json) { + return null; + } + return new self( + battery: isset($json->battery) + ? AccessoryKeypad\Battery::from_json($json->battery) + : null, + is_connected: $json->is_connected ?? null, + ); + } + + public function __construct( + /** + * Keypad battery properties. + */ + public AccessoryKeypad\Battery|null $battery, + /** + * Indicates if an accessory keypad is connected to the device. + */ + public bool|null $is_connected, + ) {} + } -/** - * Keypad battery properties. - */ -class DeviceBattery -{ - public static function from_json(mixed $json): DeviceBattery|null + /** + * Appearance-related properties, as reported by the device. + */ + class Appearance { - if (!$json) { - return null; + public static function from_json(mixed $json): Appearance|null + { + if (!$json) { + return null; + } + return new self(name: $json->name ?? null); } - return new self(level: $json->level ?? null); + + public function __construct( + /** + * Name of the device as seen from the provider API and application, not settable through Seam. + */ + public string|null $name, + ) {} } - public function __construct(public float|null $level) {} -} + /** + * Represents the current status of the battery charge level. + */ + class Battery + { + public static function from_json(mixed $json): Battery|null + { + if (!$json) { + return null; + } + return new self( + level: $json->level ?? null, + status: $json->status ?? null, + ); + } -/** - * Metadata for a Brivo device. - */ -class DeviceBrivoMetadata -{ - public static function from_json(mixed $json): DeviceBrivoMetadata|null - { - if (!$json) { - return null; - } - return new self( - activation_enabled: $json->activation_enabled ?? null, - device_name: $json->device_name ?? null, - ); - } - - public function __construct( - /** - * Indicates whether the Brivo access point has activation (remote unlock) enabled. - */ - public bool|null $activation_enabled, - /** - * Device name for a Brivo device. - */ - public string|null $device_name, - ) {} -} + public function __construct( + /** + * Battery charge level as a value between 0 and 1, inclusive. + */ + public float|null $level, + /** + * Represents the current status of the battery charge level. Values are `critical`, which indicates an extremely low level, suggesting imminent shutdown or an urgent need for charging; `low`, which signifies that the battery is under the preferred threshold and should be charged soon; `good`, which denotes a satisfactory charge level, adequate for normal use without the immediate need for recharging; and `full`, which represents a battery that is fully charged, providing the maximum duration of usage. + */ + public string|null $status, + ) {} + } -/** - * Constraints on access codes for the device. Seam represents each constraint as an object with a `constraint_type` property. Depending on the constraint type, there may also be additional properties. Note that some constraints are manufacturer- or device-specific. - */ -class DeviceCodeConstraints -{ - public static function from_json(mixed $json): DeviceCodeConstraints|null - { - if (!$json) { - return null; - } - return new self( - constraint_type: $json->constraint_type ?? null, - max_length: $json->max_length ?? null, - min_length: $json->min_length ?? null, - ); - } - - public function __construct( - public string|null $constraint_type, - /** - * Maximum name length constraint for access codes. - */ - public float|null $max_length, - /** - * Minimum name length constraint for access codes. - */ - public float|null $min_length, - ) {} -} + /** + * Device model-related properties. + */ + class Model + { + public static function from_json(mixed $json): Model|null + { + if (!$json) { + return null; + } + return new self( + accessory_keypad_supported: $json->accessory_keypad_supported ?? + null, + can_connect_accessory_keypad: $json->can_connect_accessory_keypad ?? + null, + display_name: $json->display_name ?? null, + has_built_in_keypad: $json->has_built_in_keypad ?? null, + manufacturer_display_name: $json->manufacturer_display_name ?? + null, + offline_access_codes_supported: $json->offline_access_codes_supported ?? + null, + online_access_codes_supported: $json->online_access_codes_supported ?? + null, + ); + } -/** - * Metadata for a ControlByWeb device. - */ -class DeviceControlbywebMetadata -{ - public static function from_json( - mixed $json, - ): DeviceControlbywebMetadata|null { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - relay_name: $json->relay_name ?? null, - ); - } - - public function __construct( - /** - * Device ID for a ControlByWeb device. - */ - public string|null $device_id, - /** - * Device name for a ControlByWeb device. - */ - public string|null $device_name, - /** - * Relay name for a ControlByWeb device. - */ - public string|null $relay_name, - ) {} -} + public function __construct( + /** + * @deprecated use device.properties.model.can_connect_accessory_keypad + */ + public bool|null $accessory_keypad_supported, + /** + * Indicates whether the device can connect a accessory keypad. + */ + public bool|null $can_connect_accessory_keypad, + /** + * Display name of the device model. + */ + public string|null $display_name, + /** + * Indicates whether the device has a built in accessory keypad. + */ + public bool|null $has_built_in_keypad, + /** + * Display name that corresponds to the manufacturer-specific terminology for the device. + */ + public string|null $manufacturer_display_name, + /** + * @deprecated use device.can_program_offline_access_codes. + */ + public bool|null $offline_access_codes_supported, + /** + * @deprecated use device.can_program_online_access_codes. + */ + public bool|null $online_access_codes_supported, + ) {} + } -/** - * Current climate setting. - */ -class DeviceCurrentClimateSetting -{ - public static function from_json( - mixed $json, - ): DeviceCurrentClimateSetting|null { - if (!$json) { - return null; - } - return new self( - can_delete: $json->can_delete ?? null, - can_edit: $json->can_edit ?? null, - can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? - null, - climate_preset_key: $json->climate_preset_key ?? null, - climate_preset_mode: $json->climate_preset_mode ?? null, - cooling_set_point_celsius: $json->cooling_set_point_celsius ?? null, - cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? - null, - display_name: $json->display_name ?? null, - ecobee_metadata: isset($json->ecobee_metadata) - ? DeviceEcobeeMetadata::from_json($json->ecobee_metadata) - : null, - fan_mode_setting: $json->fan_mode_setting ?? null, - heating_set_point_celsius: $json->heating_set_point_celsius ?? null, - heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? - null, - hvac_mode_setting: $json->hvac_mode_setting ?? null, - manual_override_allowed: $json->manual_override_allowed ?? null, - name: $json->name ?? null, - ); - } - - public function __construct( - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. - */ - public bool|null $can_delete, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. - */ - public bool|null $can_edit, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. - */ - public bool|null $can_use_with_thermostat_daily_programs, - /** - * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $climate_preset_key, - /** - * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. - */ - public string|null $climate_preset_mode, - /** - * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_celsius, - /** - * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_fahrenheit, - /** - * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $display_name, - /** - * Metadata specific to the Ecobee climate, if applicable. - */ - public DeviceEcobeeMetadata|null $ecobee_metadata, - /** - * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. - */ - public string|null $fan_mode_setting, - /** - * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_celsius, - /** - * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_fahrenheit, - /** - * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. - */ - public string|null $hvac_mode_setting, - /** - * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - * - * @deprecated Use 'thermostat_schedule.is_override_allowed' - */ - public bool|null $manual_override_allowed, - /** - * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $name, - ) {} -} + /** + * ASSA ABLOY Credential Service metadata for the phone. + */ + class AssaAbloyCredentialServiceMetadata + { + public static function from_json( + mixed $json, + ): AssaAbloyCredentialServiceMetadata|null { + if (!$json) { + return null; + } + return new self( + endpoints: array_map( + fn( + $e, + ) => AssaAbloyCredentialServiceMetadata\Endpoints::from_json( + $e, + ), + $json->endpoints ?? [], + ), + has_active_endpoint: $json->has_active_endpoint ?? null, + ); + } -class DeviceDefaultClimateSetting -{ - public static function from_json( - mixed $json, - ): DeviceDefaultClimateSetting|null { - if (!$json) { - return null; - } - return new self( - can_delete: $json->can_delete ?? null, - can_edit: $json->can_edit ?? null, - can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? - null, - climate_preset_key: $json->climate_preset_key ?? null, - climate_preset_mode: $json->climate_preset_mode ?? null, - cooling_set_point_celsius: $json->cooling_set_point_celsius ?? null, - cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? - null, - display_name: $json->display_name ?? null, - ecobee_metadata: isset($json->ecobee_metadata) - ? DeviceEcobeeMetadata::from_json($json->ecobee_metadata) - : null, - fan_mode_setting: $json->fan_mode_setting ?? null, - heating_set_point_celsius: $json->heating_set_point_celsius ?? null, - heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? - null, - hvac_mode_setting: $json->hvac_mode_setting ?? null, - manual_override_allowed: $json->manual_override_allowed ?? null, - name: $json->name ?? null, - ); - } - - public function __construct( - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. - */ - public bool|null $can_delete, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. - */ - public bool|null $can_edit, - /** - * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. - */ - public bool|null $can_use_with_thermostat_daily_programs, - /** - * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $climate_preset_key, - /** - * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. - */ - public string|null $climate_preset_mode, - /** - * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_celsius, - /** - * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_fahrenheit, - /** - * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $display_name, - /** - * Metadata specific to the Ecobee climate, if applicable. - */ - public DeviceEcobeeMetadata|null $ecobee_metadata, - /** - * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. - */ - public string|null $fan_mode_setting, - /** - * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_celsius, - /** - * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_fahrenheit, - /** - * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. - */ - public string|null $hvac_mode_setting, - /** - * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - * - * @deprecated Use 'thermostat_schedule.is_override_allowed' - */ - public bool|null $manual_override_allowed, - /** - * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). - */ - public string|null $name, - ) {} -} + public function __construct( + /** + * Endpoints associated with the phone. + */ + public array $endpoints, + /** + * Indicates whether the credential service has active endpoints associated with the phone. + */ + public bool|null $has_active_endpoint, + ) {} + } -/** - * Manufacturer of the device. Represents the hardware brand, which may differ from the provider. - */ -class DeviceDeviceManufacturer -{ - public static function from_json(mixed $json): DeviceDeviceManufacturer|null - { - if (!$json) { - return null; - } - return new self( - display_name: $json->display_name ?? null, - image_url: $json->image_url ?? null, - manufacturer: $json->manufacturer ?? null, - ); - } - - public function __construct( - /** - * Display name for the manufacturer, such as `August`, `Yale`, `Salto`, and so on. - */ - public string|null $display_name, - /** - * Image URL for the manufacturer logo. - */ - public string|null $image_url, - /** - * Manufacturer identifier, such as `august`, `yale`, `salto`, and so on. - */ - public string|null $manufacturer, - ) {} -} + /** + * Salto Space credential service metadata for the phone. + */ + class SaltoSpaceCredentialServiceMetadata + { + public static function from_json( + mixed $json, + ): SaltoSpaceCredentialServiceMetadata|null { + if (!$json) { + return null; + } + return new self(has_active_phone: $json->has_active_phone ?? null); + } -/** - * Provider of the device. Represents the third-party service through which the device is controlled. - */ -class DeviceDeviceProvider -{ - public static function from_json(mixed $json): DeviceDeviceProvider|null - { - if (!$json) { - return null; - } - return new self( - device_provider_name: $json->device_provider_name ?? null, - display_name: $json->display_name ?? null, - image_url: $json->image_url ?? null, - provider_category: $json->provider_category ?? null, - ); - } - - public function __construct( - /** - * Device provider name. Corresponds to the integration type, such as `august`, `schlage`, `yale_access`, and so on. - */ - public string|null $device_provider_name, - /** - * Display name for the device provider type. - */ - public string|null $display_name, - /** - * Image URL for the device provider. - */ - public string|null $image_url, - /** - * Provider category. Indicates the third-party provider type, such as `stable`, for stable integrations, or `internal`, for internal integrations. - */ - public string|null $provider_category, - ) {} -} + public function __construct( + /** + * Indicates whether the credential service has an active associated phone. + */ + public bool|null $has_active_phone, + ) {} + } -/** - * Metadata for a dormakaba Oracode device. - */ -class DeviceDormakabaOracodeMetadata -{ - public static function from_json( - mixed $json, - ): DeviceDormakabaOracodeMetadata|null { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - door_id: $json->door_id ?? null, - 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( - fn($p) => DevicePredefinedTimeSlots::from_json($p), - $json->predefined_time_slots ?? [], - ), - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - ); - } - - public function __construct( - /** - * Device ID for a dormakaba Oracode device. - */ - public mixed $device_id, - /** - * Door ID for a dormakaba Oracode device. - */ - public float|null $door_id, - /** - * Indicates whether a door is wireless for a dormakaba Oracode device. - */ - public bool|null $door_is_wireless, - /** - * Door name for a dormakaba Oracode device. - */ - public string|null $door_name, - /** - * IANA time zone for a dormakaba Oracode device. - */ - public string|null $iana_timezone, - /** - * Predefined time slots for a dormakaba Oracode device. - */ - public array $predefined_time_slots, - /** - * Site ID for a dormakaba Oracode device. - * - * @deprecated Previously marked as "@DEPRECATED." - */ - public float|null $site_id, - /** - * Site name for a dormakaba Oracode device. - */ - public string|null $site_name, - ) {} -} + /** + * Metadata for an Akiles device. + */ + class AkilesMetadata + { + public static function from_json(mixed $json): AkilesMetadata|null + { + if (!$json) { + return null; + } + return new self( + _member_group_id: $json->_member_group_id ?? null, + gadget_id: $json->gadget_id ?? null, + gadget_name: $json->gadget_name ?? null, + product_name: $json->product_name ?? null, + ); + } -/** - * Metadata for an ecobee device. - */ -class DeviceEcobeeMetadata -{ - public static function from_json(mixed $json): DeviceEcobeeMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_name: $json->device_name ?? null, - ecobee_device_id: $json->ecobee_device_id ?? null, - ); - } - - public function __construct( - /** - * Device name for an ecobee device. - */ - public string|null $device_name, - /** - * Device ID for an ecobee device. - */ - public string|null $ecobee_device_id, - ) {} -} + public function __construct( + /** + * Group ID to which to add users for an Akiles device. + */ + public string|null $_member_group_id, + /** + * Gadget ID for an Akiles device. + */ + public string|null $gadget_id, + /** + * Gadget name for an Akiles device. + */ + public string|null $gadget_name, + /** + * Product name for an Akiles device. + */ + public string|null $product_name, + ) {} + } -/** - * Endpoints associated with the phone. - */ -class DeviceEndpoints -{ - public static function from_json(mixed $json): DeviceEndpoints|null - { - if (!$json) { - return null; - } - return new self( - endpoint_id: $json->endpoint_id ?? null, - is_active: $json->is_active ?? null, - ); - } - - public function __construct( - /** - * ID of the associated endpoint. - */ - public string|null $endpoint_id, - /** - * Indicated whether the endpoint is active. - */ - public bool|null $is_active, - ) {} -} + /** + * Metadata for an Aqara device. + */ + class AqaraMetadata + { + public static function from_json(mixed $json): AqaraMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_name: $json->device_name ?? null, + did: $json->did ?? null, + firmware_version: $json->firmware_version ?? null, + model: $json->model ?? null, + model_type: $json->model_type ?? null, + parent_did: $json->parent_did ?? null, + position_id: $json->position_id ?? null, + time_zone: $json->time_zone ?? null, + ); + } -/** - * Array of errors associated with the device. Each error object within the array contains two fields: `error_code` and `message`. `error_code` is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. - */ -class DeviceErrors -{ - public static function from_json(mixed $json): DeviceErrors|null - { - if (!$json) { - return null; - } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - is_device_error: $json->is_device_error ?? null, - message: $json->message ?? null, - ); - } - - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error, - /** - * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. - */ - public bool|null $is_connected_account_error, - /** - * Indicates that the error is not a device error. - */ - public bool|null $is_device_error, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Device name for an Aqara device. + */ + public string|null $device_name, + /** + * Device ID (did) for an Aqara device. + */ + public string|null $did, + /** + * Firmware version for an Aqara device. + */ + public string|null $firmware_version, + /** + * Model identifier for an Aqara device. + */ + public string|null $model, + /** + * Model type for an Aqara device. + */ + public float|null $model_type, + /** + * Parent gateway device ID for an Aqara device. + */ + public string|null $parent_did, + /** + * Position (room) ID for an Aqara device. + */ + public string|null $position_id, + /** + * Time zone reported for an Aqara device (e.g. GMT-07:00). + */ + public string|null $time_zone, + ) {} + } -/** - * Features for a TTLock device. - */ -class DeviceFeatures -{ - public static function from_json(mixed $json): DeviceFeatures|null - { - if (!$json) { - return null; - } - return new self( - auto_lock_time_config: $json->auto_lock_time_config ?? null, - incomplete_keyboard_passcode: $json->incomplete_keyboard_passcode ?? - null, - lock_command: $json->lock_command ?? null, - passcode: $json->passcode ?? null, - passcode_management: $json->passcode_management ?? null, - unlock_via_gateway: $json->unlock_via_gateway ?? null, - wifi: $json->wifi ?? null, - ); - } - - public function __construct( - /** - * Indicates whether a TTLock device supports auto-lock time configuration. - */ - public bool|null $auto_lock_time_config, - /** - * Indicates whether a TTLock device supports an incomplete keyboard passcode. - */ - public bool|null $incomplete_keyboard_passcode, - /** - * Indicates whether a TTLock device supports the lock command. - */ - public bool|null $lock_command, - /** - * Indicates whether a TTLock device supports a passcode. - */ - public bool|null $passcode, - /** - * Indicates whether a TTLock device supports passcode management. - */ - public bool|null $passcode_management, - /** - * Indicates whether a TTLock device supports unlock via gateway. - */ - public bool|null $unlock_via_gateway, - /** - * Indicates whether a TTLock device supports Wi-Fi. - */ - public bool|null $wifi, - ) {} -} + /** + * Metadata for an ASSA ABLOY Vostio system. + */ + class AssaAbloyVostioMetadata + { + public static function from_json( + mixed $json, + ): AssaAbloyVostioMetadata|null { + if (!$json) { + return null; + } + return new self(encoder_name: $json->encoder_name ?? null); + } -/** - * Metadata for a 4SUITES device. - */ -class DeviceFourSuitesMetadata -{ - public static function from_json(mixed $json): DeviceFourSuitesMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - reclose_delay_in_seconds: $json->reclose_delay_in_seconds ?? null, - ); - } - - public function __construct( - /** - * Device ID for a 4SUITES device. - */ - public float|null $device_id, - /** - * Device name for a 4SUITES device. - */ - public string|null $device_name, - /** - * Reclose delay, in seconds, for a 4SUITES device. - */ - public float|null $reclose_delay_in_seconds, - ) {} -} + public function __construct( + /** + * Encoder name for an ASSA ABLOY Vostio system. + */ + public string|null $encoder_name, + ) {} + } -/** - * Metadata for a Genie device. - */ -class DeviceGenieMetadata -{ - public static function from_json(mixed $json): DeviceGenieMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_name: $json->device_name ?? null, - door_name: $json->door_name ?? null, - ); - } - - public function __construct( - /** - * Lock name for a Genie device. - */ - public string|null $device_name, - /** - * Door name for a Genie device. - */ - public string|null $door_name, - ) {} -} + /** + * Metadata for an August device. + */ + class AugustMetadata + { + public static function from_json(mixed $json): AugustMetadata|null + { + if (!$json) { + return null; + } + return new self( + has_keypad: $json->has_keypad ?? null, + house_id: $json->house_id ?? null, + house_name: $json->house_name ?? null, + keypad_battery_level: $json->keypad_battery_level ?? null, + lock_id: $json->lock_id ?? null, + lock_name: $json->lock_name ?? null, + model: $json->model ?? null, + ); + } -/** - * Metadata for a Honeywell Resideo device. - */ -class DeviceHoneywellResideoMetadata -{ - public static function from_json( - mixed $json, - ): DeviceHoneywellResideoMetadata|null { - if (!$json) { - return null; - } - return new self( - device_name: $json->device_name ?? null, - honeywell_resideo_device_id: $json->honeywell_resideo_device_id ?? - null, - ); - } - - public function __construct( - /** - * Device name for a Honeywell Resideo device. - */ - public string|null $device_name, - /** - * Device ID for a Honeywell Resideo device. - */ - public string|null $honeywell_resideo_device_id, - ) {} -} + public function __construct( + /** + * Indicates whether an August device has a keypad. + */ + public bool|null $has_keypad, + /** + * House ID for an August device. + */ + public string|null $house_id, + /** + * House name for an August device. + */ + public string|null $house_name, + /** + * Keypad battery level for an August device. + */ + public string|null $keypad_battery_level, + /** + * Lock ID for an August device. + */ + public string|null $lock_id, + /** + * Lock name for an August device. + */ + public string|null $lock_name, + /** + * Model for an August device. + */ + public string|null $model, + ) {} + } -/** - * Latest humidity reading for a Minut device. - */ -class DeviceHumidity -{ - public static function from_json(mixed $json): DeviceHumidity|null - { - if (!$json) { - return null; - } - return new self(time: $json->time ?? null, value: $json->value ?? null); - } - - public function __construct( - /** - * Time of latest humidity reading for a Minut device. - */ - public string|null $time, - /** - * Value of latest humidity reading for a Minut device. - */ - public float|null $value, - ) {} -} + /** + * Metadata for an Avigilon Alta system. + */ + class AvigilonAltaMetadata + { + public static function from_json(mixed $json): AvigilonAltaMetadata|null + { + if (!$json) { + return null; + } + return new self( + entry_name: $json->entry_name ?? null, + entry_relays_total_count: $json->entry_relays_total_count ?? + null, + org_name: $json->org_name ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + zone_id: $json->zone_id ?? null, + zone_name: $json->zone_name ?? null, + ); + } -/** - * Metadata for an igloohome device. - */ -class DeviceIgloohomeMetadata -{ - public static function from_json(mixed $json): DeviceIgloohomeMetadata|null - { - if (!$json) { - return null; - } - return new self( - bridge_id: $json->bridge_id ?? null, - bridge_name: $json->bridge_name ?? null, - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - is_accessory_keypad_linked_to_bridge: $json->is_accessory_keypad_linked_to_bridge ?? - null, - keypad_id: $json->keypad_id ?? null, - ); - } - - public function __construct( - /** - * Bridge ID for an igloohome device. - */ - public string|null $bridge_id, - /** - * Bridge name for an igloohome device. - */ - public string|null $bridge_name, - /** - * Device ID for an igloohome device. - */ - public string|null $device_id, - /** - * Device name for an igloohome device. - */ - public string|null $device_name, - /** - * Indicates whether a keypad is linked to a bridge for an igloohome device. - */ - public bool|null $is_accessory_keypad_linked_to_bridge, - /** - * Keypad ID for an igloohome device. - */ - public string|null $keypad_id, - ) {} -} + public function __construct( + /** + * Entry name for an Avigilon Alta system. + */ + public string|null $entry_name, + /** + * Total count of entry relays for an Avigilon Alta system. + */ + public float|null $entry_relays_total_count, + /** + * Organization name for an Avigilon Alta system. + */ + public string|null $org_name, + /** + * Site ID for an Avigilon Alta system. + */ + public float|null $site_id, + /** + * Site name for an Avigilon Alta system. + */ + public string|null $site_name, + /** + * Zone ID for an Avigilon Alta system. + */ + public float|null $zone_id, + /** + * Zone name for an Avigilon Alta system. + */ + public string|null $zone_name, + ) {} + } -/** - * Metadata for an igloo device. - */ -class DeviceIglooMetadata -{ - public static function from_json(mixed $json): DeviceIglooMetadata|null - { - if (!$json) { - return null; - } - return new self( - bridge_id: $json->bridge_id ?? null, - device_id: $json->device_id ?? null, - model: $json->model ?? null, - ); - } - - public function __construct( - /** - * Bridge ID for an igloo device. - */ - public string|null $bridge_id, - /** - * Device ID for an igloo device. - */ - public string|null $device_id, - /** - * Model for an igloo device. - */ - public string|null $model, - ) {} -} + /** + * Metadata for a Brivo device. + */ + class BrivoMetadata + { + public static function from_json(mixed $json): BrivoMetadata|null + { + if (!$json) { + return null; + } + return new self( + activation_enabled: $json->activation_enabled ?? null, + device_name: $json->device_name ?? null, + ); + } -/** - * Metadata for a KeyNest device. - */ -class DeviceKeynestMetadata -{ - public static function from_json(mixed $json): DeviceKeynestMetadata|null - { - if (!$json) { - return null; - } - return new self( - address: $json->address ?? null, - current_or_last_store_id: $json->current_or_last_store_id ?? null, - current_status: $json->current_status ?? null, - current_user_company: $json->current_user_company ?? null, - current_user_email: $json->current_user_email ?? null, - current_user_name: $json->current_user_name ?? null, - current_user_phone_number: $json->current_user_phone_number ?? null, - default_office_id: $json->default_office_id ?? null, - device_name: $json->device_name ?? null, - fob_id: $json->fob_id ?? null, - handover_method: $json->handover_method ?? null, - has_photo: $json->has_photo ?? null, - is_quadient_locker: $json->is_quadient_locker ?? null, - key_id: $json->key_id ?? null, - key_notes: $json->key_notes ?? null, - keynest_app_user: $json->keynest_app_user ?? null, - last_movement: $json->last_movement ?? null, - property_id: $json->property_id ?? null, - property_postcode: $json->property_postcode ?? null, - status_type: $json->status_type ?? null, - subscription_plan: $json->subscription_plan ?? null, - ); - } - - public function __construct( - /** - * Address for a KeyNest device. - */ - public string|null $address, - /** - * Current or last store ID for a KeyNest device. - */ - public float|null $current_or_last_store_id, - /** - * Current status for a KeyNest device. - */ - public string|null $current_status, - /** - * Current user company for a KeyNest device. - */ - public string|null $current_user_company, - /** - * Current user email for a KeyNest device. - */ - public string|null $current_user_email, - /** - * Current user name for a KeyNest device. - */ - public string|null $current_user_name, - /** - * Current user phone number for a KeyNest device. - */ - public string|null $current_user_phone_number, - /** - * Default office ID for a KeyNest device. - */ - public float|null $default_office_id, - /** - * Device name for a KeyNest device. - */ - public string|null $device_name, - /** - * Fob ID for a KeyNest device. - */ - public float|null $fob_id, - /** - * Handover method for a KeyNest device. - */ - public string|null $handover_method, - /** - * Whether the KeyNest device has a photo. - */ - public bool|null $has_photo, - /** - * Whether the key is in a locker that does not support the access codes API. - */ - public bool|null $is_quadient_locker, - /** - * Key ID for a KeyNest device. - */ - public string|null $key_id, - /** - * Key notes for a KeyNest device. - */ - public string|null $key_notes, - /** - * KeyNest app user for a KeyNest device. - */ - public string|null $keynest_app_user, - /** - * Last movement timestamp for a KeyNest device. - */ - public string|null $last_movement, - /** - * Property ID for a KeyNest device. - */ - public string|null $property_id, - /** - * Property postcode for a KeyNest device. - */ - public string|null $property_postcode, - /** - * Status type for a KeyNest device. - */ - public string|null $status_type, - /** - * Subscription plan for a KeyNest device. - */ - public string|null $subscription_plan, - ) {} -} + public function __construct( + /** + * Indicates whether the Brivo access point has activation (remote unlock) enabled. + */ + public bool|null $activation_enabled, + /** + * Device name for a Brivo device. + */ + public string|null $device_name, + ) {} + } -/** - * Keypad battery status. - */ -class DeviceKeypadBattery -{ - public static function from_json(mixed $json): DeviceKeypadBattery|null + /** + * Metadata for a ControlByWeb device. + */ + class ControlbywebMetadata { - if (!$json) { - return null; + public static function from_json(mixed $json): ControlbywebMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + relay_name: $json->relay_name ?? null, + ); } - return new self(level: $json->level ?? null); + + public function __construct( + /** + * Device ID for a ControlByWeb device. + */ + public string|null $device_id, + /** + * Device name for a ControlByWeb device. + */ + public string|null $device_name, + /** + * Relay name for a ControlByWeb device. + */ + public string|null $relay_name, + ) {} } - public function __construct( - /** - * Keypad battery charge level. - */ - public float|null $level, - ) {} -} + /** + * Metadata for a dormakaba Oracode device. + */ + class DormakabaOracodeMetadata + { + public static function from_json( + mixed $json, + ): DormakabaOracodeMetadata|null { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + door_id: $json->door_id ?? null, + 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( + fn( + $p, + ) => DormakabaOracodeMetadata\PredefinedTimeSlots::from_json( + $p, + ), + $json->predefined_time_slots ?? [], + ), + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + ); + } -/** - * Metadata for a Kisi device. - */ -class DeviceKisiMetadata -{ - public static function from_json(mixed $json): DeviceKisiMetadata|null - { - if (!$json) { - return null; - } - return new self( - description: $json->description ?? null, - lock_id: $json->lock_id ?? null, - lock_name: $json->lock_name ?? null, - place_name: $json->place_name ?? null, - ); - } - - public function __construct( - /** - * Description for a Kisi device. - */ - public string|null $description, - /** - * Lock ID for a Kisi device. - */ - public float|null $lock_id, - /** - * Lock name for a Kisi device. - */ - public string|null $lock_name, - /** - * Place name for a Kisi device. - */ - public string|null $place_name, - ) {} -} + public function __construct( + /** + * Device ID for a dormakaba Oracode device. + */ + public mixed $device_id, + /** + * Door ID for a dormakaba Oracode device. + */ + public float|null $door_id, + /** + * Indicates whether a door is wireless for a dormakaba Oracode device. + */ + public bool|null $door_is_wireless, + /** + * Door name for a dormakaba Oracode device. + */ + public string|null $door_name, + /** + * IANA time zone for a dormakaba Oracode device. + */ + public string|null $iana_timezone, + /** + * Predefined time slots for a dormakaba Oracode device. + */ + public array $predefined_time_slots, + /** + * Site ID for a dormakaba Oracode device. + * + * @deprecated Previously marked as "@DEPRECATED." + */ + public float|null $site_id, + /** + * Site name for a dormakaba Oracode device. + */ + public string|null $site_name, + ) {} + } -/** - * Metadata for a Korelock device. - */ -class DeviceKorelockMetadata -{ - public static function from_json(mixed $json): DeviceKorelockMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - firmware_version: $json->firmware_version ?? null, - location_id: $json->location_id ?? null, - model_code: $json->model_code ?? null, - serial_number: $json->serial_number ?? null, - wifi_signal_strength: $json->wifi_signal_strength ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Korelock device. - */ - public string|null $device_id, - /** - * Device name for a Korelock device. - */ - public string|null $device_name, - /** - * Firmware version for a Korelock device. - */ - public string|null $firmware_version, - /** - * Location ID for a Korelock device. Required for timebound access codes. - */ - public string|null $location_id, - /** - * Model code for a Korelock device. - */ - public string|null $model_code, - /** - * Serial number for a Korelock device. - */ - public string|null $serial_number, - /** - * WiFi signal strength (0-1) for a Korelock device. - */ - public float|null $wifi_signal_strength, - ) {} -} + /** + * Metadata for an ecobee device. + */ + class EcobeeMetadata + { + public static function from_json(mixed $json): EcobeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_name: $json->device_name ?? null, + ecobee_device_id: $json->ecobee_device_id ?? null, + ); + } -/** - * Metadata for a Kwikset device. - */ -class DeviceKwiksetMetadata -{ - public static function from_json(mixed $json): DeviceKwiksetMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - model_number: $json->model_number ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Kwikset device. - */ - public string|null $device_id, - /** - * Device name for a Kwikset device. - */ - public string|null $device_name, - /** - * Model number for a Kwikset device. - */ - public string|null $model_number, - ) {} -} + public function __construct( + /** + * Device name for an ecobee device. + */ + public string|null $device_name, + /** + * Device ID for an ecobee device. + */ + public string|null $ecobee_device_id, + ) {} + } -/** - * Latest sensor values for a Minut device. - */ -class DeviceLatestSensorValues -{ - public static function from_json(mixed $json): DeviceLatestSensorValues|null - { - if (!$json) { - return null; - } - return new self( - accelerometer_z: isset($json->accelerometer_z) - ? DeviceAccelerometerZ::from_json($json->accelerometer_z) - : null, - humidity: isset($json->humidity) - ? DeviceHumidity::from_json($json->humidity) - : null, - pressure: isset($json->pressure) - ? DevicePressure::from_json($json->pressure) - : null, - sound: isset($json->sound) - ? DeviceSound::from_json($json->sound) - : null, - temperature: isset($json->temperature) - ? DeviceTemperature::from_json($json->temperature) - : null, - ); - } - - public function __construct( - /** - * Latest accelerometer Z-axis reading for a Minut device. - */ - public DeviceAccelerometerZ|null $accelerometer_z, - /** - * Latest humidity reading for a Minut device. - */ - public DeviceHumidity|null $humidity, - /** - * Latest pressure reading for a Minut device. - */ - public DevicePressure|null $pressure, - /** - * Latest sound reading for a Minut device. - */ - public DeviceSound|null $sound, - /** - * Latest temperature reading for a Minut device. - */ - public DeviceTemperature|null $temperature, - ) {} -} + /** + * Metadata for a 4SUITES device. + */ + class FourSuitesMetadata + { + public static function from_json(mixed $json): FourSuitesMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + reclose_delay_in_seconds: $json->reclose_delay_in_seconds ?? + null, + ); + } -/** - * Location information for the device. - */ -class DeviceLocation -{ - public static function from_json(mixed $json): DeviceLocation|null - { - if (!$json) { - return null; - } - return new self( - location_name: $json->location_name ?? null, - time_zone: $json->time_zone ?? null, - timezone: $json->timezone ?? null, - ); - } - - public function __construct( - /** - * Name of the device location. - */ - public string|null $location_name, - /** - * Time zone of the device location. - */ - public string|null $time_zone, - /** - * Time zone of the device location. - * - * @deprecated Use `time_zone` instead. - */ - public string|null $timezone, - ) {} -} + public function __construct( + /** + * Device ID for a 4SUITES device. + */ + public float|null $device_id, + /** + * Device name for a 4SUITES device. + */ + public string|null $device_name, + /** + * Reclose delay, in seconds, for a 4SUITES device. + */ + public float|null $reclose_delay_in_seconds, + ) {} + } -/** - * Metadata for a Lockly device. - */ -class DeviceLocklyMetadata -{ - public static function from_json(mixed $json): DeviceLocklyMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - model: $json->model ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Lockly device. - */ - public string|null $device_id, - /** - * Device name for a Lockly device. - */ - public string|null $device_name, - /** - * Model for a Lockly device. - */ - public string|null $model, - ) {} -} + /** + * Metadata for a Genie device. + */ + class GenieMetadata + { + public static function from_json(mixed $json): GenieMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_name: $json->device_name ?? null, + door_name: $json->door_name ?? null, + ); + } -/** - * Metadata for a Minut device. - */ -class DeviceMinutMetadata -{ - public static function from_json(mixed $json): DeviceMinutMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - latest_sensor_values: isset($json->latest_sensor_values) - ? DeviceLatestSensorValues::from_json( - $json->latest_sensor_values, - ) - : null, - ); - } - - public function __construct( - /** - * Device ID for a Minut device. - */ - public string|null $device_id, - /** - * Device name for a Minut device. - */ - public string|null $device_name, - /** - * Latest sensor values for a Minut device. - */ - public DeviceLatestSensorValues|null $latest_sensor_values, - ) {} -} + public function __construct( + /** + * Lock name for a Genie device. + */ + public string|null $device_name, + /** + * Door name for a Genie device. + */ + public string|null $door_name, + ) {} + } -/** - * Device model-related properties. - */ -class DeviceModel -{ - public static function from_json(mixed $json): DeviceModel|null - { - if (!$json) { - return null; - } - return new self( - accessory_keypad_supported: $json->accessory_keypad_supported ?? - null, - can_connect_accessory_keypad: $json->can_connect_accessory_keypad ?? - null, - display_name: $json->display_name ?? null, - has_built_in_keypad: $json->has_built_in_keypad ?? null, - manufacturer_display_name: $json->manufacturer_display_name ?? null, - offline_access_codes_supported: $json->offline_access_codes_supported ?? - null, - online_access_codes_supported: $json->online_access_codes_supported ?? - null, - ); - } - - public function __construct( - /** - * @deprecated use device.properties.model.can_connect_accessory_keypad - */ - public bool|null $accessory_keypad_supported, - /** - * Indicates whether the device can connect a accessory keypad. - */ - public bool|null $can_connect_accessory_keypad, - /** - * Display name of the device model. - */ - public string|null $display_name, - /** - * Indicates whether the device has a built in accessory keypad. - */ - public bool|null $has_built_in_keypad, - /** - * Display name that corresponds to the manufacturer-specific terminology for the device. - */ - public string|null $manufacturer_display_name, - /** - * @deprecated use device.can_program_offline_access_codes. - */ - public bool|null $offline_access_codes_supported, - /** - * @deprecated use device.can_program_online_access_codes. - */ - public bool|null $online_access_codes_supported, - ) {} -} + /** + * Metadata for a Honeywell Resideo device. + */ + class HoneywellResideoMetadata + { + public static function from_json( + mixed $json, + ): HoneywellResideoMetadata|null { + if (!$json) { + return null; + } + return new self( + device_name: $json->device_name ?? null, + honeywell_resideo_device_id: $json->honeywell_resideo_device_id ?? + null, + ); + } -/** - * Metadata for a Google Nest device. - */ -class DeviceNestMetadata -{ - public static function from_json(mixed $json): DeviceNestMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_custom_name: $json->device_custom_name ?? null, - device_name: $json->device_name ?? null, - display_name: $json->display_name ?? null, - nest_device_id: $json->nest_device_id ?? null, - ); - } - - public function __construct( - /** - * Custom device name for a Google Nest device. The device owner sets this value. - */ - public string|null $device_custom_name, - /** - * Device name for a Google Nest device. Google sets this value. - */ - public string|null $device_name, - /** - * Display name for a Google Nest device. - */ - public string|null $display_name, - /** - * Device ID for a Google Nest device. - */ - public string|null $nest_device_id, - ) {} -} + public function __construct( + /** + * Device name for a Honeywell Resideo device. + */ + public string|null $device_name, + /** + * Device ID for a Honeywell Resideo device. + */ + public string|null $honeywell_resideo_device_id, + ) {} + } -/** - * Metadata for a NoiseAware device. - */ -class DeviceNoiseawareMetadata -{ - public static function from_json(mixed $json): DeviceNoiseawareMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_model: $json->device_model ?? null, - device_name: $json->device_name ?? null, - noise_level_decibel: $json->noise_level_decibel ?? null, - noise_level_nrs: $json->noise_level_nrs ?? null, - ); - } - - public function __construct( - /** - * Device ID for a NoiseAware device. - */ - public string|null $device_id, - /** - * Device model for a NoiseAware device. - */ - public string|null $device_model, - /** - * Device name for a NoiseAware device. - */ - public string|null $device_name, - /** - * Noise level, in decibels, for a NoiseAware device. - */ - public float|null $noise_level_decibel, - /** - * Noise level, expressed as a Noise Risk Score (NRS), for a NoiseAware device. - */ - public float|null $noise_level_nrs, - ) {} -} + /** + * Metadata for an igloo device. + */ + class IglooMetadata + { + public static function from_json(mixed $json): IglooMetadata|null + { + if (!$json) { + return null; + } + return new self( + bridge_id: $json->bridge_id ?? null, + device_id: $json->device_id ?? null, + model: $json->model ?? null, + ); + } -/** - * Metadata for a Nuki device. - */ -class DeviceNukiMetadata -{ - public static function from_json(mixed $json): DeviceNukiMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - keypad_2_paired: $json->keypad_2_paired ?? null, - keypad_battery_critical: $json->keypad_battery_critical ?? null, - keypad_paired: $json->keypad_paired ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Nuki device. - */ - public string|null $device_id, - /** - * Device name for a Nuki device. - */ - public string|null $device_name, - /** - * Indicates whether keypad 2 is paired for a Nuki device. - */ - public bool|null $keypad_2_paired, - /** - * Indicates whether the keypad battery is in a critical state for a Nuki device. - */ - public bool|null $keypad_battery_critical, - /** - * Indicates whether the keypad is paired for a Nuki device. - */ - public bool|null $keypad_paired, - ) {} -} + public function __construct( + /** + * Bridge ID for an igloo device. + */ + public string|null $bridge_id, + /** + * Device ID for an igloo device. + */ + public string|null $device_id, + /** + * Model for an igloo device. + */ + public string|null $model, + ) {} + } -/** - * Time frames that may be requested when creating an offline access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by `display_name` when they do) and satisfies that one option's rules. When `undefined`, any time frame works. - */ -class DeviceOfflineTimeFrameOptions -{ - public static function from_json( - mixed $json, - ): DeviceOfflineTimeFrameOptions|null { - if (!$json) { - return null; - } - return new self( - display_name: $json->display_name ?? null, - end_date_recurrence_rule: $json->end_date_recurrence_rule ?? null, - matching_start_end_time: $json->matching_start_end_time ?? null, - max_duration: $json->max_duration ?? null, - min_duration: $json->min_duration ?? null, - start_date_recurrence_rule: $json->start_date_recurrence_rule ?? - null, - time_pairs: array_map( - fn($t) => DeviceTimePairs::from_json($t), - $json->time_pairs ?? [], - ), - time_zone: $json->time_zone ?? null, - ); - } - - public function __construct( - /** - * Label for this option. For a single-option device, the product name (for example, `algoPIN` or `SmartPIN`); for a multi-option device, a label that distinguishes it (for example, `Hourly` or `Fixed start times`). - */ - public string|null $display_name, - /** - * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. - */ - public string|null $end_date_recurrence_rule, - /** - * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. - */ - public bool|null $matching_start_end_time, - /** - * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. - */ - public string|null $max_duration, - /** - * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. - */ - public string|null $min_duration, - /** - * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. - */ - public string|null $start_date_recurrence_rule, - /** - * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. - */ - public array $time_pairs, - /** - * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. - */ - public string|null $time_zone, - ) {} -} + /** + * Metadata for an igloohome device. + */ + class IgloohomeMetadata + { + public static function from_json(mixed $json): IgloohomeMetadata|null + { + if (!$json) { + return null; + } + return new self( + bridge_id: $json->bridge_id ?? null, + bridge_name: $json->bridge_name ?? null, + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + is_accessory_keypad_linked_to_bridge: $json->is_accessory_keypad_linked_to_bridge ?? + null, + keypad_id: $json->keypad_id ?? null, + ); + } -/** - * Metadata for an Omnitec device. - */ -class DeviceOmnitecMetadata -{ - public static function from_json(mixed $json): DeviceOmnitecMetadata|null - { - if (!$json) { - return null; - } - return new self( - has_gateway: $json->has_gateway ?? null, - lock_alias: $json->lock_alias ?? null, - lock_id: $json->lock_id ?? null, - lock_mac: $json->lock_mac ?? null, - lock_name: $json->lock_name ?? null, - time_zone: $json->time_zone ?? null, - timezone_raw_offset_ms: $json->timezone_raw_offset_ms ?? null, - ); - } - - public function __construct( - /** - * Whether the Omnitec lock has a connected gateway for remote operations. - */ - public bool|null $has_gateway, - /** - * Operator-assigned alias for an Omnitec device. - */ - public string|null $lock_alias, - /** - * Lock ID for an Omnitec device. - */ - public float|null $lock_id, - /** - * Bluetooth MAC address for an Omnitec device. - */ - public string|null $lock_mac, - /** - * Lock name for an Omnitec device. - */ - public string|null $lock_name, - /** - * IANA time zone for the Omnitec device, used to schedule time-bound access codes at the correct local time (accounting for DST). - */ - public string|null $time_zone, - /** - * Static UTC offset of the Omnitec lock in milliseconds. Does not account for DST. - */ - public float|null $timezone_raw_offset_ms, - ) {} -} + public function __construct( + /** + * Bridge ID for an igloohome device. + */ + public string|null $bridge_id, + /** + * Bridge name for an igloohome device. + */ + public string|null $bridge_name, + /** + * Device ID for an igloohome device. + */ + public string|null $device_id, + /** + * Device name for an igloohome device. + */ + public string|null $device_name, + /** + * Indicates whether a keypad is linked to a bridge for an igloohome device. + */ + public bool|null $is_accessory_keypad_linked_to_bridge, + /** + * Keypad ID for an igloohome device. + */ + public string|null $keypad_id, + ) {} + } -/** - * Time frames that may be requested when creating an online access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by `display_name` when they do) and satisfies that one option's rules. When `undefined`, any time frame works. - */ -class DeviceOnlineTimeFrameOptions -{ - public static function from_json( - mixed $json, - ): DeviceOnlineTimeFrameOptions|null { - if (!$json) { - return null; - } - return new self( - display_name: $json->display_name ?? null, - end_date_recurrence_rule: $json->end_date_recurrence_rule ?? null, - matching_start_end_time: $json->matching_start_end_time ?? null, - max_duration: $json->max_duration ?? null, - min_duration: $json->min_duration ?? null, - start_date_recurrence_rule: $json->start_date_recurrence_rule ?? - null, - time_pairs: array_map( - fn($t) => DeviceTimePairs::from_json($t), - $json->time_pairs ?? [], - ), - time_zone: $json->time_zone ?? null, - ); - } - - public function __construct( - /** - * Label for this option. For a single-option device, the product name (for example, `algoPIN` or `SmartPIN`); for a multi-option device, a label that distinguishes it (for example, `Hourly` or `Fixed start times`). - */ - public string|null $display_name, - /** - * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. - */ - public string|null $end_date_recurrence_rule, - /** - * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. - */ - public bool|null $matching_start_end_time, - /** - * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. - */ - public string|null $max_duration, - /** - * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. - */ - public string|null $min_duration, - /** - * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. - */ - public string|null $start_date_recurrence_rule, - /** - * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. - */ - public array $time_pairs, - /** - * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. - */ - public string|null $time_zone, - ) {} -} + /** + * Metadata for a KeyNest device. + */ + class KeynestMetadata + { + public static function from_json(mixed $json): KeynestMetadata|null + { + if (!$json) { + return null; + } + return new self( + address: $json->address ?? null, + current_or_last_store_id: $json->current_or_last_store_id ?? + null, + current_status: $json->current_status ?? null, + current_user_company: $json->current_user_company ?? null, + current_user_email: $json->current_user_email ?? null, + current_user_name: $json->current_user_name ?? null, + current_user_phone_number: $json->current_user_phone_number ?? + null, + default_office_id: $json->default_office_id ?? null, + device_name: $json->device_name ?? null, + fob_id: $json->fob_id ?? null, + handover_method: $json->handover_method ?? null, + has_photo: $json->has_photo ?? null, + is_quadient_locker: $json->is_quadient_locker ?? null, + key_id: $json->key_id ?? null, + key_notes: $json->key_notes ?? null, + keynest_app_user: $json->keynest_app_user ?? null, + last_movement: $json->last_movement ?? null, + property_id: $json->property_id ?? null, + property_postcode: $json->property_postcode ?? null, + status_type: $json->status_type ?? null, + subscription_plan: $json->subscription_plan ?? null, + ); + } -/** - * Array of thermostat daily program periods. - */ -class DevicePeriods -{ - public static function from_json(mixed $json): DevicePeriods|null - { - if (!$json) { - return null; - } - return new self( - climate_preset_key: $json->climate_preset_key ?? null, - starts_at_time: $json->starts_at_time ?? null, - ); - } - - public function __construct( - /** - * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to activate at the `starts_at_time`. - */ - public string|null $climate_preset_key, - /** - * Time at which the thermostat daily program period starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at_time, - ) {} -} + public function __construct( + /** + * Address for a KeyNest device. + */ + public string|null $address, + /** + * Current or last store ID for a KeyNest device. + */ + public float|null $current_or_last_store_id, + /** + * Current status for a KeyNest device. + */ + public string|null $current_status, + /** + * Current user company for a KeyNest device. + */ + public string|null $current_user_company, + /** + * Current user email for a KeyNest device. + */ + public string|null $current_user_email, + /** + * Current user name for a KeyNest device. + */ + public string|null $current_user_name, + /** + * Current user phone number for a KeyNest device. + */ + public string|null $current_user_phone_number, + /** + * Default office ID for a KeyNest device. + */ + public float|null $default_office_id, + /** + * Device name for a KeyNest device. + */ + public string|null $device_name, + /** + * Fob ID for a KeyNest device. + */ + public float|null $fob_id, + /** + * Handover method for a KeyNest device. + */ + public string|null $handover_method, + /** + * Whether the KeyNest device has a photo. + */ + public bool|null $has_photo, + /** + * Whether the key is in a locker that does not support the access codes API. + */ + public bool|null $is_quadient_locker, + /** + * Key ID for a KeyNest device. + */ + public string|null $key_id, + /** + * Key notes for a KeyNest device. + */ + public string|null $key_notes, + /** + * KeyNest app user for a KeyNest device. + */ + public string|null $keynest_app_user, + /** + * Last movement timestamp for a KeyNest device. + */ + public string|null $last_movement, + /** + * Property ID for a KeyNest device. + */ + public string|null $property_id, + /** + * Property postcode for a KeyNest device. + */ + public string|null $property_postcode, + /** + * Status type for a KeyNest device. + */ + public string|null $status_type, + /** + * Subscription plan for a KeyNest device. + */ + public string|null $subscription_plan, + ) {} + } -/** - * Predefined time slots for a dormakaba Oracode device. - */ -class DevicePredefinedTimeSlots -{ - public static function from_json( - mixed $json, - ): DevicePredefinedTimeSlots|null { - if (!$json) { - return null; - } - return new self( - check_in_time: $json->check_in_time ?? null, - check_out_time: $json->check_out_time ?? null, - dormakaba_oracode_user_level_id: $json->dormakaba_oracode_user_level_id ?? - null, - dormakaba_oracode_user_level_prefix: $json->dormakaba_oracode_user_level_prefix ?? - null, - is_24_hour: $json->is_24_hour ?? null, - is_biweekly_mode: $json->is_biweekly_mode ?? null, - is_master: $json->is_master ?? null, - is_one_shot: $json->is_one_shot ?? null, - name: $json->name ?? null, - prefix: $json->prefix ?? null, - ); - } - - public function __construct( - /** - * Check in time for a time slot for a dormakaba Oracode device. - */ - public string|null $check_in_time, - /** - * Checkout time for a time slot for a dormakaba Oracode device. - */ - public string|null $check_out_time, - /** - * ID of a user level for a dormakaba Oracode device. - */ - public string|null $dormakaba_oracode_user_level_id, - /** - * Prefix for a user level for a dormakaba Oracode device. - */ - public float|null $dormakaba_oracode_user_level_prefix, - /** - * Indicates whether a time slot for a dormakaba Oracode device is a 24-hour time slot. - */ - public bool|null $is_24_hour, - /** - * Indicates whether a time slot for a dormakaba Oracode device is in biweekly mode. - */ - public bool|null $is_biweekly_mode, - /** - * Indicates whether a time slot for a dormakaba Oracode device is a master time slot. - */ - public bool|null $is_master, - /** - * Indicates whether a time slot for a dormakaba Oracode device is a one-shot time slot. - */ - public bool|null $is_one_shot, - /** - * Name of a time slot for a dormakaba Oracode device. - */ - public string|null $name, - /** - * Prefix for a time slot for a dormakaba Oracode device. - */ - public float|null $prefix, - ) {} -} + /** + * Metadata for a Kisi device. + */ + class KisiMetadata + { + public static function from_json(mixed $json): KisiMetadata|null + { + if (!$json) { + return null; + } + return new self( + description: $json->description ?? null, + lock_id: $json->lock_id ?? null, + lock_name: $json->lock_name ?? null, + place_name: $json->place_name ?? null, + ); + } -/** - * Latest pressure reading for a Minut device. - */ -class DevicePressure -{ - public static function from_json(mixed $json): DevicePressure|null - { - if (!$json) { - return null; - } - return new self(time: $json->time ?? null, value: $json->value ?? null); - } - - public function __construct( - /** - * Time of latest pressure reading for a Minut device. - */ - public string|null $time, - /** - * Value of latest pressure reading for a Minut device. - */ - public float|null $value, - ) {} -} + public function __construct( + /** + * Description for a Kisi device. + */ + public string|null $description, + /** + * Lock ID for a Kisi device. + */ + public float|null $lock_id, + /** + * Lock name for a Kisi device. + */ + public string|null $lock_name, + /** + * Place name for a Kisi device. + */ + public string|null $place_name, + ) {} + } -/** - * Properties of the device. - */ -class DeviceProperties -{ - public static function from_json(mixed $json): DeviceProperties|null - { - if (!$json) { - return null; - } - return new self( - accessory_keypad: isset($json->accessory_keypad) - ? DeviceAccessoryKeypad::from_json($json->accessory_keypad) - : null, - active_thermostat_schedule: isset($json->active_thermostat_schedule) - ? DeviceActiveThermostatSchedule::from_json( - $json->active_thermostat_schedule, - ) - : null, - active_thermostat_schedule_id: $json->active_thermostat_schedule_id ?? - null, - akiles_metadata: isset($json->akiles_metadata) - ? DeviceAkilesMetadata::from_json($json->akiles_metadata) - : null, - appearance: isset($json->appearance) - ? DeviceAppearance::from_json($json->appearance) - : null, - aqara_metadata: isset($json->aqara_metadata) - ? DeviceAqaraMetadata::from_json($json->aqara_metadata) - : null, - assa_abloy_credential_service_metadata: isset( - $json->assa_abloy_credential_service_metadata, - ) - ? DeviceAssaAbloyCredentialServiceMetadata::from_json( - $json->assa_abloy_credential_service_metadata, - ) - : null, - assa_abloy_vostio_metadata: isset($json->assa_abloy_vostio_metadata) - ? DeviceAssaAbloyVostioMetadata::from_json( - $json->assa_abloy_vostio_metadata, - ) - : null, - august_metadata: isset($json->august_metadata) - ? DeviceAugustMetadata::from_json($json->august_metadata) - : null, - auto_lock_delay_seconds: $json->auto_lock_delay_seconds ?? null, - auto_lock_enabled: $json->auto_lock_enabled ?? null, - available_climate_preset_modes: $json->available_climate_preset_modes ?? - null, - available_climate_presets: array_map( - fn($a) => DeviceAvailableClimatePresets::from_json($a), - $json->available_climate_presets ?? [], - ), - available_fan_mode_settings: $json->available_fan_mode_settings ?? - null, - available_hvac_mode_settings: $json->available_hvac_mode_settings ?? - null, - avigilon_alta_metadata: isset($json->avigilon_alta_metadata) - ? DeviceAvigilonAltaMetadata::from_json( - $json->avigilon_alta_metadata, - ) - : null, - backup_access_code_pool_enabled: $json->backup_access_code_pool_enabled ?? - null, - battery: isset($json->battery) - ? DeviceBattery::from_json($json->battery) - : null, - battery_level: $json->battery_level ?? null, - brivo_metadata: isset($json->brivo_metadata) - ? DeviceBrivoMetadata::from_json($json->brivo_metadata) - : null, - code_constraints: array_map( - fn($c) => DeviceCodeConstraints::from_json($c), - $json->code_constraints ?? [], - ), - controlbyweb_metadata: isset($json->controlbyweb_metadata) - ? DeviceControlbywebMetadata::from_json( - $json->controlbyweb_metadata, - ) - : null, - current_climate_setting: isset($json->current_climate_setting) - ? DeviceCurrentClimateSetting::from_json( - $json->current_climate_setting, - ) - : null, - currently_triggering_noise_threshold_ids: $json->currently_triggering_noise_threshold_ids ?? - null, - default_climate_setting: isset($json->default_climate_setting) - ? DeviceDefaultClimateSetting::from_json( - $json->default_climate_setting, - ) - : null, - door_open: $json->door_open ?? null, - dormakaba_oracode_metadata: isset($json->dormakaba_oracode_metadata) - ? DeviceDormakabaOracodeMetadata::from_json( - $json->dormakaba_oracode_metadata, - ) - : null, - ecobee_metadata: isset($json->ecobee_metadata) - ? DeviceEcobeeMetadata::from_json($json->ecobee_metadata) - : null, - fallback_climate_preset_key: $json->fallback_climate_preset_key ?? - null, - fan_mode_setting: $json->fan_mode_setting ?? null, - four_suites_metadata: isset($json->four_suites_metadata) - ? DeviceFourSuitesMetadata::from_json( - $json->four_suites_metadata, - ) - : null, - genie_metadata: isset($json->genie_metadata) - ? DeviceGenieMetadata::from_json($json->genie_metadata) - : null, - has_direct_power: $json->has_direct_power ?? null, - has_native_entry_events: $json->has_native_entry_events ?? null, - honeywell_resideo_metadata: isset($json->honeywell_resideo_metadata) - ? DeviceHoneywellResideoMetadata::from_json( - $json->honeywell_resideo_metadata, - ) - : null, - igloo_metadata: isset($json->igloo_metadata) - ? DeviceIglooMetadata::from_json($json->igloo_metadata) - : null, - igloohome_metadata: isset($json->igloohome_metadata) - ? DeviceIgloohomeMetadata::from_json($json->igloohome_metadata) - : null, - image_alt_text: $json->image_alt_text ?? null, - image_url: $json->image_url ?? null, - is_cooling: $json->is_cooling ?? null, - is_fan_running: $json->is_fan_running ?? null, - is_heating: $json->is_heating ?? null, - is_temporary_manual_override_active: $json->is_temporary_manual_override_active ?? - null, - keynest_metadata: isset($json->keynest_metadata) - ? DeviceKeynestMetadata::from_json($json->keynest_metadata) - : null, - keypad_battery: isset($json->keypad_battery) - ? DeviceKeypadBattery::from_json($json->keypad_battery) - : null, - kisi_metadata: isset($json->kisi_metadata) - ? DeviceKisiMetadata::from_json($json->kisi_metadata) - : null, - korelock_metadata: isset($json->korelock_metadata) - ? DeviceKorelockMetadata::from_json($json->korelock_metadata) - : null, - kwikset_metadata: isset($json->kwikset_metadata) - ? DeviceKwiksetMetadata::from_json($json->kwikset_metadata) - : null, - locked: $json->locked ?? null, - lockly_metadata: isset($json->lockly_metadata) - ? DeviceLocklyMetadata::from_json($json->lockly_metadata) - : null, - manufacturer: $json->manufacturer ?? null, - max_active_codes_supported: $json->max_active_codes_supported ?? - null, - max_cooling_set_point_celsius: $json->max_cooling_set_point_celsius ?? - null, - max_cooling_set_point_fahrenheit: $json->max_cooling_set_point_fahrenheit ?? - null, - max_heating_set_point_celsius: $json->max_heating_set_point_celsius ?? - null, - max_heating_set_point_fahrenheit: $json->max_heating_set_point_fahrenheit ?? - null, - max_thermostat_daily_program_periods_per_day: $json->max_thermostat_daily_program_periods_per_day ?? - null, - max_unique_climate_presets_per_thermostat_weekly_program: $json->max_unique_climate_presets_per_thermostat_weekly_program ?? - null, - min_cooling_set_point_celsius: $json->min_cooling_set_point_celsius ?? - null, - min_cooling_set_point_fahrenheit: $json->min_cooling_set_point_fahrenheit ?? - null, - min_heating_cooling_delta_celsius: $json->min_heating_cooling_delta_celsius ?? - null, - min_heating_cooling_delta_fahrenheit: $json->min_heating_cooling_delta_fahrenheit ?? - null, - min_heating_set_point_celsius: $json->min_heating_set_point_celsius ?? - null, - min_heating_set_point_fahrenheit: $json->min_heating_set_point_fahrenheit ?? - null, - minut_metadata: isset($json->minut_metadata) - ? DeviceMinutMetadata::from_json($json->minut_metadata) - : null, - model: isset($json->model) - ? DeviceModel::from_json($json->model) - : null, - name: $json->name ?? null, - nest_metadata: isset($json->nest_metadata) - ? DeviceNestMetadata::from_json($json->nest_metadata) - : null, - noise_level_decibels: $json->noise_level_decibels ?? null, - noiseaware_metadata: isset($json->noiseaware_metadata) - ? DeviceNoiseawareMetadata::from_json( - $json->noiseaware_metadata, - ) - : null, - nuki_metadata: isset($json->nuki_metadata) - ? DeviceNukiMetadata::from_json($json->nuki_metadata) - : null, - offline_access_codes_enabled: $json->offline_access_codes_enabled ?? - null, - offline_time_frame_options: array_map( - fn($o) => DeviceOfflineTimeFrameOptions::from_json($o), - $json->offline_time_frame_options ?? [], - ), - omnitec_metadata: isset($json->omnitec_metadata) - ? DeviceOmnitecMetadata::from_json($json->omnitec_metadata) - : null, - online: $json->online ?? null, - online_access_codes_enabled: $json->online_access_codes_enabled ?? - null, - online_time_frame_options: array_map( - fn($o) => DeviceOnlineTimeFrameOptions::from_json($o), - $json->online_time_frame_options ?? [], - ), - relative_humidity: $json->relative_humidity ?? null, - ring_metadata: isset($json->ring_metadata) - ? DeviceRingMetadata::from_json($json->ring_metadata) - : null, - salto_ks_metadata: isset($json->salto_ks_metadata) - ? DeviceSaltoKsMetadata::from_json($json->salto_ks_metadata) - : null, - salto_metadata: isset($json->salto_metadata) - ? DeviceSaltoMetadata::from_json($json->salto_metadata) - : null, - salto_space_credential_service_metadata: isset( - $json->salto_space_credential_service_metadata, - ) - ? DeviceSaltoSpaceCredentialServiceMetadata::from_json( - $json->salto_space_credential_service_metadata, - ) - : null, - schlage_metadata: isset($json->schlage_metadata) - ? DeviceSchlageMetadata::from_json($json->schlage_metadata) - : null, - seam_bridge_metadata: isset($json->seam_bridge_metadata) - ? DeviceSeamBridgeMetadata::from_json( - $json->seam_bridge_metadata, - ) - : null, - sensi_metadata: isset($json->sensi_metadata) - ? DeviceSensiMetadata::from_json($json->sensi_metadata) - : null, - serial_number: $json->serial_number ?? null, - smartthings_metadata: isset($json->smartthings_metadata) - ? DeviceSmartthingsMetadata::from_json( - $json->smartthings_metadata, - ) - : null, - supported_code_lengths: $json->supported_code_lengths ?? null, - supports_accessory_keypad: $json->supports_accessory_keypad ?? null, - supports_backup_access_code_pool: $json->supports_backup_access_code_pool ?? - null, - supports_offline_access_codes: $json->supports_offline_access_codes ?? - null, - tado_metadata: isset($json->tado_metadata) - ? DeviceTadoMetadata::from_json($json->tado_metadata) - : null, - tedee_metadata: isset($json->tedee_metadata) - ? DeviceTedeeMetadata::from_json($json->tedee_metadata) - : null, - temperature_celsius: $json->temperature_celsius ?? null, - temperature_fahrenheit: $json->temperature_fahrenheit ?? null, - temperature_threshold: isset($json->temperature_threshold) - ? DeviceTemperatureThreshold::from_json( - $json->temperature_threshold, - ) - : null, - thermostat_daily_program_period_precision_minutes: $json->thermostat_daily_program_period_precision_minutes ?? - null, - thermostat_daily_programs: array_map( - fn($t) => DeviceThermostatDailyPrograms::from_json($t), - $json->thermostat_daily_programs ?? [], - ), - thermostat_weekly_program: isset($json->thermostat_weekly_program) - ? DeviceThermostatWeeklyProgram::from_json( - $json->thermostat_weekly_program, - ) - : null, - ttlock_metadata: isset($json->ttlock_metadata) - ? DeviceTtlockMetadata::from_json($json->ttlock_metadata) - : null, - two_n_metadata: isset($json->two_n_metadata) - ? DeviceTwoNMetadata::from_json($json->two_n_metadata) - : null, - ultraloq_metadata: isset($json->ultraloq_metadata) - ? DeviceUltraloqMetadata::from_json($json->ultraloq_metadata) - : null, - visionline_metadata: isset($json->visionline_metadata) - ? DeviceVisionlineMetadata::from_json( - $json->visionline_metadata, - ) - : null, - wyze_metadata: isset($json->wyze_metadata) - ? DeviceWyzeMetadata::from_json($json->wyze_metadata) - : null, - ); - } - - public function __construct( - /** - * Accessory keypad properties and state. - */ - public DeviceAccessoryKeypad|null $accessory_keypad, - /** - * Active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - * - * @deprecated Use `active_thermostat_schedule_id` with `/thermostats/schedules/get` instead. - */ - public DeviceActiveThermostatSchedule|null $active_thermostat_schedule, - /** - * ID of the active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $active_thermostat_schedule_id, - /** - * Metadata for an Akiles device. - */ - public DeviceAkilesMetadata|null $akiles_metadata, - /** - * Appearance-related properties, as reported by the device. - */ - public DeviceAppearance|null $appearance, - /** - * Metadata for an Aqara device. - */ - public DeviceAqaraMetadata|null $aqara_metadata, - /** - * ASSA ABLOY Credential Service metadata for the phone. - */ - public DeviceAssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata, - /** - * Metadata for an ASSA ABLOY Vostio system. - */ - public DeviceAssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, - /** - * Metadata for an August device. - */ - public DeviceAugustMetadata|null $august_metadata, - /** - * The delay in seconds before the lock automatically locks after being unlocked. - */ - public float|null $auto_lock_delay_seconds, - /** - * Indicates whether automatic locking is enabled. - */ - public bool|null $auto_lock_enabled, - /** - * Climate preset modes that the thermostat supports, such as "home", "away", "wake", "sleep", "occupied", and "unoccupied". - */ - public array|null $available_climate_preset_modes, - /** - * Available [climate presets](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) for the thermostat. - */ - public array $available_climate_presets, - /** - * Fan mode settings that the thermostat supports. - */ - public array|null $available_fan_mode_settings, - /** - * HVAC mode settings that the thermostat supports. - */ - public array|null $available_hvac_mode_settings, - /** - * Metadata for an Avigilon Alta system. - */ - public DeviceAvigilonAltaMetadata|null $avigilon_alta_metadata, - /** - * Indicates whether the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is currently enabled for the device. To disable it, set this to `false` using [/devices/update](https://docs.seam.co/api/devices/update). - */ - public bool|null $backup_access_code_pool_enabled, - /** - * Represents the current status of the battery charge level. - */ - public DeviceBattery|null $battery, - /** - * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. - */ - public float|null $battery_level, - /** - * Metadata for a Brivo device. - */ - public DeviceBrivoMetadata|null $brivo_metadata, - /** - * Constraints on access codes for the device. Seam represents each constraint as an object with a `constraint_type` property. Depending on the constraint type, there may also be additional properties. Note that some constraints are manufacturer- or device-specific. - */ - public array $code_constraints, - /** - * Metadata for a ControlByWeb device. - */ - public DeviceControlbywebMetadata|null $controlbyweb_metadata, - /** - * Current climate setting. - */ - public DeviceCurrentClimateSetting|null $current_climate_setting, - /** - * Array of noise threshold IDs that are currently triggering. - */ - public array|null $currently_triggering_noise_threshold_ids, - /** - * @deprecated use fallback_climate_preset_key to specify a fallback climate preset instead. - */ - public DeviceDefaultClimateSetting|null $default_climate_setting, - /** - * Indicates whether the door is open. - */ - public bool|null $door_open, - /** - * Metadata for a dormakaba Oracode device. - */ - public DeviceDormakabaOracodeMetadata|null $dormakaba_oracode_metadata, - /** - * Metadata for an ecobee device. - */ - public DeviceEcobeeMetadata|null $ecobee_metadata, - /** - * Key of the [fallback climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets/setting-the-fallback-climate-preset) for the thermostat. - */ - public string|null $fallback_climate_preset_key, - /** - * @deprecated Use `current_climate_setting.fan_mode_setting` instead. - */ - public string|null $fan_mode_setting, - /** - * Metadata for a 4SUITES device. - */ - public DeviceFourSuitesMetadata|null $four_suites_metadata, - /** - * Metadata for a Genie device. - */ - public DeviceGenieMetadata|null $genie_metadata, - /** - * Indicates whether the device has direct power. - */ - public bool|null $has_direct_power, - /** - * Indicates whether the device supports native entry events. - */ - public bool|null $has_native_entry_events, - /** - * Metadata for a Honeywell Resideo device. - */ - public DeviceHoneywellResideoMetadata|null $honeywell_resideo_metadata, - /** - * Metadata for an igloo device. - */ - public DeviceIglooMetadata|null $igloo_metadata, - /** - * Metadata for an igloohome device. - */ - public DeviceIgloohomeMetadata|null $igloohome_metadata, - /** - * Alt text for the device image. - */ - public string|null $image_alt_text, - /** - * Image URL for the device. - */ - public string|null $image_url, - /** - * Indicates whether the connected HVAC system is currently cooling, as reported by the thermostat. - */ - public bool|null $is_cooling, - /** - * Indicates whether the fan in the connected HVAC system is currently running, as reported by the thermostat. - */ - public bool|null $is_fan_running, - /** - * Indicates whether the connected HVAC system is currently heating, as reported by the thermostat. - */ - public bool|null $is_heating, - /** - * Indicates whether the current thermostat settings differ from the most recent active program or schedule that Seam activated. For this condition to occur, `current_climate_setting.manual_override_allowed` must also be `true`. - */ - public bool|null $is_temporary_manual_override_active, - /** - * Metadata for a KeyNest device. - */ - public DeviceKeynestMetadata|null $keynest_metadata, - /** - * Keypad battery status. - */ - public DeviceKeypadBattery|null $keypad_battery, - /** - * Metadata for a Kisi device. - */ - public DeviceKisiMetadata|null $kisi_metadata, - /** - * Metadata for a Korelock device. - */ - public DeviceKorelockMetadata|null $korelock_metadata, - /** - * Metadata for a Kwikset device. - */ - public DeviceKwiksetMetadata|null $kwikset_metadata, - /** - * Indicates whether the lock is locked. - */ - public bool|null $locked, - /** - * Metadata for a Lockly device. - */ - public DeviceLocklyMetadata|null $lockly_metadata, - /** - * Manufacturer of the device. When a device, such as a smart lock, is connected through a smart hub, the manufacturer of the device might be different from that of the smart hub. - */ - public string|null $manufacturer, - /** - * Maximum number of active access codes that the device supports. - */ - public float|null $max_active_codes_supported, - /** - * Maximum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °C. - */ - public float|null $max_cooling_set_point_celsius, - /** - * Maximum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °F. - */ - public float|null $max_cooling_set_point_fahrenheit, - /** - * Maximum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °C. - */ - public float|null $max_heating_set_point_celsius, - /** - * Maximum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °F. - */ - public float|null $max_heating_set_point_fahrenheit, - /** - * Maximum number of periods that the thermostat can support per day. For example, if the thermostat supports 4 periods per day, this value is 4. - */ - public float|null $max_thermostat_daily_program_periods_per_day, - /** - * Maximum number of climate presets that the thermostat can support for weekly programming. - */ - public float|null $max_unique_climate_presets_per_thermostat_weekly_program, - /** - * Minimum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °C. - */ - public float|null $min_cooling_set_point_celsius, - /** - * Minimum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °F. - */ - public float|null $min_cooling_set_point_fahrenheit, - /** - * Minimum [temperature difference](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#minimum-heating-cooling-temperature-delta) in °C between the cooling and heating set points when in heat-cool (auto) mode. - */ - public float|null $min_heating_cooling_delta_celsius, - /** - * Minimum [temperature difference](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#minimum-heating-cooling-temperature-delta) in °F between the cooling and heating set points when in heat-cool (auto) mode. - */ - public float|null $min_heating_cooling_delta_fahrenheit, - /** - * Minimum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °C. - */ - public float|null $min_heating_set_point_celsius, - /** - * Minimum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °F. - */ - public float|null $min_heating_set_point_fahrenheit, - /** - * Metadata for a Minut device. - */ - public DeviceMinutMetadata|null $minut_metadata, - /** - * Device model-related properties. - */ - public DeviceModel|null $model, - /** - * Name of the device. - * - * @deprecated use device.display_name instead - */ - public string|null $name, - /** - * Metadata for a Google Nest device. - */ - public DeviceNestMetadata|null $nest_metadata, - /** - * Indicates current noise level in decibels, if the device supports noise detection. - */ - public float|null $noise_level_decibels, - /** - * Metadata for a NoiseAware device. - */ - public DeviceNoiseawareMetadata|null $noiseaware_metadata, - /** - * Metadata for a Nuki device. - */ - public DeviceNukiMetadata|null $nuki_metadata, - /** - * Indicates whether it is currently possible to use offline access codes for the device. - * - * @deprecated use device.can_program_offline_access_codes - */ - public bool|null $offline_access_codes_enabled, - /** - * Time frames that may be requested when creating an offline access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by `display_name` when they do) and satisfies that one option's rules. When `undefined`, any time frame works. - */ - public array $offline_time_frame_options, - /** - * Metadata for an Omnitec device. - */ - public DeviceOmnitecMetadata|null $omnitec_metadata, - /** - * Indicates whether the device is online. - */ - public bool|null $online, - /** - * Indicates whether it is currently possible to use online access codes for the device. - * - * @deprecated use device.can_program_online_access_codes - */ - public bool|null $online_access_codes_enabled, - /** - * Time frames that may be requested when creating an online access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by `display_name` when they do) and satisfies that one option's rules. When `undefined`, any time frame works. - */ - public array $online_time_frame_options, - /** - * Reported relative humidity, as a value between 0 and 1, inclusive. - */ - public float|null $relative_humidity, - /** - * Metadata for a Ring device. - */ - public DeviceRingMetadata|null $ring_metadata, - /** - * Metadata for a Salto KS device. - */ - public DeviceSaltoKsMetadata|null $salto_ks_metadata, - /** - * Metada for a Salto device. - * - * @deprecated Use `salto_ks_metadata ` instead. - */ - public DeviceSaltoMetadata|null $salto_metadata, - /** - * Salto Space credential service metadata for the phone. - */ - public DeviceSaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata, - /** - * Metadata for a Schlage device. - */ - public DeviceSchlageMetadata|null $schlage_metadata, - /** - * Metadata for Seam Bridge. - */ - public DeviceSeamBridgeMetadata|null $seam_bridge_metadata, - /** - * Metadata for a Sensi device. - */ - public DeviceSensiMetadata|null $sensi_metadata, - /** - * Serial number of the device. - */ - public string|null $serial_number, - /** - * Metadata for a SmartThings device. - */ - public DeviceSmartthingsMetadata|null $smartthings_metadata, - /** - * Supported code lengths for access codes. - */ - public array|null $supported_code_lengths, - /** - * @deprecated use device.properties.model.can_connect_accessory_keypad - */ - public bool|null $supports_accessory_keypad, - /** - * Indicates whether the device supports a [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes). - */ - public bool|null $supports_backup_access_code_pool, - /** - * @deprecated use offline_access_codes_enabled - */ - public bool|null $supports_offline_access_codes, - /** - * Metadata for a tado° device. - */ - public DeviceTadoMetadata|null $tado_metadata, - /** - * Metadata for a Tedee device. - */ - public DeviceTedeeMetadata|null $tedee_metadata, - /** - * Reported temperature in °C. - */ - public float|null $temperature_celsius, - /** - * Reported temperature in °F. - */ - public float|null $temperature_fahrenheit, - /** - * Current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ - public DeviceTemperatureThreshold|null $temperature_threshold, - /** - * Precision of the thermostat's period in minutes. For example, if the thermostat supports 15-minute periods, this value is 15. All values are relative to the top of the hour, so for 15 minutes, the periods would be 0, 15, 30, and 45 minutes past the hour. - */ - public float|null $thermostat_daily_program_period_precision_minutes, - /** - * Configured [daily programs](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. - */ - public array $thermostat_daily_programs, - /** - * Current [weekly program](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. - */ - public DeviceThermostatWeeklyProgram|null $thermostat_weekly_program, - /** - * Metadata for a TTLock device. - */ - public DeviceTtlockMetadata|null $ttlock_metadata, - /** - * Metadata for a 2N device. - */ - public DeviceTwoNMetadata|null $two_n_metadata, - /** - * Metadata for an Ultraloq device. - */ - public DeviceUltraloqMetadata|null $ultraloq_metadata, - /** - * Metadata for an ASSA ABLOY Visionline system. - */ - public DeviceVisionlineMetadata|null $visionline_metadata, - /** - * Metadata for a Wyze device. - */ - public DeviceWyzeMetadata|null $wyze_metadata, - ) {} -} + /** + * Metadata for a Korelock device. + */ + class KorelockMetadata + { + public static function from_json(mixed $json): KorelockMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + firmware_version: $json->firmware_version ?? null, + location_id: $json->location_id ?? null, + model_code: $json->model_code ?? null, + serial_number: $json->serial_number ?? null, + wifi_signal_strength: $json->wifi_signal_strength ?? null, + ); + } -/** - * Metadata for a Ring device. - */ -class DeviceRingMetadata -{ - public static function from_json(mixed $json): DeviceRingMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Ring device. - */ - public string|null $device_id, - /** - * Device name for a Ring device. - */ - public string|null $device_name, - ) {} -} + public function __construct( + /** + * Device ID for a Korelock device. + */ + public string|null $device_id, + /** + * Device name for a Korelock device. + */ + public string|null $device_name, + /** + * Firmware version for a Korelock device. + */ + public string|null $firmware_version, + /** + * Location ID for a Korelock device. Required for timebound access codes. + */ + public string|null $location_id, + /** + * Model code for a Korelock device. + */ + public string|null $model_code, + /** + * Serial number for a Korelock device. + */ + public string|null $serial_number, + /** + * WiFi signal strength (0-1) for a Korelock device. + */ + public float|null $wifi_signal_strength, + ) {} + } -/** - * Metadata for a Salto KS device. - */ -class DeviceSaltoKsMetadata -{ - public static function from_json(mixed $json): DeviceSaltoKsMetadata|null - { - if (!$json) { - return null; - } - return new self( - battery_level: $json->battery_level ?? null, - customer_reference: $json->customer_reference ?? null, - has_custom_pin_subscription: $json->has_custom_pin_subscription ?? - null, - lock_id: $json->lock_id ?? null, - lock_type: $json->lock_type ?? null, - locked_state: $json->locked_state ?? null, - model: $json->model ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - ); - } - - public function __construct( - /** - * Battery level for a Salto KS device. - */ - public string|null $battery_level, - /** - * Customer reference for a Salto KS device. - */ - public string|null $customer_reference, - /** - * Indicates whether the site has a Salto KS subscription that supports custom PINs. - */ - public bool|null $has_custom_pin_subscription, - /** - * Lock ID for a Salto KS device. - */ - public string|null $lock_id, - /** - * Lock type for a Salto KS device. - */ - public string|null $lock_type, - /** - * Locked state for a Salto KS device. - */ - public string|null $locked_state, - /** - * Model for a Salto KS device. - */ - public string|null $model, - /** - * Site ID for the Salto KS site to which the device belongs. - */ - public string|null $site_id, - /** - * Site name for the Salto KS site to which the device belongs. - */ - public string|null $site_name, - ) {} -} + /** + * Metadata for a Kwikset device. + */ + class KwiksetMetadata + { + public static function from_json(mixed $json): KwiksetMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + model_number: $json->model_number ?? null, + ); + } -/** - * Metada for a Salto device. - */ -class DeviceSaltoMetadata -{ - public static function from_json(mixed $json): DeviceSaltoMetadata|null - { - if (!$json) { - return null; - } - return new self( - battery_level: $json->battery_level ?? null, - customer_reference: $json->customer_reference ?? null, - lock_id: $json->lock_id ?? null, - lock_type: $json->lock_type ?? null, - locked_state: $json->locked_state ?? null, - model: $json->model ?? null, - site_id: $json->site_id ?? null, - site_name: $json->site_name ?? null, - ); - } - - public function __construct( - /** - * Battery level for a Salto device. - */ - public string|null $battery_level, - /** - * Customer reference for a Salto device. - */ - public string|null $customer_reference, - /** - * Lock ID for a Salto device. - */ - public string|null $lock_id, - /** - * Lock type for a Salto device. - */ - public string|null $lock_type, - /** - * Locked state for a Salto device. - */ - public string|null $locked_state, - /** - * Model for a Salto device. - */ - public string|null $model, - /** - * Site ID for the Salto KS site to which the device belongs. - */ - public string|null $site_id, - /** - * Site name for the Salto KS site to which the device belongs. - */ - public string|null $site_name, - ) {} -} + public function __construct( + /** + * Device ID for a Kwikset device. + */ + public string|null $device_id, + /** + * Device name for a Kwikset device. + */ + public string|null $device_name, + /** + * Model number for a Kwikset device. + */ + public string|null $model_number, + ) {} + } -/** - * Salto Space credential service metadata for the phone. - */ -class DeviceSaltoSpaceCredentialServiceMetadata -{ - public static function from_json( - mixed $json, - ): DeviceSaltoSpaceCredentialServiceMetadata|null { - if (!$json) { - return null; - } - return new self(has_active_phone: $json->has_active_phone ?? null); - } - - public function __construct( - /** - * Indicates whether the credential service has an active associated phone. - */ - public bool|null $has_active_phone, - ) {} -} + /** + * Metadata for a Lockly device. + */ + class LocklyMetadata + { + public static function from_json(mixed $json): LocklyMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + model: $json->model ?? null, + ); + } -/** - * Metadata for a Schlage device. - */ -class DeviceSchlageMetadata -{ - public static function from_json(mixed $json): DeviceSchlageMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - model: $json->model ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Schlage device. - */ - public string|null $device_id, - /** - * Device name for a Schlage device. - */ - public string|null $device_name, - /** - * Model for a Schlage device. - */ - public string|null $model, - ) {} -} + public function __construct( + /** + * Device ID for a Lockly device. + */ + public string|null $device_id, + /** + * Device name for a Lockly device. + */ + public string|null $device_name, + /** + * Model for a Lockly device. + */ + public string|null $model, + ) {} + } -/** - * Metadata for Seam Bridge. - */ -class DeviceSeamBridgeMetadata -{ - public static function from_json(mixed $json): DeviceSeamBridgeMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_num: $json->device_num ?? null, - name: $json->name ?? null, - unlock_method: $json->unlock_method ?? null, - ); - } - - public function __construct( - /** - * Device number for Seam Bridge. - */ - public float|null $device_num, - /** - * Name for Seam Bridge. - */ - public string|null $name, - /** - * Unlock method for Seam Bridge. - */ - public string|null $unlock_method, - ) {} -} + /** + * Metadata for a Minut device. + */ + class MinutMetadata + { + public static function from_json(mixed $json): MinutMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + latest_sensor_values: isset($json->latest_sensor_values) + ? MinutMetadata\LatestSensorValues::from_json( + $json->latest_sensor_values, + ) + : null, + ); + } -/** - * Metadata for a Sensi device. - */ -class DeviceSensiMetadata -{ - public static function from_json(mixed $json): DeviceSensiMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - dual_setpoints_not_supported: $json->dual_setpoints_not_supported ?? - null, - enforced_setpoint_range_celsius: $json->enforced_setpoint_range_celsius ?? - null, - product_type: $json->product_type ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Sensi device. - */ - public string|null $device_id, - /** - * Device name for a Sensi device. - */ - public string|null $device_name, - /** - * Set to true when the device does not support the /dual-setpoints API endpoint. - */ - public bool|null $dual_setpoints_not_supported, - /** - * Enforced setpoint range in Celsius for a Sensi device, derived from an OutOfRange API error. - */ - public array|null $enforced_setpoint_range_celsius, - /** - * Product type for a Sensi device. - */ - public string|null $product_type, - ) {} -} + public function __construct( + /** + * Device ID for a Minut device. + */ + public string|null $device_id, + /** + * Device name for a Minut device. + */ + public string|null $device_name, + /** + * Latest sensor values for a Minut device. + */ + public MinutMetadata\LatestSensorValues|null $latest_sensor_values, + ) {} + } -/** - * Metadata for a SmartThings device. - */ -class DeviceSmartthingsMetadata -{ - public static function from_json( - mixed $json, - ): DeviceSmartthingsMetadata|null { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - location_id: $json->location_id ?? null, - model: $json->model ?? null, - ); - } - - public function __construct( - /** - * Device ID for a SmartThings device. - */ - public string|null $device_id, - /** - * Device name for a SmartThings device. - */ - public string|null $device_name, - /** - * Location ID for a SmartThings device. - */ - public string|null $location_id, - /** - * Model for a SmartThings device. - */ - public string|null $model, - ) {} -} + /** + * Metadata for a Google Nest device. + */ + class NestMetadata + { + public static function from_json(mixed $json): NestMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_custom_name: $json->device_custom_name ?? null, + device_name: $json->device_name ?? null, + display_name: $json->display_name ?? null, + nest_device_id: $json->nest_device_id ?? null, + ); + } -/** - * Latest sound reading for a Minut device. - */ -class DeviceSound -{ - public static function from_json(mixed $json): DeviceSound|null - { - if (!$json) { - return null; - } - return new self(time: $json->time ?? null, value: $json->value ?? null); - } - - public function __construct( - /** - * Time of latest sound reading for a Minut device. - */ - public string|null $time, - /** - * Value of latest sound reading for a Minut device. - */ - public float|null $value, - ) {} -} + public function __construct( + /** + * Custom device name for a Google Nest device. The device owner sets this value. + */ + public string|null $device_custom_name, + /** + * Device name for a Google Nest device. Google sets this value. + */ + public string|null $device_name, + /** + * Display name for a Google Nest device. + */ + public string|null $display_name, + /** + * Device ID for a Google Nest device. + */ + public string|null $nest_device_id, + ) {} + } -/** - * Metadata for a tado° device. - */ -class DeviceTadoMetadata -{ - public static function from_json(mixed $json): DeviceTadoMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_type: $json->device_type ?? null, - serial_no: $json->serial_no ?? null, - ); - } - - public function __construct( - /** - * Device type for a tado° device. - */ - public string|null $device_type, - /** - * Serial number for a tado° device. - */ - public string|null $serial_no, - ) {} -} + /** + * Metadata for a NoiseAware device. + */ + class NoiseawareMetadata + { + public static function from_json(mixed $json): NoiseawareMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_model: $json->device_model ?? null, + device_name: $json->device_name ?? null, + noise_level_decibel: $json->noise_level_decibel ?? null, + noise_level_nrs: $json->noise_level_nrs ?? null, + ); + } + + public function __construct( + /** + * Device ID for a NoiseAware device. + */ + public string|null $device_id, + /** + * Device model for a NoiseAware device. + */ + public string|null $device_model, + /** + * Device name for a NoiseAware device. + */ + public string|null $device_name, + /** + * Noise level, in decibels, for a NoiseAware device. + */ + public float|null $noise_level_decibel, + /** + * Noise level, expressed as a Noise Risk Score (NRS), for a NoiseAware device. + */ + public float|null $noise_level_nrs, + ) {} + } + + /** + * Metadata for a Nuki device. + */ + class NukiMetadata + { + public static function from_json(mixed $json): NukiMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + keypad_2_paired: $json->keypad_2_paired ?? null, + keypad_battery_critical: $json->keypad_battery_critical ?? null, + keypad_paired: $json->keypad_paired ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Nuki device. + */ + public string|null $device_id, + /** + * Device name for a Nuki device. + */ + public string|null $device_name, + /** + * Indicates whether keypad 2 is paired for a Nuki device. + */ + public bool|null $keypad_2_paired, + /** + * Indicates whether the keypad battery is in a critical state for a Nuki device. + */ + public bool|null $keypad_battery_critical, + /** + * Indicates whether the keypad is paired for a Nuki device. + */ + public bool|null $keypad_paired, + ) {} + } + + /** + * Metadata for an Omnitec device. + */ + class OmnitecMetadata + { + public static function from_json(mixed $json): OmnitecMetadata|null + { + if (!$json) { + return null; + } + return new self( + has_gateway: $json->has_gateway ?? null, + lock_alias: $json->lock_alias ?? null, + lock_id: $json->lock_id ?? null, + lock_mac: $json->lock_mac ?? null, + lock_name: $json->lock_name ?? null, + time_zone: $json->time_zone ?? null, + timezone_raw_offset_ms: $json->timezone_raw_offset_ms ?? null, + ); + } + + public function __construct( + /** + * Whether the Omnitec lock has a connected gateway for remote operations. + */ + public bool|null $has_gateway, + /** + * Operator-assigned alias for an Omnitec device. + */ + public string|null $lock_alias, + /** + * Lock ID for an Omnitec device. + */ + public float|null $lock_id, + /** + * Bluetooth MAC address for an Omnitec device. + */ + public string|null $lock_mac, + /** + * Lock name for an Omnitec device. + */ + public string|null $lock_name, + /** + * IANA time zone for the Omnitec device, used to schedule time-bound access codes at the correct local time (accounting for DST). + */ + public string|null $time_zone, + /** + * Static UTC offset of the Omnitec lock in milliseconds. Does not account for DST. + */ + public float|null $timezone_raw_offset_ms, + ) {} + } + + /** + * Metadata for a Ring device. + */ + class RingMetadata + { + public static function from_json(mixed $json): RingMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Ring device. + */ + public string|null $device_id, + /** + * Device name for a Ring device. + */ + public string|null $device_name, + ) {} + } + + /** + * Metadata for a Salto KS device. + */ + class SaltoKsMetadata + { + public static function from_json(mixed $json): SaltoKsMetadata|null + { + if (!$json) { + return null; + } + return new self( + battery_level: $json->battery_level ?? null, + customer_reference: $json->customer_reference ?? null, + has_custom_pin_subscription: $json->has_custom_pin_subscription ?? + null, + lock_id: $json->lock_id ?? null, + lock_type: $json->lock_type ?? null, + locked_state: $json->locked_state ?? null, + model: $json->model ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + ); + } + + public function __construct( + /** + * Battery level for a Salto KS device. + */ + public string|null $battery_level, + /** + * Customer reference for a Salto KS device. + */ + public string|null $customer_reference, + /** + * Indicates whether the site has a Salto KS subscription that supports custom PINs. + */ + public bool|null $has_custom_pin_subscription, + /** + * Lock ID for a Salto KS device. + */ + public string|null $lock_id, + /** + * Lock type for a Salto KS device. + */ + public string|null $lock_type, + /** + * Locked state for a Salto KS device. + */ + public string|null $locked_state, + /** + * Model for a Salto KS device. + */ + public string|null $model, + /** + * Site ID for the Salto KS site to which the device belongs. + */ + public string|null $site_id, + /** + * Site name for the Salto KS site to which the device belongs. + */ + public string|null $site_name, + ) {} + } + + /** + * Metada for a Salto device. + * + * @deprecated Use `salto_ks_metadata ` instead. + */ + class SaltoMetadata + { + public static function from_json(mixed $json): SaltoMetadata|null + { + if (!$json) { + return null; + } + return new self( + battery_level: $json->battery_level ?? null, + customer_reference: $json->customer_reference ?? null, + lock_id: $json->lock_id ?? null, + lock_type: $json->lock_type ?? null, + locked_state: $json->locked_state ?? null, + model: $json->model ?? null, + site_id: $json->site_id ?? null, + site_name: $json->site_name ?? null, + ); + } + + public function __construct( + /** + * Battery level for a Salto device. + */ + public string|null $battery_level, + /** + * Customer reference for a Salto device. + */ + public string|null $customer_reference, + /** + * Lock ID for a Salto device. + */ + public string|null $lock_id, + /** + * Lock type for a Salto device. + */ + public string|null $lock_type, + /** + * Locked state for a Salto device. + */ + public string|null $locked_state, + /** + * Model for a Salto device. + */ + public string|null $model, + /** + * Site ID for the Salto KS site to which the device belongs. + */ + public string|null $site_id, + /** + * Site name for the Salto KS site to which the device belongs. + */ + public string|null $site_name, + ) {} + } + + /** + * Metadata for a Schlage device. + */ + class SchlageMetadata + { + public static function from_json(mixed $json): SchlageMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + model: $json->model ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Schlage device. + */ + public string|null $device_id, + /** + * Device name for a Schlage device. + */ + public string|null $device_name, + /** + * Model for a Schlage device. + */ + public string|null $model, + ) {} + } -/** - * Metadata for a Tedee device. - */ -class DeviceTedeeMetadata -{ - public static function from_json(mixed $json): DeviceTedeeMetadata|null - { - if (!$json) { - return null; - } - return new self( - bridge_id: $json->bridge_id ?? null, - bridge_name: $json->bridge_name ?? null, - device_id: $json->device_id ?? null, - device_model: $json->device_model ?? null, - device_name: $json->device_name ?? null, - keypad_id: $json->keypad_id ?? null, - serial_number: $json->serial_number ?? null, - ); - } - - public function __construct( - /** - * Bridge ID for a Tedee device. - */ - public float|null $bridge_id, - /** - * Bridge name for a Tedee device. - */ - public string|null $bridge_name, - /** - * Device ID for a Tedee device. - */ - public float|null $device_id, - /** - * Device model for a Tedee device. - */ - public string|null $device_model, - /** - * Device name for a Tedee device. - */ - public string|null $device_name, - /** - * Keypad ID for a Tedee device. - */ - public float|null $keypad_id, - /** - * Serial number for a Tedee device. - */ - public string|null $serial_number, - ) {} + /** + * Metadata for Seam Bridge. + */ + class SeamBridgeMetadata + { + public static function from_json(mixed $json): SeamBridgeMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_num: $json->device_num ?? null, + name: $json->name ?? null, + unlock_method: $json->unlock_method ?? null, + ); + } + + public function __construct( + /** + * Device number for Seam Bridge. + */ + public float|null $device_num, + /** + * Name for Seam Bridge. + */ + public string|null $name, + /** + * Unlock method for Seam Bridge. + */ + public string|null $unlock_method, + ) {} + } + + /** + * Metadata for a Sensi device. + */ + class SensiMetadata + { + public static function from_json(mixed $json): SensiMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + dual_setpoints_not_supported: $json->dual_setpoints_not_supported ?? + null, + enforced_setpoint_range_celsius: $json->enforced_setpoint_range_celsius ?? + null, + product_type: $json->product_type ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Sensi device. + */ + public string|null $device_id, + /** + * Device name for a Sensi device. + */ + public string|null $device_name, + /** + * Set to true when the device does not support the /dual-setpoints API endpoint. + */ + public bool|null $dual_setpoints_not_supported, + /** + * Enforced setpoint range in Celsius for a Sensi device, derived from an OutOfRange API error. + */ + public array|null $enforced_setpoint_range_celsius, + /** + * Product type for a Sensi device. + */ + public string|null $product_type, + ) {} + } + + /** + * Metadata for a SmartThings device. + */ + class SmartthingsMetadata + { + public static function from_json(mixed $json): SmartthingsMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + location_id: $json->location_id ?? null, + model: $json->model ?? null, + ); + } + + public function __construct( + /** + * Device ID for a SmartThings device. + */ + public string|null $device_id, + /** + * Device name for a SmartThings device. + */ + public string|null $device_name, + /** + * Location ID for a SmartThings device. + */ + public string|null $location_id, + /** + * Model for a SmartThings device. + */ + public string|null $model, + ) {} + } + + /** + * Metadata for a tado° device. + */ + class TadoMetadata + { + public static function from_json(mixed $json): TadoMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_type: $json->device_type ?? null, + serial_no: $json->serial_no ?? null, + ); + } + + public function __construct( + /** + * Device type for a tado° device. + */ + public string|null $device_type, + /** + * Serial number for a tado° device. + */ + public string|null $serial_no, + ) {} + } + + /** + * Metadata for a Tedee device. + */ + class TedeeMetadata + { + public static function from_json(mixed $json): TedeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + bridge_id: $json->bridge_id ?? null, + bridge_name: $json->bridge_name ?? null, + device_id: $json->device_id ?? null, + device_model: $json->device_model ?? null, + device_name: $json->device_name ?? null, + keypad_id: $json->keypad_id ?? null, + serial_number: $json->serial_number ?? null, + ); + } + + public function __construct( + /** + * Bridge ID for a Tedee device. + */ + public float|null $bridge_id, + /** + * Bridge name for a Tedee device. + */ + public string|null $bridge_name, + /** + * Device ID for a Tedee device. + */ + public float|null $device_id, + /** + * Device model for a Tedee device. + */ + public string|null $device_model, + /** + * Device name for a Tedee device. + */ + public string|null $device_name, + /** + * Keypad ID for a Tedee device. + */ + public float|null $keypad_id, + /** + * Serial number for a Tedee device. + */ + public string|null $serial_number, + ) {} + } + + /** + * Metadata for a TTLock device. + */ + class TtlockMetadata + { + public static function from_json(mixed $json): TtlockMetadata|null + { + if (!$json) { + return null; + } + return new self( + feature_value: $json->feature_value ?? null, + features: isset($json->features) + ? TtlockMetadata\Features::from_json($json->features) + : null, + has_gateway: $json->has_gateway ?? 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( + fn($w) => TtlockMetadata\WirelessKeypads::from_json($w), + $json->wireless_keypads ?? [], + ), + ); + } + + public function __construct( + /** + * Feature value for a TTLock device. + */ + public string|null $feature_value, + /** + * Features for a TTLock device. + */ + public TtlockMetadata\Features|null $features, + /** + * Indicates whether a TTLock device has a gateway. + */ + public bool|null $has_gateway, + /** + * Lock alias for a TTLock device. + */ + public string|null $lock_alias, + /** + * Lock ID for a TTLock device. + */ + public float|null $lock_id, + /** + * Lock-side timezone offset in milliseconds east of UTC, as configured in the TTLock app. Source of truth for the lock's wall-clock interpretation of access code start/end times — a misconfigured value here is the typical cause of customer "codes offset by N hours" reports. Diagnostic only; Seam does not convert times based on this value. + */ + public float|null $timezone_raw_offset_ms, + /** + * Wireless keypads for a TTLock device. + */ + public array $wireless_keypads, + ) {} + } + + /** + * Metadata for a 2N device. + */ + class TwoNMetadata + { + public static function from_json(mixed $json): TwoNMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + ); + } + + public function __construct( + /** + * Device ID for a 2N device. + */ + public float|null $device_id, + /** + * Device name for a 2N device. + */ + public string|null $device_name, + ) {} + } + + /** + * Metadata for an Ultraloq device. + */ + class UltraloqMetadata + { + public static function from_json(mixed $json): UltraloqMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + device_type: $json->device_type ?? null, + time_zone: $json->time_zone ?? null, + ); + } + + public function __construct( + /** + * Device ID for an Ultraloq device. + */ + public string|null $device_id, + /** + * Device name for an Ultraloq device. + */ + public string|null $device_name, + /** + * Device type for an Ultraloq device. + */ + public string|null $device_type, + /** + * IANA timezone for the Ultraloq device. + */ + public string|null $time_zone, + ) {} + } + + /** + * Metadata for an ASSA ABLOY Visionline system. + */ + class VisionlineMetadata + { + public static function from_json(mixed $json): VisionlineMetadata|null + { + if (!$json) { + return null; + } + return new self(encoder_id: $json->encoder_id ?? null); + } + + public function __construct( + /** + * Encoder ID for an ASSA ABLOY Visionline system. + */ + public string|null $encoder_id, + ) {} + } + + /** + * Metadata for a Wyze device. + */ + class WyzeMetadata + { + public static function from_json(mixed $json): WyzeMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_info_model: $json->device_info_model ?? null, + device_name: $json->device_name ?? null, + keypad_uuid: $json->keypad_uuid ?? null, + locker_status_hardlock: $json->locker_status_hardlock ?? null, + product_model: $json->product_model ?? null, + product_name: $json->product_name ?? null, + product_type: $json->product_type ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Wyze device. + */ + public string|null $device_id, + /** + * Device information model for a Wyze device. + */ + public string|null $device_info_model, + /** + * Device name for a Wyze device. + */ + public string|null $device_name, + /** + * Keypad UUID for a Wyze device. + */ + public string|null $keypad_uuid, + /** + * Locker status (hardlock) for a Wyze device. + */ + public float|null $locker_status_hardlock, + /** + * Product model for a Wyze device. + */ + public string|null $product_model, + /** + * Product name for a Wyze device. + */ + public string|null $product_name, + /** + * Product type for a Wyze device. + */ + public string|null $product_type, + ) {} + } + + /** + * Constraints on access codes for the device. Seam represents each constraint as an object with a `constraint_type` property. Depending on the constraint type, there may also be additional properties. Note that some constraints are manufacturer- or device-specific. + */ + class CodeConstraints + { + public static function from_json(mixed $json): CodeConstraints|null + { + if (!$json) { + return null; + } + return new self( + constraint_type: $json->constraint_type ?? null, + max_length: $json->max_length ?? null, + min_length: $json->min_length ?? null, + ); + } + + public function __construct( + public string|null $constraint_type, + /** + * Maximum name length constraint for access codes. + */ + public float|null $max_length, + /** + * Minimum name length constraint for access codes. + */ + public float|null $min_length, + ) {} + } + + /** + * Keypad battery status. + */ + class KeypadBattery + { + public static function from_json(mixed $json): KeypadBattery|null + { + if (!$json) { + return null; + } + return new self(level: $json->level ?? null); + } + + public function __construct( + /** + * Keypad battery charge level. + */ + public float|null $level, + ) {} + } + + /** + * Time frames that may be requested when creating an offline access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by `display_name` when they do) and satisfies that one option's rules. When `undefined`, any time frame works. + */ + class OfflineTimeFrameOptions + { + public static function from_json( + mixed $json, + ): OfflineTimeFrameOptions|null { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + end_date_recurrence_rule: $json->end_date_recurrence_rule ?? + null, + matching_start_end_time: $json->matching_start_end_time ?? null, + max_duration: $json->max_duration ?? null, + min_duration: $json->min_duration ?? null, + start_date_recurrence_rule: $json->start_date_recurrence_rule ?? + null, + time_pairs: array_map( + fn($t) => OfflineTimeFrameOptions\TimePairs::from_json($t), + $json->time_pairs ?? [], + ), + time_zone: $json->time_zone ?? null, + ); + } + + public function __construct( + /** + * Label for this option. For a single-option device, the product name (for example, `algoPIN` or `SmartPIN`); for a multi-option device, a label that distinguishes it (for example, `Hourly` or `Fixed start times`). + */ + public string|null $display_name, + /** + * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. + */ + public string|null $end_date_recurrence_rule, + /** + * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. + */ + public bool|null $matching_start_end_time, + /** + * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. + */ + public string|null $max_duration, + /** + * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. + */ + public string|null $min_duration, + /** + * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. + */ + public string|null $start_date_recurrence_rule, + /** + * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + */ + public array $time_pairs, + /** + * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. + */ + public string|null $time_zone, + ) {} + } + + /** + * Time frames that may be requested when creating an online access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by `display_name` when they do) and satisfies that one option's rules. When `undefined`, any time frame works. + */ + class OnlineTimeFrameOptions + { + public static function from_json( + mixed $json, + ): OnlineTimeFrameOptions|null { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + end_date_recurrence_rule: $json->end_date_recurrence_rule ?? + null, + matching_start_end_time: $json->matching_start_end_time ?? null, + max_duration: $json->max_duration ?? null, + min_duration: $json->min_duration ?? null, + start_date_recurrence_rule: $json->start_date_recurrence_rule ?? + null, + time_pairs: array_map( + fn($t) => OnlineTimeFrameOptions\TimePairs::from_json($t), + $json->time_pairs ?? [], + ), + time_zone: $json->time_zone ?? null, + ); + } + + public function __construct( + /** + * Label for this option. For a single-option device, the product name (for example, `algoPIN` or `SmartPIN`); for a multi-option device, a label that distinguishes it (for example, `Hourly` or `Fixed start times`). + */ + public string|null $display_name, + /** + * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. + */ + public string|null $end_date_recurrence_rule, + /** + * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. + */ + public bool|null $matching_start_end_time, + /** + * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. + */ + public string|null $max_duration, + /** + * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. + */ + public string|null $min_duration, + /** + * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. + */ + public string|null $start_date_recurrence_rule, + /** + * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + */ + public array $time_pairs, + /** + * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. + */ + public string|null $time_zone, + ) {} + } + + /** + * Active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + * + * @deprecated Use `active_thermostat_schedule_id` with `/thermostats/schedules/get` instead. + */ + class ActiveThermostatSchedule + { + public static function from_json( + mixed $json, + ): ActiveThermostatSchedule|null { + if (!$json) { + return null; + } + return new self( + climate_preset_key: $json->climate_preset_key ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => ActiveThermostatSchedule\Errors::from_json($e), + $json->errors ?? [], + ), + is_override_allowed: $json->is_override_allowed ?? null, + max_override_period_minutes: $json->max_override_period_minutes ?? + null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + thermostat_schedule_id: $json->thermostat_schedule_id ?? null, + workspace_id: $json->workspace_id ?? null, + ); + } + + public function __construct( + /** + * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to use for the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $climate_preset_key, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) was created. + */ + public string|null $created_at, + /** + * ID of the desired [thermostat](https://docs.seam.co/capability-guides/thermostats) device. + */ + public string|null $device_id, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $ends_at, + /** + * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public array $errors, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. + */ + public bool|null $is_override_allowed, + /** + * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + */ + public int|null $max_override_period_minutes, + /** + * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $name, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + /** + * ID of the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $thermostat_schedule_id, + /** + * ID of the workspace that contains the thermostat schedule. + */ + public string|null $workspace_id, + ) {} + } + + /** + * Available [climate presets](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) for the thermostat. + */ + class AvailableClimatePresets + { + public static function from_json( + mixed $json, + ): AvailableClimatePresets|null { + if (!$json) { + return null; + } + return new self( + can_delete: $json->can_delete ?? null, + can_edit: $json->can_edit ?? null, + can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? + null, + climate_preset_key: $json->climate_preset_key ?? null, + climate_preset_mode: $json->climate_preset_mode ?? null, + cooling_set_point_celsius: $json->cooling_set_point_celsius ?? + null, + cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? + null, + display_name: $json->display_name ?? null, + ecobee_metadata: isset($json->ecobee_metadata) + ? AvailableClimatePresets\EcobeeMetadata::from_json( + $json->ecobee_metadata, + ) + : null, + fan_mode_setting: $json->fan_mode_setting ?? null, + heating_set_point_celsius: $json->heating_set_point_celsius ?? + null, + heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? + null, + hvac_mode_setting: $json->hvac_mode_setting ?? null, + manual_override_allowed: $json->manual_override_allowed ?? null, + name: $json->name ?? null, + ); + } + + public function __construct( + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. + */ + public bool|null $can_delete, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. + */ + public bool|null $can_edit, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. + */ + public bool|null $can_use_with_thermostat_daily_programs, + /** + * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $climate_preset_key, + /** + * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. + */ + public string|null $climate_preset_mode, + /** + * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_celsius, + /** + * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_fahrenheit, + /** + * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $display_name, + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + public AvailableClimatePresets\EcobeeMetadata|null $ecobee_metadata, + /** + * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + */ + public string|null $fan_mode_setting, + /** + * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_celsius, + /** + * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_fahrenheit, + /** + * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + */ + public string|null $hvac_mode_setting, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * + * @deprecated Use 'thermostat_schedule.is_override_allowed' + */ + public bool|null $manual_override_allowed, + /** + * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $name, + ) {} + } + + /** + * Current climate setting. + */ + class CurrentClimateSetting + { + public static function from_json( + mixed $json, + ): CurrentClimateSetting|null { + if (!$json) { + return null; + } + return new self( + can_delete: $json->can_delete ?? null, + can_edit: $json->can_edit ?? null, + can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? + null, + climate_preset_key: $json->climate_preset_key ?? null, + climate_preset_mode: $json->climate_preset_mode ?? null, + cooling_set_point_celsius: $json->cooling_set_point_celsius ?? + null, + cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? + null, + display_name: $json->display_name ?? null, + ecobee_metadata: isset($json->ecobee_metadata) + ? CurrentClimateSetting\EcobeeMetadata::from_json( + $json->ecobee_metadata, + ) + : null, + fan_mode_setting: $json->fan_mode_setting ?? null, + heating_set_point_celsius: $json->heating_set_point_celsius ?? + null, + heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? + null, + hvac_mode_setting: $json->hvac_mode_setting ?? null, + manual_override_allowed: $json->manual_override_allowed ?? null, + name: $json->name ?? null, + ); + } + + public function __construct( + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. + */ + public bool|null $can_delete, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. + */ + public bool|null $can_edit, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. + */ + public bool|null $can_use_with_thermostat_daily_programs, + /** + * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $climate_preset_key, + /** + * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. + */ + public string|null $climate_preset_mode, + /** + * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_celsius, + /** + * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_fahrenheit, + /** + * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $display_name, + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + public CurrentClimateSetting\EcobeeMetadata|null $ecobee_metadata, + /** + * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + */ + public string|null $fan_mode_setting, + /** + * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_celsius, + /** + * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_fahrenheit, + /** + * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + */ + public string|null $hvac_mode_setting, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * + * @deprecated Use 'thermostat_schedule.is_override_allowed' + */ + public bool|null $manual_override_allowed, + /** + * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $name, + ) {} + } + + /** + * @deprecated use fallback_climate_preset_key to specify a fallback climate preset instead. + */ + class DefaultClimateSetting + { + public static function from_json( + mixed $json, + ): DefaultClimateSetting|null { + if (!$json) { + return null; + } + return new self( + can_delete: $json->can_delete ?? null, + can_edit: $json->can_edit ?? null, + can_use_with_thermostat_daily_programs: $json->can_use_with_thermostat_daily_programs ?? + null, + climate_preset_key: $json->climate_preset_key ?? null, + climate_preset_mode: $json->climate_preset_mode ?? null, + cooling_set_point_celsius: $json->cooling_set_point_celsius ?? + null, + cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? + null, + display_name: $json->display_name ?? null, + ecobee_metadata: isset($json->ecobee_metadata) + ? DefaultClimateSetting\EcobeeMetadata::from_json( + $json->ecobee_metadata, + ) + : null, + fan_mode_setting: $json->fan_mode_setting ?? null, + heating_set_point_celsius: $json->heating_set_point_celsius ?? + null, + heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? + null, + hvac_mode_setting: $json->hvac_mode_setting ?? null, + manual_override_allowed: $json->manual_override_allowed ?? null, + name: $json->name ?? null, + ); + } + + public function __construct( + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. + */ + public bool|null $can_delete, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. + */ + public bool|null $can_edit, + /** + * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. + */ + public bool|null $can_use_with_thermostat_daily_programs, + /** + * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $climate_preset_key, + /** + * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. + */ + public string|null $climate_preset_mode, + /** + * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_celsius, + /** + * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_fahrenheit, + /** + * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $display_name, + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + public DefaultClimateSetting\EcobeeMetadata|null $ecobee_metadata, + /** + * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + */ + public string|null $fan_mode_setting, + /** + * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_celsius, + /** + * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_fahrenheit, + /** + * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + */ + public string|null $hvac_mode_setting, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + * + * @deprecated Use 'thermostat_schedule.is_override_allowed' + */ + public bool|null $manual_override_allowed, + /** + * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). + */ + public string|null $name, + ) {} + } + + /** + * Current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + class TemperatureThreshold + { + public static function from_json(mixed $json): TemperatureThreshold|null + { + if (!$json) { + return null; + } + return new self( + lower_limit_celsius: $json->lower_limit_celsius ?? null, + lower_limit_fahrenheit: $json->lower_limit_fahrenheit ?? null, + upper_limit_celsius: $json->upper_limit_celsius ?? null, + upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, + ); + } + + public function __construct( + /** + * Lower limit in °C within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public float|null $lower_limit_celsius, + /** + * Lower limit in °F within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public float|null $lower_limit_fahrenheit, + /** + * Upper limit in °C within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public float|null $upper_limit_celsius, + /** + * Upper limit in °F within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. + */ + public float|null $upper_limit_fahrenheit, + ) {} + } + + /** + * Configured [daily programs](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. + */ + class ThermostatDailyPrograms + { + public static function from_json( + mixed $json, + ): ThermostatDailyPrograms|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + name: $json->name ?? null, + periods: array_map( + fn($p) => ThermostatDailyPrograms\Periods::from_json($p), + $json->periods ?? [], + ), + thermostat_daily_program_id: $json->thermostat_daily_program_id ?? + null, + workspace_id: $json->workspace_id ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the thermostat daily program was created. + */ + public string|null $created_at, + /** + * ID of the thermostat device on which the thermostat daily program is configured. + */ + public string|null $device_id, + /** + * User-friendly name to identify the thermostat daily program. + */ + public string|null $name, + /** + * Array of thermostat daily program periods. + */ + public array $periods, + /** + * ID of the thermostat daily program. + */ + public string|null $thermostat_daily_program_id, + /** + * ID of the workspace that contains the thermostat daily program. + */ + public string|null $workspace_id, + ) {} + } + + /** + * Current [weekly program](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. + */ + class ThermostatWeeklyProgram + { + public static function from_json( + mixed $json, + ): ThermostatWeeklyProgram|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + friday_program_id: $json->friday_program_id ?? null, + monday_program_id: $json->monday_program_id ?? null, + saturday_program_id: $json->saturday_program_id ?? null, + sunday_program_id: $json->sunday_program_id ?? null, + thursday_program_id: $json->thursday_program_id ?? null, + tuesday_program_id: $json->tuesday_program_id ?? null, + wednesday_program_id: $json->wednesday_program_id ?? null, + ); + } + + public function __construct( + /** + * Date and time at which the thermostat weekly program was created. + */ + public string|null $created_at, + /** + * ID of the thermostat daily program to run on Fridays. + */ + public string|null $friday_program_id, + /** + * ID of the thermostat daily program to run on Mondays. + */ + public string|null $monday_program_id, + /** + * ID of the thermostat daily program to run on Saturdays. + */ + public string|null $saturday_program_id, + /** + * ID of the thermostat daily program to run on Sundays. + */ + public string|null $sunday_program_id, + /** + * ID of the thermostat daily program to run on Thursdays. + */ + public string|null $thursday_program_id, + /** + * ID of the thermostat daily program to run on Tuesdays. + */ + public string|null $tuesday_program_id, + /** + * ID of the thermostat daily program to run on Wednesdays. + */ + public string|null $wednesday_program_id, + ) {} + } } -/** - * Latest temperature reading for a Minut device. - */ -class DeviceTemperature -{ - public static function from_json(mixed $json): DeviceTemperature|null - { - if (!$json) { - return null; - } - return new self(time: $json->time ?? null, value: $json->value ?? null); - } - - public function __construct( - /** - * Time of latest temperature reading for a Minut device. - */ - public string|null $time, - /** - * Value of latest temperature reading for a Minut device. - */ - public float|null $value, - ) {} +namespace Seam\Resources\Device\Properties\AccessoryKeypad { + /** + * Keypad battery properties. + */ + class Battery + { + public static function from_json(mixed $json): Battery|null + { + if (!$json) { + return null; + } + return new self(level: $json->level ?? null); + } + + public function __construct(public float|null $level) {} + } } -/** - * Current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ -class DeviceTemperatureThreshold -{ - public static function from_json( - mixed $json, - ): DeviceTemperatureThreshold|null { - if (!$json) { - return null; - } - return new self( - lower_limit_celsius: $json->lower_limit_celsius ?? null, - lower_limit_fahrenheit: $json->lower_limit_fahrenheit ?? null, - upper_limit_celsius: $json->upper_limit_celsius ?? null, - upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, - ); - } - - public function __construct( - /** - * Lower limit in °C within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ - public float|null $lower_limit_celsius, - /** - * Lower limit in °F within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ - public float|null $lower_limit_fahrenheit, - /** - * Upper limit in °C within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ - public float|null $upper_limit_celsius, - /** - * Upper limit in °F within the current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. - */ - public float|null $upper_limit_fahrenheit, - ) {} +namespace Seam\Resources\Device\Properties\AssaAbloyCredentialServiceMetadata { + /** + * Endpoints associated with the phone. + */ + class Endpoints + { + public static function from_json(mixed $json): Endpoints|null + { + if (!$json) { + return null; + } + return new self( + endpoint_id: $json->endpoint_id ?? null, + is_active: $json->is_active ?? null, + ); + } + + public function __construct( + /** + * ID of the associated endpoint. + */ + public string|null $endpoint_id, + /** + * Indicated whether the endpoint is active. + */ + public bool|null $is_active, + ) {} + } } -/** - * Configured [daily programs](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. - */ -class DeviceThermostatDailyPrograms -{ - public static function from_json( - mixed $json, - ): DeviceThermostatDailyPrograms|null { - if (!$json) { - return null; - } - return new self( - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - name: $json->name ?? null, - periods: array_map( - fn($p) => DevicePeriods::from_json($p), - $json->periods ?? [], - ), - thermostat_daily_program_id: $json->thermostat_daily_program_id ?? - null, - workspace_id: $json->workspace_id ?? null, - ); - } - - public function __construct( - /** - * Date and time at which the thermostat daily program was created. - */ - public string|null $created_at, - /** - * ID of the thermostat device on which the thermostat daily program is configured. - */ - public string|null $device_id, - /** - * User-friendly name to identify the thermostat daily program. - */ - public string|null $name, - /** - * Array of thermostat daily program periods. - */ - public array $periods, - /** - * ID of the thermostat daily program. - */ - public string|null $thermostat_daily_program_id, - /** - * ID of the workspace that contains the thermostat daily program. - */ - public string|null $workspace_id, - ) {} +namespace Seam\Resources\Device\Properties\DormakabaOracodeMetadata { + /** + * Predefined time slots for a dormakaba Oracode device. + */ + class PredefinedTimeSlots + { + public static function from_json(mixed $json): PredefinedTimeSlots|null + { + if (!$json) { + return null; + } + return new self( + check_in_time: $json->check_in_time ?? null, + check_out_time: $json->check_out_time ?? null, + dormakaba_oracode_user_level_id: $json->dormakaba_oracode_user_level_id ?? + null, + dormakaba_oracode_user_level_prefix: $json->dormakaba_oracode_user_level_prefix ?? + null, + is_24_hour: $json->is_24_hour ?? null, + is_biweekly_mode: $json->is_biweekly_mode ?? null, + is_master: $json->is_master ?? null, + is_one_shot: $json->is_one_shot ?? null, + name: $json->name ?? null, + prefix: $json->prefix ?? null, + ); + } + + public function __construct( + /** + * Check in time for a time slot for a dormakaba Oracode device. + */ + public string|null $check_in_time, + /** + * Checkout time for a time slot for a dormakaba Oracode device. + */ + public string|null $check_out_time, + /** + * ID of a user level for a dormakaba Oracode device. + */ + public string|null $dormakaba_oracode_user_level_id, + /** + * Prefix for a user level for a dormakaba Oracode device. + */ + public float|null $dormakaba_oracode_user_level_prefix, + /** + * Indicates whether a time slot for a dormakaba Oracode device is a 24-hour time slot. + */ + public bool|null $is_24_hour, + /** + * Indicates whether a time slot for a dormakaba Oracode device is in biweekly mode. + */ + public bool|null $is_biweekly_mode, + /** + * Indicates whether a time slot for a dormakaba Oracode device is a master time slot. + */ + public bool|null $is_master, + /** + * Indicates whether a time slot for a dormakaba Oracode device is a one-shot time slot. + */ + public bool|null $is_one_shot, + /** + * Name of a time slot for a dormakaba Oracode device. + */ + public string|null $name, + /** + * Prefix for a time slot for a dormakaba Oracode device. + */ + public float|null $prefix, + ) {} + } } -/** - * Current [weekly program](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. - */ -class DeviceThermostatWeeklyProgram -{ - public static function from_json( - mixed $json, - ): DeviceThermostatWeeklyProgram|null { - if (!$json) { - return null; - } - return new self( - created_at: $json->created_at ?? null, - friday_program_id: $json->friday_program_id ?? null, - monday_program_id: $json->monday_program_id ?? null, - saturday_program_id: $json->saturday_program_id ?? null, - sunday_program_id: $json->sunday_program_id ?? null, - thursday_program_id: $json->thursday_program_id ?? null, - tuesday_program_id: $json->tuesday_program_id ?? null, - wednesday_program_id: $json->wednesday_program_id ?? null, - ); - } - - public function __construct( - /** - * Date and time at which the thermostat weekly program was created. - */ - public string|null $created_at, - /** - * ID of the thermostat daily program to run on Fridays. - */ - public string|null $friday_program_id, - /** - * ID of the thermostat daily program to run on Mondays. - */ - public string|null $monday_program_id, - /** - * ID of the thermostat daily program to run on Saturdays. - */ - public string|null $saturday_program_id, - /** - * ID of the thermostat daily program to run on Sundays. - */ - public string|null $sunday_program_id, - /** - * ID of the thermostat daily program to run on Thursdays. - */ - public string|null $thursday_program_id, - /** - * ID of the thermostat daily program to run on Tuesdays. - */ - public string|null $tuesday_program_id, - /** - * ID of the thermostat daily program to run on Wednesdays. - */ - public string|null $wednesday_program_id, - ) {} +namespace Seam\Resources\Device\Properties\MinutMetadata { + /** + * Latest sensor values for a Minut device. + */ + class LatestSensorValues + { + public static function from_json(mixed $json): LatestSensorValues|null + { + if (!$json) { + return null; + } + return new self( + accelerometer_z: isset($json->accelerometer_z) + ? LatestSensorValues\AccelerometerZ::from_json( + $json->accelerometer_z, + ) + : null, + humidity: isset($json->humidity) + ? LatestSensorValues\Humidity::from_json($json->humidity) + : null, + pressure: isset($json->pressure) + ? LatestSensorValues\Pressure::from_json($json->pressure) + : null, + sound: isset($json->sound) + ? LatestSensorValues\Sound::from_json($json->sound) + : null, + temperature: isset($json->temperature) + ? LatestSensorValues\Temperature::from_json( + $json->temperature, + ) + : null, + ); + } + + public function __construct( + /** + * Latest accelerometer Z-axis reading for a Minut device. + */ + public LatestSensorValues\AccelerometerZ|null $accelerometer_z, + /** + * Latest humidity reading for a Minut device. + */ + public LatestSensorValues\Humidity|null $humidity, + /** + * Latest pressure reading for a Minut device. + */ + public LatestSensorValues\Pressure|null $pressure, + /** + * Latest sound reading for a Minut device. + */ + public LatestSensorValues\Sound|null $sound, + /** + * Latest temperature reading for a Minut device. + */ + public LatestSensorValues\Temperature|null $temperature, + ) {} + } } -/** - * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. - */ -class DeviceTimePairs -{ - public static function from_json(mixed $json): DeviceTimePairs|null - { - if (!$json) { - return null; - } - return new self( - display_name: $json->display_name ?? null, - end_time: $json->end_time ?? null, - start_time: $json->start_time ?? null, - ); - } - - public function __construct( - /** - * Label for the start/end time pairing. - */ - public string|null $display_name, - /** - * End time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. An `end_time` earlier on the clock than `start_time` means the end falls on a later date. - */ - public string|null $end_time, - /** - * Start time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. - */ - public string|null $start_time, - ) {} +namespace Seam\Resources\Device\Properties\MinutMetadata\LatestSensorValues { + /** + * Latest accelerometer Z-axis reading for a Minut device. + */ + class AccelerometerZ + { + public static function from_json(mixed $json): AccelerometerZ|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest accelerometer Z-axis reading for a Minut device. + */ + public string|null $time, + /** + * Value of latest accelerometer Z-axis reading for a Minut device. + */ + public float|null $value, + ) {} + } + + /** + * Latest humidity reading for a Minut device. + */ + class Humidity + { + public static function from_json(mixed $json): Humidity|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest humidity reading for a Minut device. + */ + public string|null $time, + /** + * Value of latest humidity reading for a Minut device. + */ + public float|null $value, + ) {} + } + + /** + * Latest pressure reading for a Minut device. + */ + class Pressure + { + public static function from_json(mixed $json): Pressure|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest pressure reading for a Minut device. + */ + public string|null $time, + /** + * Value of latest pressure reading for a Minut device. + */ + public float|null $value, + ) {} + } + + /** + * Latest sound reading for a Minut device. + */ + class Sound + { + public static function from_json(mixed $json): Sound|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest sound reading for a Minut device. + */ + public string|null $time, + /** + * Value of latest sound reading for a Minut device. + */ + public float|null $value, + ) {} + } + + /** + * Latest temperature reading for a Minut device. + */ + class Temperature + { + public static function from_json(mixed $json): Temperature|null + { + if (!$json) { + return null; + } + return new self( + time: $json->time ?? null, + value: $json->value ?? null, + ); + } + + public function __construct( + /** + * Time of latest temperature reading for a Minut device. + */ + public string|null $time, + /** + * Value of latest temperature reading for a Minut device. + */ + public float|null $value, + ) {} + } } -/** - * Metadata for a TTLock device. - */ -class DeviceTtlockMetadata -{ - public static function from_json(mixed $json): DeviceTtlockMetadata|null - { - if (!$json) { - return null; - } - return new self( - feature_value: $json->feature_value ?? null, - features: isset($json->features) - ? DeviceFeatures::from_json($json->features) - : null, - has_gateway: $json->has_gateway ?? 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( - fn($w) => DeviceWirelessKeypads::from_json($w), - $json->wireless_keypads ?? [], - ), - ); - } - - public function __construct( - /** - * Feature value for a TTLock device. - */ - public string|null $feature_value, - /** - * Features for a TTLock device. - */ - public DeviceFeatures|null $features, - /** - * Indicates whether a TTLock device has a gateway. - */ - public bool|null $has_gateway, - /** - * Lock alias for a TTLock device. - */ - public string|null $lock_alias, - /** - * Lock ID for a TTLock device. - */ - public float|null $lock_id, - /** - * Lock-side timezone offset in milliseconds east of UTC, as configured in the TTLock app. Source of truth for the lock's wall-clock interpretation of access code start/end times — a misconfigured value here is the typical cause of customer "codes offset by N hours" reports. Diagnostic only; Seam does not convert times based on this value. - */ - public float|null $timezone_raw_offset_ms, - /** - * Wireless keypads for a TTLock device. - */ - public array $wireless_keypads, - ) {} +namespace Seam\Resources\Device\Properties\TtlockMetadata { + /** + * Features for a TTLock device. + */ + class Features + { + public static function from_json(mixed $json): Features|null + { + if (!$json) { + return null; + } + return new self( + auto_lock_time_config: $json->auto_lock_time_config ?? null, + incomplete_keyboard_passcode: $json->incomplete_keyboard_passcode ?? + null, + lock_command: $json->lock_command ?? null, + passcode: $json->passcode ?? null, + passcode_management: $json->passcode_management ?? null, + unlock_via_gateway: $json->unlock_via_gateway ?? null, + wifi: $json->wifi ?? null, + ); + } + + public function __construct( + /** + * Indicates whether a TTLock device supports auto-lock time configuration. + */ + public bool|null $auto_lock_time_config, + /** + * Indicates whether a TTLock device supports an incomplete keyboard passcode. + */ + public bool|null $incomplete_keyboard_passcode, + /** + * Indicates whether a TTLock device supports the lock command. + */ + public bool|null $lock_command, + /** + * Indicates whether a TTLock device supports a passcode. + */ + public bool|null $passcode, + /** + * Indicates whether a TTLock device supports passcode management. + */ + public bool|null $passcode_management, + /** + * Indicates whether a TTLock device supports unlock via gateway. + */ + public bool|null $unlock_via_gateway, + /** + * Indicates whether a TTLock device supports Wi-Fi. + */ + public bool|null $wifi, + ) {} + } + + /** + * Wireless keypads for a TTLock device. + */ + class WirelessKeypads + { + public static function from_json(mixed $json): WirelessKeypads|null + { + if (!$json) { + return null; + } + return new self( + wireless_keypad_id: $json->wireless_keypad_id ?? null, + wireless_keypad_name: $json->wireless_keypad_name ?? null, + ); + } + + public function __construct( + /** + * ID for a wireless keypad for a TTLock device. + */ + public float|null $wireless_keypad_id, + /** + * Name for a wireless keypad for a TTLock device. + */ + public string|null $wireless_keypad_name, + ) {} + } } -/** - * Metadata for a 2N device. - */ -class DeviceTwoNMetadata -{ - public static function from_json(mixed $json): DeviceTwoNMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - ); - } - - public function __construct( - /** - * Device ID for a 2N device. - */ - public float|null $device_id, - /** - * Device name for a 2N device. - */ - public string|null $device_name, - ) {} +namespace Seam\Resources\Device\Properties\OfflineTimeFrameOptions { + /** + * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + */ + class TimePairs + { + public static function from_json(mixed $json): TimePairs|null + { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + end_time: $json->end_time ?? null, + start_time: $json->start_time ?? null, + ); + } + + public function __construct( + /** + * Label for the start/end time pairing. + */ + public string|null $display_name, + /** + * End time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. An `end_time` earlier on the clock than `start_time` means the end falls on a later date. + */ + public string|null $end_time, + /** + * Start time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. + */ + public string|null $start_time, + ) {} + } } -/** - * Metadata for an Ultraloq device. - */ -class DeviceUltraloqMetadata -{ - public static function from_json(mixed $json): DeviceUltraloqMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_name: $json->device_name ?? null, - device_type: $json->device_type ?? null, - time_zone: $json->time_zone ?? null, - ); - } - - public function __construct( - /** - * Device ID for an Ultraloq device. - */ - public string|null $device_id, - /** - * Device name for an Ultraloq device. - */ - public string|null $device_name, - /** - * Device type for an Ultraloq device. - */ - public string|null $device_type, - /** - * IANA timezone for the Ultraloq device. - */ - public string|null $time_zone, - ) {} +namespace Seam\Resources\Device\Properties\OnlineTimeFrameOptions { + /** + * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. + */ + class TimePairs + { + public static function from_json(mixed $json): TimePairs|null + { + if (!$json) { + return null; + } + return new self( + display_name: $json->display_name ?? null, + end_time: $json->end_time ?? null, + start_time: $json->start_time ?? null, + ); + } + + public function __construct( + /** + * Label for the start/end time pairing. + */ + public string|null $display_name, + /** + * End time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. An `end_time` earlier on the clock than `start_time` means the end falls on a later date. + */ + public string|null $end_time, + /** + * Start time of day as a 24-hour `HH:MM` value, interpreted in the option's `time_zone`. + */ + public string|null $start_time, + ) {} + } } -/** - * Metadata for an ASSA ABLOY Visionline system. - */ -class DeviceVisionlineMetadata -{ - public static function from_json(mixed $json): DeviceVisionlineMetadata|null +namespace Seam\Resources\Device\Properties\ActiveThermostatSchedule { + /** + * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self(encoder_id: $json->encoder_id ?? null); + + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} } +} + +namespace Seam\Resources\Device\Properties\AvailableClimatePresets { + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + class EcobeeMetadata + { + public static function from_json(mixed $json): EcobeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + climate_ref: $json->climate_ref ?? null, + is_optimized: $json->is_optimized ?? null, + owner: $json->owner ?? null, + ); + } - public function __construct( - /** - * Encoder ID for an ASSA ABLOY Visionline system. - */ - public string|null $encoder_id, - ) {} + public function __construct( + /** + * Reference to the Ecobee climate, if applicable. + */ + public string|null $climate_ref, + /** + * Indicates if the climate preset is optimized by Ecobee. + */ + public bool|null $is_optimized, + /** + * Indicates whether the climate preset is owned by the user or the system. + */ + public string|null $owner, + ) {} + } } -/** - * Array of warnings associated with the device. Each warning object within the array contains two fields: `warning_code` and `message`. `warning_code` is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. - */ -class DeviceWarnings -{ - public static function from_json(mixed $json): DeviceWarnings|null - { - if (!$json) { - return null; - } - return new self( - active_access_code_count: $json->active_access_code_count ?? null, - created_at: $json->created_at ?? null, - max_active_access_code_count: $json->max_active_access_code_count ?? - null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - - public function __construct( - /** - * Number of active access codes on the device when the warning was set. - */ - public int|null $active_access_code_count, - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Maximum number of active access codes supported by the device. - */ - public int|null $max_active_access_code_count, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} +namespace Seam\Resources\Device\Properties\CurrentClimateSetting { + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + class EcobeeMetadata + { + public static function from_json(mixed $json): EcobeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + climate_ref: $json->climate_ref ?? null, + is_optimized: $json->is_optimized ?? null, + owner: $json->owner ?? null, + ); + } + + public function __construct( + /** + * Reference to the Ecobee climate, if applicable. + */ + public string|null $climate_ref, + /** + * Indicates if the climate preset is optimized by Ecobee. + */ + public bool|null $is_optimized, + /** + * Indicates whether the climate preset is owned by the user or the system. + */ + public string|null $owner, + ) {} + } } -/** - * Wireless keypads for a TTLock device. - */ -class DeviceWirelessKeypads -{ - public static function from_json(mixed $json): DeviceWirelessKeypads|null - { - if (!$json) { - return null; - } - return new self( - wireless_keypad_id: $json->wireless_keypad_id ?? null, - wireless_keypad_name: $json->wireless_keypad_name ?? null, - ); - } - - public function __construct( - /** - * ID for a wireless keypad for a TTLock device. - */ - public float|null $wireless_keypad_id, - /** - * Name for a wireless keypad for a TTLock device. - */ - public string|null $wireless_keypad_name, - ) {} +namespace Seam\Resources\Device\Properties\DefaultClimateSetting { + /** + * Metadata specific to the Ecobee climate, if applicable. + */ + class EcobeeMetadata + { + public static function from_json(mixed $json): EcobeeMetadata|null + { + if (!$json) { + return null; + } + return new self( + climate_ref: $json->climate_ref ?? null, + is_optimized: $json->is_optimized ?? null, + owner: $json->owner ?? null, + ); + } + + public function __construct( + /** + * Reference to the Ecobee climate, if applicable. + */ + public string|null $climate_ref, + /** + * Indicates if the climate preset is optimized by Ecobee. + */ + public bool|null $is_optimized, + /** + * Indicates whether the climate preset is owned by the user or the system. + */ + public string|null $owner, + ) {} + } } -/** - * Metadata for a Wyze device. - */ -class DeviceWyzeMetadata -{ - public static function from_json(mixed $json): DeviceWyzeMetadata|null - { - if (!$json) { - return null; - } - return new self( - device_id: $json->device_id ?? null, - device_info_model: $json->device_info_model ?? null, - device_name: $json->device_name ?? null, - keypad_uuid: $json->keypad_uuid ?? null, - locker_status_hardlock: $json->locker_status_hardlock ?? null, - product_model: $json->product_model ?? null, - product_name: $json->product_name ?? null, - product_type: $json->product_type ?? null, - ); - } - - public function __construct( - /** - * Device ID for a Wyze device. - */ - public string|null $device_id, - /** - * Device information model for a Wyze device. - */ - public string|null $device_info_model, - /** - * Device name for a Wyze device. - */ - public string|null $device_name, - /** - * Keypad UUID for a Wyze device. - */ - public string|null $keypad_uuid, - /** - * Locker status (hardlock) for a Wyze device. - */ - public float|null $locker_status_hardlock, - /** - * Product model for a Wyze device. - */ - public string|null $product_model, - /** - * Product name for a Wyze device. - */ - public string|null $product_name, - /** - * Product type for a Wyze device. - */ - public string|null $product_type, - ) {} +namespace Seam\Resources\Device\Properties\ThermostatDailyPrograms { + /** + * Array of thermostat daily program periods. + */ + class Periods + { + public static function from_json(mixed $json): Periods|null + { + if (!$json) { + return null; + } + return new self( + climate_preset_key: $json->climate_preset_key ?? null, + starts_at_time: $json->starts_at_time ?? null, + ); + } + + public function __construct( + /** + * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to activate at the `starts_at_time`. + */ + public string|null $climate_preset_key, + /** + * Time at which the thermostat daily program period starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at_time, + ) {} + } } diff --git a/src/Resources/DeviceProvider.php b/src/Resources/DeviceProvider.php index 2f1539f6..a3276967 100644 --- a/src/Resources/DeviceProvider.php +++ b/src/Resources/DeviceProvider.php @@ -1,148 +1,148 @@ can_configure_auto_lock ?? null, + can_hvac_cool: $json->can_hvac_cool ?? null, + can_hvac_heat: $json->can_hvac_heat ?? null, + can_hvac_heat_cool: $json->can_hvac_heat_cool ?? null, + can_program_offline_access_codes: $json->can_program_offline_access_codes ?? + null, + can_program_online_access_codes: $json->can_program_online_access_codes ?? + null, + can_program_thermostat_programs_as_different_each_day: $json->can_program_thermostat_programs_as_different_each_day ?? + null, + can_program_thermostat_programs_as_same_each_day: $json->can_program_thermostat_programs_as_same_each_day ?? + null, + can_program_thermostat_programs_as_weekday_weekend: $json->can_program_thermostat_programs_as_weekday_weekend ?? + null, + can_remotely_lock: $json->can_remotely_lock ?? null, + can_remotely_unlock: $json->can_remotely_unlock ?? null, + can_run_thermostat_programs: $json->can_run_thermostat_programs ?? + null, + can_simulate_connection: $json->can_simulate_connection ?? null, + can_simulate_disconnection: $json->can_simulate_disconnection ?? + null, + can_simulate_hub_connection: $json->can_simulate_hub_connection ?? + null, + can_simulate_hub_disconnection: $json->can_simulate_hub_disconnection ?? + null, + can_simulate_paid_subscription: $json->can_simulate_paid_subscription ?? + null, + can_simulate_removal: $json->can_simulate_removal ?? null, + can_turn_off_hvac: $json->can_turn_off_hvac ?? null, + can_unlock_with_code: $json->can_unlock_with_code ?? null, + device_provider_name: $json->device_provider_name ?? null, + display_name: $json->display_name ?? null, + image_url: $json->image_url ?? null, + provider_categories: $json->provider_categories ?? null, + ); } - return new self( - can_configure_auto_lock: $json->can_configure_auto_lock ?? null, - can_hvac_cool: $json->can_hvac_cool ?? null, - can_hvac_heat: $json->can_hvac_heat ?? null, - can_hvac_heat_cool: $json->can_hvac_heat_cool ?? null, - can_program_offline_access_codes: $json->can_program_offline_access_codes ?? - null, - can_program_online_access_codes: $json->can_program_online_access_codes ?? - null, - can_program_thermostat_programs_as_different_each_day: $json->can_program_thermostat_programs_as_different_each_day ?? - null, - can_program_thermostat_programs_as_same_each_day: $json->can_program_thermostat_programs_as_same_each_day ?? - null, - can_program_thermostat_programs_as_weekday_weekend: $json->can_program_thermostat_programs_as_weekday_weekend ?? - null, - can_remotely_lock: $json->can_remotely_lock ?? null, - can_remotely_unlock: $json->can_remotely_unlock ?? null, - can_run_thermostat_programs: $json->can_run_thermostat_programs ?? - null, - can_simulate_connection: $json->can_simulate_connection ?? null, - can_simulate_disconnection: $json->can_simulate_disconnection ?? - null, - can_simulate_hub_connection: $json->can_simulate_hub_connection ?? - null, - can_simulate_hub_disconnection: $json->can_simulate_hub_disconnection ?? - null, - can_simulate_paid_subscription: $json->can_simulate_paid_subscription ?? - null, - can_simulate_removal: $json->can_simulate_removal ?? null, - can_turn_off_hvac: $json->can_turn_off_hvac ?? null, - can_unlock_with_code: $json->can_unlock_with_code ?? null, - device_provider_name: $json->device_provider_name ?? null, - display_name: $json->display_name ?? null, - image_url: $json->image_url ?? null, - provider_categories: $json->provider_categories ?? null, - ); - } - public function __construct( - /** - * Indicates whether the lock supports configuring automatic locking. - */ - public bool|null $can_configure_auto_lock, - /** - * Indicates whether the thermostat supports cooling. - */ - public bool|null $can_hvac_cool, - /** - * Indicates whether the thermostat supports heating. - */ - public bool|null $can_hvac_heat, - /** - * Indicates whether the thermostat supports simultaneous heating and cooling. - */ - public bool|null $can_hvac_heat_cool, - /** - * Indicates whether the device supports programming offline access codes. - */ - public bool|null $can_program_offline_access_codes, - /** - * Indicates whether the device supports programming online access codes. - */ - public bool|null $can_program_online_access_codes, - /** - * Indicates whether the thermostat supports different climate programs for each day of the week. - */ - public bool|null $can_program_thermostat_programs_as_different_each_day, - /** - * Indicates whether the thermostat supports a single climate program applied to every day. - */ - public bool|null $can_program_thermostat_programs_as_same_each_day, - /** - * Indicates whether the thermostat supports weekday/weekend climate programs. - */ - public bool|null $can_program_thermostat_programs_as_weekday_weekend, - /** - * Indicates whether the device supports remote locking. - */ - public bool|null $can_remotely_lock, - /** - * Indicates whether the device supports remote unlocking. - */ - public bool|null $can_remotely_unlock, - /** - * Indicates whether the thermostat supports running climate programs. - */ - public bool|null $can_run_thermostat_programs, - /** - * Indicates whether the device supports simulating connection in a sandbox. - */ - public bool|null $can_simulate_connection, - /** - * Indicates whether the device supports simulating disconnection in a sandbox. - */ - public bool|null $can_simulate_disconnection, - /** - * Indicates whether the hub supports simulating connection in a sandbox. - */ - public bool|null $can_simulate_hub_connection, - /** - * Indicates whether the hub supports simulating disconnection in a sandbox. - */ - public bool|null $can_simulate_hub_disconnection, - /** - * Indicates whether the device supports simulating a paid subscription in a sandbox. - */ - public bool|null $can_simulate_paid_subscription, - /** - * Indicates whether the device supports simulating removal in a sandbox. - */ - public bool|null $can_simulate_removal, - /** - * Indicates whether the thermostat can be turned off. - */ - public bool|null $can_turn_off_hvac, - /** - * Indicates whether the lock supports unlocking with an access code. - */ - public bool|null $can_unlock_with_code, - /** - * Name of the device provider. - */ - public string|null $device_provider_name, - /** - * Display name for the device provider. - */ - public string|null $display_name, - /** - * Image URL for the device provider. - */ - public string|null $image_url, - /** - * List of provider categories to which the device provider belongs, such as `stable`, `consumer_smartlocks`, `thermostats`, and so on. - */ - public array|null $provider_categories, - ) {} + public function __construct( + /** + * Indicates whether the lock supports configuring automatic locking. + */ + public bool|null $can_configure_auto_lock, + /** + * Indicates whether the thermostat supports cooling. + */ + public bool|null $can_hvac_cool, + /** + * Indicates whether the thermostat supports heating. + */ + public bool|null $can_hvac_heat, + /** + * Indicates whether the thermostat supports simultaneous heating and cooling. + */ + public bool|null $can_hvac_heat_cool, + /** + * Indicates whether the device supports programming offline access codes. + */ + public bool|null $can_program_offline_access_codes, + /** + * Indicates whether the device supports programming online access codes. + */ + public bool|null $can_program_online_access_codes, + /** + * Indicates whether the thermostat supports different climate programs for each day of the week. + */ + public bool|null $can_program_thermostat_programs_as_different_each_day, + /** + * Indicates whether the thermostat supports a single climate program applied to every day. + */ + public bool|null $can_program_thermostat_programs_as_same_each_day, + /** + * Indicates whether the thermostat supports weekday/weekend climate programs. + */ + public bool|null $can_program_thermostat_programs_as_weekday_weekend, + /** + * Indicates whether the device supports remote locking. + */ + public bool|null $can_remotely_lock, + /** + * Indicates whether the device supports remote unlocking. + */ + public bool|null $can_remotely_unlock, + /** + * Indicates whether the thermostat supports running climate programs. + */ + public bool|null $can_run_thermostat_programs, + /** + * Indicates whether the device supports simulating connection in a sandbox. + */ + public bool|null $can_simulate_connection, + /** + * Indicates whether the device supports simulating disconnection in a sandbox. + */ + public bool|null $can_simulate_disconnection, + /** + * Indicates whether the hub supports simulating connection in a sandbox. + */ + public bool|null $can_simulate_hub_connection, + /** + * Indicates whether the hub supports simulating disconnection in a sandbox. + */ + public bool|null $can_simulate_hub_disconnection, + /** + * Indicates whether the device supports simulating a paid subscription in a sandbox. + */ + public bool|null $can_simulate_paid_subscription, + /** + * Indicates whether the device supports simulating removal in a sandbox. + */ + public bool|null $can_simulate_removal, + /** + * Indicates whether the thermostat can be turned off. + */ + public bool|null $can_turn_off_hvac, + /** + * Indicates whether the lock supports unlocking with an access code. + */ + public bool|null $can_unlock_with_code, + /** + * Name of the device provider. + */ + public string|null $device_provider_name, + /** + * Display name for the device provider. + */ + public string|null $display_name, + /** + * Image URL for the device provider. + */ + public string|null $image_url, + /** + * List of provider categories to which the device provider belongs, such as `stable`, `consumer_smartlocks`, `thermostats`, and so on. + */ + public array|null $provider_categories, + ) {} + } } diff --git a/src/Resources/Event.php b/src/Resources/Event.php index 9277ed2f..4ce80506 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -1,946 +1,878 @@ Event\AccessCodeErrors::from_json($a), + $json->access_code_errors ?? [], + ), + access_code_id: $json->access_code_id ?? null, + access_code_is_managed: $json->access_code_is_managed ?? null, + access_code_warnings: array_map( + fn($a) => Event\AccessCodeWarnings::from_json($a), + $json->access_code_warnings ?? [], + ), + access_grant_id: $json->access_grant_id ?? null, + access_grant_ids: $json->access_grant_ids ?? null, + access_grant_key: $json->access_grant_key ?? null, + access_grant_keys: $json->access_grant_keys ?? null, + access_method_id: $json->access_method_id ?? null, + acs_access_group_id: $json->acs_access_group_id ?? null, + acs_credential_id: $json->acs_credential_id ?? null, + acs_encoder_id: $json->acs_encoder_id ?? null, + acs_entrance_id: $json->acs_entrance_id ?? null, + acs_entrance_ids: $json->acs_entrance_ids ?? null, + acs_system_errors: array_map( + fn($a) => Event\AcsSystemErrors::from_json($a), + $json->acs_system_errors ?? [], + ), + acs_system_id: $json->acs_system_id ?? null, + acs_system_warnings: array_map( + fn($a) => Event\AcsSystemWarnings::from_json($a), + $json->acs_system_warnings ?? [], + ), + acs_user_id: $json->acs_user_id ?? null, + action_attempt_id: $json->action_attempt_id ?? null, + action_type: $json->action_type ?? null, + activation_reason: $json->activation_reason ?? null, + backup_access_code_id: $json->backup_access_code_id ?? null, + battery_level: $json->battery_level ?? null, + battery_status: $json->battery_status ?? null, + change_reason: $json->change_reason ?? null, + changed_properties: array_map( + fn($c) => Event\ChangedProperties::from_json($c), + $json->changed_properties ?? [], + ), + client_session_id: $json->client_session_id ?? null, + climate_preset_key: $json->climate_preset_key ?? null, + code: $json->code ?? null, + connect_webview_id: $json->connect_webview_id ?? null, + connected_account_custom_metadata: $json->connected_account_custom_metadata ?? + null, + connected_account_errors: array_map( + fn($c) => Event\ConnectedAccountErrors::from_json($c), + $json->connected_account_errors ?? [], + ), + connected_account_id: $json->connected_account_id ?? null, + connected_account_type: $json->connected_account_type ?? null, + connected_account_warnings: array_map( + fn($c) => Event\ConnectedAccountWarnings::from_json($c), + $json->connected_account_warnings ?? [], + ), + cooling_set_point_celsius: $json->cooling_set_point_celsius ?? + null, + cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? + null, + created_at: $json->created_at ?? null, + customer_key: $json->customer_key ?? null, + description: $json->description ?? null, + desired_temperature_celsius: $json->desired_temperature_celsius ?? + null, + desired_temperature_fahrenheit: $json->desired_temperature_fahrenheit ?? + null, + device_custom_metadata: $json->device_custom_metadata ?? null, + device_errors: array_map( + fn($d) => Event\DeviceErrors::from_json($d), + $json->device_errors ?? [], + ), + device_id: $json->device_id ?? null, + device_ids: $json->device_ids ?? null, + device_name: $json->device_name ?? null, + device_warnings: array_map( + fn($d) => Event\DeviceWarnings::from_json($d), + $json->device_warnings ?? [], + ), + ends_at: $json->ends_at ?? null, + error_code: $json->error_code ?? null, + error_message: $json->error_message ?? null, + event_description: $json->event_description ?? null, + event_id: $json->event_id ?? null, + event_type: $json->event_type ?? null, + fan_mode_setting: $json->fan_mode_setting ?? null, + from: isset($json->from) + ? Event\From::from_json($json->from) + : null, + heating_set_point_celsius: $json->heating_set_point_celsius ?? + null, + heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? + null, + hvac_mode_setting: $json->hvac_mode_setting ?? null, + image_url: $json->image_url ?? null, + is_backup_code: $json->is_backup_code ?? null, + is_fallback_climate_preset: $json->is_fallback_climate_preset ?? + null, + is_via_bluetooth: $json->is_via_bluetooth ?? null, + is_via_nfc: $json->is_via_nfc ?? null, + lower_limit_celsius: $json->lower_limit_celsius ?? null, + lower_limit_fahrenheit: $json->lower_limit_fahrenheit ?? null, + method: $json->method ?? null, + minut_metadata: $json->minut_metadata ?? null, + missing_device_ids: $json->missing_device_ids ?? null, + motion_sub_type: $json->motion_sub_type ?? null, + noise_level_decibels: $json->noise_level_decibels ?? null, + noise_level_nrs: $json->noise_level_nrs ?? null, + noise_threshold_id: $json->noise_threshold_id ?? null, + noise_threshold_name: $json->noise_threshold_name ?? null, + noiseaware_metadata: $json->noiseaware_metadata ?? null, + occurred_at: $json->occurred_at ?? null, + reason: isset($json->reason) + ? Event\Reason::from_json($json->reason) + : null, + requested_mutations: array_map( + fn($r) => Event\RequestedMutations::from_json($r), + $json->requested_mutations ?? [], + ), + space_id: $json->space_id ?? null, + space_key: $json->space_key ?? null, + starts_at: $json->starts_at ?? null, + status: $json->status ?? null, + temperature_celsius: $json->temperature_celsius ?? null, + temperature_fahrenheit: $json->temperature_fahrenheit ?? null, + thermostat_schedule_id: $json->thermostat_schedule_id ?? null, + to: isset($json->to) ? Event\To::from_json($json->to) : null, + upper_limit_celsius: $json->upper_limit_celsius ?? null, + upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, + user_identity_id: $json->user_identity_id ?? null, + video_url: $json->video_url ?? null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - access_code_errors: array_map( - fn($a) => EventAccessCodeErrors::from_json($a), - $json->access_code_errors ?? [], - ), - access_code_id: $json->access_code_id ?? null, - access_code_is_managed: $json->access_code_is_managed ?? null, - access_code_warnings: array_map( - fn($a) => EventAccessCodeWarnings::from_json($a), - $json->access_code_warnings ?? [], - ), - access_grant_id: $json->access_grant_id ?? null, - access_grant_ids: $json->access_grant_ids ?? null, - access_grant_key: $json->access_grant_key ?? null, - access_grant_keys: $json->access_grant_keys ?? null, - access_method_id: $json->access_method_id ?? null, - acs_access_group_id: $json->acs_access_group_id ?? null, - acs_credential_id: $json->acs_credential_id ?? null, - acs_encoder_id: $json->acs_encoder_id ?? null, - acs_entrance_id: $json->acs_entrance_id ?? null, - acs_entrance_ids: $json->acs_entrance_ids ?? null, - acs_system_errors: array_map( - fn($a) => EventAcsSystemErrors::from_json($a), - $json->acs_system_errors ?? [], - ), - acs_system_id: $json->acs_system_id ?? null, - acs_system_warnings: array_map( - fn($a) => EventAcsSystemWarnings::from_json($a), - $json->acs_system_warnings ?? [], - ), - acs_user_id: $json->acs_user_id ?? null, - action_attempt_id: $json->action_attempt_id ?? null, - action_type: $json->action_type ?? null, - activation_reason: $json->activation_reason ?? null, - backup_access_code_id: $json->backup_access_code_id ?? null, - battery_level: $json->battery_level ?? null, - battery_status: $json->battery_status ?? null, - change_reason: $json->change_reason ?? null, - changed_properties: array_map( - fn($c) => EventChangedProperties::from_json($c), - $json->changed_properties ?? [], - ), - client_session_id: $json->client_session_id ?? null, - climate_preset_key: $json->climate_preset_key ?? null, - code: $json->code ?? null, - connect_webview_id: $json->connect_webview_id ?? null, - connected_account_custom_metadata: $json->connected_account_custom_metadata ?? - null, - connected_account_errors: array_map( - fn($c) => EventConnectedAccountErrors::from_json($c), - $json->connected_account_errors ?? [], - ), - connected_account_id: $json->connected_account_id ?? null, - connected_account_type: $json->connected_account_type ?? null, - connected_account_warnings: array_map( - fn($c) => EventConnectedAccountWarnings::from_json($c), - $json->connected_account_warnings ?? [], - ), - cooling_set_point_celsius: $json->cooling_set_point_celsius ?? null, - cooling_set_point_fahrenheit: $json->cooling_set_point_fahrenheit ?? - null, - created_at: $json->created_at ?? null, - customer_key: $json->customer_key ?? null, - description: $json->description ?? null, - desired_temperature_celsius: $json->desired_temperature_celsius ?? - null, - desired_temperature_fahrenheit: $json->desired_temperature_fahrenheit ?? - null, - device_custom_metadata: $json->device_custom_metadata ?? null, - device_errors: array_map( - fn($d) => EventDeviceErrors::from_json($d), - $json->device_errors ?? [], - ), - device_id: $json->device_id ?? null, - device_ids: $json->device_ids ?? null, - device_name: $json->device_name ?? null, - device_warnings: array_map( - fn($d) => EventDeviceWarnings::from_json($d), - $json->device_warnings ?? [], - ), - ends_at: $json->ends_at ?? null, - error_code: $json->error_code ?? null, - error_message: $json->error_message ?? null, - event_description: $json->event_description ?? null, - event_id: $json->event_id ?? null, - event_type: $json->event_type ?? null, - fan_mode_setting: $json->fan_mode_setting ?? null, - from: isset($json->from) ? EventFrom::from_json($json->from) : null, - heating_set_point_celsius: $json->heating_set_point_celsius ?? null, - heating_set_point_fahrenheit: $json->heating_set_point_fahrenheit ?? - null, - hvac_mode_setting: $json->hvac_mode_setting ?? null, - image_url: $json->image_url ?? null, - is_backup_code: $json->is_backup_code ?? null, - is_fallback_climate_preset: $json->is_fallback_climate_preset ?? - null, - is_via_bluetooth: $json->is_via_bluetooth ?? null, - is_via_nfc: $json->is_via_nfc ?? null, - lower_limit_celsius: $json->lower_limit_celsius ?? null, - lower_limit_fahrenheit: $json->lower_limit_fahrenheit ?? null, - method: $json->method ?? null, - minut_metadata: $json->minut_metadata ?? null, - missing_device_ids: $json->missing_device_ids ?? null, - motion_sub_type: $json->motion_sub_type ?? null, - noise_level_decibels: $json->noise_level_decibels ?? null, - noise_level_nrs: $json->noise_level_nrs ?? null, - noise_threshold_id: $json->noise_threshold_id ?? null, - noise_threshold_name: $json->noise_threshold_name ?? null, - noiseaware_metadata: $json->noiseaware_metadata ?? null, - occurred_at: $json->occurred_at ?? null, - reason: isset($json->reason) - ? EventReason::from_json($json->reason) - : null, - requested_mutations: array_map( - fn($r) => EventRequestedMutations::from_json($r), - $json->requested_mutations ?? [], - ), - space_id: $json->space_id ?? null, - space_key: $json->space_key ?? null, - starts_at: $json->starts_at ?? null, - status: $json->status ?? null, - temperature_celsius: $json->temperature_celsius ?? null, - temperature_fahrenheit: $json->temperature_fahrenheit ?? null, - thermostat_schedule_id: $json->thermostat_schedule_id ?? null, - to: isset($json->to) ? EventTo::from_json($json->to) : null, - upper_limit_celsius: $json->upper_limit_celsius ?? null, - upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, - user_identity_id: $json->user_identity_id ?? null, - video_url: $json->video_url ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Errors associated with the access code. - */ - public array $access_code_errors, - /** - * ID of the affected access code. - */ - public string|null $access_code_id, - /** - * Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. - */ - public bool|null $access_code_is_managed, - /** - * Warnings associated with the access code. - */ - public array $access_code_warnings, - /** - * ID of the affected Access Grant. - */ - public string|null $access_grant_id, - /** - * IDs of the access grants associated with this access method. - */ - public array|null $access_grant_ids, - /** - * Key of the affected Access Grant (if present). - */ - public string|null $access_grant_key, - /** - * Keys of the access grants associated with this access method (if present). - */ - public array|null $access_grant_keys, - /** - * ID of the affected access method. - */ - public string|null $access_method_id, - /** - * ID of the affected access group. - */ - public string|null $acs_access_group_id, - /** - * ID of the affected credential. - */ - public string|null $acs_credential_id, - /** - * ID of the affected encoder. - */ - public string|null $acs_encoder_id, - /** - * ID of the affected [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). - */ - public string|null $acs_entrance_id, - /** - * IDs of all ACS entrances currently attached to the space. - */ - public array|null $acs_entrance_ids, - /** - * Errors associated with the access control system. - */ - public array $acs_system_errors, - /** - * ID of the access system. - */ - public string|null $acs_system_id, - /** - * Warnings associated with the access control system. - */ - public array $acs_system_warnings, - /** - * ID of the affected access system user. - */ - public string|null $acs_user_id, - /** - * ID of the affected action attempt. - */ - public string|null $action_attempt_id, - /** - * Type of the action. - */ - public string|null $action_type, - /** - * The reason the camera was activated. - */ - public string|null $activation_reason, - /** - * ID of the backup access code that was pulled from the pool. - */ - public string|null $backup_access_code_id, - /** - * Number in the range 0 to 1.0 indicating the amount of battery in the affected device, as reported by the device. - */ - public float|null $battery_level, - /** - * Battery status of the affected device, calculated from the numeric `battery_level` value. - */ - public string|null $battery_status, - /** - * Human-readable reason for the change (e.g. `ongoing code auto-renewed`). - */ - public string|null $change_reason, - /** - * List of properties that changed on the access code. - */ - public array $changed_properties, - /** - * ID of the affected client session. - */ - public string|null $client_session_id, - /** - * Key of the climate preset that was activated. - */ - public string|null $climate_preset_key, - /** - * Code for the affected access code. - */ - public string|null $code, - /** - * ID of the Connect Webview associated with the event. - */ - public string|null $connect_webview_id, - /** - * Custom metadata of the connected account, present when connected_account_id is provided. - */ - public mixed $connected_account_custom_metadata, - /** - * Errors associated with the connected account. - */ - public array $connected_account_errors, - /** - * ID of the connected account associated with the affected access code. - */ - public string|null $connected_account_id, - /** - * undocumented: Unreleased. - */ - public string|null $connected_account_type, - /** - * Warnings associated with the connected account. - */ - public array $connected_account_warnings, - /** - * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_celsius, - /** - * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $cooling_set_point_fahrenheit, - /** - * Date and time at which the event was created. - */ - public string|null $created_at, - /** - * The customer key associated with this connected account, if any. - */ - public string|null $customer_key, - /** - * Human-readable description of the change and its source. - */ - public string|null $description, - /** - * Desired temperature, in °C, defined by the affected thermostat's cooling or heating set point. - */ - public float|null $desired_temperature_celsius, - /** - * Desired temperature, in °F, defined by the affected thermostat's cooling or heating set point. - */ - public float|null $desired_temperature_fahrenheit, - /** - * Custom metadata of the device, present when device_id is provided. - */ - public mixed $device_custom_metadata, - /** - * Errors associated with the device. - */ - public array $device_errors, - /** - * ID of the device associated with the affected access code. - */ - public string|null $device_id, - /** - * IDs of all devices currently attached to the space. - */ - public array|null $device_ids, - /** - * Name of the deleted device, captured at deletion time. The device record no longer exists when this event fires, so the name is preserved here. Null when the device had no resolvable name. - */ - public string|null $device_name, - /** - * Warnings associated with the device. - */ - public array $device_warnings, - /** - * The new end time for the access grant. - */ - public string|null $ends_at, - /** - * Error code associated with the disconnection event, if any. - */ - public string|null $error_code, - /** - * Description of why the access methods could not be created. - */ - public string|null $error_message, - /** - * Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. - */ - public string|null $event_description, - /** - * ID of the event. - */ - public string|null $event_id, - public string|null $event_type, - /** - * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. - */ - public string|null $fan_mode_setting, - /** - * Previous access code name configuration. - */ - public EventFrom|null $from, - /** - * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_celsius, - /** - * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). - */ - public float|null $heating_set_point_fahrenheit, - /** - * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. - */ - public string|null $hvac_mode_setting, - /** - * URL to a thumbnail image captured at the time of activation. - */ - public string|null $image_url, - /** - * Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). - */ - public bool|null $is_backup_code, - /** - * Indicates whether the climate preset that was activated is the fallback climate preset for the thermostat. - */ - public bool|null $is_fallback_climate_preset, - /** - * Whether the lock action was performed over Bluetooth by a remote client (such as the provider's mobile app), rather than a direct physical interaction or a Seam-initiated remote action. - */ - public bool|null $is_via_bluetooth, - /** - * Whether the lock action was performed by an NFC credential tap (such as an Apple Home Key or an NFC key fob) presented to the lock, rather than a direct physical interaction or a Seam-initiated remote action. - */ - public bool|null $is_via_nfc, - /** - * Lower temperature limit, in °C, defined by the set threshold. - */ - public float|null $lower_limit_celsius, - /** - * Lower temperature limit, in °F, defined by the set threshold. - */ - public float|null $lower_limit_fahrenheit, - /** - * Method by which the lock was locked. `keycode`: an access code was used (see `access_code_id`). `manual`: a physical action such as a thumbturn or button press. `remote`: a remote action via an app, Bluetooth, or the Seam API (see `action_attempt_id` if Seam-initiated; see `is_via_bluetooth` or `is_via_nfc` for the transport). `automatic`: triggered automatically, for example by an auto-relock timer. `unknown`: could not be determined. - */ - public string|null $method, - /** - * Metadata from Minut. - */ - public mixed $minut_metadata, - /** - * IDs of the devices that did not receive a requested access method. Use these to identify which specific devices failed without having to fetch the Access Grant. - */ - public array|null $missing_device_ids, - /** - * Sub-type of motion detected, if available. - */ - public string|null $motion_sub_type, - /** - * Detected noise level in decibels. - */ - public float|null $noise_level_decibels, - /** - * Detected noise level in Noiseaware Noise Risk Score (NRS). - */ - public float|null $noise_level_nrs, - /** - * ID of the noise threshold that was triggered. - */ - public string|null $noise_threshold_id, - /** - * Name of the noise threshold that was triggered. - */ - public string|null $noise_threshold_name, - /** - * Metadata from Noiseaware. - */ - public mixed $noiseaware_metadata, - /** - * Date and time at which the event occurred. - */ - public string|null $occurred_at, - /** - * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. - */ - public EventReason|null $reason, - /** - * Array of mutations requested on the access code, each containing the mutation type and from/to values. - */ - public array $requested_mutations, - /** - * ID of the affected space. - */ - public string|null $space_id, - /** - * Unique key for the space within the workspace. - */ - public string|null $space_key, - /** - * The new start time for the access grant. - */ - public string|null $starts_at, - /** - * Status of the action. - */ - public string|null $status, - /** - * Temperature, in °C, reported by the affected thermostat. - */ - public float|null $temperature_celsius, - /** - * Temperature, in °F, reported by the affected thermostat. - */ - public float|null $temperature_fahrenheit, - /** - * ID of the thermostat schedule that prompted the affected climate preset to be activated. - */ - public string|null $thermostat_schedule_id, - /** - * New access code name configuration. - */ - public EventTo|null $to, - /** - * Upper temperature limit, in °C, defined by the set threshold. - */ - public float|null $upper_limit_celsius, - /** - * Upper temperature limit, in °F, defined by the set threshold. - */ - public float|null $upper_limit_fahrenheit, - /** - * undocumented: Unreleased. - * --- - * ID of the user identity associated with the lock event. - */ - public string|null $user_identity_id, - /** - * URL to a short video clip captured at the time of activation. - */ - public string|null $video_url, - /** - * ID of the workspace associated with the event. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Errors associated with the access code. + */ + public array $access_code_errors, + public string|null $access_code_id, + /** + * Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. + */ + public bool|null $access_code_is_managed, + /** + * Warnings associated with the access code. + */ + public array $access_code_warnings, + /** + * ID of the affected Access Grant. + */ + public string|null $access_grant_id, + /** + * IDs of the access grants associated with this access method. + */ + public array|null $access_grant_ids, + /** + * Key of the affected Access Grant (if present). + */ + public string|null $access_grant_key, + /** + * Keys of the access grants associated with this access method (if present). + */ + public array|null $access_grant_keys, + /** + * ID of the affected access method. + */ + public string|null $access_method_id, + /** + * ID of the affected access group. + */ + public string|null $acs_access_group_id, + /** + * ID of the affected credential. + */ + public string|null $acs_credential_id, + /** + * ID of the affected encoder. + */ + public string|null $acs_encoder_id, + public string|null $acs_entrance_id, + public array|null $acs_entrance_ids, + /** + * Errors associated with the access control system. + */ + public array $acs_system_errors, + public string|null $acs_system_id, + /** + * Warnings associated with the access control system. + */ + public array $acs_system_warnings, + public string|null $acs_user_id, + public string|null $action_attempt_id, + /** + * Type of the action. + */ + public string|null $action_type, + /** + * The reason the camera was activated. + */ + public string|null $activation_reason, + /** + * ID of the backup access code that was pulled from the pool. + */ + public string|null $backup_access_code_id, + /** + * Number in the range 0 to 1.0 indicating the amount of battery in the affected device, as reported by the device. + */ + public float|null $battery_level, + /** + * Battery status of the affected device, calculated from the numeric `battery_level` value. + */ + public string|null $battery_status, + /** + * Human-readable reason for the change (e.g. `ongoing code auto-renewed`). + */ + public string|null $change_reason, + /** + * List of properties that changed on the access code. + */ + public array $changed_properties, + /** + * ID of the affected client session. + */ + public string|null $client_session_id, + /** + * Key of the climate preset that was activated. + */ + public string|null $climate_preset_key, + public string|null $code, + public string|null $connect_webview_id, + public mixed $connected_account_custom_metadata, + /** + * Errors associated with the connected account. + */ + public array $connected_account_errors, + public string|null $connected_account_id, + /** + * undocumented: Unreleased. + */ + public string|null $connected_account_type, + /** + * Warnings associated with the connected account. + */ + public array $connected_account_warnings, + /** + * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_celsius, + /** + * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $cooling_set_point_fahrenheit, + /** + * Date and time at which the event was created. + */ + public string|null $created_at, + public string|null $customer_key, + /** + * Human-readable description of the change and its source. + */ + public string|null $description, + /** + * Desired temperature, in °C, defined by the affected thermostat's cooling or heating set point. + */ + public float|null $desired_temperature_celsius, + /** + * Desired temperature, in °F, defined by the affected thermostat's cooling or heating set point. + */ + public float|null $desired_temperature_fahrenheit, + public mixed $device_custom_metadata, + /** + * Errors associated with the device. + */ + public array $device_errors, + public string|null $device_id, + public array|null $device_ids, + public string|null $device_name, + /** + * Warnings associated with the device. + */ + public array $device_warnings, + /** + * The new end time for the access grant. + */ + public string|null $ends_at, + /** + * Error code associated with the disconnection event, if any. + */ + public string|null $error_code, + /** + * Description of why the access methods could not be created. + */ + public string|null $error_message, + /** + * Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + */ + public string|null $event_description, + /** + * ID of the event. + */ + public string|null $event_id, + /** + * Type of the event. + */ + public string|null $event_type, + /** + * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. + */ + public string|null $fan_mode_setting, + public Event\From|null $from, + /** + * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_celsius, + /** + * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). + */ + public float|null $heating_set_point_fahrenheit, + /** + * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. + */ + public string|null $hvac_mode_setting, + public string|null $image_url, + /** + * Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). + */ + public bool|null $is_backup_code, + /** + * Indicates whether the climate preset that was activated is the fallback climate preset for the thermostat. + */ + public bool|null $is_fallback_climate_preset, + public bool|null $is_via_bluetooth, + public bool|null $is_via_nfc, + /** + * Lower temperature limit, in °C, defined by the set threshold. + */ + public float|null $lower_limit_celsius, + /** + * Lower temperature limit, in °F, defined by the set threshold. + */ + public float|null $lower_limit_fahrenheit, + public string|null $method, + /** + * Metadata from Minut. + */ + public mixed $minut_metadata, + /** + * IDs of the devices that did not receive a requested access method. Use these to identify which specific devices failed without having to fetch the Access Grant. + */ + public array|null $missing_device_ids, + /** + * Sub-type of motion detected, if available. + */ + public string|null $motion_sub_type, + /** + * Detected noise level in decibels. + */ + public float|null $noise_level_decibels, + /** + * Detected noise level in Noiseaware Noise Risk Score (NRS). + */ + public float|null $noise_level_nrs, + /** + * ID of the noise threshold that was triggered. + */ + public string|null $noise_threshold_id, + /** + * Name of the noise threshold that was triggered. + */ + public string|null $noise_threshold_name, + /** + * Metadata from Noiseaware. + */ + public mixed $noiseaware_metadata, + /** + * Date and time at which the event occurred. + */ + public string|null $occurred_at, + /** + * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. + */ + public Event\Reason|null $reason, + /** + * Array of mutations requested on the access code, each containing the mutation type and from/to values. + */ + public array $requested_mutations, + /** + * ID of the affected space. + */ + public string|null $space_id, + /** + * Unique key for the space within the workspace. + */ + public string|null $space_key, + /** + * The new start time for the access grant. + */ + public string|null $starts_at, + /** + * Status of the action. + */ + public string|null $status, + /** + * Temperature, in °C, reported by the affected thermostat. + */ + public float|null $temperature_celsius, + /** + * Temperature, in °F, reported by the affected thermostat. + */ + public float|null $temperature_fahrenheit, + /** + * ID of the thermostat schedule that prompted the affected climate preset to be activated. + */ + public string|null $thermostat_schedule_id, + public Event\To|null $to, + /** + * Upper temperature limit, in °C, defined by the set threshold. + */ + public float|null $upper_limit_celsius, + /** + * Upper temperature limit, in °F, defined by the set threshold. + */ + public float|null $upper_limit_fahrenheit, + public string|null $user_identity_id, + public string|null $video_url, + /** + * ID of the workspace associated with the event. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the access code. - */ -class EventAccessCodeErrors -{ - public static function from_json(mixed $json): EventAccessCodeErrors|null +namespace Seam\Resources\Event { + /** + * List of properties that changed on the access code. + */ + class ChangedProperties { - if (!$json) { - return null; + public static function from_json(mixed $json): ChangedProperties|null + { + if (!$json) { + return null; + } + return new self( + from: $json->from ?? null, + property: $json->property ?? null, + to: $json->to ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Previous value of the property, or null if not set. + */ + public string|null $from, + /** + * Name of the property that changed (e.g. `code`). + */ + public string|null $property, + /** + * New value of the property, or null if cleared. + */ + public string|null $to, + ) {} + } -/** - * Warnings associated with the access code. - */ -class EventAccessCodeWarnings -{ - public static function from_json(mixed $json): EventAccessCodeWarnings|null + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} -} + public function __construct( + /** + * Previous pin code. + */ + public string|null $code, + /** + * Previous end time. + */ + public string|null $ends_at, + /** + * Previous name of the access code. + */ + public string|null $name, + /** + * Previous start time. + */ + public string|null $starts_at, + ) {} + } -/** - * Errors associated with the access control system. - */ -class EventAcsSystemErrors -{ - public static function from_json(mixed $json): EventAcsSystemErrors|null + class To { - if (!$json) { - return null; + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * New pin code. + */ + public string|null $code, + /** + * New end time. + */ + public string|null $ends_at, + /** + * New name of the access code. + */ + public string|null $name, + /** + * New start time. + */ + public string|null $starts_at, + ) {} + } -/** - * Warnings associated with the access control system. - */ -class EventAcsSystemWarnings -{ - public static function from_json(mixed $json): EventAcsSystemWarnings|null + /** + * Array of mutations requested on the access code, each containing the mutation type and from/to values. + */ + class RequestedMutations { - if (!$json) { - return null; + public static function from_json(mixed $json): RequestedMutations|null + { + if (!$json) { + return null; + } + return new self( + from: $json->from ?? null, + mutation_code: $json->mutation_code ?? null, + to: $json->to ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} -} + public function __construct( + /** + * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + */ + public mixed $from, + /** + * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. + */ + public string|null $mutation_code, + /** + * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + */ + public mixed $to, + ) {} + } -/** - * List of properties that changed on the access code. - */ -class EventChangedProperties -{ - public static function from_json(mixed $json): EventChangedProperties|null + /** + * Errors associated with the access code. + */ + class AccessCodeErrors { - if (!$json) { - return null; + public static function from_json(mixed $json): AccessCodeErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - from: $json->from ?? null, - property: $json->property ?? null, - to: $json->to ?? null, - ); - } - public function __construct( - /** - * Previous value of the property, or null if not set. - */ - public string|null $from, - /** - * Name of the property that changed (e.g. `code`). - */ - public string|null $property, - /** - * New value of the property, or null if cleared. - */ - public string|null $to, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Errors associated with the connected account. - */ -class EventConnectedAccountErrors -{ - public static function from_json( - mixed $json, - ): EventConnectedAccountErrors|null { - if (!$json) { - return null; + /** + * Warnings associated with the access code. + */ + class AccessCodeWarnings + { + public static function from_json(mixed $json): AccessCodeWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } -/** - * Warnings associated with the connected account. - */ -class EventConnectedAccountWarnings -{ - public static function from_json( - mixed $json, - ): EventConnectedAccountWarnings|null { - if (!$json) { - return null; + /** + * Errors associated with the connected account. + */ + class ConnectedAccountErrors + { + public static function from_json( + mixed $json, + ): ConnectedAccountErrors|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Errors associated with the device. - */ -class EventDeviceErrors -{ - public static function from_json(mixed $json): EventDeviceErrors|null + /** + * Warnings associated with the connected account. + */ + class ConnectedAccountWarnings { - if (!$json) { - return null; + public static function from_json( + mixed $json, + ): ConnectedAccountWarnings|null { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } -/** - * Warnings associated with the device. - */ -class EventDeviceWarnings -{ - public static function from_json(mixed $json): EventDeviceWarnings|null + /** + * Errors associated with the device. + */ + class DeviceErrors { - if (!$json) { - return null; + public static function from_json(mixed $json): DeviceErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Previous access code name configuration. - */ -class EventFrom -{ - public static function from_json(mixed $json): EventFrom|null + /** + * Warnings associated with the device. + */ + class DeviceWarnings { - if (!$json) { - return null; + public static function from_json(mixed $json): DeviceWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Previous pin code. - */ - public string|null $code, - /** - * Previous end time. - */ - public string|null $ends_at, - /** - * Previous name of the access code. - */ - public string|null $name, - /** - * Previous start time. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } -/** - * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. - */ -class EventReason -{ - public static function from_json(mixed $json): EventReason|null + /** + * Errors associated with the access control system. + */ + class AcsSystemErrors { - if (!$json) { - return null; + public static function from_json(mixed $json): AcsSystemErrors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - message: $json->message ?? null, - reason_code: $json->reason_code ?? null, - ); - } - public function __construct( - /** - * Human-readable explanation of why access was denied. - */ - public string|null $message, - /** - * Normalized reason a lock denied access. Provider-agnostic; not all providers report every value. - */ - public string|null $reason_code, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Array of mutations requested on the access code, each containing the mutation type and from/to values. - */ -class EventRequestedMutations -{ - public static function from_json(mixed $json): EventRequestedMutations|null + /** + * Warnings associated with the access control system. + */ + class AcsSystemWarnings { - if (!$json) { - return null; + public static function from_json(mixed $json): AcsSystemWarnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - from: $json->from ?? null, - mutation_code: $json->mutation_code ?? null, - to: $json->to ?? null, - ); - } - public function __construct( - /** - * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. - */ - public mixed $from, - /** - * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. - */ - public string|null $mutation_code, - /** - * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. - */ - public mixed $to, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } -/** - * New access code name configuration. - */ -class EventTo -{ - public static function from_json(mixed $json): EventTo|null + /** + * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. + */ + class Reason { - if (!$json) { - return null; + public static function from_json(mixed $json): Reason|null + { + if (!$json) { + return null; + } + return new self( + message: $json->message ?? null, + reason_code: $json->reason_code ?? null, + ); } - return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * New pin code. - */ - public string|null $code, - /** - * New end time. - */ - public string|null $ends_at, - /** - * New name of the access code. - */ - public string|null $name, - /** - * New start time. - */ - public string|null $starts_at, - ) {} + public function __construct( + /** + * Human-readable explanation of why access was denied. + */ + public string|null $message, + /** + * Normalized reason a lock denied access. Provider-agnostic; not all providers report every value. + */ + public string|null $reason_code, + ) {} + } } diff --git a/src/Resources/InstantKey.php b/src/Resources/InstantKey.php index b14e3cd5..cbeb2807 100644 --- a/src/Resources/InstantKey.php +++ b/src/Resources/InstantKey.php @@ -1,103 +1,106 @@ client_session_id ?? null, + created_at: $json->created_at ?? null, + customization: isset($json->customization) + ? InstantKey\Customization::from_json($json->customization) + : null, + customization_profile_id: $json->customization_profile_id ?? + null, + expires_at: $json->expires_at ?? null, + instant_key_id: $json->instant_key_id ?? null, + instant_key_url: $json->instant_key_url ?? null, + user_identity_id: $json->user_identity_id ?? null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - client_session_id: $json->client_session_id ?? null, - created_at: $json->created_at ?? null, - customization: isset($json->customization) - ? InstantKeyCustomization::from_json($json->customization) - : null, - customization_profile_id: $json->customization_profile_id ?? null, - expires_at: $json->expires_at ?? null, - instant_key_id: $json->instant_key_id ?? null, - instant_key_url: $json->instant_key_url ?? null, - user_identity_id: $json->user_identity_id ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * ID of the client session associated with the Instant Key. - */ - public string|null $client_session_id, - /** - * Date and time at which the Instant Key was created. - */ - public string|null $created_at, - /** - * Customization applied to the Instant Key UI. - */ - public InstantKeyCustomization|null $customization, - /** - * ID of the customization profile associated with the Instant Key. - */ - public string|null $customization_profile_id, - /** - * Date and time at which the Instant Key expires. - */ - public string|null $expires_at, - /** - * ID of the Instant Key. - */ - public string|null $instant_key_id, - /** - * Shareable URL for the Instant Key. Use the URL to deliver the Instant Key to your user through a link in a text message or email or by embedding it in your web app. - */ - public string|null $instant_key_url, - /** - * ID of the user identity associated with the Instant Key. - */ - public string|null $user_identity_id, - /** - * ID of the workspace that contains the Instant Key. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the client session associated with the Instant Key. + */ + public string|null $client_session_id, + /** + * Date and time at which the Instant Key was created. + */ + public string|null $created_at, + /** + * Customization applied to the Instant Key UI. + */ + public InstantKey\Customization|null $customization, + /** + * ID of the customization profile associated with the Instant Key. + */ + public string|null $customization_profile_id, + /** + * Date and time at which the Instant Key expires. + */ + public string|null $expires_at, + /** + * ID of the Instant Key. + */ + public string|null $instant_key_id, + /** + * Shareable URL for the Instant Key. Use the URL to deliver the Instant Key to your user through a link in a text message or email or by embedding it in your web app. + */ + public string|null $instant_key_url, + /** + * ID of the user identity associated with the Instant Key. + */ + public string|null $user_identity_id, + /** + * ID of the workspace that contains the Instant Key. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Customization applied to the Instant Key UI. - */ -class InstantKeyCustomization -{ - public static function from_json(mixed $json): InstantKeyCustomization|null +namespace Seam\Resources\InstantKey { + /** + * Customization applied to the Instant Key UI. + */ + class Customization { - if (!$json) { - return null; + public static function from_json(mixed $json): Customization|null + { + if (!$json) { + return null; + } + return new self( + logo_url: $json->logo_url ?? null, + primary_color: $json->primary_color ?? null, + secondary_color: $json->secondary_color ?? null, + ); } - return new self( - logo_url: $json->logo_url ?? null, - primary_color: $json->primary_color ?? null, - secondary_color: $json->secondary_color ?? null, - ); - } - public function __construct( - /** - * URL of the logo displayed on the Instant Key. - */ - public string|null $logo_url, - /** - * Primary color used in the Instant Key UI. - */ - public string|null $primary_color, - /** - * Secondary color used in the Instant Key UI. - */ - public string|null $secondary_color, - ) {} + public function __construct( + /** + * URL of the logo displayed on the Instant Key. + */ + public string|null $logo_url, + /** + * Primary color used in the Instant Key UI. + */ + public string|null $primary_color, + /** + * Secondary color used in the Instant Key UI. + */ + public string|null $secondary_color, + ) {} + } } diff --git a/src/Resources/NoiseThreshold.php b/src/Resources/NoiseThreshold.php index 31a7b30d..765329d2 100644 --- a/src/Resources/NoiseThreshold.php +++ b/src/Resources/NoiseThreshold.php @@ -1,56 +1,57 @@ device_id ?? null, + ends_daily_at: $json->ends_daily_at ?? null, + name: $json->name ?? null, + noise_threshold_decibels: $json->noise_threshold_decibels ?? + null, + noise_threshold_id: $json->noise_threshold_id ?? null, + noise_threshold_nrs: $json->noise_threshold_nrs ?? null, + starts_daily_at: $json->starts_daily_at ?? null, + ); } - return new self( - device_id: $json->device_id ?? null, - ends_daily_at: $json->ends_daily_at ?? null, - name: $json->name ?? null, - noise_threshold_decibels: $json->noise_threshold_decibels ?? null, - noise_threshold_id: $json->noise_threshold_id ?? null, - noise_threshold_nrs: $json->noise_threshold_nrs ?? null, - starts_daily_at: $json->starts_daily_at ?? null, - ); - } - public function __construct( - /** - * Unique identifier for the device that contains the noise threshold. - */ - public string|null $device_id, - /** - * Time at which the noise threshold should become inactive daily. - */ - public string|null $ends_daily_at, - /** - * Name of the noise threshold. - */ - public string|null $name, - /** - * Noise level in decibels for the noise threshold. - */ - public float|null $noise_threshold_decibels, - /** - * Unique identifier for the noise threshold. - */ - public string|null $noise_threshold_id, - /** - * Noise level in Noiseaware Noise Risk Score (NRS) for the noise threshold. This parameter is only relevant for [Noiseaware sensors](https://docs.seam.co/device-and-system-integration-guides/noiseaware-sensors). - */ - public float|null $noise_threshold_nrs, - /** - * Time at which the noise threshold should become active daily. - */ - public string|null $starts_daily_at, - ) {} + public function __construct( + /** + * Unique identifier for the device that contains the noise threshold. + */ + public string|null $device_id, + /** + * Time at which the noise threshold should become inactive daily. + */ + public string|null $ends_daily_at, + /** + * Name of the noise threshold. + */ + public string|null $name, + /** + * Noise level in decibels for the noise threshold. + */ + public float|null $noise_threshold_decibels, + /** + * Unique identifier for the noise threshold. + */ + public string|null $noise_threshold_id, + /** + * Noise level in Noiseaware Noise Risk Score (NRS) for the noise threshold. This parameter is only relevant for [Noiseaware sensors](https://docs.seam.co/device-and-system-integration-guides/noiseaware-sensors). + */ + public float|null $noise_threshold_nrs, + /** + * Time at which the noise threshold should become active daily. + */ + public string|null $starts_daily_at, + ) {} + } } diff --git a/src/Resources/Phone.php b/src/Resources/Phone.php index 866153d1..28f7d0d8 100644 --- a/src/Resources/Phone.php +++ b/src/Resources/Phone.php @@ -1,267 +1,277 @@ created_at ?? null, + custom_metadata: $json->custom_metadata ?? null, + device_id: $json->device_id ?? null, + device_type: $json->device_type ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => Phone\Errors::from_json($e), + $json->errors ?? [], + ), + nickname: $json->nickname ?? null, + properties: isset($json->properties) + ? Phone\Properties::from_json($json->properties) + : null, + warnings: array_map( + fn($w) => Phone\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - custom_metadata: $json->custom_metadata ?? null, - device_id: $json->device_id ?? null, - device_type: $json->device_type ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => PhoneErrors::from_json($e), - $json->errors ?? [], - ), - nickname: $json->nickname ?? null, - properties: isset($json->properties) - ? PhoneProperties::from_json($json->properties) - : null, - warnings: array_map( - fn($w) => PhoneWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - - public function __construct( - /** - * Date and time at which the phone was created. - */ - public string|null $created_at, - /** - * Optional [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) for the phone. - */ - public mixed $custom_metadata, - /** - * ID of the phone. - */ - public string|null $device_id, - /** - * Type of the phone device, such as `ios_phone` or `android_phone`. - */ - public string|null $device_type, - /** - * Display name of the phone. Defaults to `nickname` (if it is set) or `properties.appearance.name`, otherwise. Enables administrators and users to identify the phone easily, especially when there are numerous phones. - */ - public string|null $display_name, - /** - * Errors associated with the phone. - */ - public array $errors, - /** - * Optional nickname to describe the phone, settable through Seam. - */ - public string|null $nickname, - /** - * Properties of the phone. - */ - public PhoneProperties|null $properties, - /** - * Warnings associated with the phone. - */ - public array $warnings, - /** - * ID of the workspace that contains the phone. - */ - public string|null $workspace_id, - ) {} -} -/** - * ASSA ABLOY Credential Service metadata for the phone. - */ -class PhoneAssaAbloyCredentialServiceMetadata -{ - public static function from_json( - mixed $json, - ): PhoneAssaAbloyCredentialServiceMetadata|null { - if (!$json) { - return null; - } - return new self( - endpoints: array_map( - fn($e) => PhoneEndpoints::from_json($e), - $json->endpoints ?? [], - ), - has_active_endpoint: $json->has_active_endpoint ?? null, - ); + public function __construct( + /** + * Date and time at which the phone was created. + */ + public string|null $created_at, + /** + * Optional [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) for the phone. + */ + public mixed $custom_metadata, + /** + * ID of the phone. + */ + public string|null $device_id, + /** + * Type of the phone device, such as `ios_phone` or `android_phone`. + */ + public string|null $device_type, + /** + * Display name of the phone. Defaults to `nickname` (if it is set) or `properties.appearance.name`, otherwise. Enables administrators and users to identify the phone easily, especially when there are numerous phones. + */ + public string|null $display_name, + /** + * Errors associated with the phone. + */ + public array $errors, + /** + * Optional nickname to describe the phone, settable through Seam. + */ + public string|null $nickname, + /** + * Properties of the phone. + */ + public Phone\Properties|null $properties, + /** + * Warnings associated with the phone. + */ + public array $warnings, + /** + * ID of the workspace that contains the phone. + */ + public string|null $workspace_id, + ) {} } - - public function __construct( - /** - * Endpoints associated with the phone. - */ - public array $endpoints, - /** - * Indicates whether the credential service has active endpoints associated with the phone. - */ - public bool|null $has_active_endpoint, - ) {} } -/** - * Endpoints associated with the phone. - */ -class PhoneEndpoints -{ - public static function from_json(mixed $json): PhoneEndpoints|null +namespace Seam\Resources\Phone { + /** + * Errors associated with the phone. + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - endpoint_id: $json->endpoint_id ?? null, - is_active: $json->is_active ?? null, - ); - } - public function __construct( - /** - * ID of the associated endpoint. - */ - public string|null $endpoint_id, - /** - * Indicated whether the endpoint is active. - */ - public bool|null $is_active, - ) {} -} - -/** - * Errors associated with the phone. - */ -class PhoneErrors -{ - public static function from_json(mixed $json): PhoneErrors|null - { - if (!$json) { - return null; - } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. + */ + public string|null $error_code, + /** + * Detailed description of the error. + */ + public string|null $message, + ) {} } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. - */ - public string|null $error_code, - /** - * Detailed description of the error. - */ - public string|null $message, - ) {} -} - -/** - * Properties of the phone. - */ -class PhoneProperties -{ - public static function from_json(mixed $json): PhoneProperties|null + /** + * Properties of the phone. + */ + class Properties { - if (!$json) { - return null; - } - return new self( - assa_abloy_credential_service_metadata: isset( - $json->assa_abloy_credential_service_metadata, - ) - ? PhoneAssaAbloyCredentialServiceMetadata::from_json( + public static function from_json(mixed $json): Properties|null + { + if (!$json) { + return null; + } + return new self( + assa_abloy_credential_service_metadata: isset( $json->assa_abloy_credential_service_metadata, ) - : null, - salto_space_credential_service_metadata: isset( - $json->salto_space_credential_service_metadata, - ) - ? PhoneSaltoSpaceCredentialServiceMetadata::from_json( + ? Properties\AssaAbloyCredentialServiceMetadata::from_json( + $json->assa_abloy_credential_service_metadata, + ) + : null, + salto_space_credential_service_metadata: isset( $json->salto_space_credential_service_metadata, ) - : null, - ); + ? Properties\SaltoSpaceCredentialServiceMetadata::from_json( + $json->salto_space_credential_service_metadata, + ) + : null, + ); + } + + public function __construct( + /** + * ASSA ABLOY Credential Service metadata for the phone. + */ + public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata, + /** + * Salto Space credential service metadata for the phone. + */ + public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata, + ) {} } - public function __construct( - /** - * ASSA ABLOY Credential Service metadata for the phone. - */ - public PhoneAssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata, - /** - * Salto Space credential service metadata for the phone. - */ - public PhoneSaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata, - ) {} + /** + * Warnings associated with the phone. + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. + */ + public string|null $warning_code, + ) {} + } } -/** - * Salto Space credential service metadata for the phone. - */ -class PhoneSaltoSpaceCredentialServiceMetadata -{ - public static function from_json( - mixed $json, - ): PhoneSaltoSpaceCredentialServiceMetadata|null { - if (!$json) { - return null; +namespace Seam\Resources\Phone\Properties { + /** + * ASSA ABLOY Credential Service metadata for the phone. + */ + class AssaAbloyCredentialServiceMetadata + { + public static function from_json( + mixed $json, + ): AssaAbloyCredentialServiceMetadata|null { + if (!$json) { + return null; + } + return new self( + endpoints: array_map( + fn( + $e, + ) => AssaAbloyCredentialServiceMetadata\Endpoints::from_json( + $e, + ), + $json->endpoints ?? [], + ), + has_active_endpoint: $json->has_active_endpoint ?? null, + ); } - return new self(has_active_phone: $json->has_active_phone ?? null); + + public function __construct( + /** + * Endpoints associated with the phone. + */ + public array $endpoints, + /** + * Indicates whether the credential service has active endpoints associated with the phone. + */ + public bool|null $has_active_endpoint, + ) {} } - public function __construct( - /** - * Indicates whether the credential service has an active associated phone. - */ - public bool|null $has_active_phone, - ) {} + /** + * Salto Space credential service metadata for the phone. + */ + class SaltoSpaceCredentialServiceMetadata + { + public static function from_json( + mixed $json, + ): SaltoSpaceCredentialServiceMetadata|null { + if (!$json) { + return null; + } + return new self(has_active_phone: $json->has_active_phone ?? null); + } + + public function __construct( + /** + * Indicates whether the credential service has an active associated phone. + */ + public bool|null $has_active_phone, + ) {} + } } -/** - * Warnings associated with the phone. - */ -class PhoneWarnings -{ - public static function from_json(mixed $json): PhoneWarnings|null +namespace Seam\Resources\Phone\Properties\AssaAbloyCredentialServiceMetadata { + /** + * Endpoints associated with the phone. + */ + class Endpoints { - if (!$json) { - return null; + public static function from_json(mixed $json): Endpoints|null + { + if (!$json) { + return null; + } + return new self( + endpoint_id: $json->endpoint_id ?? null, + is_active: $json->is_active ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * ID of the associated endpoint. + */ + public string|null $endpoint_id, + /** + * Indicated whether the endpoint is active. + */ + public bool|null $is_active, + ) {} + } } diff --git a/src/Resources/Space.php b/src/Resources/Space.php index 433326fa..eb7c6777 100644 --- a/src/Resources/Space.php +++ b/src/Resources/Space.php @@ -1,146 +1,148 @@ acs_entrance_count ?? null, + created_at: $json->created_at ?? null, + customer_data: isset($json->customer_data) + ? Space\CustomerData::from_json($json->customer_data) + : null, + customer_key: $json->customer_key ?? null, + device_count: $json->device_count ?? null, + display_name: $json->display_name ?? null, + geolocation: isset($json->geolocation) + ? Space\Geolocation::from_json($json->geolocation) + : null, + name: $json->name ?? null, + space_id: $json->space_id ?? null, + space_key: $json->space_key ?? null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - acs_entrance_count: $json->acs_entrance_count ?? null, - created_at: $json->created_at ?? null, - customer_data: isset($json->customer_data) - ? SpaceCustomerData::from_json($json->customer_data) - : null, - customer_key: $json->customer_key ?? null, - device_count: $json->device_count ?? null, - display_name: $json->display_name ?? null, - geolocation: isset($json->geolocation) - ? SpaceGeolocation::from_json($json->geolocation) - : null, - name: $json->name ?? null, - space_id: $json->space_id ?? null, - space_key: $json->space_key ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Number of entrances in the space. - */ - public float|null $acs_entrance_count, - /** - * Date and time at which the space was created. - */ - public string|null $created_at, - /** - * Reservation/stay-related defaults for the space. Also carries the provider/PMS-supplied name under a `_name` key (e.g. `guesty_name`), which Seam preserves when you rename the space (read-only — managed by Seam). - */ - public SpaceCustomerData|null $customer_data, - /** - * Customer key associated with the space. - */ - public string|null $customer_key, - /** - * Number of devices in the space. - */ - public float|null $device_count, - /** - * Display name for the space. - */ - public string|null $display_name, - /** - * Geographic coordinates (latitude and longitude) of the space. - */ - public SpaceGeolocation|null $geolocation, - /** - * Name of the space. - */ - public string|null $name, - /** - * ID of the space. - */ - public string|null $space_id, - /** - * Unique key for the space within the workspace. - */ - public string|null $space_key, - /** - * ID of the workspace associated with the space. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Number of entrances in the space. + */ + public float|null $acs_entrance_count, + /** + * Date and time at which the space was created. + */ + public string|null $created_at, + /** + * Reservation/stay-related defaults for the space. Also carries the provider/PMS-supplied name under a `_name` key (e.g. `guesty_name`), which Seam preserves when you rename the space (read-only — managed by Seam). + */ + public Space\CustomerData|null $customer_data, + /** + * Customer key associated with the space. + */ + public string|null $customer_key, + /** + * Number of devices in the space. + */ + public float|null $device_count, + /** + * Display name for the space. + */ + public string|null $display_name, + /** + * Geographic coordinates (latitude and longitude) of the space. + */ + public Space\Geolocation|null $geolocation, + /** + * Name of the space. + */ + public string|null $name, + /** + * ID of the space. + */ + public string|null $space_id, + /** + * Unique key for the space within the workspace. + */ + public string|null $space_key, + /** + * ID of the workspace associated with the space. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Reservation/stay-related defaults for the space. Also carries the provider/PMS-supplied name under a `_name` key (e.g. `guesty_name`), which Seam preserves when you rename the space (read-only — managed by Seam). - */ -class SpaceCustomerData -{ - public static function from_json(mixed $json): SpaceCustomerData|null +namespace Seam\Resources\Space { + /** + * Reservation/stay-related defaults for the space. Also carries the provider/PMS-supplied name under a `_name` key (e.g. `guesty_name`), which Seam preserves when you rename the space (read-only — managed by Seam). + */ + class CustomerData { - if (!$json) { - return null; + public static function from_json(mixed $json): CustomerData|null + { + if (!$json) { + return null; + } + return new self( + address: $json->address ?? null, + default_checkin_time: $json->default_checkin_time ?? null, + default_checkout_time: $json->default_checkout_time ?? null, + time_zone: $json->time_zone ?? null, + ); } - return new self( - address: $json->address ?? null, - default_checkin_time: $json->default_checkin_time ?? null, - default_checkout_time: $json->default_checkout_time ?? null, - time_zone: $json->time_zone ?? null, - ); - } - public function __construct( - /** - * Postal address for the space. - */ - public string|null $address, - /** - * Default check-in time for reservations at the space, as HH:mm or HH:mm:ss. - */ - public string|null $default_checkin_time, - /** - * Default check-out time for reservations at the space, as HH:mm or HH:mm:ss. - */ - public string|null $default_checkout_time, - /** - * IANA time zone for the space, e.g. America/Los_Angeles. - */ - public string|null $time_zone, - ) {} -} + public function __construct( + /** + * Postal address for the space. + */ + public string|null $address, + /** + * Default check-in time for reservations at the space, as HH:mm or HH:mm:ss. + */ + public string|null $default_checkin_time, + /** + * Default check-out time for reservations at the space, as HH:mm or HH:mm:ss. + */ + public string|null $default_checkout_time, + /** + * IANA time zone for the space, e.g. America/Los_Angeles. + */ + public string|null $time_zone, + ) {} + } -/** - * Geographic coordinates (latitude and longitude) of the space. - */ -class SpaceGeolocation -{ - public static function from_json(mixed $json): SpaceGeolocation|null + /** + * Geographic coordinates (latitude and longitude) of the space. + */ + class Geolocation { - if (!$json) { - return null; + public static function from_json(mixed $json): Geolocation|null + { + if (!$json) { + return null; + } + return new self( + latitude: $json->latitude ?? null, + longitude: $json->longitude ?? null, + ); } - return new self( - latitude: $json->latitude ?? null, - longitude: $json->longitude ?? null, - ); - } - public function __construct( - /** - * Latitude of the space, in decimal degrees. - */ - public float|null $latitude, - /** - * Longitude of the space, in decimal degrees. - */ - public float|null $longitude, - ) {} + public function __construct( + /** + * Latitude of the space, in decimal degrees. + */ + public float|null $latitude, + /** + * Longitude of the space, in decimal degrees. + */ + public float|null $longitude, + ) {} + } } diff --git a/src/Resources/ThermostatDailyProgram.php b/src/Resources/ThermostatDailyProgram.php index 1232a323..1733a4dc 100644 --- a/src/Resources/ThermostatDailyProgram.php +++ b/src/Resources/ThermostatDailyProgram.php @@ -1,84 +1,86 @@ created_at ?? null, + device_id: $json->device_id ?? null, + name: $json->name ?? null, + periods: array_map( + fn($p) => ThermostatDailyProgram\Periods::from_json($p), + $json->periods ?? [], + ), + thermostat_daily_program_id: $json->thermostat_daily_program_id ?? + null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - name: $json->name ?? null, - periods: array_map( - fn($p) => ThermostatDailyProgramPeriods::from_json($p), - $json->periods ?? [], - ), - thermostat_daily_program_id: $json->thermostat_daily_program_id ?? - null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Date and time at which the thermostat daily program was created. - */ - public string|null $created_at, - /** - * ID of the thermostat device on which the thermostat daily program is configured. - */ - public string|null $device_id, - /** - * User-friendly name to identify the thermostat daily program. - */ - public string|null $name, - /** - * Array of thermostat daily program periods. - */ - public array $periods, - /** - * ID of the thermostat daily program. - */ - public string|null $thermostat_daily_program_id, - /** - * ID of the workspace that contains the thermostat daily program. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Date and time at which the thermostat daily program was created. + */ + public string|null $created_at, + /** + * ID of the thermostat device on which the thermostat daily program is configured. + */ + public string|null $device_id, + /** + * User-friendly name to identify the thermostat daily program. + */ + public string|null $name, + /** + * Array of thermostat daily program periods. + */ + public array $periods, + /** + * ID of the thermostat daily program. + */ + public string|null $thermostat_daily_program_id, + /** + * ID of the workspace that contains the thermostat daily program. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Array of thermostat daily program periods. - */ -class ThermostatDailyProgramPeriods -{ - public static function from_json( - mixed $json, - ): ThermostatDailyProgramPeriods|null { - if (!$json) { - return null; +namespace Seam\Resources\ThermostatDailyProgram { + /** + * Array of thermostat daily program periods. + */ + class Periods + { + public static function from_json(mixed $json): Periods|null + { + if (!$json) { + return null; + } + return new self( + climate_preset_key: $json->climate_preset_key ?? null, + starts_at_time: $json->starts_at_time ?? null, + ); } - return new self( - climate_preset_key: $json->climate_preset_key ?? null, - starts_at_time: $json->starts_at_time ?? null, - ); - } - public function __construct( - /** - * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to activate at the `starts_at_time`. - */ - public string|null $climate_preset_key, - /** - * Time at which the thermostat daily program period starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at_time, - ) {} + public function __construct( + /** + * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to activate at the `starts_at_time`. + */ + public string|null $climate_preset_key, + /** + * Time at which the thermostat daily program period starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at_time, + ) {} + } } diff --git a/src/Resources/ThermostatSchedule.php b/src/Resources/ThermostatSchedule.php index 6c44947c..9d719a87 100644 --- a/src/Resources/ThermostatSchedule.php +++ b/src/Resources/ThermostatSchedule.php @@ -1,113 +1,115 @@ climate_preset_key ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => ThermostatSchedule\Errors::from_json($e), + $json->errors ?? [], + ), + is_override_allowed: $json->is_override_allowed ?? null, + max_override_period_minutes: $json->max_override_period_minutes ?? + null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + thermostat_schedule_id: $json->thermostat_schedule_id ?? null, + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - climate_preset_key: $json->climate_preset_key ?? null, - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => ThermostatScheduleErrors::from_json($e), - $json->errors ?? [], - ), - is_override_allowed: $json->is_override_allowed ?? null, - max_override_period_minutes: $json->max_override_period_minutes ?? - null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - thermostat_schedule_id: $json->thermostat_schedule_id ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to use for the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $climate_preset_key, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) was created. - */ - public string|null $created_at, - /** - * ID of the desired [thermostat](https://docs.seam.co/capability-guides/thermostats) device. - */ - public string|null $device_id, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $ends_at, - /** - * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public array $errors, - /** - * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. - */ - public bool|null $is_override_allowed, - /** - * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). - */ - public int|null $max_override_period_minutes, - /** - * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $name, - /** - * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. - */ - public string|null $starts_at, - /** - * ID of the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ - public string|null $thermostat_schedule_id, - /** - * ID of the workspace that contains the thermostat schedule. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Key of the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) to use for the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $climate_preset_key, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) was created. + */ + public string|null $created_at, + /** + * ID of the desired [thermostat](https://docs.seam.co/capability-guides/thermostats) device. + */ + public string|null $device_id, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $ends_at, + /** + * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public array $errors, + /** + * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. + */ + public bool|null $is_override_allowed, + /** + * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). + */ + public int|null $max_override_period_minutes, + /** + * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $name, + /** + * Date and time at which the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. + */ + public string|null $starts_at, + /** + * ID of the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + public string|null $thermostat_schedule_id, + /** + * ID of the workspace that contains the thermostat schedule. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). - */ -class ThermostatScheduleErrors -{ - public static function from_json(mixed $json): ThermostatScheduleErrors|null +namespace Seam\Resources\ThermostatSchedule { + /** + * Errors associated with the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } } diff --git a/src/Resources/UnmanagedAccessCode.php b/src/Resources/UnmanagedAccessCode.php index 4804b688..5855b3ac 100644 --- a/src/Resources/UnmanagedAccessCode.php +++ b/src/Resources/UnmanagedAccessCode.php @@ -1,340 +1,373 @@ access_code_id ?? null, - cannot_be_managed: $json->cannot_be_managed ?? null, - cannot_delete_unmanaged_access_code: $json->cannot_delete_unmanaged_access_code ?? - null, - code: $json->code ?? null, - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - dormakaba_oracode_metadata: isset($json->dormakaba_oracode_metadata) - ? UnmanagedAccessCodeDormakabaOracodeMetadata::from_json( + public static function from_json(mixed $json): UnmanagedAccessCode|null + { + if (!$json) { + return null; + } + return new self( + access_code_id: $json->access_code_id ?? null, + cannot_be_managed: $json->cannot_be_managed ?? null, + cannot_delete_unmanaged_access_code: $json->cannot_delete_unmanaged_access_code ?? + null, + code: $json->code ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + dormakaba_oracode_metadata: isset( $json->dormakaba_oracode_metadata, ) - : null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => UnmanagedAccessCodeErrors::from_json($e), - $json->errors ?? [], - ), - is_managed: $json->is_managed ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, - status: $json->status ?? null, - type: $json->type ?? null, - warnings: array_map( - fn($w) => UnmanagedAccessCodeWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } + ? UnmanagedAccessCode\DormakabaOracodeMetadata::from_json( + $json->dormakaba_oracode_metadata, + ) + : null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => UnmanagedAccessCode\Errors::from_json($e), + $json->errors ?? [], + ), + is_managed: $json->is_managed ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + status: $json->status ?? null, + type: $json->type ?? null, + warnings: array_map( + fn($w) => UnmanagedAccessCode\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); + } - public function __construct( - /** - * Unique identifier for the access code. - */ - public string|null $access_code_id, - /** - * Indicates that Seam cannot convert this unmanaged access code to a managed access code. Some providers do not support management of unmanaged access codes through API integrations. - */ - public bool|null $cannot_be_managed, - /** - * Indicates that Seam cannot delete this unmanaged access code through the provider. If this access code needs to be deleted, it will only be possible from the manufacturer app. - */ - public bool|null $cannot_delete_unmanaged_access_code, - /** - * Code used for access. Typically, a numeric or alphanumeric string. - */ - public string|null $code, - /** - * Date and time at which the access code was created. - */ - public string|null $created_at, - /** - * Unique identifier for the device associated with the access code. - */ - public string|null $device_id, - /** - * Metadata for a dormakaba Oracode unmanaged access code. Only present for unmanaged access codes from dormakaba Oracode devices. - */ - public UnmanagedAccessCodeDormakabaOracodeMetadata|null $dormakaba_oracode_metadata, - /** - * Date and time after which the time-bound access code becomes inactive. - */ - public string|null $ends_at, - /** - * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ - public array $errors, - /** - * Indicates that Seam does not manage the access code. - */ - public bool|null $is_managed, - /** - * Name of the access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). - */ - public string|null $name, - /** - * Date and time at which the time-bound access code becomes active. - */ - public string|null $starts_at, - /** - * Current status of the access code within the operational lifecycle. `set` indicates that the code is active and operational. `unset` indicates that the code exists on the provider but is not usable on the device. - */ - public string|null $status, - /** - * Type of the access code. `ongoing` access codes are active continuously until deactivated manually. `time_bound` access codes have a specific duration. - */ - public string|null $type, - /** - * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ - public array $warnings, - /** - * Unique identifier for the Seam workspace associated with the access code. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Unique identifier for the access code. + */ + public string|null $access_code_id, + /** + * Indicates that Seam cannot convert this unmanaged access code to a managed access code. Some providers do not support management of unmanaged access codes through API integrations. + */ + public bool|null $cannot_be_managed, + /** + * Indicates that Seam cannot delete this unmanaged access code through the provider. If this access code needs to be deleted, it will only be possible from the manufacturer app. + */ + public bool|null $cannot_delete_unmanaged_access_code, + /** + * Code used for access. Typically, a numeric or alphanumeric string. + */ + public string|null $code, + /** + * Date and time at which the access code was created. + */ + public string|null $created_at, + /** + * Unique identifier for the device associated with the access code. + */ + public string|null $device_id, + /** + * Metadata for a dormakaba Oracode unmanaged access code. Only present for unmanaged access codes from dormakaba Oracode devices. + */ + public UnmanagedAccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata, + /** + * Date and time after which the time-bound access code becomes inactive. + */ + public string|null $ends_at, + /** + * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + public array $errors, + /** + * Indicates that Seam does not manage the access code. + */ + public bool|null $is_managed, + /** + * Name of the access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). + */ + public string|null $name, + /** + * Date and time at which the time-bound access code becomes active. + */ + public string|null $starts_at, + /** + * Current status of the access code within the operational lifecycle. `set` indicates that the code is active and operational. `unset` indicates that the code exists on the provider but is not usable on the device. + */ + public string|null $status, + /** + * Type of the access code. `ongoing` access codes are active continuously until deactivated manually. `time_bound` access codes have a specific duration. + */ + public string|null $type, + /** + * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + public array $warnings, + /** + * Unique identifier for the Seam workspace associated with the access code. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Metadata for a dormakaba Oracode unmanaged access code. Only present for unmanaged access codes from dormakaba Oracode devices. - */ -class UnmanagedAccessCodeDormakabaOracodeMetadata -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessCodeDormakabaOracodeMetadata|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessCode { + /** + * Metadata for a dormakaba Oracode unmanaged access code. Only present for unmanaged access codes from dormakaba Oracode devices. + */ + class DormakabaOracodeMetadata + { + public static function from_json( + mixed $json, + ): DormakabaOracodeMetadata|null { + if (!$json) { + return null; + } + return new self( + is_cancellable: $json->is_cancellable ?? null, + is_early_checkin_able: $json->is_early_checkin_able ?? null, + is_extendable: $json->is_extendable ?? null, + is_overridable: $json->is_overridable ?? null, + site_name: $json->site_name ?? null, + stay_id: $json->stay_id ?? null, + user_level_id: $json->user_level_id ?? null, + user_level_name: $json->user_level_name ?? null, + ); } - return new self( - is_cancellable: $json->is_cancellable ?? null, - is_early_checkin_able: $json->is_early_checkin_able ?? null, - is_extendable: $json->is_extendable ?? null, - is_overridable: $json->is_overridable ?? null, - site_name: $json->site_name ?? null, - stay_id: $json->stay_id ?? null, - user_level_id: $json->user_level_id ?? null, - user_level_name: $json->user_level_name ?? null, - ); - } - public function __construct( - /** - * Indicates whether the stay can be cancelled via the Dormakaba Oracode API. - */ - public bool|null $is_cancellable, - /** - * Indicates whether early check-in is available for this stay. - */ - public bool|null $is_early_checkin_able, - /** - * Indicates whether the stay can be extended via the Dormakaba Oracode API. - */ - public bool|null $is_extendable, - /** - * Indicates whether the access code can be overridden. When false, the maximum number of overrides has been reached. - */ - public bool|null $is_overridable, - /** - * Dormakaba Oracode site name associated with this access code. - */ - public string|null $site_name, - /** - * Dormakaba Oracode stay ID associated with this access code. - */ - public float|null $stay_id, - /** - * Dormakaba Oracode user level ID associated with this access code. - */ - public string|null $user_level_id, - /** - * Dormakaba Oracode user level name associated with this access code. - */ - public string|null $user_level_name, - ) {} -} + public function __construct( + /** + * Indicates whether the stay can be cancelled via the Dormakaba Oracode API. + */ + public bool|null $is_cancellable, + /** + * Indicates whether early check-in is available for this stay. + */ + public bool|null $is_early_checkin_able, + /** + * Indicates whether the stay can be extended via the Dormakaba Oracode API. + */ + public bool|null $is_extendable, + /** + * Indicates whether the access code can be overridden. When false, the maximum number of overrides has been reached. + */ + public bool|null $is_overridable, + /** + * Dormakaba Oracode site name associated with this access code. + */ + public string|null $site_name, + /** + * Dormakaba Oracode stay ID associated with this access code. + */ + public float|null $stay_id, + /** + * Dormakaba Oracode user level ID associated with this access code. + */ + public string|null $user_level_id, + /** + * Dormakaba Oracode user level name associated with this access code. + */ + public string|null $user_level_name, + ) {} + } -/** - * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ -class UnmanagedAccessCodeErrors -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessCodeErrors|null { - if (!$json) { - return null; + /** + * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + change_type: $json->change_type ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_access_code_error: $json->is_access_code_error ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + managed_access_code_id: $json->managed_access_code_id ?? null, + message: $json->message ?? null, + modified_fields: array_map( + fn($m) => Errors\ModifiedFields::from_json($m), + $json->modified_fields ?? [], + ), + unmanaged_access_code_id: $json->unmanaged_access_code_id ?? + null, + ); } - return new self( - change_type: $json->change_type ?? null, - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_access_code_error: $json->is_access_code_error ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - is_device_error: $json->is_device_error ?? null, - managed_access_code_id: $json->managed_access_code_id ?? null, - message: $json->message ?? null, - modified_fields: array_map( - fn($m) => UnmanagedAccessCodeModifiedFields::from_json($m), - $json->modified_fields ?? [], - ), - unmanaged_access_code_id: $json->unmanaged_access_code_id ?? null, - ); + + public function __construct( + /** + * Indicates the type of external modification. `modified` means the code's PIN or schedule was changed. `removed` means the code was deleted from the device. + */ + public string|null $change_type, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Indicates that this is an access code error. + */ + public bool|null $is_access_code_error, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error, + public bool|null $is_connected_account_error, + public bool|null $is_device_error, + /** + * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. + */ + public string|null $managed_access_code_id, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * List of fields that were changed externally, with their previous and new values. + */ + public array $modified_fields, + /** + * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. + */ + public string|null $unmanaged_access_code_id, + ) {} } - public function __construct( - /** - * Indicates the type of external modification. `modified` means the code's PIN or schedule was changed. `removed` means the code was deleted from the device. - */ - public string|null $change_type, - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Indicates that this is an access code error. - */ - public bool|null $is_access_code_error, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error, - /** - * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. - */ - public bool|null $is_connected_account_error, - /** - * Indicates that the error is not a device error. - */ - public bool|null $is_device_error, - /** - * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. - */ - public string|null $managed_access_code_id, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * List of fields that were changed externally, with their previous and new values. - */ - public array $modified_fields, - /** - * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. - */ - public string|null $unmanaged_access_code_id, - ) {} + /** + * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + change_type: $json->change_type ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + modified_fields: array_map( + fn($m) => Warnings\ModifiedFields::from_json($m), + $json->modified_fields ?? [], + ), + warning_code: $json->warning_code ?? null, + ); + } + + public function __construct( + /** + * Indicates the type of external modification. `modified` means the code's PIN or schedule was changed. `removed` means the code was deleted from the device. + */ + public string|null $change_type, + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * List of fields that were changed externally, with their previous and new values. + */ + public array $modified_fields, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * List of fields that were changed externally, with their previous and new values. - */ -class UnmanagedAccessCodeModifiedFields -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessCodeModifiedFields|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessCode\Errors { + /** + * List of fields that were changed externally, with their previous and new values. + */ + class ModifiedFields + { + public static function from_json(mixed $json): ModifiedFields|null + { + if (!$json) { + return null; + } + return new self( + field: $json->field ?? null, + from: $json->from ?? null, + to: $json->to ?? null, + ); } - return new self( - field: $json->field ?? null, - from: $json->from ?? null, - to: $json->to ?? null, - ); - } - public function __construct( - /** - * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). - */ - public string|null $field, - /** - * The previous value of the field. - */ - public string|null $from, - /** - * The new value of the field. - */ - public string|null $to, - ) {} + public function __construct( + /** + * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + */ + public string|null $field, + /** + * The previous value of the field. + */ + public string|null $from, + /** + * The new value of the field. + */ + public string|null $to, + ) {} + } } -/** - * Warnings associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). - */ -class UnmanagedAccessCodeWarnings -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessCodeWarnings|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessCode\Warnings { + /** + * List of fields that were changed externally, with their previous and new values. + */ + class ModifiedFields + { + public static function from_json(mixed $json): ModifiedFields|null + { + if (!$json) { + return null; + } + return new self( + field: $json->field ?? null, + from: $json->from ?? null, + to: $json->to ?? null, + ); } - return new self( - change_type: $json->change_type ?? null, - created_at: $json->created_at ?? null, - message: $json->message ?? null, - modified_fields: array_map( - fn($m) => UnmanagedAccessCodeModifiedFields::from_json($m), - $json->modified_fields ?? [], - ), - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Indicates the type of external modification. `modified` means the code's PIN or schedule was changed. `removed` means the code was deleted from the device. - */ - public string|null $change_type, - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * List of fields that were changed externally, with their previous and new values. - */ - public array $modified_fields, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * The name of the field that was changed (e.g. `code`, `starts_at`, `ends_at`). + */ + public string|null $field, + /** + * The previous value of the field. + */ + public string|null $from, + /** + * The new value of the field. + */ + public string|null $to, + ) {} + } } diff --git a/src/Resources/UnmanagedAccessGrant.php b/src/Resources/UnmanagedAccessGrant.php index 6161249d..cc079819 100644 --- a/src/Resources/UnmanagedAccessGrant.php +++ b/src/Resources/UnmanagedAccessGrant.php @@ -1,428 +1,418 @@ access_grant_id ?? null, - access_method_ids: $json->access_method_ids ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - ends_at: $json->ends_at ?? null, - errors: array_map( - fn($e) => UnmanagedAccessGrantErrors::from_json($e), - $json->errors ?? [], - ), - location_ids: $json->location_ids ?? null, - name: $json->name ?? null, - pending_mutations: array_map( - fn($p) => UnmanagedAccessGrantPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - requested_access_methods: array_map( - fn($r) => UnmanagedAccessGrantRequestedAccessMethods::from_json( - $r, + public static function from_json(mixed $json): UnmanagedAccessGrant|null + { + if (!$json) { + return null; + } + return new self( + access_grant_id: $json->access_grant_id ?? null, + access_method_ids: $json->access_method_ids ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + ends_at: $json->ends_at ?? null, + errors: array_map( + fn($e) => UnmanagedAccessGrant\Errors::from_json($e), + $json->errors ?? [], ), - $json->requested_access_methods ?? [], - ), - reservation_key: $json->reservation_key ?? null, - space_ids: $json->space_ids ?? null, - starts_at: $json->starts_at ?? null, - user_identity_id: $json->user_identity_id ?? null, - warnings: array_map( - fn($w) => UnmanagedAccessGrantWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } + location_ids: $json->location_ids ?? null, + name: $json->name ?? null, + pending_mutations: array_map( + fn($p) => UnmanagedAccessGrant\PendingMutations::from_json( + $p, + ), + $json->pending_mutations ?? [], + ), + requested_access_methods: array_map( + fn( + $r, + ) => UnmanagedAccessGrant\RequestedAccessMethods::from_json( + $r, + ), + $json->requested_access_methods ?? [], + ), + reservation_key: $json->reservation_key ?? null, + space_ids: $json->space_ids ?? null, + starts_at: $json->starts_at ?? null, + user_identity_id: $json->user_identity_id ?? null, + warnings: array_map( + fn($w) => UnmanagedAccessGrant\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); + } - public function __construct( - /** - * ID of the Access Grant. - */ - public string|null $access_grant_id, - /** - * IDs of the access methods created for the Access Grant. - */ - public array|null $access_method_ids, - /** - * Date and time at which the Access Grant was created. - */ - public string|null $created_at, - /** - * Display name of the Access Grant. - */ - public string|null $display_name, - /** - * Date and time at which the Access Grant ends. - */ - public string|null $ends_at, - /** - * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ - public array $errors, - /** - * @deprecated Use `space_ids`. - */ - public array|null $location_ids, - /** - * Name of the Access Grant. If not provided, the display name will be computed. - */ - public string|null $name, - /** - * List of pending mutations for the access grant. This shows updates that are in progress. - */ - public array $pending_mutations, - /** - * Access methods that the user requested for the Access Grant. - */ - public array $requested_access_methods, - /** - * Reservation key for the access grant. - */ - public string|null $reservation_key, - /** - * IDs of the spaces to which the Access Grant gives access. - */ - public array|null $space_ids, - /** - * Date and time at which the Access Grant starts. - */ - public string|null $starts_at, - /** - * ID of user identity to which the Access Grant gives access. - */ - public string|null $user_identity_id, - /** - * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ - public array $warnings, - /** - * ID of the Seam workspace associated with the Access Grant. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the Access Grant. + */ + public string|null $access_grant_id, + /** + * IDs of the access methods created for the Access Grant. + */ + public array|null $access_method_ids, + /** + * Date and time at which the Access Grant was created. + */ + public string|null $created_at, + /** + * Display name of the Access Grant. + */ + public string|null $display_name, + /** + * Date and time at which the Access Grant ends. + */ + public string|null $ends_at, + /** + * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + public array $errors, + /** + * @deprecated Use `space_ids`. + */ + public array|null $location_ids, + /** + * Name of the Access Grant. If not provided, the display name will be computed. + */ + public string|null $name, + /** + * List of pending mutations for the access grant. This shows updates that are in progress. + */ + public array $pending_mutations, + /** + * Access methods that the user requested for the Access Grant. + */ + public array $requested_access_methods, + /** + * Reservation key for the access grant. + */ + public string|null $reservation_key, + /** + * IDs of the spaces to which the Access Grant gives access. + */ + public array|null $space_ids, + /** + * Date and time at which the Access Grant starts. + */ + public string|null $starts_at, + /** + * ID of user identity to which the Access Grant gives access. + */ + public string|null $user_identity_id, + /** + * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + public array $warnings, + /** + * ID of the Seam workspace associated with the Access Grant. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ -class UnmanagedAccessGrantErrors -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessGrantErrors|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessGrant { + /** + * Errors associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + missing_device_ids: $json->missing_device_ids ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - missing_device_ids: $json->missing_device_ids ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. - */ - public array|null $missing_device_ids, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. + */ + public array|null $missing_device_ids, + ) {} + } -/** - * Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). - */ -class UnmanagedAccessGrantFailedDevices -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessGrantFailedDevices|null { - if (!$json) { - return null; + /** + * List of pending mutations for the access grant. This shows updates that are in progress. + */ + class PendingMutations + { + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + access_method_ids: $json->access_method_ids ?? null, + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); } - return new self( - device_id: $json->device_id ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Device whose access code could not be revoked. - */ - public string|null $device_id, - /** - * Reason the access code could not be revoked (e.g. `offline_access_code_not_revocable`). - */ - public string|null $error_code, - /** - * Human-readable description of why revocation failed. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * IDs of the access methods being updated. + */ + public array|null $access_method_ids, + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + public PendingMutations\To|null $to, + ) {} + } -/** - * Previous location configuration. - */ -class UnmanagedAccessGrantFrom -{ - public static function from_json(mixed $json): UnmanagedAccessGrantFrom|null + /** + * Access methods that the user requested for the Access Grant. + */ + class RequestedAccessMethods { - if (!$json) { - return null; + public static function from_json( + mixed $json, + ): RequestedAccessMethods|null { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + created_access_method_ids: $json->created_access_method_ids ?? + null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + instant_key_max_use_count: $json->instant_key_max_use_count ?? + null, + mode: $json->mode ?? null, + ); } - return new self( - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Previous device IDs where access codes existed. - */ - public array|null $device_ids, - /** - * Previous end time for access. - */ - public string|null $ends_at, - /** - * Previous start time for access. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Specific PIN code to use for this access method. Only applicable when mode is 'code'. + */ + public string|null $code, + /** + * IDs of the access methods created for the requested access method. + */ + public array|null $created_access_method_ids, + /** + * Date and time at which the requested access method was added to the Access Grant. + */ + public string|null $created_at, + /** + * Display name of the access method. + */ + public string|null $display_name, + /** + * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. + */ + public int|null $instant_key_max_use_count, + /** + * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $mode, + ) {} + } -/** - * List of pending mutations for the access grant. This shows updates that are in progress. - */ -class UnmanagedAccessGrantPendingMutations -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessGrantPendingMutations|null { - if (!$json) { - return null; + /** + * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + access_method_ids: $json->access_method_ids ?? null, + created_at: $json->created_at ?? null, + device_id: $json->device_id ?? null, + failed_devices: array_map( + fn($f) => Warnings\FailedDevices::from_json($f), + $json->failed_devices ?? [], + ), + message: $json->message ?? null, + new_code: $json->new_code ?? null, + original_code: $json->original_code ?? null, + reason: $json->reason ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - access_method_ids: $json->access_method_ids ?? null, - created_at: $json->created_at ?? null, - from: isset($json->from) - ? UnmanagedAccessGrantFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) - ? UnmanagedAccessGrantTo::from_json($json->to) - : null, - ); - } - public function __construct( - /** - * IDs of the access methods being updated. - */ - public array|null $access_method_ids, - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Previous location configuration. - */ - public UnmanagedAccessGrantFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of updating the spaces (devices) associated with this access grant. - */ - public string|null $mutation_code, - /** - * New location configuration. - */ - public UnmanagedAccessGrantTo|null $to, - ) {} + public function __construct( + /** + * IDs of the access methods being updated. + */ + public array|null $access_method_ids, + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + public string|null $device_id, + /** + * Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). + */ + public array $failed_devices, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * The new PIN code that was assigned instead. + */ + public string|null $new_code, + /** + * The originally requested PIN code that was unavailable. + */ + public string|null $original_code, + /** + * Specific reason why the grant's times are not programmable on the device. + */ + public string|null $reason, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Access methods that the user requested for the Access Grant. - */ -class UnmanagedAccessGrantRequestedAccessMethods -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessGrantRequestedAccessMethods|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessGrant\PendingMutations { + class From + { + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - code: $json->code ?? null, - created_access_method_ids: $json->created_access_method_ids ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - instant_key_max_use_count: $json->instant_key_max_use_count ?? null, - mode: $json->mode ?? null, - ); - } - public function __construct( - /** - * Specific PIN code to use for this access method. Only applicable when mode is 'code'. - */ - public string|null $code, - /** - * IDs of the access methods created for the requested access method. - */ - public array|null $created_access_method_ids, - /** - * Date and time at which the requested access method was added to the Access Grant. - */ - public string|null $created_at, - /** - * Display name of the access method. - */ - public string|null $display_name, - /** - * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. - */ - public int|null $instant_key_max_use_count, - /** - * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $mode, - ) {} -} + public function __construct( + /** + * Previous device IDs where access codes existed. + */ + public array|null $device_ids, + /** + * Previous end time for access. + */ + public string|null $ends_at, + /** + * Previous start time for access. + */ + public string|null $starts_at, + ) {} + } -/** - * New location configuration. - */ -class UnmanagedAccessGrantTo -{ - public static function from_json(mixed $json): UnmanagedAccessGrantTo|null + class To { - if (!$json) { - return null; + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + common_code_key: $json->common_code_key ?? null, + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - common_code_key: $json->common_code_key ?? null, - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Common code key to ensure PIN code reuse across devices. - */ - public string|null $common_code_key, - /** - * New device IDs where access codes should be created. - */ - public array|null $device_ids, - /** - * New end time for access. - */ - public string|null $ends_at, - /** - * New start time for access. - */ - public string|null $starts_at, - ) {} + public function __construct( + /** + * Common code key to ensure PIN code reuse across devices. + */ + public string|null $common_code_key, + /** + * New device IDs where access codes should be created. + */ + public array|null $device_ids, + /** + * New end time for access. + */ + public string|null $ends_at, + /** + * New start time for access. + */ + public string|null $starts_at, + ) {} + } } -/** - * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). - */ -class UnmanagedAccessGrantWarnings -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessGrantWarnings|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessGrant\Warnings { + /** + * Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). + */ + class FailedDevices + { + public static function from_json(mixed $json): FailedDevices|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - access_method_ids: $json->access_method_ids ?? null, - created_at: $json->created_at ?? null, - device_id: $json->device_id ?? null, - failed_devices: array_map( - fn($f) => UnmanagedAccessGrantFailedDevices::from_json($f), - $json->failed_devices ?? [], - ), - message: $json->message ?? null, - new_code: $json->new_code ?? null, - original_code: $json->original_code ?? null, - reason: $json->reason ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * IDs of the access methods being updated. - */ - public array|null $access_method_ids, - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * ID of the device where the requested code was unavailable. - */ - public string|null $device_id, - /** - * Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). - */ - public array $failed_devices, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * The new PIN code that was assigned instead. - */ - public string|null $new_code, - /** - * The originally requested PIN code that was unavailable. - */ - public string|null $original_code, - /** - * Specific reason why the grant's times are not programmable on the device. - */ - public string|null $reason, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Device whose access code could not be revoked. + */ + public string|null $device_id, + /** + * Reason the access code could not be revoked (e.g. `offline_access_code_not_revocable`). + */ + public string|null $error_code, + /** + * Human-readable description of why revocation failed. + */ + public string|null $message, + ) {} + } } diff --git a/src/Resources/UnmanagedAccessMethod.php b/src/Resources/UnmanagedAccessMethod.php index b6ac79a7..ab715685 100644 --- a/src/Resources/UnmanagedAccessMethod.php +++ b/src/Resources/UnmanagedAccessMethod.php @@ -1,293 +1,276 @@ access_method_id ?? null, + code: $json->code ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + errors: array_map( + fn($e) => UnmanagedAccessMethod\Errors::from_json($e), + $json->errors ?? [], + ), + is_assignment_required: $json->is_assignment_required ?? null, + is_encoding_required: $json->is_encoding_required ?? null, + is_issued: $json->is_issued ?? null, + is_ready_for_assignment: $json->is_ready_for_assignment ?? null, + is_ready_for_encoding: $json->is_ready_for_encoding ?? null, + issued_at: $json->issued_at ?? null, + mode: $json->mode ?? null, + pending_mutations: array_map( + fn($p) => UnmanagedAccessMethod\PendingMutations::from_json( + $p, + ), + $json->pending_mutations ?? [], + ), + warnings: array_map( + fn($w) => UnmanagedAccessMethod\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - access_method_id: $json->access_method_id ?? null, - code: $json->code ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - errors: array_map( - fn($e) => UnmanagedAccessMethodErrors::from_json($e), - $json->errors ?? [], - ), - is_assignment_required: $json->is_assignment_required ?? null, - is_encoding_required: $json->is_encoding_required ?? null, - is_issued: $json->is_issued ?? null, - is_ready_for_assignment: $json->is_ready_for_assignment ?? null, - is_ready_for_encoding: $json->is_ready_for_encoding ?? null, - issued_at: $json->issued_at ?? null, - mode: $json->mode ?? null, - pending_mutations: array_map( - fn($p) => UnmanagedAccessMethodPendingMutations::from_json($p), - $json->pending_mutations ?? [], - ), - warnings: array_map( - fn($w) => UnmanagedAccessMethodWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * ID of the access method. - */ - public string|null $access_method_id, - /** - * The actual PIN code for code access methods. - */ - public string|null $code, - /** - * Date and time at which the access method was created. - */ - public string|null $created_at, - /** - * Display name of the access method. - */ - public string|null $display_name, - /** - * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ - public array $errors, - /** - * Indicates whether an existing card credential must be assigned to this access method before it can be issued. Only applies to card-mode access methods on systems that support credential assignment. - */ - public bool|null $is_assignment_required, - /** - * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. - */ - public bool|null $is_encoding_required, - /** - * Indicates whether the access method has been issued. - */ - public bool|null $is_issued, - /** - * Indicates whether the access method is ready for card assignment. This is true when the access method is in card mode, has not yet been issued, and the system supports credential assignment. - */ - public bool|null $is_ready_for_assignment, - /** - * Indicates whether the access method is ready to be encoded. This is true when the credential has been created and the card has not yet been issued. - */ - public bool|null $is_ready_for_encoding, - /** - * Date and time at which the access method was issued. - */ - public string|null $issued_at, - /** - * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. - */ - public string|null $mode, - /** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ - public array $pending_mutations, - /** - * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ - public array $warnings, - /** - * ID of the Seam workspace associated with the access method. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * ID of the access method. + */ + public string|null $access_method_id, + /** + * The actual PIN code for code access methods. + */ + public string|null $code, + /** + * Date and time at which the access method was created. + */ + public string|null $created_at, + /** + * Display name of the access method. + */ + public string|null $display_name, + /** + * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + public array $errors, + /** + * Indicates whether an existing card credential must be assigned to this access method before it can be issued. Only applies to card-mode access methods on systems that support credential assignment. + */ + public bool|null $is_assignment_required, + /** + * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. + */ + public bool|null $is_encoding_required, + /** + * Indicates whether the access method has been issued. + */ + public bool|null $is_issued, + /** + * Indicates whether the access method is ready for card assignment. This is true when the access method is in card mode, has not yet been issued, and the system supports credential assignment. + */ + public bool|null $is_ready_for_assignment, + /** + * Indicates whether the access method is ready to be encoded. This is true when the credential has been created and the card has not yet been issued. + */ + public bool|null $is_ready_for_encoding, + /** + * Date and time at which the access method was issued. + */ + public string|null $issued_at, + /** + * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. + */ + public string|null $mode, + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + public array $pending_mutations, + /** + * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + public array $warnings, + /** + * ID of the Seam workspace associated with the access method. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ -class UnmanagedAccessMethodErrors -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessMethodErrors|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedAccessMethod { + /** + * Errors associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Previous device configuration. - */ -class UnmanagedAccessMethodFrom -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessMethodFrom|null { - if (!$json) { - return null; + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + class PendingMutations + { + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); } - return new self( - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * Previous device IDs where access was provisioned. - */ - public array|null $device_ids, - /** - * Previous end time for access. - */ - public string|null $ends_at, - /** - * Previous start time for access. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + public string|null $mutation_code, + public PendingMutations\To|null $to, + ) {} + } -/** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ -class UnmanagedAccessMethodPendingMutations -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessMethodPendingMutations|null { - if (!$json) { - return null; + /** + * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + original_access_method_id: $json->original_access_method_id ?? + null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - from: isset($json->from) - ? UnmanagedAccessMethodFrom::from_json($json->from) - : null, - message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) - ? UnmanagedAccessMethodTo::from_json($json->to) - : null, - ); - } - public function __construct( - /** - * Date and time at which the mutation was created. - */ - public string|null $created_at, - /** - * Previous device configuration. - */ - public UnmanagedAccessMethodFrom|null $from, - /** - * Detailed description of the mutation. - */ - public string|null $message, - /** - * Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. - */ - public string|null $mutation_code, - /** - * New device configuration. - */ - public UnmanagedAccessMethodTo|null $to, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * ID of the original access method from which this backup access method was split, if applicable. + */ + public string|null $original_access_method_id, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * New device configuration. - */ -class UnmanagedAccessMethodTo -{ - public static function from_json(mixed $json): UnmanagedAccessMethodTo|null +namespace Seam\Resources\UnmanagedAccessMethod\PendingMutations { + class From { - if (!$json) { - return null; + public static function from_json(mixed $json): From|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - device_ids: $json->device_ids ?? null, - ends_at: $json->ends_at ?? null, - starts_at: $json->starts_at ?? null, - ); - } - public function __construct( - /** - * New device IDs where access is being provisioned. - */ - public array|null $device_ids, - /** - * New end time for access. - */ - public string|null $ends_at, - /** - * New start time for access. - */ - public string|null $starts_at, - ) {} -} + public function __construct( + public array|null $device_ids, + /** + * Previous end time for access. + */ + public string|null $ends_at, + /** + * Previous start time for access. + */ + public string|null $starts_at, + ) {} + } -/** - * Warnings associated with the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). - */ -class UnmanagedAccessMethodWarnings -{ - public static function from_json( - mixed $json, - ): UnmanagedAccessMethodWarnings|null { - if (!$json) { - return null; + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + device_ids: $json->device_ids ?? null, + ends_at: $json->ends_at ?? null, + starts_at: $json->starts_at ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - original_access_method_id: $json->original_access_method_id ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * ID of the original access method from which this backup access method was split, if applicable. - */ - public string|null $original_access_method_id, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + public array|null $device_ids, + /** + * New end time for access. + */ + public string|null $ends_at, + /** + * New start time for access. + */ + public string|null $starts_at, + ) {} + } } diff --git a/src/Resources/UnmanagedDevice.php b/src/Resources/UnmanagedDevice.php index 0dc8fdd0..25d35b1e 100644 --- a/src/Resources/UnmanagedDevice.php +++ b/src/Resources/UnmanagedDevice.php @@ -1,523 +1,551 @@ can_configure_auto_lock ?? null, + can_hvac_cool: $json->can_hvac_cool ?? null, + can_hvac_heat: $json->can_hvac_heat ?? null, + can_hvac_heat_cool: $json->can_hvac_heat_cool ?? null, + can_program_offline_access_codes: $json->can_program_offline_access_codes ?? + null, + can_program_online_access_codes: $json->can_program_online_access_codes ?? + null, + can_program_thermostat_programs_as_different_each_day: $json->can_program_thermostat_programs_as_different_each_day ?? + null, + can_program_thermostat_programs_as_same_each_day: $json->can_program_thermostat_programs_as_same_each_day ?? + null, + can_program_thermostat_programs_as_weekday_weekend: $json->can_program_thermostat_programs_as_weekday_weekend ?? + null, + can_remotely_lock: $json->can_remotely_lock ?? null, + can_remotely_unlock: $json->can_remotely_unlock ?? null, + can_run_thermostat_programs: $json->can_run_thermostat_programs ?? + null, + can_simulate_connection: $json->can_simulate_connection ?? null, + can_simulate_disconnection: $json->can_simulate_disconnection ?? + null, + can_simulate_hub_connection: $json->can_simulate_hub_connection ?? + null, + can_simulate_hub_disconnection: $json->can_simulate_hub_disconnection ?? + null, + can_simulate_paid_subscription: $json->can_simulate_paid_subscription ?? + null, + can_simulate_removal: $json->can_simulate_removal ?? null, + can_turn_off_hvac: $json->can_turn_off_hvac ?? null, + can_unlock_with_code: $json->can_unlock_with_code ?? null, + capabilities_supported: $json->capabilities_supported ?? null, + connected_account_id: $json->connected_account_id ?? null, + created_at: $json->created_at ?? null, + custom_metadata: $json->custom_metadata ?? null, + device_id: $json->device_id ?? null, + device_type: $json->device_type ?? null, + errors: array_map( + fn($e) => UnmanagedDevice\Errors::from_json($e), + $json->errors ?? [], + ), + is_managed: $json->is_managed ?? null, + location: isset($json->location) + ? UnmanagedDevice\Location::from_json($json->location) + : null, + properties: isset($json->properties) + ? UnmanagedDevice\Properties::from_json($json->properties) + : null, + warnings: array_map( + fn($w) => UnmanagedDevice\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - can_configure_auto_lock: $json->can_configure_auto_lock ?? null, - can_hvac_cool: $json->can_hvac_cool ?? null, - can_hvac_heat: $json->can_hvac_heat ?? null, - can_hvac_heat_cool: $json->can_hvac_heat_cool ?? null, - can_program_offline_access_codes: $json->can_program_offline_access_codes ?? - null, - can_program_online_access_codes: $json->can_program_online_access_codes ?? - null, - can_program_thermostat_programs_as_different_each_day: $json->can_program_thermostat_programs_as_different_each_day ?? - null, - can_program_thermostat_programs_as_same_each_day: $json->can_program_thermostat_programs_as_same_each_day ?? - null, - can_program_thermostat_programs_as_weekday_weekend: $json->can_program_thermostat_programs_as_weekday_weekend ?? - null, - can_remotely_lock: $json->can_remotely_lock ?? null, - can_remotely_unlock: $json->can_remotely_unlock ?? null, - can_run_thermostat_programs: $json->can_run_thermostat_programs ?? - null, - can_simulate_connection: $json->can_simulate_connection ?? null, - can_simulate_disconnection: $json->can_simulate_disconnection ?? - null, - can_simulate_hub_connection: $json->can_simulate_hub_connection ?? - null, - can_simulate_hub_disconnection: $json->can_simulate_hub_disconnection ?? - null, - can_simulate_paid_subscription: $json->can_simulate_paid_subscription ?? - null, - can_simulate_removal: $json->can_simulate_removal ?? null, - can_turn_off_hvac: $json->can_turn_off_hvac ?? null, - can_unlock_with_code: $json->can_unlock_with_code ?? null, - capabilities_supported: $json->capabilities_supported ?? null, - connected_account_id: $json->connected_account_id ?? null, - created_at: $json->created_at ?? null, - custom_metadata: $json->custom_metadata ?? null, - device_id: $json->device_id ?? null, - device_type: $json->device_type ?? null, - errors: array_map( - fn($e) => UnmanagedDeviceErrors::from_json($e), - $json->errors ?? [], - ), - is_managed: $json->is_managed ?? null, - location: isset($json->location) - ? UnmanagedDeviceLocation::from_json($json->location) - : null, - properties: isset($json->properties) - ? UnmanagedDeviceProperties::from_json($json->properties) - : null, - warnings: array_map( - fn($w) => UnmanagedDeviceWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Indicates whether the lock supports configuring automatic locking. - */ - public bool|null $can_configure_auto_lock, - /** - * Indicates whether the thermostat supports cooling. - */ - public bool|null $can_hvac_cool, - /** - * Indicates whether the thermostat supports heating. - */ - public bool|null $can_hvac_heat, - /** - * Indicates whether the thermostat supports simultaneous heating and cooling. - */ - public bool|null $can_hvac_heat_cool, - /** - * Indicates whether the device supports programming offline access codes. - */ - public bool|null $can_program_offline_access_codes, - /** - * Indicates whether the device supports programming online access codes. - */ - public bool|null $can_program_online_access_codes, - /** - * Indicates whether the thermostat supports different climate programs for each day of the week. - */ - public bool|null $can_program_thermostat_programs_as_different_each_day, - /** - * Indicates whether the thermostat supports a single climate program applied to every day. - */ - public bool|null $can_program_thermostat_programs_as_same_each_day, - /** - * Indicates whether the thermostat supports weekday/weekend climate programs. - */ - public bool|null $can_program_thermostat_programs_as_weekday_weekend, - /** - * Indicates whether the device supports remote locking. - */ - public bool|null $can_remotely_lock, - /** - * Indicates whether the device supports remote unlocking. - */ - public bool|null $can_remotely_unlock, - /** - * Indicates whether the thermostat supports running climate programs. - */ - public bool|null $can_run_thermostat_programs, - /** - * Indicates whether the device supports simulating connection in a sandbox. - */ - public bool|null $can_simulate_connection, - /** - * Indicates whether the device supports simulating disconnection in a sandbox. - */ - public bool|null $can_simulate_disconnection, - /** - * Indicates whether the hub supports simulating connection in a sandbox. - */ - public bool|null $can_simulate_hub_connection, - /** - * Indicates whether the hub supports simulating disconnection in a sandbox. - */ - public bool|null $can_simulate_hub_disconnection, - /** - * Indicates whether the device supports simulating a paid subscription in a sandbox. - */ - public bool|null $can_simulate_paid_subscription, - /** - * Indicates whether the device supports simulating removal in a sandbox. - */ - public bool|null $can_simulate_removal, - /** - * Indicates whether the thermostat can be turned off. - */ - public bool|null $can_turn_off_hvac, - /** - * Indicates whether the lock supports unlocking with an access code. - */ - public bool|null $can_unlock_with_code, - /** - * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). - */ - public array|null $capabilities_supported, - /** - * Unique identifier for the account associated with the device. - */ - public string|null $connected_account_id, - /** - * Date and time at which the device object was created. - */ - public string|null $created_at, - /** - * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. - */ - public mixed $custom_metadata, - /** - * ID of the device. - */ - public string|null $device_id, - /** - * Type of the device. - */ - public string|null $device_type, - /** - * Array of errors associated with the device. Each error object within the array contains two fields: `error_code` and `message`. `error_code` is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. - */ - public array $errors, - /** - * Indicates that Seam does not manage the device. - */ - public bool|null $is_managed, - /** - * Location information for the device. - */ - public UnmanagedDeviceLocation|null $location, - /** - * properties of the device. - */ - public UnmanagedDeviceProperties|null $properties, - /** - * Array of warnings associated with the device. Each warning object within the array contains two fields: `warning_code` and `message`. `warning_code` is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. - */ - public array $warnings, - /** - * Unique identifier for the Seam workspace associated with the device. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Indicates whether the lock supports configuring automatic locking. + */ + public bool|null $can_configure_auto_lock, + /** + * Indicates whether the thermostat supports cooling. + */ + public bool|null $can_hvac_cool, + /** + * Indicates whether the thermostat supports heating. + */ + public bool|null $can_hvac_heat, + /** + * Indicates whether the thermostat supports simultaneous heating and cooling. + */ + public bool|null $can_hvac_heat_cool, + /** + * Indicates whether the device supports programming offline access codes. + */ + public bool|null $can_program_offline_access_codes, + /** + * Indicates whether the device supports programming online access codes. + */ + public bool|null $can_program_online_access_codes, + /** + * Indicates whether the thermostat supports different climate programs for each day of the week. + */ + public bool|null $can_program_thermostat_programs_as_different_each_day, + /** + * Indicates whether the thermostat supports a single climate program applied to every day. + */ + public bool|null $can_program_thermostat_programs_as_same_each_day, + /** + * Indicates whether the thermostat supports weekday/weekend climate programs. + */ + public bool|null $can_program_thermostat_programs_as_weekday_weekend, + /** + * Indicates whether the device supports remote locking. + */ + public bool|null $can_remotely_lock, + /** + * Indicates whether the device supports remote unlocking. + */ + public bool|null $can_remotely_unlock, + /** + * Indicates whether the thermostat supports running climate programs. + */ + public bool|null $can_run_thermostat_programs, + /** + * Indicates whether the device supports simulating connection in a sandbox. + */ + public bool|null $can_simulate_connection, + /** + * Indicates whether the device supports simulating disconnection in a sandbox. + */ + public bool|null $can_simulate_disconnection, + /** + * Indicates whether the hub supports simulating connection in a sandbox. + */ + public bool|null $can_simulate_hub_connection, + /** + * Indicates whether the hub supports simulating disconnection in a sandbox. + */ + public bool|null $can_simulate_hub_disconnection, + /** + * Indicates whether the device supports simulating a paid subscription in a sandbox. + */ + public bool|null $can_simulate_paid_subscription, + /** + * Indicates whether the device supports simulating removal in a sandbox. + */ + public bool|null $can_simulate_removal, + /** + * Indicates whether the thermostat can be turned off. + */ + public bool|null $can_turn_off_hvac, + /** + * Indicates whether the lock supports unlocking with an access code. + */ + public bool|null $can_unlock_with_code, + /** + * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). + */ + public array|null $capabilities_supported, + /** + * Unique identifier for the account associated with the device. + */ + public string|null $connected_account_id, + /** + * Date and time at which the device object was created. + */ + public string|null $created_at, + /** + * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. + */ + public mixed $custom_metadata, + /** + * ID of the device. + */ + public string|null $device_id, + /** + * Type of the device. + */ + public string|null $device_type, + /** + * Array of errors associated with the device. Each error object within the array contains two fields: `error_code` and `message`. `error_code` is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. + */ + public array $errors, + /** + * Indicates that Seam does not manage the device. + */ + public bool|null $is_managed, + /** + * Location information for the device. + */ + public UnmanagedDevice\Location|null $location, + /** + * properties of the device. + */ + public UnmanagedDevice\Properties|null $properties, + /** + * Array of warnings associated with the device. Each warning object within the array contains two fields: `warning_code` and `message`. `warning_code` is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. + */ + public array $warnings, + /** + * Unique identifier for the Seam workspace associated with the device. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Accessory keypad properties and state. - */ -class UnmanagedDeviceAccessoryKeypad -{ - public static function from_json( - mixed $json, - ): UnmanagedDeviceAccessoryKeypad|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedDevice { + /** + * Array of errors associated with the device. Each error object within the array contains two fields: `error_code` and `message`. `error_code` is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + is_bridge_error: $json->is_bridge_error ?? null, + is_connected_account_error: $json->is_connected_account_error ?? + null, + is_device_error: $json->is_device_error ?? null, + message: $json->message ?? null, + ); } - return new self( - battery: isset($json->battery) - ? UnmanagedDeviceBattery::from_json($json->battery) - : null, - is_connected: $json->is_connected ?? null, - ); - } - public function __construct( - /** - * Keypad battery properties. - */ - public UnmanagedDeviceBattery|null $battery, - /** - * Indicates if an accessory keypad is connected to the device. - */ - public bool|null $is_connected, - ) {} -} + public function __construct( + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). + */ + public bool|null $is_bridge_error, + public bool|null $is_connected_account_error, + public bool|null $is_device_error, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Keypad battery properties. - */ -class UnmanagedDeviceBattery -{ - public static function from_json(mixed $json): UnmanagedDeviceBattery|null + /** + * Location information for the device. + */ + class Location { - if (!$json) { - return null; + public static function from_json(mixed $json): Location|null + { + if (!$json) { + return null; + } + return new self( + location_name: $json->location_name ?? null, + time_zone: $json->time_zone ?? null, + timezone: $json->timezone ?? null, + ); } - return new self(level: $json->level ?? null); - } - public function __construct(public float|null $level) {} -} + public function __construct( + /** + * Name of the device location. + */ + public string|null $location_name, + /** + * Time zone of the device location. + */ + public string|null $time_zone, + /** + * Time zone of the device location. + * + * @deprecated Use `time_zone` instead. + */ + public string|null $timezone, + ) {} + } -/** - * Array of errors associated with the device. Each error object within the array contains two fields: `error_code` and `message`. `error_code` is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. - */ -class UnmanagedDeviceErrors -{ - public static function from_json(mixed $json): UnmanagedDeviceErrors|null + /** + * properties of the device. + */ + class Properties { - if (!$json) { - return null; + public static function from_json(mixed $json): Properties|null + { + if (!$json) { + return null; + } + return new self( + accessory_keypad: isset($json->accessory_keypad) + ? Properties\AccessoryKeypad::from_json( + $json->accessory_keypad, + ) + : null, + battery: isset($json->battery) + ? Properties\Battery::from_json($json->battery) + : null, + battery_level: $json->battery_level ?? null, + image_alt_text: $json->image_alt_text ?? null, + image_url: $json->image_url ?? null, + manufacturer: $json->manufacturer ?? null, + model: isset($json->model) + ? Properties\Model::from_json($json->model) + : null, + name: $json->name ?? null, + offline_access_codes_enabled: $json->offline_access_codes_enabled ?? + null, + online: $json->online ?? null, + online_access_codes_enabled: $json->online_access_codes_enabled ?? + null, + ); } - return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - is_bridge_error: $json->is_bridge_error ?? null, - is_connected_account_error: $json->is_connected_account_error ?? - null, - is_device_error: $json->is_device_error ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). - */ - public bool|null $is_bridge_error, - /** - * Indicates that the error is a [connected account](https://docs.seam.co/api/connected_accounts) error. - */ - public bool|null $is_connected_account_error, - /** - * Indicates that the error is not a device error. - */ - public bool|null $is_device_error, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * Accessory keypad properties and state. + */ + public Properties\AccessoryKeypad|null $accessory_keypad, + /** + * Represents the current status of the battery charge level. + */ + public Properties\Battery|null $battery, + /** + * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. + */ + public float|null $battery_level, + /** + * Alt text for the device image. + */ + public string|null $image_alt_text, + /** + * Image URL for the device. + */ + public string|null $image_url, + /** + * Manufacturer of the device. When a device, such as a smart lock, is connected through a smart hub, the manufacturer of the device might be different from that of the smart hub. + */ + public string|null $manufacturer, + /** + * Device model-related properties. + */ + public Properties\Model|null $model, + /** + * Name of the device. + * + * @deprecated use device.display_name instead + */ + public string|null $name, + /** + * Indicates whether it is currently possible to use offline access codes for the device. + * + * @deprecated use device.can_program_offline_access_codes + */ + public bool|null $offline_access_codes_enabled, + /** + * Indicates whether the device is online. + */ + public bool|null $online, + /** + * Indicates whether it is currently possible to use online access codes for the device. + * + * @deprecated use device.can_program_online_access_codes + */ + public bool|null $online_access_codes_enabled, + ) {} + } -/** - * Location information for the device. - */ -class UnmanagedDeviceLocation -{ - public static function from_json(mixed $json): UnmanagedDeviceLocation|null + /** + * Array of warnings associated with the device. Each warning object within the array contains two fields: `warning_code` and `message`. `warning_code` is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. + */ + class Warnings { - if (!$json) { - return null; + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + active_access_code_count: $json->active_access_code_count ?? + null, + created_at: $json->created_at ?? null, + max_active_access_code_count: $json->max_active_access_code_count ?? + null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - location_name: $json->location_name ?? null, - time_zone: $json->time_zone ?? null, - timezone: $json->timezone ?? null, - ); - } - public function __construct( - /** - * Name of the device location. - */ - public string|null $location_name, - /** - * Time zone of the device location. - */ - public string|null $time_zone, - /** - * Time zone of the device location. - * - * @deprecated Use `time_zone` instead. - */ - public string|null $timezone, - ) {} + public function __construct( + /** + * Number of active access codes on the device when the warning was set. + */ + public int|null $active_access_code_count, + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Maximum number of active access codes supported by the device. + */ + public int|null $max_active_access_code_count, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } -/** - * Device model-related properties. - */ -class UnmanagedDeviceModel -{ - public static function from_json(mixed $json): UnmanagedDeviceModel|null +namespace Seam\Resources\UnmanagedDevice\Properties { + /** + * Accessory keypad properties and state. + */ + class AccessoryKeypad { - if (!$json) { - return null; + public static function from_json(mixed $json): AccessoryKeypad|null + { + if (!$json) { + return null; + } + return new self( + battery: isset($json->battery) + ? AccessoryKeypad\Battery::from_json($json->battery) + : null, + is_connected: $json->is_connected ?? null, + ); } - return new self( - accessory_keypad_supported: $json->accessory_keypad_supported ?? - null, - can_connect_accessory_keypad: $json->can_connect_accessory_keypad ?? - null, - display_name: $json->display_name ?? null, - has_built_in_keypad: $json->has_built_in_keypad ?? null, - manufacturer_display_name: $json->manufacturer_display_name ?? null, - offline_access_codes_supported: $json->offline_access_codes_supported ?? - null, - online_access_codes_supported: $json->online_access_codes_supported ?? - null, - ); - } - public function __construct( - /** - * @deprecated use device.properties.model.can_connect_accessory_keypad - */ - public bool|null $accessory_keypad_supported, - /** - * Indicates whether the device can connect a accessory keypad. - */ - public bool|null $can_connect_accessory_keypad, - /** - * Display name of the device model. - */ - public string|null $display_name, - /** - * Indicates whether the device has a built in accessory keypad. - */ - public bool|null $has_built_in_keypad, - /** - * Display name that corresponds to the manufacturer-specific terminology for the device. - */ - public string|null $manufacturer_display_name, - /** - * @deprecated use device.can_program_offline_access_codes. - */ - public bool|null $offline_access_codes_supported, - /** - * @deprecated use device.can_program_online_access_codes. - */ - public bool|null $online_access_codes_supported, - ) {} -} + public function __construct( + /** + * Keypad battery properties. + */ + public AccessoryKeypad\Battery|null $battery, + /** + * Indicates if an accessory keypad is connected to the device. + */ + public bool|null $is_connected, + ) {} + } -/** - * properties of the device. - */ -class UnmanagedDeviceProperties -{ - public static function from_json( - mixed $json, - ): UnmanagedDeviceProperties|null { - if (!$json) { - return null; + /** + * Represents the current status of the battery charge level. + */ + class Battery + { + public static function from_json(mixed $json): Battery|null + { + if (!$json) { + return null; + } + return new self( + level: $json->level ?? null, + status: $json->status ?? null, + ); } - return new self( - accessory_keypad: isset($json->accessory_keypad) - ? UnmanagedDeviceAccessoryKeypad::from_json( - $json->accessory_keypad, - ) - : null, - battery: isset($json->battery) - ? UnmanagedDeviceBattery::from_json($json->battery) - : null, - battery_level: $json->battery_level ?? null, - image_alt_text: $json->image_alt_text ?? null, - image_url: $json->image_url ?? null, - manufacturer: $json->manufacturer ?? null, - model: isset($json->model) - ? UnmanagedDeviceModel::from_json($json->model) - : null, - name: $json->name ?? null, - offline_access_codes_enabled: $json->offline_access_codes_enabled ?? - null, - online: $json->online ?? null, - online_access_codes_enabled: $json->online_access_codes_enabled ?? - null, - ); + + public function __construct( + /** + * Battery charge level as a value between 0 and 1, inclusive. + */ + public float|null $level, + /** + * Represents the current status of the battery charge level. Values are `critical`, which indicates an extremely low level, suggesting imminent shutdown or an urgent need for charging; `low`, which signifies that the battery is under the preferred threshold and should be charged soon; `good`, which denotes a satisfactory charge level, adequate for normal use without the immediate need for recharging; and `full`, which represents a battery that is fully charged, providing the maximum duration of usage. + */ + public string|null $status, + ) {} } - public function __construct( - /** - * Accessory keypad properties and state. - */ - public UnmanagedDeviceAccessoryKeypad|null $accessory_keypad, - /** - * Represents the current status of the battery charge level. - */ - public UnmanagedDeviceBattery|null $battery, - /** - * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. - */ - public float|null $battery_level, - /** - * Alt text for the device image. - */ - public string|null $image_alt_text, - /** - * Image URL for the device. - */ - public string|null $image_url, - /** - * Manufacturer of the device. When a device, such as a smart lock, is connected through a smart hub, the manufacturer of the device might be different from that of the smart hub. - */ - public string|null $manufacturer, - /** - * Device model-related properties. - */ - public UnmanagedDeviceModel|null $model, - /** - * Name of the device. - * - * @deprecated use device.display_name instead - */ - public string|null $name, - /** - * Indicates whether it is currently possible to use offline access codes for the device. - * - * @deprecated use device.can_program_offline_access_codes - */ - public bool|null $offline_access_codes_enabled, - /** - * Indicates whether the device is online. - */ - public bool|null $online, - /** - * Indicates whether it is currently possible to use online access codes for the device. - * - * @deprecated use device.can_program_online_access_codes - */ - public bool|null $online_access_codes_enabled, - ) {} + /** + * Device model-related properties. + */ + class Model + { + public static function from_json(mixed $json): Model|null + { + if (!$json) { + return null; + } + return new self( + accessory_keypad_supported: $json->accessory_keypad_supported ?? + null, + can_connect_accessory_keypad: $json->can_connect_accessory_keypad ?? + null, + display_name: $json->display_name ?? null, + has_built_in_keypad: $json->has_built_in_keypad ?? null, + manufacturer_display_name: $json->manufacturer_display_name ?? + null, + offline_access_codes_supported: $json->offline_access_codes_supported ?? + null, + online_access_codes_supported: $json->online_access_codes_supported ?? + null, + ); + } + + public function __construct( + /** + * @deprecated use device.properties.model.can_connect_accessory_keypad + */ + public bool|null $accessory_keypad_supported, + /** + * Indicates whether the device can connect a accessory keypad. + */ + public bool|null $can_connect_accessory_keypad, + /** + * Display name of the device model. + */ + public string|null $display_name, + /** + * Indicates whether the device has a built in accessory keypad. + */ + public bool|null $has_built_in_keypad, + /** + * Display name that corresponds to the manufacturer-specific terminology for the device. + */ + public string|null $manufacturer_display_name, + /** + * @deprecated use device.can_program_offline_access_codes. + */ + public bool|null $offline_access_codes_supported, + /** + * @deprecated use device.can_program_online_access_codes. + */ + public bool|null $online_access_codes_supported, + ) {} + } } -/** - * Array of warnings associated with the device. Each warning object within the array contains two fields: `warning_code` and `message`. `warning_code` is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. `message` provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. - */ -class UnmanagedDeviceWarnings -{ - public static function from_json(mixed $json): UnmanagedDeviceWarnings|null +namespace Seam\Resources\UnmanagedDevice\Properties\AccessoryKeypad { + /** + * Keypad battery properties. + */ + class Battery { - if (!$json) { - return null; + public static function from_json(mixed $json): Battery|null + { + if (!$json) { + return null; + } + return new self(level: $json->level ?? null); } - return new self( - active_access_code_count: $json->active_access_code_count ?? null, - created_at: $json->created_at ?? null, - max_active_access_code_count: $json->max_active_access_code_count ?? - null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Number of active access codes on the device when the warning was set. - */ - public int|null $active_access_code_count, - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Maximum number of active access codes supported by the device. - */ - public int|null $max_active_access_code_count, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct(public float|null $level) {} + } } diff --git a/src/Resources/UnmanagedUserIdentity.php b/src/Resources/UnmanagedUserIdentity.php index 6f950ce7..a1da9a34 100644 --- a/src/Resources/UnmanagedUserIdentity.php +++ b/src/Resources/UnmanagedUserIdentity.php @@ -1,155 +1,156 @@ acs_user_ids ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + email_address: $json->email_address ?? null, + errors: array_map( + fn($e) => UnmanagedUserIdentity\Errors::from_json($e), + $json->errors ?? [], + ), + full_name: $json->full_name ?? null, + phone_number: $json->phone_number ?? null, + user_identity_id: $json->user_identity_id ?? null, + warnings: array_map( + fn($w) => UnmanagedUserIdentity\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - acs_user_ids: $json->acs_user_ids ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - email_address: $json->email_address ?? null, - errors: array_map( - fn($e) => UnmanagedUserIdentityErrors::from_json($e), - $json->errors ?? [], - ), - full_name: $json->full_name ?? null, - phone_number: $json->phone_number ?? null, - user_identity_id: $json->user_identity_id ?? null, - warnings: array_map( - fn($w) => UnmanagedUserIdentityWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Array of access system user IDs associated with the user identity. - */ - public array|null $acs_user_ids, - /** - * Date and time at which the user identity was created. - */ - public string|null $created_at, - /** - * Display name for the user identity. - */ - public string|null $display_name, - /** - * Unique email address for the user identity. - */ - public string|null $email_address, - /** - * Array of errors associated with the user identity. Each error object within the array contains fields like "error_code" and "message." "error_code" is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. - */ - public array $errors, - /** - * Full name of the user associated with the user identity. - */ - public string|null $full_name, - /** - * Unique phone number for the user identity in [E.164 format](https://www.itu.int/rec/T-REC-E.164/en) (for example, +15555550100). - */ - public string|null $phone_number, - /** - * ID of the user identity. - */ - public string|null $user_identity_id, - /** - * Array of warnings associated with the user identity. Each warning object within the array contains two fields: "warning_code" and "message." "warning_code" is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. - */ - public array $warnings, - /** - * ID of the workspace that contains the user identity. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Array of access system user IDs associated with the user identity. + */ + public array|null $acs_user_ids, + /** + * Date and time at which the user identity was created. + */ + public string|null $created_at, + /** + * Display name for the user identity. + */ + public string|null $display_name, + /** + * Unique email address for the user identity. + */ + public string|null $email_address, + /** + * Array of errors associated with the user identity. Each error object within the array contains fields like "error_code" and "message." "error_code" is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. + */ + public array $errors, + /** + * Full name of the user associated with the user identity. + */ + public string|null $full_name, + /** + * Unique phone number for the user identity in [E.164 format](https://www.itu.int/rec/T-REC-E.164/en) (for example, +15555550100). + */ + public string|null $phone_number, + /** + * ID of the user identity. + */ + public string|null $user_identity_id, + /** + * Array of warnings associated with the user identity. Each warning object within the array contains two fields: "warning_code" and "message." "warning_code" is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. + */ + public array $warnings, + /** + * ID of the workspace that contains the user identity. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Array of errors associated with the user identity. Each error object within the array contains fields like "error_code" and "message." "error_code" is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. - */ -class UnmanagedUserIdentityErrors -{ - public static function from_json( - mixed $json, - ): UnmanagedUserIdentityErrors|null { - if (!$json) { - return null; +namespace Seam\Resources\UnmanagedUserIdentity { + /** + * Array of errors associated with the user identity. Each error object within the array contains fields like "error_code" and "message." "error_code" is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. + */ + class Errors + { + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * ID of the access system that the user identity is associated with. - */ - public string|null $acs_system_id, - /** - * ID of the access system user that has an issue. - */ - public string|null $acs_user_id, - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * ID of the access system that the user identity is associated with. + */ + public string|null $acs_system_id, + /** + * ID of the access system user that has an issue. + */ + public string|null $acs_user_id, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Array of warnings associated with the user identity. Each warning object within the array contains two fields: "warning_code" and "message." "warning_code" is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. - */ -class UnmanagedUserIdentityWarnings -{ - public static function from_json( - mixed $json, - ): UnmanagedUserIdentityWarnings|null { - if (!$json) { - return null; + /** + * Array of warnings associated with the user identity. Each warning object within the array contains two fields: "warning_code" and "message." "warning_code" is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. + */ + class Warnings + { + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } diff --git a/src/Resources/UserIdentity.php b/src/Resources/UserIdentity.php index d96105cd..ededd870 100644 --- a/src/Resources/UserIdentity.php +++ b/src/Resources/UserIdentity.php @@ -1,158 +1,160 @@ acs_user_ids ?? null, + created_at: $json->created_at ?? null, + display_name: $json->display_name ?? null, + email_address: $json->email_address ?? null, + errors: array_map( + fn($e) => UserIdentity\Errors::from_json($e), + $json->errors ?? [], + ), + full_name: $json->full_name ?? 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( + fn($w) => UserIdentity\Warnings::from_json($w), + $json->warnings ?? [], + ), + workspace_id: $json->workspace_id ?? null, + ); } - return new self( - acs_user_ids: $json->acs_user_ids ?? null, - created_at: $json->created_at ?? null, - display_name: $json->display_name ?? null, - email_address: $json->email_address ?? null, - errors: array_map( - fn($e) => UserIdentityErrors::from_json($e), - $json->errors ?? [], - ), - full_name: $json->full_name ?? 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( - fn($w) => UserIdentityWarnings::from_json($w), - $json->warnings ?? [], - ), - workspace_id: $json->workspace_id ?? null, - ); - } - public function __construct( - /** - * Array of access system user IDs associated with the user identity. - */ - public array|null $acs_user_ids, - /** - * Date and time at which the user identity was created. - */ - public string|null $created_at, - /** - * Display name for the user identity. - */ - public string|null $display_name, - /** - * Unique email address for the user identity. - */ - public string|null $email_address, - /** - * Array of errors associated with the user identity. Each error object within the array contains fields like "error_code" and "message." "error_code" is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. - */ - public array $errors, - /** - * Full name of the user associated with the user identity. - */ - public string|null $full_name, - /** - * Unique phone number for the user identity in [E.164 format](https://www.itu.int/rec/T-REC-E.164/en) (for example, +15555550100). - */ - public string|null $phone_number, - /** - * ID of the user identity. - */ - public string|null $user_identity_id, - /** - * Unique key for the user identity. - */ - public string|null $user_identity_key, - /** - * Array of warnings associated with the user identity. Each warning object within the array contains two fields: "warning_code" and "message." "warning_code" is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. - */ - public array $warnings, - /** - * ID of the workspace that contains the user identity. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Array of access system user IDs associated with the user identity. + */ + public array|null $acs_user_ids, + /** + * Date and time at which the user identity was created. + */ + public string|null $created_at, + /** + * Display name for the user identity. + */ + public string|null $display_name, + /** + * Unique email address for the user identity. + */ + public string|null $email_address, + /** + * Array of errors associated with the user identity. Each error object within the array contains fields like "error_code" and "message." "error_code" is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. + */ + public array $errors, + /** + * Full name of the user associated with the user identity. + */ + public string|null $full_name, + /** + * Unique phone number for the user identity in [E.164 format](https://www.itu.int/rec/T-REC-E.164/en) (for example, +15555550100). + */ + public string|null $phone_number, + /** + * ID of the user identity. + */ + public string|null $user_identity_id, + /** + * Unique key for the user identity. + */ + public string|null $user_identity_key, + /** + * Array of warnings associated with the user identity. Each warning object within the array contains two fields: "warning_code" and "message." "warning_code" is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. + */ + public array $warnings, + /** + * ID of the workspace that contains the user identity. + */ + public string|null $workspace_id, + ) {} + } } -/** - * Array of errors associated with the user identity. Each error object within the array contains fields like "error_code" and "message." "error_code" is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. - */ -class UserIdentityErrors -{ - public static function from_json(mixed $json): UserIdentityErrors|null +namespace Seam\Resources\UserIdentity { + /** + * Array of errors associated with the user identity. Each error object within the array contains fields like "error_code" and "message." "error_code" is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. + */ + class Errors { - if (!$json) { - return null; + public static function from_json(mixed $json): Errors|null + { + if (!$json) { + return null; + } + return new self( + acs_system_id: $json->acs_system_id ?? null, + acs_user_id: $json->acs_user_id ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, + ); } - return new self( - acs_system_id: $json->acs_system_id ?? null, - acs_user_id: $json->acs_user_id ?? null, - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, - ); - } - public function __construct( - /** - * ID of the access system that the user identity is associated with. - */ - public string|null $acs_system_id, - /** - * ID of the access system user that has an issue. - */ - public string|null $acs_user_id, - /** - * Date and time at which Seam created the error. - */ - public string|null $created_at, - /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - */ - public string|null $error_code, - /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - ) {} -} + public function __construct( + /** + * ID of the access system that the user identity is associated with. + */ + public string|null $acs_system_id, + /** + * ID of the access system user that has an issue. + */ + public string|null $acs_user_id, + /** + * Date and time at which Seam created the error. + */ + public string|null $created_at, + /** + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + */ + public string|null $error_code, + /** + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + ) {} + } -/** - * Array of warnings associated with the user identity. Each warning object within the array contains two fields: "warning_code" and "message." "warning_code" is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. - */ -class UserIdentityWarnings -{ - public static function from_json(mixed $json): UserIdentityWarnings|null + /** + * Array of warnings associated with the user identity. Each warning object within the array contains two fields: "warning_code" and "message." "warning_code" is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. + */ + class Warnings { - if (!$json) { - return null; + public static function from_json(mixed $json): Warnings|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, + ); } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, - ); - } - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - */ - public string|null $warning_code, - ) {} + public function __construct( + /** + * Date and time at which Seam created the warning. + */ + public string|null $created_at, + /** + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + */ + public string|null $message, + /** + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + */ + public string|null $warning_code, + ) {} + } } diff --git a/src/Resources/Webhook.php b/src/Resources/Webhook.php index 6a39a342..37eb5751 100644 --- a/src/Resources/Webhook.php +++ b/src/Resources/Webhook.php @@ -1,41 +1,41 @@ event_types ?? null, + secret: $json->secret ?? null, + url: $json->url ?? null, + webhook_id: $json->webhook_id ?? null, + ); } - return new self( - event_types: $json->event_types ?? null, - secret: $json->secret ?? null, - url: $json->url ?? null, - webhook_id: $json->webhook_id ?? null, - ); - } - public function __construct( - /** - * Types of events that the [webhook](https://docs.seam.co/developer-tools/webhooks) should receive. - */ - public array|null $event_types, - /** - * Secret associated with the [webhook](https://docs.seam.co/developer-tools/webhooks). - */ - public string|null $secret, - /** - * URL for the [webhook](https://docs.seam.co/developer-tools/webhooks). - */ - public string|null $url, - /** - * ID of the webhook. - */ - public string|null $webhook_id, - ) {} + public function __construct( + /** + * Types of events that the [webhook](https://docs.seam.co/developer-tools/webhooks) should receive. + */ + public array|null $event_types, + /** + * Secret associated with the [webhook](https://docs.seam.co/developer-tools/webhooks). + */ + public string|null $secret, + /** + * URL for the [webhook](https://docs.seam.co/developer-tools/webhooks). + */ + public string|null $url, + /** + * ID of the webhook. + */ + public string|null $webhook_id, + ) {} + } } diff --git a/src/Resources/Workspace.php b/src/Resources/Workspace.php index 86e4acb1..4901d453 100644 --- a/src/Resources/Workspace.php +++ b/src/Resources/Workspace.php @@ -1,116 +1,119 @@ company_name ?? null, - connect_partner_name: $json->connect_partner_name ?? null, - connect_webview_customization: isset( - $json->connect_webview_customization, - ) - ? WorkspaceConnectWebviewCustomization::from_json( + public static function from_json(mixed $json): Workspace|null + { + if (!$json) { + return null; + } + return new self( + company_name: $json->company_name ?? null, + connect_partner_name: $json->connect_partner_name ?? null, + connect_webview_customization: isset( $json->connect_webview_customization, ) - : null, - is_publishable_key_auth_enabled: $json->is_publishable_key_auth_enabled ?? - null, - is_sandbox: $json->is_sandbox ?? null, - is_suspended: $json->is_suspended ?? null, - name: $json->name ?? null, - organization_id: $json->organization_id ?? null, - publishable_key: $json->publishable_key ?? null, - workspace_id: $json->workspace_id ?? null, - ); - } + ? Workspace\ConnectWebviewCustomization::from_json( + $json->connect_webview_customization, + ) + : null, + is_publishable_key_auth_enabled: $json->is_publishable_key_auth_enabled ?? + null, + is_sandbox: $json->is_sandbox ?? null, + is_suspended: $json->is_suspended ?? null, + name: $json->name ?? null, + organization_id: $json->organization_id ?? null, + publishable_key: $json->publishable_key ?? null, + workspace_id: $json->workspace_id ?? null, + ); + } - public function __construct( - /** - * Company name associated with the [workspace](https://docs.seam.co/core-concepts/workspaces). - */ - public string|null $company_name, - /** - * @deprecated Use `company_name` instead. - */ - public string|null $connect_partner_name, - public WorkspaceConnectWebviewCustomization|null $connect_webview_customization, - /** - * Indicates whether publishable key authentication is enabled for this workspace. - */ - public bool|null $is_publishable_key_auth_enabled, - /** - * Indicates whether the workspace is a [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces). - */ - public bool|null $is_sandbox, - /** - * Indicates whether the [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces) is suspended. Seam suspends sandbox workspaces that have not been accessed in 14 days. - */ - public bool|null $is_suspended, - /** - * Name of the [workspace](https://docs.seam.co/core-concepts/workspaces). - */ - public string|null $name, - /** - * ID of the organization to which the workspace belongs, or `null` if the workspace is not assigned to an organization. - */ - public string|null $organization_id, - /** - * Publishable key for the [workspace](https://docs.seam.co/core-concepts/workspaces). This key is used to identify the workspace in client-side applications. - */ - public string|null $publishable_key, - /** - * ID of the workspace. - */ - public string|null $workspace_id, - ) {} + public function __construct( + /** + * Company name associated with the [workspace](https://docs.seam.co/core-concepts/workspaces). + */ + public string|null $company_name, + /** + * @deprecated Use `company_name` instead. + */ + public string|null $connect_partner_name, + public Workspace\ConnectWebviewCustomization|null $connect_webview_customization, + /** + * Indicates whether publishable key authentication is enabled for this workspace. + */ + public bool|null $is_publishable_key_auth_enabled, + /** + * Indicates whether the workspace is a [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces). + */ + public bool|null $is_sandbox, + /** + * Indicates whether the [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces) is suspended. Seam suspends sandbox workspaces that have not been accessed in 14 days. + */ + public bool|null $is_suspended, + /** + * Name of the [workspace](https://docs.seam.co/core-concepts/workspaces). + */ + public string|null $name, + /** + * ID of the organization to which the workspace belongs, or `null` if the workspace is not assigned to an organization. + */ + public string|null $organization_id, + /** + * Publishable key for the [workspace](https://docs.seam.co/core-concepts/workspaces). This key is used to identify the workspace in client-side applications. + */ + public string|null $publishable_key, + /** + * ID of the workspace. + */ + public string|null $workspace_id, + ) {} + } } -class WorkspaceConnectWebviewCustomization -{ - public static function from_json( - mixed $json, - ): WorkspaceConnectWebviewCustomization|null { - if (!$json) { - return null; +namespace Seam\Resources\Workspace { + class ConnectWebviewCustomization + { + public static function from_json( + mixed $json, + ): ConnectWebviewCustomization|null { + if (!$json) { + return null; + } + return new self( + inviter_logo_url: $json->inviter_logo_url ?? null, + logo_shape: $json->logo_shape ?? null, + primary_button_color: $json->primary_button_color ?? null, + primary_button_text_color: $json->primary_button_text_color ?? + null, + success_message: $json->success_message ?? null, + ); } - return new self( - inviter_logo_url: $json->inviter_logo_url ?? null, - logo_shape: $json->logo_shape ?? null, - primary_button_color: $json->primary_button_color ?? null, - primary_button_text_color: $json->primary_button_text_color ?? null, - success_message: $json->success_message ?? null, - ); - } - public function __construct( - /** - * URL of the inviter logo for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). - */ - public string|null $inviter_logo_url, - /** - * Logo shape for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). - */ - public string|null $logo_shape, - /** - * Primary button color for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). - */ - public string|null $primary_button_color, - /** - * Primary button text color for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). - */ - public string|null $primary_button_text_color, - /** - * Success message for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). - */ - public string|null $success_message, - ) {} + public function __construct( + /** + * URL of the inviter logo for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). + */ + public string|null $inviter_logo_url, + /** + * Logo shape for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). + */ + public string|null $logo_shape, + /** + * Primary button color for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). + */ + public string|null $primary_button_color, + /** + * Primary button text color for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). + */ + public string|null $primary_button_text_color, + /** + * Success message for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). + */ + public string|null $success_message, + ) {} + } } diff --git a/tests/ResourceTest.php b/tests/ResourceTest.php new file mode 100644 index 00000000..58d19ed7 --- /dev/null +++ b/tests/ResourceTest.php @@ -0,0 +1,161 @@ + [ + "battery" => ["level" => 0.4, "status" => "low"], + "accessory_keypad" => ["battery" => ["level" => 0.25]], + ], + "errors" => [ + [ + "error_code" => "device_offline", + "is_device_error" => true, + ], + ], + ]), + ), + ); + + $this->assertNotNull($device); + + return $device; + } + + public function testNestedPropertyClassesAreNamespacedByTheirOwner(): void + { + $device = $this->device(); + + $this->assertInstanceOf(Device\Properties::class, $device->properties); + $this->assertInstanceOf( + Device\Properties\Battery::class, + $device->properties->battery, + ); + $this->assertInstanceOf( + Device\Properties\AccessoryKeypad::class, + $device->properties->accessory_keypad, + ); + $this->assertInstanceOf( + Device\Properties\AccessoryKeypad\Battery::class, + $device->properties->accessory_keypad->battery, + ); + } + + /** + * The two batteries used to collapse onto one Seam\Resources\DeviceBattery, + * and whichever was generated first won, so the device battery lost its + * status. + */ + public function testSameNamedPropertiesAtDifferentDepthsKeepTheirShapes(): void + { + $device = $this->device(); + + $this->assertNotSame( + Device\Properties\Battery::class, + Device\Properties\AccessoryKeypad\Battery::class, + ); + + $this->assertSame(0.4, $device->properties->battery->level); + $this->assertSame("low", $device->properties->battery->status); + + $this->assertSame( + 0.25, + $device->properties->accessory_keypad->battery->level, + ); + $this->assertObjectNotHasProperty( + "status", + $device->properties->accessory_keypad->battery, + ); + } + + public function testNestedClassesAreNotDeclaredInTheResourcesNamespace(): void + { + $this->assertFalse( + class_exists("Seam\\Resources\\DeviceBattery"), + "Nested classes must not be flattened into Seam\\Resources", + ); + $this->assertFalse( + class_exists("Seam\\Resources\\DeviceProperties"), + "Nested classes must not be flattened into Seam\\Resources", + ); + } + + /** + * A list of discriminated objects collapses into one class carrying the + * union of the variant properties, so a field only some variants declare is + * still readable. + */ + public function testDiscriminatedListMergesEveryVariantProperty(): void + { + $device = $this->device(); + + $error = $device->errors[0]; + + $this->assertInstanceOf(Device\Errors::class, $error); + $this->assertSame("device_offline", $error->error_code); + $this->assertTrue($error->is_device_error); + $this->assertObjectHasProperty("is_bridge_error", $error); + $this->assertNull($error->is_bridge_error); + } + + /** + * Every class in the file has to be registered, because src/Resources is + * autoloaded by classmap rather than PSR-4. + */ + public function testNestedClassesAreAutoloadable(): void + { + foreach ( + [ + Device\Properties\Battery::class, + Device\Properties\AccessoryKeypad\Battery::class, + Device\Properties\AvailableClimatePresets::class, + Device\Properties\AvailableClimatePresets\EcobeeMetadata::class, + ] + as $className + ) { + $this->assertTrue( + class_exists($className), + "{$className} should be autoloadable", + ); + } + } + + /** + * The climate preset metadata used to take the shape of the device level + * ecobee_metadata, losing these three fields. + */ + public function testClimatePresetMetadataKeepsItsOwnShape(): void + { + $preset = Device\Properties\AvailableClimatePresets\EcobeeMetadata::from_json( + json_decode( + json_encode([ + "climate_ref" => "sleep", + "is_optimized" => true, + "owner" => "user", + ]), + ), + ); + + $this->assertNotNull($preset); + $this->assertSame("sleep", $preset->climate_ref); + $this->assertTrue($preset->is_optimized); + $this->assertSame("user", $preset->owner); + } +} From 7e04f8e5ff1b0ed6c40fa3f72a11a8939d67bcb7 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 20:20:37 +0000 Subject: [PATCH 04/35] refactor: share the Ruby SDK's merge-properties verbatim The merge module was written from the specification rather than copied, so it had drifted from the Ruby SDK in details that matter for keeping the two in step: the merged list was not sorted, the path in a list recursion omitted the [] segment, the format list in the disagreement error was sorted, and the error messages were worded differently. Take codegen/lib/merge-properties.ts from seamapi/ruby as it stands, so the two SDKs share one implementation and a future change to the semantics is a single diff to port rather than a reconciliation. Only the sort is observable here: it reorders the properties of the two merged resources, which reorders the nested classes emitted for them. The class and namespace sets of ActionAttempt and Event are unchanged. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- codegen/lib/merge-properties.ts | 169 +++++++++----------- src/Resources/ActionAttempt.php | 120 +++++++-------- src/Resources/Event.php | 262 ++++++++++++++++---------------- 3 files changed, 262 insertions(+), 289 deletions(-) diff --git a/codegen/lib/merge-properties.ts b/codegen/lib/merge-properties.ts index 43a28523..70c7bd8e 100644 --- a/codegen/lib/merge-properties.ts +++ b/codegen/lib/merge-properties.ts @@ -1,70 +1,59 @@ -// Merges the property lists of a discriminated union into one list. -// -// Two things in the blueprint are unions the SDK collapses into a single -// class: the event and action attempt resources, which the blueprint models as -// one resource per variant, and any property that is a list of discriminated -// objects. -// -// This logic is shared with the Ruby and Python SDKs. Changing the semantics -// here means changing them there too, or the SDKs drift. - import type { Property } from '@seamapi/blueprint' -/** - * Unions property lists by name, recursively. - * - * A name carried by one variant is taken as is. A name carried by several is - * merged: objects and lists of objects recurse, lists of discriminated objects - * concatenate their variants for whoever consumes the list to merge in turn, - * and scalars collapse onto one another because they map to the same PHP type. - */ -export const mergeProperties = ( - propertyLists: Property[][], - path = '', -): Property[] => { - const occurrencesByName = new Map() +const formatKey = (property: Property): string => + property.format === 'list' ? `list<${property.itemFormat}>` : property.format - for (const properties of propertyLists) { - for (const property of properties) { - const occurrences = occurrencesByName.get(property.name) - if (occurrences == null) { - occurrencesByName.set(property.name, [property]) - continue - } - occurrences.push(property) - } - } +const isScalar = (property: Property): boolean => + property.format !== 'list' && property.format !== 'object' - return [...occurrencesByName.entries()].map(([name, occurrences]) => - mergeOccurrences(occurrences, path === '' ? name : `${path}.${name}`), - ) +interface MergedDocs { + description: string + isDeprecated: boolean + deprecationMessage: string } -const mergeOccurrences = (occurrences: Property[], path: string): Property => { - const [first] = occurrences +// Each variant documents a property for its own case, which is accurate there +// but not for the single class the variants merge into. Some are merely narrow +// ("Previous code configuration" on a shape that also covers names); others +// contradict each other outright ("the error is not a device error" against +// "the error is a device error"). No description beats a wrong one, so keep one +// only when every variant that documents the property agrees. +const mergeDocs = (occurrences: Property[]): MergedDocs => { + const descriptions = [ + ...new Set( + occurrences + .map((occurrence) => occurrence.description.trim()) + .filter((description) => description !== ''), + ), + ] + const deprecated = occurrences.find(({ isDeprecated }) => isDeprecated) - if (first == null) { - throw new Error(`Cannot merge ${path}: no occurrences`) + return { + description: descriptions.length === 1 ? (descriptions[0] ?? '') : '', + // Deprecating in any variant deprecates the merged property, so a warning + // is never dropped just because another variant omits it. + isDeprecated: deprecated != null, + deprecationMessage: deprecated?.deprecationMessage ?? '', } +} - if (occurrences.length === 1) return first +// The variants of a discriminated union collapse into a single class, so a +// property carried by more than one variant has to end up with every field any +// variant gives it. Keeping only the first occurrence silently drops the rest, +// which loses data once the merged shape is a typed class rather than a hash. +const mergeOccurrences = (occurrences: Property[], path: string): Property => { + const [first, ...rest] = occurrences + if (first == null) throw new Error(`Nothing to merge at ${path}.`) + if (rest.length === 0) return first - // Each variant documents the property for its own case, which is accurate - // there and not necessarily on the class the variants collapse into. Keep - // the text only when every variant that documents it agrees, since no - // documentation beats wrong documentation. const docs = mergeDocs(occurrences) - const formatKeys = new Set(occurrences.map(getFormatKey)) - - if (formatKeys.size > 1) { - if (occurrences.every(isScalar)) { - // Differing scalar formats map to the same PHP type, so any one will do. - return { ...first, ...docs } - } - + const formats = new Set(occurrences.map(formatKey)) + if (formats.size > 1) { + // Scalars all become a plain accessor, so any of them represents the rest. + if (occurrences.every(isScalar)) return { ...first, ...docs } throw new Error( - `Cannot merge ${path}: variants disagree on its shape (${[...formatKeys].sort().join(', ')})`, + `Cannot merge ${path}: variants disagree on its shape (${[...formats].join(', ')}).`, ) } @@ -73,8 +62,8 @@ const mergeOccurrences = (occurrences: Property[], path: string): Property => { ...first, ...docs, properties: mergeProperties( - occurrences.map((occurrence) => - occurrence.format === 'object' ? occurrence.properties : [], + occurrences.map( + (occurrence) => (occurrence as typeof first).properties, ), path, ), @@ -86,25 +75,21 @@ const mergeOccurrences = (occurrences: Property[], path: string): Property => { ...first, ...docs, itemProperties: mergeProperties( - occurrences.map((occurrence) => - occurrence.format === 'list' && occurrence.itemFormat === 'object' - ? occurrence.itemProperties - : [], + occurrences.map( + (occurrence) => (occurrence as typeof first).itemProperties, ), - path, + `${path}[]`, ), } } if (first.format === 'list' && first.itemFormat === 'discriminated_object') { + // Keep every variant. Whoever consumes this list merges them in turn. return { ...first, ...docs, - variants: occurrences.flatMap((occurrence) => - occurrence.format === 'list' && - occurrence.itemFormat === 'discriminated_object' - ? occurrence.variants - : [], + variants: occurrences.flatMap( + (occurrence) => (occurrence as typeof first).variants, ), } } @@ -112,37 +97,25 @@ const mergeOccurrences = (occurrences: Property[], path: string): Property => { return { ...first, ...docs } } -interface MergedDocs { - description: string - isDeprecated: boolean - deprecationMessage: string -} - -const mergeDocs = (occurrences: Property[]): MergedDocs => { - const descriptions = [ - ...new Set( - occurrences - .map((occurrence) => occurrence.description.trim()) - .filter((description) => description !== ''), - ), - ] - - // Deprecating a property in any variant deprecates it on the merged class: - // first wins could silently undeprecate a field depending on the order the - // blueprint happens to list the variants in. - const deprecated = occurrences.find((occurrence) => occurrence.isDeprecated) - - return { - description: descriptions.length === 1 ? (descriptions[0] ?? '') : '', - isDeprecated: deprecated != null, - deprecationMessage: deprecated?.deprecationMessage ?? '', +export const mergeProperties = ( + propertyLists: Property[][], + path = '', +): Property[] => { + const occurrences = new Map() + for (const properties of propertyLists) { + for (const property of properties) { + const group = occurrences.get(property.name) + if (group == null) { + occurrences.set(property.name, [property]) + } else { + group.push(property) + } + } } -} - -// A list of objects and a list of scalars are different shapes, so the item -// format is part of the key. -const getFormatKey = (property: Property): string => - property.format === 'list' ? `list<${property.itemFormat}>` : property.format -const isScalar = (property: Property): boolean => - property.format !== 'list' && property.format !== 'object' + return [...occurrences.entries()] + .map(([name, group]) => + mergeOccurrences(group, path === '' ? name : `${path}.${name}`), + ) + .sort((a, b) => a.name.localeCompare(b.name)) +} diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 3a471b9d..2af52ae4 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -545,44 +545,6 @@ public function __construct( ) {} } - class Warnings - { - public static function from_json(mixed $json): Warnings|null - { - if (!$json) { - return null; - } - return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - original_access_method_id: $json->original_access_method_id ?? - null, - warning_code: $json->warning_code ?? null, - warning_message: $json->warning_message ?? null, - ); - } - - public function __construct( - /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $message, - /** - * ID of the original access method from which this backup access method was split, if applicable. - */ - public string|null $original_access_method_id, - public string|null $warning_code, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - */ - public string|null $warning_message, - ) {} - } - /** * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -684,6 +646,53 @@ public function __construct( ) {} } + /** + * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. + */ + class PendingMutations + { + public static function from_json(mixed $json): PendingMutations|null + { + if (!$json) { + return null; + } + return new self( + created_at: $json->created_at ?? null, + from: isset($json->from) + ? PendingMutations\From::from_json($json->from) + : null, + message: $json->message ?? null, + mutation_code: $json->mutation_code ?? null, + to: isset($json->to) + ? PendingMutations\To::from_json($json->to) + : null, + ); + } + + public function __construct( + /** + * Date and time at which the mutation was created. + */ + public string|null $created_at, + /** + * Previous access time configuration. + */ + public PendingMutations\From|null $from, + /** + * Detailed description of the mutation. + */ + public string|null $message, + /** + * Mutation code to indicate that Seam is in the process of updating the access times for this access method. + */ + public string|null $mutation_code, + /** + * New access time configuration. + */ + public PendingMutations\To|null $to, + ) {} + } + /** * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -743,50 +752,41 @@ public function __construct( ) {} } - /** - * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. - */ - class PendingMutations + class Warnings { - public static function from_json(mixed $json): PendingMutations|null + public static function from_json(mixed $json): Warnings|null { if (!$json) { return null; } return new self( created_at: $json->created_at ?? null, - from: isset($json->from) - ? PendingMutations\From::from_json($json->from) - : null, message: $json->message ?? null, - mutation_code: $json->mutation_code ?? null, - to: isset($json->to) - ? PendingMutations\To::from_json($json->to) - : null, + original_access_method_id: $json->original_access_method_id ?? + null, + warning_code: $json->warning_code ?? null, + warning_message: $json->warning_message ?? null, ); } public function __construct( /** - * Date and time at which the mutation was created. + * Date and time at which Seam created the warning. */ public string|null $created_at, /** - * Previous access time configuration. - */ - public PendingMutations\From|null $from, - /** - * Detailed description of the mutation. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, /** - * Mutation code to indicate that Seam is in the process of updating the access times for this access method. + * ID of the original access method from which this backup access method was split, if applicable. */ - public string|null $mutation_code, + public string|null $original_access_method_id, + public string|null $warning_code, /** - * New access time configuration. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public PendingMutations\To|null $to, + public string|null $warning_message, ) {} } } diff --git a/src/Resources/Event.php b/src/Resources/Event.php index 4ce80506..f11c863e 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -447,204 +447,167 @@ public function __construct( namespace Seam\Resources\Event { /** - * List of properties that changed on the access code. + * Errors associated with the access code. */ - class ChangedProperties - { - public static function from_json(mixed $json): ChangedProperties|null - { - if (!$json) { - return null; - } - return new self( - from: $json->from ?? null, - property: $json->property ?? null, - to: $json->to ?? null, - ); - } - - public function __construct( - /** - * Previous value of the property, or null if not set. - */ - public string|null $from, - /** - * Name of the property that changed (e.g. `code`). - */ - public string|null $property, - /** - * New value of the property, or null if cleared. - */ - public string|null $to, - ) {} - } - - class From + class AccessCodeErrors { - public static function from_json(mixed $json): From|null + public static function from_json(mixed $json): AccessCodeErrors|null { if (!$json) { return null; } return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Previous pin code. - */ - public string|null $code, - /** - * Previous end time. + * Date and time at which Seam created the error. */ - public string|null $ends_at, + public string|null $created_at, /** - * Previous name of the access code. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public string|null $name, + public string|null $error_code, /** - * Previous start time. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public string|null $starts_at, + public string|null $message, ) {} } - class To + /** + * Warnings associated with the access code. + */ + class AccessCodeWarnings { - public static function from_json(mixed $json): To|null + public static function from_json(mixed $json): AccessCodeWarnings|null { if (!$json) { return null; } return new self( - code: $json->code ?? null, - ends_at: $json->ends_at ?? null, - name: $json->name ?? null, - starts_at: $json->starts_at ?? null, + created_at: $json->created_at ?? null, + message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * New pin code. - */ - public string|null $code, - /** - * New end time. + * Date and time at which Seam created the warning. */ - public string|null $ends_at, + public string|null $created_at, /** - * New name of the access code. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $name, + public string|null $message, /** - * New start time. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public string|null $starts_at, + public string|null $warning_code, ) {} } /** - * Array of mutations requested on the access code, each containing the mutation type and from/to values. + * Errors associated with the access control system. */ - class RequestedMutations + class AcsSystemErrors { - public static function from_json(mixed $json): RequestedMutations|null + public static function from_json(mixed $json): AcsSystemErrors|null { if (!$json) { return null; } return new self( - from: $json->from ?? null, - mutation_code: $json->mutation_code ?? null, - to: $json->to ?? null, + created_at: $json->created_at ?? null, + error_code: $json->error_code ?? null, + message: $json->message ?? null, ); } public function __construct( /** - * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + * Date and time at which Seam created the error. */ - public mixed $from, + public string|null $created_at, /** - * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. + * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ - public string|null $mutation_code, + public string|null $error_code, /** - * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ - public mixed $to, + public string|null $message, ) {} } /** - * Errors associated with the access code. + * Warnings associated with the access control system. */ - class AccessCodeErrors + class AcsSystemWarnings { - public static function from_json(mixed $json): AccessCodeErrors|null + public static function from_json(mixed $json): AcsSystemWarnings|null { if (!$json) { return null; } return new self( created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, message: $json->message ?? null, + warning_code: $json->warning_code ?? null, ); } public function __construct( /** - * Date and time at which Seam created the error. + * Date and time at which Seam created the warning. */ public string|null $created_at, /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ - public string|null $error_code, + public string|null $message, /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ - public string|null $message, + public string|null $warning_code, ) {} } /** - * Warnings associated with the access code. + * List of properties that changed on the access code. */ - class AccessCodeWarnings + class ChangedProperties { - public static function from_json(mixed $json): AccessCodeWarnings|null + public static function from_json(mixed $json): ChangedProperties|null { if (!$json) { return null; } return new self( - created_at: $json->created_at ?? null, - message: $json->message ?? null, - warning_code: $json->warning_code ?? null, + from: $json->from ?? null, + property: $json->property ?? null, + to: $json->to ?? null, ); } public function __construct( /** - * Date and time at which Seam created the warning. + * Previous value of the property, or null if not set. */ - public string|null $created_at, + public string|null $from, /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + * Name of the property that changed (e.g. `code`). */ - public string|null $message, + public string|null $property, /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * New value of the property, or null if cleared. */ - public string|null $warning_code, + public string|null $to, ) {} } @@ -782,97 +745,134 @@ public function __construct( ) {} } - /** - * Errors associated with the access control system. - */ - class AcsSystemErrors + class From { - public static function from_json(mixed $json): AcsSystemErrors|null + public static function from_json(mixed $json): From|null { if (!$json) { return null; } return new self( - created_at: $json->created_at ?? null, - error_code: $json->error_code ?? null, - message: $json->message ?? null, + code: $json->code ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, ); } public function __construct( /** - * Date and time at which Seam created the error. + * Previous pin code. */ - public string|null $created_at, + public string|null $code, /** - * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + * Previous end time. */ - public string|null $error_code, + public string|null $ends_at, /** - * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + * Previous name of the access code. */ - public string|null $message, + public string|null $name, + /** + * Previous start time. + */ + public string|null $starts_at, ) {} } /** - * Warnings associated with the access control system. + * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. */ - class AcsSystemWarnings + class Reason { - public static function from_json(mixed $json): AcsSystemWarnings|null + public static function from_json(mixed $json): Reason|null { if (!$json) { return null; } return new self( - created_at: $json->created_at ?? null, message: $json->message ?? null, - warning_code: $json->warning_code ?? null, + reason_code: $json->reason_code ?? null, ); } public function __construct( /** - * Date and time at which Seam created the warning. - */ - public string|null $created_at, - /** - * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + * Human-readable explanation of why access was denied. */ public string|null $message, /** - * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + * Normalized reason a lock denied access. Provider-agnostic; not all providers report every value. */ - public string|null $warning_code, + public string|null $reason_code, ) {} } /** - * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. + * Array of mutations requested on the access code, each containing the mutation type and from/to values. */ - class Reason + class RequestedMutations { - public static function from_json(mixed $json): Reason|null + public static function from_json(mixed $json): RequestedMutations|null { if (!$json) { return null; } return new self( - message: $json->message ?? null, - reason_code: $json->reason_code ?? null, + from: $json->from ?? null, + mutation_code: $json->mutation_code ?? null, + to: $json->to ?? null, ); } public function __construct( /** - * Human-readable explanation of why access was denied. + * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. */ - public string|null $message, + public mixed $from, /** - * Normalized reason a lock denied access. Provider-agnostic; not all providers report every value. + * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. */ - public string|null $reason_code, + public string|null $mutation_code, + /** + * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. + */ + public mixed $to, + ) {} + } + + class To + { + public static function from_json(mixed $json): To|null + { + if (!$json) { + return null; + } + return new self( + code: $json->code ?? null, + ends_at: $json->ends_at ?? null, + name: $json->name ?? null, + starts_at: $json->starts_at ?? null, + ); + } + + public function __construct( + /** + * New pin code. + */ + public string|null $code, + /** + * New end time. + */ + public string|null $ends_at, + /** + * New name of the access code. + */ + public string|null $name, + /** + * New start time. + */ + public string|null $starts_at, ) {} } } From 4ed18b39fb36967a98462225c5963a18d8b502f9 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 20:33:59 +0000 Subject: [PATCH 05/35] fix: lock dependencies against the minimum supported PHP composer.json requires php ^8.1 but set no resolution platform, so the lock was resolved against whatever PHP generated it. Generated on 8.4, that selected Symfony 8.x, which requires php >=8.4.1, and composer install then refused the lockfile on 8.1, 8.2 and 8.3: Your lock file does not contain a compatible set of packages. - symfony/console is locked to version v8.1.2 ... - symfony/console v8.1.2 requires php >=8.4.1 -> your php version (8.2.33) does not satisfy that requirement. Pin config.platform.php to the oldest PHP this package supports so the lock represents that platform rather than the machine that happened to write it. Symfony drops to 6.4 LTS; Psalm and PHPUnit are unchanged. The patch version is 8.1.31 because Psalm 6 requires ~8.1.31 on the 8.1 line. The Install jobs passed throughout because they synthesize a composer.json and never read this lockfile, which is why only Test and Lint caught it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- composer.json | 3 + composer.lock | 235 ++++++++++++++++---------------------------------- 2 files changed, 76 insertions(+), 162 deletions(-) diff --git a/composer.json b/composer.json index 486452eb..d5125706 100644 --- a/composer.json +++ b/composer.json @@ -48,6 +48,9 @@ } }, "config": { + "platform": { + "php": "8.1.31" + }, "sort-packages": true }, "archive": { diff --git a/composer.lock b/composer.lock index a604302c..65ea9390 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "25d67117228a0425a94bd9f40c53f692", + "content-hash": "05bbb1b6caed944f0bc5ea91d511b7de", "packages": [ { "name": "caseyamcl/guzzle_retry_middleware", @@ -2400,20 +2400,20 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.4", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.0" }, "conflict": { "doctrine/collections": "<1.6.8", @@ -2448,15 +2448,15 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + "source": "https://github.com/myclabs/DeepCopy/tree/1.14.0" }, "funding": [ { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" + "url": "https://github.com/mnapoli", + "type": "github" } ], - "time": "2025-08-01T08:46:24+00:00" + "time": "2026-08-11T10:17:44+00:00" }, { "name": "netresearch/jsonmapper", @@ -4266,16 +4266,16 @@ }, { "name": "sebastian/recursion-context", - "version": "5.0.1", + "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" + "reference": "5d32fe257a9b39cb63146924d6b4e32a22d4502a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5d32fe257a9b39cb63146924d6b4e32a22d4502a", + "reference": "5d32fe257a9b39cb63146924d6b4e32a22d4502a", "shasum": "" }, "require": { @@ -4318,7 +4318,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.2" }, "funding": [ { @@ -4338,7 +4338,7 @@ "type": "tidelift" } ], - "time": "2025-08-10T07:50:56+00:00" + "time": "2026-08-11T05:27:39+00:00" }, { "name": "sebastian/type", @@ -4519,49 +4519,47 @@ }, { "name": "symfony/console", - "version": "v8.1.2", + "version": "v6.4.43", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "535e18a1b8925f6c01a55b171d157ab66c2ace15" + "reference": "3b643aa587acbc42f967a429af088a56ed8f046d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/535e18a1b8925f6c01a55b171d157ab66c2ace15", - "reference": "535e18a1b8925f6c01a55b171d157ab66c2ace15", + "url": "https://api.github.com/repos/symfony/console/zipball/3b643aa587acbc42f967a429af088a56ed8f046d", + "reference": "3b643aa587acbc42f967a429af088a56ed8f046d", "shasum": "" }, "require": { - "php": ">=8.4.1", + "php": ">=8.1", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "^1.0", - "symfony/polyfill-php85": "^1.32", + "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.4.6|^8.0.6" + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { - "symfony/dependency-injection": "<8.1", - "symfony/event-dispatcher": "<8.1" + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^7.4|^8.0", - "symfony/dependency-injection": "^8.1", - "symfony/event-dispatcher": "^8.1", - "symfony/filesystem": "^7.4|^8.0", - "symfony/http-foundation": "^7.4|^8.0", - "symfony/http-kernel": "^7.4|^8.0", - "symfony/lock": "^7.4|^8.0", - "symfony/messenger": "^7.4|^8.0", - "symfony/mime": "^7.4|^8.0", - "symfony/process": "^7.4|^8.0", - "symfony/stopwatch": "^7.4|^8.0", - "symfony/uid": "^7.4|^8.0", - "symfony/validator": "^7.4|^8.0", - "symfony/var-dumper": "^7.4|^8.0" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -4595,7 +4593,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v8.1.2" + "source": "https://github.com/symfony/console/tree/v6.4.43" }, "funding": [ { @@ -4615,30 +4613,29 @@ "type": "tidelift" } ], - "time": "2026-07-27T13:58:19+00:00" + "time": "2026-07-26T14:44:19+00:00" }, { "name": "symfony/filesystem", - "version": "v8.1.2", + "version": "v6.4.43", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "17856b7a222664a26a5ea1cb06ee0721c2438217" + "reference": "9ff03da12d67649fbd1f34ca95951554624d0a16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/17856b7a222664a26a5ea1cb06ee0721c2438217", - "reference": "17856b7a222664a26a5ea1cb06ee0721c2438217", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/9ff03da12d67649fbd1f34ca95951554624d0a16", + "reference": "9ff03da12d67649fbd1f34ca95951554624d0a16", "shasum": "" }, "require": { - "php": ">=8.4.1", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^7.4|^8.0" + "symfony/process": "^5.4|^6.4|^7.0" }, "type": "library", "autoload": { @@ -4666,7 +4663,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v8.1.2" + "source": "https://github.com/symfony/filesystem/tree/v6.4.43" }, "funding": [ { @@ -4686,7 +4683,7 @@ "type": "tidelift" } ], - "time": "2026-07-22T15:42:13+00:00" + "time": "2026-06-27T10:13:35+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5103,86 +5100,6 @@ ], "time": "2026-05-26T12:51:13+00:00" }, - { - "name": "symfony/polyfill-php85", - "version": "v1.41.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/255fab485aaa1006ed411040c42aecd7b5302d7a", - "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php85\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php85/tree/v1.41.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-07-01T12:47:55+00:00" - }, { "name": "symfony/service-contracts", "version": "v3.7.1", @@ -5272,34 +5189,33 @@ }, { "name": "symfony/string", - "version": "v8.1.2", + "version": "v6.4.43", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc" + "reference": "2a8d515c3eaa5d33cf76d5fa277cdadd0a4e5b49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc", - "reference": "286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc", + "url": "https://api.github.com/repos/symfony/string/zipball/2a8d515c3eaa5d33cf76d5fa277cdadd0a4e5b49", + "reference": "2a8d515c3eaa5d33cf76d5fa277cdadd0a4e5b49", "shasum": "" }, "require": { - "php": ">=8.4.1", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-intl-grapheme": "^1.33", - "symfony/polyfill-intl-normalizer": "^1.0", - "symfony/polyfill-mbstring": "^1.0" + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.4|^8.0", - "symfony/http-client": "^7.4|^8.0", - "symfony/intl": "^7.4|^8.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^7.4|^8.0" + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -5338,7 +5254,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.1.2" + "source": "https://github.com/symfony/string/tree/v6.4.43" }, "funding": [ { @@ -5358,7 +5274,7 @@ "type": "tidelift" } ], - "time": "2026-07-28T07:35:25+00:00" + "time": "2026-07-28T07:28:15+00:00" }, { "name": "theseer/tokenizer", @@ -5530,23 +5446,23 @@ }, { "name": "webmozart/assert", - "version": "2.4.1", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70" + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70", - "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", "shasum": "" }, "require": { "ext-ctype": "*", "ext-date": "*", "ext-filter": "*", - "php": "^8.2" + "php": "^7.2 || ^8.0" }, "suggest": { "ext-intl": "", @@ -5555,12 +5471,8 @@ }, "type": "library", "extra": { - "psalm": { - "pluginClass": "Webmozart\\Assert\\PsalmPlugin" - }, "branch-alias": { - "dev-master": "2.0-dev", - "dev-feature/2-0": "2.0-dev" + "dev-master": "1.10-dev" } }, "autoload": { @@ -5576,10 +5488,6 @@ { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" - }, - { - "name": "Woody Gilk", - "email": "woody.gilk@gmail.com" } ], "description": "Assertions to validate method input/output with nice error messages.", @@ -5590,9 +5498,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/2.4.1" + "source": "https://github.com/webmozarts/assert/tree/1.12.1" }, - "time": "2026-06-15T15:31:57+00:00" + "time": "2025-10-29T15:56:20+00:00" } ], "aliases": [], @@ -5604,5 +5512,8 @@ "php": "^8.1" }, "platform-dev": {}, + "platform-overrides": { + "php": "8.1.31" + }, "plugin-api-version": "2.6.0" } From 76ae49f32b1050f49b85076d522a810f4b1d0d2b Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 22:22:37 +0000 Subject: [PATCH 06/35] feat: drop support for PHP 8.1 PHP 8.1 reached end of life in December 2025, so the oldest version this package supports is now 8.2, which has security support through 2026. Raise the floor in composer.json, move the resolution platform to 8.2.27, the patch Psalm 6 requires on the 8.2 line, and drop 8.1 from the CI matrices. Symfony moves up to 7.4 now that 8.2 is the target. BREAKING CHANGE: PHP 8.2 or later is required. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- .github/workflows/check.yml | 4 +- README.md | 9 +-- composer.json | 4 +- composer.lock | 116 ++++++++++++++++++++---------------- 4 files changed, 71 insertions(+), 62 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 749b0e39..e9772866 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -20,7 +20,6 @@ jobs: os: - ubuntu-latest php: - - '8.1' - '8.2' - '8.3' - '8.4' @@ -47,7 +46,7 @@ jobs: fail-fast: false matrix: php: - - '8.1' + - '8.2' - '8.5' steps: - name: Checkout @@ -76,7 +75,6 @@ jobs: os: - ubuntu-latest php: - - '8.1' - '8.2' - '8.3' - '8.4' diff --git a/README.md b/README.md index 9ba25b03..1eaa1324 100644 --- a/README.md +++ b/README.md @@ -391,7 +391,8 @@ try { Version 4 brings this SDK in line with the Seam SDKs for other languages. -- PHP 8.1 or later is now required. +- PHP 8.2 or later is now required. PHP 8.1 reached end of life in + December 2025. - The client class is `Seam\Seam`. `Seam\SeamClient` still works as a deprecated alias. - The constructor takes named options. `$endpoint` is no longer the second @@ -416,7 +417,7 @@ Version 4 brings this SDK in line with the Seam SDKs for other languages. ### Quickstart -Install [PHP](https://www.php.net/) 8.1 or later, +Install [PHP](https://www.php.net/) 8.2 or later, [Composer](https://getcomposer.org/) and [Node.js](https://nodejs.org/), then run @@ -471,8 +472,8 @@ create pressure to change the generator. ### Requirements -This package supports PHP 8.1 and later. -Continuous integration exercises every supported version, PHP 8.1 through 8.5. +This package supports PHP 8.2 and later. +Continuous integration exercises every supported version, PHP 8.2 through 8.5. The test suite runs against [@seamapi/fake-seam-connect], which is started automatically for each test, so `npm install` must have been run first. diff --git a/composer.json b/composer.json index d5125706..fb1f683d 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "source": "https://github.com/seamapi/php" }, "require": { - "php": "^8.1", + "php": "^8.2", "caseyamcl/guzzle_retry_middleware": "^2.13", "guzzlehttp/guzzle": "^7.5", "svix/svix": "^1.40" @@ -49,7 +49,7 @@ }, "config": { "platform": { - "php": "8.1.31" + "php": "8.2.27" }, "sort-packages": true }, diff --git a/composer.lock b/composer.lock index 65ea9390..29d6834a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "05bbb1b6caed944f0bc5ea91d511b7de", + "content-hash": "fcadd2bfc0a095fb22d744660cd07366", "packages": [ { "name": "caseyamcl/guzzle_retry_middleware", @@ -4519,47 +4519,47 @@ }, { "name": "symfony/console", - "version": "v6.4.43", + "version": "v7.4.16", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3b643aa587acbc42f967a429af088a56ed8f046d" + "reference": "f4c69c9aed03abf933b294257d618bdd9b30a06d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3b643aa587acbc42f967a429af088a56ed8f046d", - "reference": "3b643aa587acbc42f967a429af088a56ed8f046d", + "url": "https://api.github.com/repos/symfony/console/zipball/f4c69c9aed03abf933b294257d618bdd9b30a06d", + "reference": "f4c69c9aed03abf933b294257d618bdd9b30a06d", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" + "symfony/string": "^7.2|^8.0" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -4593,7 +4593,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.43" + "source": "https://github.com/symfony/console/tree/v7.4.16" }, "funding": [ { @@ -4613,29 +4613,29 @@ "type": "tidelift" } ], - "time": "2026-07-26T14:44:19+00:00" + "time": "2026-07-31T12:37:14+00:00" }, { "name": "symfony/filesystem", - "version": "v6.4.43", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "9ff03da12d67649fbd1f34ca95951554624d0a16" + "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/9ff03da12d67649fbd1f34ca95951554624d0a16", - "reference": "9ff03da12d67649fbd1f34ca95951554624d0a16", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ff16a16bf87fdf264638b8f6995b3515975e3c79", + "reference": "ff16a16bf87fdf264638b8f6995b3515975e3c79", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^5.4|^6.4|^7.0" + "symfony/process": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -4663,7 +4663,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.43" + "source": "https://github.com/symfony/filesystem/tree/v7.4.15" }, "funding": [ { @@ -4683,7 +4683,7 @@ "type": "tidelift" } ], - "time": "2026-06-27T10:13:35+00:00" + "time": "2026-07-22T07:36:05+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5189,22 +5189,23 @@ }, { "name": "symfony/string", - "version": "v6.4.43", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "2a8d515c3eaa5d33cf76d5fa277cdadd0a4e5b49" + "reference": "e394af32256bf9e7bf80849d95e589167c10097b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/2a8d515c3eaa5d33cf76d5fa277cdadd0a4e5b49", - "reference": "2a8d515c3eaa5d33cf76d5fa277cdadd0a4e5b49", + "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", + "reference": "e394af32256bf9e7bf80849d95e589167c10097b", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-grapheme": "~1.33", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, @@ -5212,10 +5213,11 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/intl": "^6.2|^7.0", + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0|^7.0" + "symfony/var-exporter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -5254,7 +5256,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.43" + "source": "https://github.com/symfony/string/tree/v7.4.15" }, "funding": [ { @@ -5274,7 +5276,7 @@ "type": "tidelift" } ], - "time": "2026-07-28T07:28:15+00:00" + "time": "2026-07-28T07:33:02+00:00" }, { "name": "theseer/tokenizer", @@ -5446,23 +5448,23 @@ }, { "name": "webmozart/assert", - "version": "1.12.1", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", - "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70", "shasum": "" }, "require": { "ext-ctype": "*", "ext-date": "*", "ext-filter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.2" }, "suggest": { "ext-intl": "", @@ -5471,8 +5473,12 @@ }, "type": "library", "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, "branch-alias": { - "dev-master": "1.10-dev" + "dev-master": "2.0-dev", + "dev-feature/2-0": "2.0-dev" } }, "autoload": { @@ -5488,6 +5494,10 @@ { "name": "Bernhard Schussek", "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" } ], "description": "Assertions to validate method input/output with nice error messages.", @@ -5498,9 +5508,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.12.1" + "source": "https://github.com/webmozarts/assert/tree/2.4.1" }, - "time": "2025-10-29T15:56:20+00:00" + "time": "2026-06-15T15:31:57+00:00" } ], "aliases": [], @@ -5509,11 +5519,11 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^8.1" + "php": "^8.2" }, "platform-dev": {}, "platform-overrides": { - "php": "8.1.31" + "php": "8.2.27" }, "plugin-api-version": "2.6.0" } From 094b05c61148a3eebeb4f49330048fd18e989758 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 22:27:40 +0000 Subject: [PATCH 07/35] docs: call endpoint methods with named arguments PHP cannot declare a parameter as name-only the way Python's keyword-only marker and Ruby's required keyword arguments do, so the calling convention can only be documented rather than enforced. Parameter order comes from the API definition, so an endpoint that gains a required parameter can reorder the ones already there, and a positional call then binds a value to the wrong parameter with nothing to catch it. Every example now passes arguments by name, and the usage section says why. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- README.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1eaa1324..83446cd6 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,14 @@ $ composer require seamapi/seam > [!NOTE] > These examples assume `SEAM_API_KEY` is set in your environment. +Endpoint methods take [named arguments], which is the supported way to call +them. PHP allows the parameters to be passed positionally too, but their order +is derived from the API definition and can change as endpoints gain parameters, +so a positional call can start binding a value to the wrong parameter after an +upgrade. Passing them by name is stable. + +[named arguments]: https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments + ### Examples #### List devices @@ -55,7 +63,7 @@ $devices = $seam->devices->list(); $seam = new Seam\Seam(); $lock = $seam->locks->get(name: "Front Door"); -$seam->locks->unlock_door($lock->device_id); +$seam->locks->unlock_door(device_id: $lock->device_id); ``` ### Authentication Method @@ -122,7 +130,7 @@ use Seam\ActionAttemptFailedError; use Seam\ActionAttemptTimeoutError; try { - $seam->locks->unlock_door($device_id); + $seam->locks->unlock_door(device_id: $device_id); } catch (ActionAttemptFailedError $error) { print "Could not unlock the door: " . $error->getMessage(); print "Error code: " . $error->getErrorCode(); @@ -137,7 +145,7 @@ Waiting may be disabled for the whole client: ```php $seam = new Seam\Seam(wait_for_action_attempt: false); -$action_attempt = $seam->locks->unlock_door($device_id); +$action_attempt = $seam->locks->unlock_door(device_id: $device_id); $action_attempt->status; // "pending" ``` @@ -145,7 +153,7 @@ or for a single request: ```php $action_attempt = $seam->locks->unlock_door( - $device_id, + device_id: $device_id, wait_for_action_attempt: false ); ``` @@ -159,7 +167,7 @@ $seam = new Seam\Seam( ); $seam->locks->unlock_door( - $device_id, + device_id: $device_id, wait_for_action_attempt: ["timeout" => 5.0] ); ``` @@ -377,7 +385,7 @@ use Seam\HttpApiError; use Seam\HttpInvalidInputError; try { - $seam->devices->get($device_id); + $seam->devices->get(device_id: $device_id); } catch (HttpInvalidInputError $error) { print_r($error->getValidationErrorMessages("device_id")); } catch (HttpApiError $error) { From 38afcd49994dde5da3dd2ff9a86957c67e11cec0 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 22:36:33 +0000 Subject: [PATCH 08/35] refactor: rename the multi workspace client to without workspace The JavaScript SDK renamed this client to SeamHttpWithoutWorkspace and left SeamHttpMultiWorkspace behind as a deprecated alias, since what the class actually does is reach the endpoints that take no workspace in scope rather than several workspaces at once. This SDK is introducing the class now, so it can start from the current name with no alias to carry. Seam\SeamMultiWorkspace becomes Seam\SeamWithoutWorkspace, mirroring Seam\Seam the way the JavaScript name mirrors SeamHttp. The auth helper and the README section follow, so multi workspace is gone as vocabulary. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- README.md | 6 ++--- src/Auth.php | 2 +- ...Workspace.php => SeamWithoutWorkspace.php} | 4 ++-- src/WorkspacesProxy.php | 2 +- tests/PersonalAccessTokenTest.php | 22 +++++++++---------- 5 files changed, 18 insertions(+), 18 deletions(-) rename src/{SeamMultiWorkspace.php => SeamWithoutWorkspace.php} (95%) diff --git a/README.md b/README.md index 83446cd6..98d661eb 100644 --- a/README.md +++ b/README.md @@ -259,13 +259,13 @@ $pages = $seam->createPaginator( $connectedAccounts = $pages->flattenToArray(); ``` -### Interacting with Multiple Workspaces +### Requests without a Workspace in scope -Some endpoints are not scoped to a workspace. Use `SeamMultiWorkspace` with a +Some endpoints are not scoped to a workspace. Use `SeamWithoutWorkspace` with a personal access token to reach them. ```php -$seam = Seam\SeamMultiWorkspace::from_personal_access_token( +$seam = Seam\SeamWithoutWorkspace::from_personal_access_token( "your-personal-access-token" ); diff --git a/src/Auth.php b/src/Auth.php index f35f5b68..6023be71 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -109,7 +109,7 @@ public static function get_auth_headers_for_personal_access_token( * * @return array */ - public static function get_auth_headers_for_multi_workspace_personal_access_token( + public static function get_auth_headers_for_personal_access_token_without_workspace( string $personal_access_token, ): array { self::assert_personal_access_token($personal_access_token); diff --git a/src/SeamMultiWorkspace.php b/src/SeamWithoutWorkspace.php similarity index 95% rename from src/SeamMultiWorkspace.php rename to src/SeamWithoutWorkspace.php index c1dfaad2..b62bcb5d 100644 --- a/src/SeamMultiWorkspace.php +++ b/src/SeamWithoutWorkspace.php @@ -12,7 +12,7 @@ * Authenticates with a personal access token and exposes only the workspace * listing and creation endpoints. Use Seam\Seam for everything else. */ -class SeamMultiWorkspace +class SeamWithoutWorkspace { public const LTS_VERSION = SeamHttpClient::LTS_VERSION; @@ -43,7 +43,7 @@ public function __construct( $this->client = new SeamHttpClient( Options::get_endpoint($endpoint), - Auth::get_auth_headers_for_multi_workspace_personal_access_token( + Auth::get_auth_headers_for_personal_access_token_without_workspace( $personal_access_token, ), $guzzle_options, diff --git a/src/WorkspacesProxy.php b/src/WorkspacesProxy.php index 904d338d..0c596e60 100644 --- a/src/WorkspacesProxy.php +++ b/src/WorkspacesProxy.php @@ -7,7 +7,7 @@ /** * Exposes only the workspace endpoints that work without a workspace in - * scope, which are the only ones a SeamMultiWorkspace client can call. + * scope, which are the only ones a SeamWithoutWorkspace client can call. */ class WorkspacesProxy { diff --git a/tests/PersonalAccessTokenTest.php b/tests/PersonalAccessTokenTest.php index bce47735..850e1016 100644 --- a/tests/PersonalAccessTokenTest.php +++ b/tests/PersonalAccessTokenTest.php @@ -7,7 +7,7 @@ use Seam\InvalidOptionsError; use Seam\InvalidTokenError; use Seam\Seam; -use Seam\SeamMultiWorkspace; +use Seam\SeamWithoutWorkspace; use Tests\Support\FakeSeamConnectTestCase; final class PersonalAccessTokenTest extends FakeSeamConnectTestCase @@ -78,9 +78,9 @@ public function testPersonalAccessTokenFormatIsChecked(): void ); } - public function testMultiWorkspaceClientListsWorkspaces(): void + public function testWithoutWorkspaceClientListsWorkspaces(): void { - $seam = SeamMultiWorkspace::from_personal_access_token( + $seam = SeamWithoutWorkspace::from_personal_access_token( $this->seed["seam_at1_token"], endpoint: $this->endpoint, ); @@ -95,9 +95,9 @@ public function testMultiWorkspaceClientListsWorkspaces(): void $this->assertContains($this->seed["seed_workspace_1"], $workspace_ids); } - public function testMultiWorkspaceConstructorListsWorkspaces(): void + public function testWithoutWorkspaceConstructorListsWorkspaces(): void { - $seam = new SeamMultiWorkspace( + $seam = new SeamWithoutWorkspace( personal_access_token: $this->seed["seam_at1_token"], endpoint: $this->endpoint, ); @@ -105,9 +105,9 @@ public function testMultiWorkspaceConstructorListsWorkspaces(): void $this->assertNotEmpty($seam->workspaces->list()); } - public function testMultiWorkspaceClientCreatesAWorkspace(): void + public function testWithoutWorkspaceClientCreatesAWorkspace(): void { - $seam = SeamMultiWorkspace::from_personal_access_token( + $seam = SeamWithoutWorkspace::from_personal_access_token( $this->seed["seam_at1_token"], endpoint: $this->endpoint, ); @@ -121,18 +121,18 @@ public function testMultiWorkspaceClientCreatesAWorkspace(): void $this->assertSame("Test Workspace", $workspace->name); } - public function testMultiWorkspaceClientRequiresAToken(): void + public function testWithoutWorkspaceClientRequiresAToken(): void { $this->expectException(InvalidOptionsError::class); - new SeamMultiWorkspace(endpoint: $this->endpoint); + new SeamWithoutWorkspace(endpoint: $this->endpoint); } - public function testMultiWorkspaceClientChecksTheTokenFormat(): void + public function testWithoutWorkspaceClientChecksTheTokenFormat(): void { $this->expectException(InvalidTokenError::class); - SeamMultiWorkspace::from_personal_access_token( + SeamWithoutWorkspace::from_personal_access_token( $this->seed["seam_apikey1_token"], endpoint: $this->endpoint, ); From c13b28dc8db417e9960beb47c5680cfefa4fd95e Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 22:52:33 +0000 Subject: [PATCH 09/35] refactor: make the client the Guzzle client $seam->client was a wrapper that had to be unwrapped with get_client() to reach the Guzzle client underneath. It is now the Guzzle client itself, so anything Guzzle can do is reachable without a detour, and there is one client object in the public surface rather than two. Error mapping moves to Guzzle middleware, which fits better than the wrapper did: it sits outside the retry middleware, so it only sees the response a request finally settled on, and it holds the real request rather than a fabricated one when raising a transport error. Reading the response body moves to Seam\Http\Body, called by the generated route methods. The timeout drops from 60 to 30 seconds and becomes an option of its own rather than something to bury in guzzle_options, alongside retries. It covers connecting as well as reading. Seam::request() is gone; use $seam->client->request(). BREAKING CHANGE: $seam->client is now the Guzzle client, so $seam->client->get_client() no longer exists, and Seam\Http\SeamHttpClient is replaced by Seam\Http\ClientFactory. Seam::request() is removed. Requests now time out after 30 seconds rather than 60. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- README.md | 165 ++++++---- codegen/layouts/partials/client-class.hbs | 4 +- codegen/layouts/partials/route-method.hbs | 6 +- codegen/layouts/seam-client.hbs | 56 ++-- codegen/lib/layouts/route.ts | 9 +- src/ActionAttemptFailedError.php | 3 +- src/Http/Body.php | 36 +++ src/Http/ClientFactory.php | 147 +++++++++ src/Http/ErrorMiddleware.php | 109 +++++++ src/Http/ResolveActionAttempt.php | 20 +- src/Http/SeamHttpClient.php | 287 ------------------ src/Options.php | 2 +- src/Routes/AccessCodesClient.php | 83 +++-- src/Routes/AccessCodesSimulateClient.php | 17 +- src/Routes/AccessCodesUnmanagedClient.php | 41 ++- src/Routes/AccessGrantsClient.php | 65 ++-- src/Routes/AccessGrantsUnmanagedClient.php | 31 +- src/Routes/AccessMethodsClient.php | 63 ++-- src/Routes/AccessMethodsUnmanagedClient.php | 23 +- src/Routes/AcsAccessGroupsClient.php | 65 ++-- src/Routes/AcsClient.php | 6 +- src/Routes/AcsCredentialsClient.php | 73 +++-- src/Routes/AcsEncodersClient.php | 49 +-- src/Routes/AcsEncodersSimulateClient.php | 14 +- src/Routes/AcsEntrancesClient.php | 49 +-- src/Routes/AcsSystemsClient.php | 41 +-- src/Routes/AcsUsersClient.php | 91 +++--- src/Routes/ActionAttemptsClient.php | 23 +- src/Routes/ClientSessionsClient.php | 63 ++-- src/Routes/ConnectWebviewsClient.php | 39 ++- src/Routes/ConnectedAccountsClient.php | 47 ++- .../ConnectedAccountsSimulateClient.php | 8 +- src/Routes/CustomersClient.php | 31 +- src/Routes/DevicesClient.php | 47 ++- src/Routes/DevicesSimulateClient.php | 48 ++- src/Routes/DevicesUnmanagedClient.php | 31 +- src/Routes/EventsClient.php | 23 +- src/Routes/InstantKeysClient.php | 31 +- src/Routes/LocksClient.php | 47 +-- src/Routes/LocksSimulateClient.php | 27 +- src/Routes/NoiseSensorsClient.php | 15 +- .../NoiseSensorsNoiseThresholdsClient.php | 41 +-- src/Routes/NoiseSensorsSimulateClient.php | 8 +- src/Routes/PhonesClient.php | 31 +- src/Routes/PhonesSimulateClient.php | 17 +- src/Routes/SpacesClient.php | 103 +++---- src/Routes/ThermostatsClient.php | 111 ++++--- src/Routes/ThermostatsDailyProgramsClient.php | 35 ++- src/Routes/ThermostatsSchedulesClient.php | 47 ++- src/Routes/ThermostatsSimulateClient.php | 10 +- src/Routes/UserIdentitiesClient.php | 115 +++---- src/Routes/UserIdentitiesUnmanagedClient.php | 31 +- src/Routes/WebhooksClient.php | 41 ++- src/Routes/WorkspacesClient.php | 31 +- src/Seam.php | 59 ++-- src/SeamClient.php | 9 - src/SeamWithoutWorkspace.php | 17 +- tests/ClientTest.php | 65 ++-- tests/HeadersTest.php | 4 +- tests/HttpErrorTest.php | 30 +- tests/PackageVersionTest.php | 2 +- tests/RetryTest.php | 10 +- tests/Support/FakeSeamConnect.php | 3 +- tests/WaitForActionAttemptTest.php | 8 +- 64 files changed, 1436 insertions(+), 1427 deletions(-) create mode 100644 src/Http/Body.php create mode 100644 src/Http/ClientFactory.php create mode 100644 src/Http/ErrorMiddleware.php delete mode 100644 src/Http/SeamHttpClient.php delete mode 100644 src/SeamClient.php diff --git a/README.md b/README.md index 98d661eb..5b8ab1b3 100644 --- a/README.md +++ b/README.md @@ -317,27 +317,29 @@ the client the SDK builds, so the authorization and SDK headers are kept. ```php $seam = new Seam\Seam( guzzle_options: [ - "timeout" => 30, "headers" => ["X-Custom-Header" => "value"], "proxy" => "http://localhost:8125", ] ); ``` -> [!NOTE] -> Unlike the other Seam SDKs, this one sets a default request timeout of 60 -> seconds so a hung connection eventually fails. Pass `timeout` to change it, -> or `0` to disable it. +#### Setting the timeout + +Requests time out after 30 seconds by default, covering both connecting and +reading. Pass `timeout` in seconds to change it, or `0` to disable it. + +```php +$seam = new Seam\Seam(timeout: 60.0); +``` #### Retries Failed requests are retried twice by default, with exponential backoff. -A request that never reached the server, e.g. a connection failure, is always -retried. A request that did reach the server is only retried on a retryable -status when the HTTP method is idempotent. Every Seam endpoint is a `POST`, so -retrying one that the server may already have processed could duplicate a -write. The other Seam SDKs make the same trade. +A request that never reached the server, such as a connection failure, is +always retried, since it cannot have had an effect. A request the server may +already have processed is retried only when repeating it is safe, so a retry +never risks duplicating a write. ```php // Retry more times @@ -347,6 +349,21 @@ $seam = new Seam\Seam(retries: 5); $seam = new Seam\Seam(retries: 0); ``` +#### Using the Guzzle client + +`$seam->client` is the [Guzzle] client, already carrying the endpoint and +authorization, so it can be used to reach an endpoint the SDK does not expose. + +[Guzzle]: https://docs.guzzlephp.org/ + +```php +$response = $seam->client->request("POST", "/devices/list", [ + "json" => (object) ["limit" => 10], +]); + +$devices = Seam\Http\Body::decode($response)->devices; +``` + #### Overriding the client Pass an already configured Guzzle client. It carries its own endpoint and @@ -363,22 +380,11 @@ $seam = Seam\Seam::from_client($client); #### Errors -Every exception the SDK raises implements `Seam\SeamException`. - -| Error | Raised when | -| --------------------------- | -------------------------------------------------- | -| `HttpApiError` | The API returned an error response. | -| `HttpUnauthorizedError` | The credentials were rejected. | -| `HttpInvalidInputError` | The request parameters were rejected. | -| `InvalidOptionsError` | The client options are incomplete or incompatible. | -| `InvalidTokenError` | The token is of the wrong kind or format. | -| `ActionAttemptFailedError` | An action attempt finished in the error state. | -| `ActionAttemptTimeoutError` | An action attempt did not finish in time. | - -An error response that is not shaped like a Seam error, e.g. a gateway -returning HTML, surfaces as the underlying `GuzzleHttp\Exception\ -BadResponseException` instead. Transport failures surface as the -corresponding Guzzle exception. +Every exception the SDK raises implements `Seam\SeamException`, so it can be +caught as a group. An API error is a `Seam\HttpApiError` carrying +`getErrorCode()`, `getStatusCode()` and `getRequestId()`, with +`Seam\HttpUnauthorizedError` and `Seam\HttpInvalidInputError` as the two +specific cases worth catching on their own. ```php use Seam\HttpApiError; @@ -390,36 +396,29 @@ try { print_r($error->getValidationErrorMessages("device_id")); } catch (HttpApiError $error) { print $error->getErrorCode(); - print $error->getStatusCode(); - print $error->getRequestId(); } ``` +A response that is not shaped like a Seam error, such as a gateway returning +HTML, raises the underlying Guzzle exception instead. + ## Upgrading from 3.x -Version 4 brings this SDK in line with the Seam SDKs for other languages. - -- PHP 8.2 or later is now required. PHP 8.1 reached end of life in - December 2025. -- The client class is `Seam\Seam`. `Seam\SeamClient` still works as a - deprecated alias. -- The constructor takes named options. `$endpoint` is no longer the second - positional argument, and `$throw_http_errors` is gone; API errors always - raise a Seam exception. -- The exception classes keep their `Seam\` namespace. They now all implement - a shared `Seam\SeamException` interface, and `Seam\InvalidOptionsError` and - `Seam\InvalidTokenError` are new. -- `$seam->action_attempts->poll_until_ready()` was removed. Use - `wait_for_action_attempt`, which now also accepts a `timeout` and - `polling_interval`. The defaults changed from 20s/0.4s to 10s/1s. -- `$seam->client` is a `Seam\Http\SeamHttpClient`. The Guzzle client is - available with `$seam->client->get_client()`. -- The `$seam->api_key` property and the global `LTS_VERSION` constant were - removed. Use `Seam\Seam::LTS_VERSION`. -- Responses in the 3xx range are no longer treated as successful. -- Requests are now retried; see [Retries](#retries). -- Pagination metadata is a `Seam\Pagination` object rather than a - `stdClass`. +- PHP 8.2 or later is required. PHP 8.1 reached end of life in December 2025. +- The client class is `Seam\Seam`, replacing `Seam\SeamClient`. +- The constructor takes named options, so `$endpoint` is no longer the second + positional argument, and `$throw_http_errors` is gone: an API error always + raises. +- The exception classes stay in the `Seam\` namespace and now share a + `Seam\SeamException` interface. +- `$seam->action_attempts->poll_until_ready()` is gone. Use + `wait_for_action_attempt`, which also takes a `timeout` and + `polling_interval`. +- `$seam->client` is the Guzzle client, and `$seam->api_key` and the global + `LTS_VERSION` constant are gone. Use `Seam\Seam::LTS_VERSION`. +- A response in the 3xx range is no longer treated as successful. +- Requests time out after 30 seconds and are retried twice. +- Pagination metadata is a `Seam\Pagination` rather than a `stdClass`. ## Development and Testing @@ -457,6 +456,17 @@ Formatting is handled by [Prettier](https://prettier.io/) via so PHP, TypeScript, JSON, YAML and Markdown are all formatted by `npm run format`. +### Source code + +The [source code] is hosted on GitHub. +Clone the project with + +``` +$ git clone git@github.com:seamapi/php.git +``` + +[source code]: https://github.com/seamapi/php + ### Running Tests Run the full suite with @@ -524,3 +534,56 @@ picks up the new tag from its GitHub webhook. Development files are kept out of the published package with `export-ignore` rules in `.gitattributes`, which `git archive` honours when GitHub builds the archives Composer downloads as `dist`. + +## GitHub Actions + +_GitHub Actions should already be configured: this section is for reference only._ + +Publishing is handled by [Packagist], which reads new versions from the git +tags this repository pushes, so no registry token is needed. + +[Packagist]: https://packagist.org/packages/seamapi/seam + +### Secrets for Optional GitHub Actions + +The version, format, generate, and semantic-release GitHub actions +require a user with write access to the repository. +Set these additional secrets to enable the action: + +- `GH_TOKEN`: A personal access token for the user. +- `GIT_USER_NAME`: The GitHub user's real name. +- `GIT_USER_EMAIL`: The GitHub user's email. +- `GPG_PRIVATE_KEY`: The GitHub user's [GPG private key]. +- `GPG_PASSPHRASE`: The GitHub user's GPG passphrase. + +[GPG private key]: https://github.com/marketplace/actions/import-gpg#prerequisites + +## Contributing + +Please submit and comment on bug reports and feature requests. + +To submit a patch: + +1. Fork it (https://github.com/seamapi/php/fork). +2. Create your feature branch (`git checkout -b my-new-feature`). +3. Make changes. +4. Commit your changes (`git commit -am 'Add some feature'`). +5. Push to the branch (`git push origin my-new-feature`). +6. Create a new Pull Request. + +## License + +This PHP package is licensed under the MIT license. + +## Warranty + +This software is provided by the copyright holders and contributors "as is" and +any express or implied warranties, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose are +disclaimed. In no event shall the copyright holder or contributors be liable for +any direct, indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or services; +loss of use, data, or profits; or business interruption) however caused and on +any theory of liability, whether in contract, strict liability, or tort +(including negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. diff --git a/codegen/layouts/partials/client-class.hbs b/codegen/layouts/partials/client-class.hbs index 79d6dd3d..8a36f141 100644 --- a/codegen/layouts/partials/client-class.hbs +++ b/codegen/layouts/partials/client-class.hbs @@ -1,6 +1,6 @@ class {{clientName}}Client { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -16,7 +16,7 @@ class {{clientName}}Client /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index bf344f74..95efcd7d 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -14,13 +14,13 @@ {{/each}} {{/if}} - {{#unless returnsVoid}}$res = {{/unless}}$this->client->request( + {{#unless returnsVoid}}$res = Body::decode({{/unless}}$this->client->request( "POST", "{{path}}", {{#if hasParams}} - json: (object) $request_payload, + ["json" => (object) $request_payload], {{/if}} - ); + ){{#unless returnsVoid}}){{/unless}}; {{#if usesActionAttempt}} return ResolveActionAttempt::resolve_action_attempt( diff --git a/codegen/layouts/seam-client.hbs b/codegen/layouts/seam-client.hbs index c452d625..c07c5d63 100644 --- a/codegen/layouts/seam-client.hbs +++ b/codegen/layouts/seam-client.hbs @@ -6,8 +6,9 @@ namespace Seam; use {{this}}; {{/each}} +use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; -use Seam\Http\SeamHttpClient; +use Seam\Http\ClientFactory; /** * Client for the Seam API. @@ -27,9 +28,12 @@ class Seam /** * The long term support version of the Seam API this SDK targets. */ - public const LTS_VERSION = SeamHttpClient::LTS_VERSION; + public const LTS_VERSION = ClientFactory::LTS_VERSION; - public SeamHttpClient $client; + /** + * The Guzzle client this instance makes its requests with. + */ + public ClientInterface $client; /** * Default request options applied to every call, currently just @@ -41,9 +45,10 @@ class Seam /** * @param bool|array{timeout?: float, polling_interval?: float}|null $wait_for_action_attempt Whether to wait for action attempts to finish, optionally with timeout and polling_interval in seconds. Defaults to true. - * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. timeout or headers. + * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. headers or proxy. * @param int|null $retries How many times to retry a failed request. Defaults to 2; pass 0 to disable. - * @param ClientInterface|null $client A preconfigured Guzzle client. It carries its own endpoint and authorization, so no other authentication option may be given with it. + * @param float|null $timeout Request timeout in seconds, covering connecting and reading. Defaults to 30; pass 0 to disable. + * @param ClientInterface|null $client A preconfigured Guzzle client, used as is. It carries its own endpoint and authorization, so no other authentication option applies to it. */ public function __construct( ?string $api_key = null, @@ -53,6 +58,7 @@ class Seam bool|array|null $wait_for_action_attempt = null, array $guzzle_options = [], ?int $retries = null, + ?float $timeout = null, ?ClientInterface $client = null ) { $this->defaults = [ @@ -61,14 +67,13 @@ class Seam // A client carries its own endpoint and authorization, so the // authentication options are only read when one has to be built. - $this->client = $client !== null - ? SeamHttpClient::from_client($client) - : new SeamHttpClient( - Options::get_endpoint($endpoint), - Auth::get_auth_headers($api_key, $personal_access_token, $workspace_id), - $guzzle_options, - $retries - ); + $this->client = $client ?? ClientFactory::create( + Options::get_endpoint($endpoint), + Auth::get_auth_headers($api_key, $personal_access_token, $workspace_id), + $guzzle_options, + $retries, + $timeout + ); {{#each parentClients}} $this->{{namespace}} = new {{clientName}}Client($this->client, $this->defaults); @@ -83,14 +88,16 @@ class Seam ?string $endpoint = null, bool|array|null $wait_for_action_attempt = null, array $guzzle_options = [], - ?int $retries = null + ?int $retries = null, + ?float $timeout = null ): static { return new static( api_key: $api_key, endpoint: $endpoint, wait_for_action_attempt: $wait_for_action_attempt, guzzle_options: $guzzle_options, - retries: $retries + retries: $retries, + timeout: $timeout ); } @@ -104,7 +111,8 @@ class Seam ?string $endpoint = null, bool|array|null $wait_for_action_attempt = null, array $guzzle_options = [], - ?int $retries = null + ?int $retries = null, + ?float $timeout = null ): static { return new static( personal_access_token: $personal_access_token, @@ -112,6 +120,7 @@ class Seam endpoint: $endpoint, wait_for_action_attempt: $wait_for_action_attempt, retries: $retries, + timeout: $timeout, guzzle_options: $guzzle_options ); } @@ -134,21 +143,6 @@ class Seam return self::LTS_VERSION; } - /** - * Makes a request against the Seam API with this client's authorization. - * - * @param array|object|null $json - * @param array|null $query - */ - public function request( - string $method, - string $path, - mixed $json = null, - ?array $query = null - ): mixed { - return $this->client->request($method, $path, $json, $query); - } - /** * Creates a paginator for a list endpoint. * diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 0cc1ac3e..8dc8c110 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -8,7 +8,8 @@ import { sortPhpClientMethodParameters, } from '../class-model.js' -const seamHttpClientClass = 'Seam\\Http\\SeamHttpClient' +const clientInterfaceClass = 'GuzzleHttp\\ClientInterface' +const bodyClass = 'Seam\\Http\\Body' const resolveActionAttemptClass = 'Seam\\Http\\ResolveActionAttempt' const resourcesNamespace = 'Seam\\Resources' @@ -147,8 +148,12 @@ const getUseStatements = (client: PhpClient): string[] => { const usesActionAttempt = resourceNames.has('ActionAttempt') + // Void endpoints never read the response, so they do not decode it. + const readsBody = client.methods.some((m) => m.returnResource !== '') + return [ - seamHttpClientClass, + clientInterfaceClass, + ...(readsBody ? [bodyClass] : []), ...(usesActionAttempt ? [resolveActionAttemptClass] : []), ...[...resourceNames].map((name) => `${resourcesNamespace}\\${name}`), ].sort((a, b) => a.localeCompare(b)) diff --git a/src/ActionAttemptFailedError.php b/src/ActionAttemptFailedError.php index af5c5d16..a69311f5 100644 --- a/src/ActionAttemptFailedError.php +++ b/src/ActionAttemptFailedError.php @@ -24,8 +24,7 @@ public function __construct(ActionAttempt $actionAttempt) * The action attempt error type. * * Named `getErrorCode` rather than `getCode` because `Exception::getCode` - * is final and returns an int. This is the equivalent of the `code` - * property on the same error in the other Seam SDKs. + * is final and returns an int. */ public function getErrorCode(): string { diff --git a/src/Http/Body.php b/src/Http/Body.php new file mode 100644 index 00000000..a63321a7 --- /dev/null +++ b/src/Http/Body.php @@ -0,0 +1,36 @@ +getBody(); + $body->rewind(); + $contents = $body->getContents(); + + if ($contents === "") { + return null; + } + + try { + return Utils::jsonDecode($contents); + } catch (\InvalidArgumentException) { + return null; + } + } +} diff --git a/src/Http/ClientFactory.php b/src/Http/ClientFactory.php new file mode 100644 index 00000000..b51f3136 --- /dev/null +++ b/src/Http/ClientFactory.php @@ -0,0 +1,147 @@ + $auth_headers + * @param array $guzzle_options + */ + public static function create( + string $endpoint, + array $auth_headers, + array $guzzle_options = [], + ?int $retries = null, + ?float $timeout = null, + ): Client { + $retries ??= self::DEFAULT_RETRIES; + $timeout ??= self::DEFAULT_TIMEOUT; + + // Middleware has to be on the handler stack the client is built with: + // a stack pushed onto after construction does not apply. + $handler = $guzzle_options["handler"] ?? HandlerStack::create(); + + if ($handler instanceof HandlerStack) { + // Pushed first, so it wraps the retry middleware and only sees the + // response a request finally settled on. + $handler->push(ErrorMiddleware::create(), "seam_error"); + $handler->push( + self::retry_options_middleware(), + "seam_retry_options", + ); + $handler->push( + GuzzleRetryMiddleware::factory([ + "retry_enabled" => $retries > 0, + "max_retry_attempts" => $retries, + "retry_on_timeout" => true, + // Set per request by the middleware above. + "retry_on_status" => [], + ]), + "seam_retry", + ); + } + + $headers = array_merge( + $auth_headers, + $guzzle_options["headers"] ?? [], + self::sdk_headers(), + ); + + return new Client( + array_merge( + [ + "base_uri" => $endpoint, + "timeout" => $timeout, + "connect_timeout" => $timeout, + ], + $guzzle_options, + [ + "handler" => $handler, + "headers" => $headers, + // ErrorMiddleware raises instead, so that a Seam error + // response becomes a Seam exception. + "http_errors" => false, + ], + ), + ); + } + + /** + * Opts a request into status based retries when repeating it is safe. + */ + private static function retry_options_middleware(): callable + { + return static fn(callable $handler): callable => static function ( + RequestInterface $request, + array $options, + ) use ($handler): PromiseInterface { + if ( + in_array( + strtoupper($request->getMethod()), + self::IDEMPOTENT_METHODS, + true, + ) + ) { + $options["retry_on_status"] = self::RETRYABLE_STATUS_CODES; + } + + return $handler($request, $options); + }; + } + + /** + * @return array + */ + private static function sdk_headers(): array + { + $version = PackageVersion::get(); + + return [ + "User-Agent" => "seam-php/" . $version, + "seam-sdk-name" => "seamapi/php", + "seam-sdk-version" => $version, + "seam-lts-version" => self::LTS_VERSION, + ]; + } +} diff --git a/src/Http/ErrorMiddleware.php b/src/Http/ErrorMiddleware.php new file mode 100644 index 00000000..a709af15 --- /dev/null +++ b/src/Http/ErrorMiddleware.php @@ -0,0 +1,109 @@ + static fn( + RequestInterface $request, + array $options, + ): PromiseInterface => $handler($request, $options)->then( + static function (ResponseInterface $response) use ( + $request, + ): ResponseInterface { + $status_code = $response->getStatusCode(); + + if ($status_code >= 200 && $status_code < 300) { + return $response; + } + + throw self::to_exception($request, $response); + }, + ); + } + + private static function to_exception( + RequestInterface $request, + ResponseInterface $response, + ): \Throwable { + $status_code = $response->getStatusCode(); + $request_id = self::get_request_id($response); + + if ($status_code === 401) { + return new HttpUnauthorizedError($request_id); + } + + $error = self::get_error($response); + + if ($error === null) { + return BadResponseException::create($request, $response); + } + + if (($error->type ?? null) === "invalid_input") { + return new HttpInvalidInputError($error, $status_code, $request_id); + } + + return new HttpApiError($error, $status_code, $request_id); + } + + /** + * The error from a Seam error envelope, i.e. JSON holding an `error` + * object with a string `type` and `message`, or null when the response is + * not one. + */ + private static function get_error(ResponseInterface $response): ?object + { + if ( + !str_starts_with( + $response->getHeaderLine("content-type"), + "application/json", + ) + ) { + return null; + } + + $body = Body::decode($response); + + if (!is_object($body)) { + return null; + } + + $error = $body->error ?? null; + + if (!is_object($error)) { + return null; + } + + return is_string($error->type ?? null) && + is_string($error->message ?? null) + ? $error + : null; + } + + private static function get_request_id(ResponseInterface $response): ?string + { + return $response->hasHeader("seam-request-id") + ? $response->getHeaderLine("seam-request-id") + : null; + } +} diff --git a/src/Http/ResolveActionAttempt.php b/src/Http/ResolveActionAttempt.php index 29ba92b9..b0bb60fd 100644 --- a/src/Http/ResolveActionAttempt.php +++ b/src/Http/ResolveActionAttempt.php @@ -2,6 +2,7 @@ namespace Seam\Http; +use GuzzleHttp\ClientInterface; use Seam\ActionAttemptFailedError; use Seam\ActionAttemptTimeoutError; use Seam\Resources\ActionAttempt; @@ -9,9 +10,8 @@ /** * Waits for an action attempt to reach a terminal state. * - * Mirrors resolveActionAttempt in the JavaScript SDK: a successful attempt is - * returned as is, a failed one raises, and a pending one is polled until it - * finishes or the timeout elapses. + * A successful attempt is returned as is, a failed one raises, and a pending + * one is polled until it finishes or the timeout elapses. */ final class ResolveActionAttempt { @@ -24,7 +24,7 @@ final class ResolveActionAttempt */ public static function resolve_action_attempt( ActionAttempt $action_attempt, - SeamHttpClient $client, + ClientInterface $client, bool|array|null $wait_for_action_attempt, ): ActionAttempt { if ($wait_for_action_attempt === false) { @@ -45,7 +45,7 @@ public static function resolve_action_attempt( private static function poll( ActionAttempt $action_attempt, - SeamHttpClient $client, + ClientInterface $client, float $timeout, float $polling_interval, ): ActionAttempt { @@ -74,13 +74,13 @@ private static function poll( } private static function get_action_attempt( - SeamHttpClient $client, + ClientInterface $client, string $action_attempt_id, ): ActionAttempt { - $res = $client->request( - "POST", - "/action_attempts/get", - json: (object) ["action_attempt_id" => $action_attempt_id], + $res = Body::decode( + $client->request("POST", "/action_attempts/get", [ + "json" => (object) ["action_attempt_id" => $action_attempt_id], + ]), ); $action_attempt = ActionAttempt::from_json( diff --git a/src/Http/SeamHttpClient.php b/src/Http/SeamHttpClient.php deleted file mode 100644 index 2ad04030..00000000 --- a/src/Http/SeamHttpClient.php +++ /dev/null @@ -1,287 +0,0 @@ - $auth_headers - * @param array $guzzle_options - */ - public function __construct( - string $endpoint, - array $auth_headers, - array $guzzle_options = [], - ?int $retries = null, - ?ClientInterface $client = null, - ) { - $this->client = - $client ?? - new GuzzleClient( - self::create_config( - $endpoint, - $auth_headers, - $guzzle_options, - $retries ?? self::DEFAULT_RETRIES, - ), - ); - } - - /** - * Wraps an already configured Guzzle client. The client carries its own - * endpoint and authorization, so none are resolved here. - */ - public static function from_client(ClientInterface $client): self - { - return new self("", [], [], null, $client); - } - - public function get_client(): ClientInterface - { - return $this->client; - } - - /** - * @param array $auth_headers - * @param array $guzzle_options - * @return array - */ - private static function create_config( - string $endpoint, - array $auth_headers, - array $guzzle_options, - int $retries, - ): array { - // The middleware has to be on the handler stack the client is built - // with: a stack pushed onto after construction does not apply. - $handler = $guzzle_options["handler"] ?? HandlerStack::create(); - - if ($handler instanceof HandlerStack) { - $handler->push( - GuzzleRetryMiddleware::factory([ - "retry_enabled" => $retries > 0, - "max_retry_attempts" => $retries, - // A connection failure never reached the server, so it is - // safe to retry whatever the method. Status codes are - // opted into per request, see request(). - "retry_on_timeout" => true, - "retry_on_status" => [], - ]), - "seam_retry", - ); - } - - $headers = array_merge( - $auth_headers, - $guzzle_options["headers"] ?? [], - self::sdk_headers(), - ); - - return array_merge( - [ - "base_uri" => $endpoint, - "timeout" => self::DEFAULT_TIMEOUT, - ], - $guzzle_options, - [ - "handler" => $handler, - "headers" => $headers, - // Error mapping happens in request(); letting Guzzle throw - // first would bypass it entirely. - "http_errors" => false, - ], - ); - } - - /** - * @return array - */ - private static function sdk_headers(): array - { - $version = PackageVersion::get(); - - return [ - "User-Agent" => "seam-php/" . $version, - "seam-sdk-name" => "seamapi/php", - "seam-sdk-version" => $version, - "seam-lts-version" => self::LTS_VERSION, - ]; - } - - /** - * @param array|object|null $json - * @param array|null $query - */ - public function request( - string $method, - string $path, - mixed $json = null, - ?array $query = null, - ): mixed { - $options = array_filter( - [ - "json" => $json, - "query" => $query, - ], - fn($option) => $option !== null, - ); - - if (in_array(strtoupper($method), self::IDEMPOTENT_METHODS, true)) { - $options["retry_on_status"] = self::RETRYABLE_STATUS_CODES; - } - - $response = $this->client->request($method, $path, $options); - $status_code = $response->getStatusCode(); - - if ($status_code < 200 || $status_code >= 300) { - $this->handle_error_response( - new Request($method, $path), - $response, - ); - } - - return self::decode_body($response); - } - - private function handle_error_response( - RequestInterface $request, - ResponseInterface $response, - ): void { - $status_code = $response->getStatusCode(); - $request_id = self::get_request_id($response); - - if ($status_code === 401) { - throw new HttpUnauthorizedError($request_id); - } - - if (!self::is_api_error_response($response)) { - // Not a Seam error response, so there is nothing to map onto a - // Seam exception. The other Seam SDKs surface the underlying - // transport error here too. - throw BadResponseException::create($request, $response); - } - - $error = self::decode_body($response)->error; - - if (($error->type ?? null) === "invalid_input") { - throw new HttpInvalidInputError($error, $status_code, $request_id); - } - - throw new HttpApiError($error, $status_code, $request_id); - } - - /** - * True when the response body is a Seam error envelope, i.e. JSON holding - * an `error` object with a string `type` and `message`. - */ - private static function is_api_error_response( - ResponseInterface $response, - ): bool { - if ( - !str_starts_with( - $response->getHeaderLine("content-type"), - "application/json", - ) - ) { - return false; - } - - $body = self::decode_body($response); - - if (!is_object($body)) { - return false; - } - - $error = $body->error ?? null; - - if (!is_object($error)) { - return false; - } - - return is_string($error->type ?? null) && - is_string($error->message ?? null); - } - - private static function decode_body(ResponseInterface $response): mixed - { - $body = $response->getBody(); - $body->rewind(); - $contents = $body->getContents(); - - if ($contents === "") { - return null; - } - - try { - return Utils::jsonDecode($contents); - } catch (\InvalidArgumentException) { - return null; - } - } - - private static function get_request_id(ResponseInterface $response): ?string - { - return $response->hasHeader("seam-request-id") - ? $response->getHeaderLine("seam-request-id") - : null; - } -} diff --git a/src/Options.php b/src/Options.php index 7fc4dcc1..e3d22aa3 100644 --- a/src/Options.php +++ b/src/Options.php @@ -4,7 +4,7 @@ /** * Resolves the API endpoint and validates mutually exclusive authentication - * options, mirroring the option handling in the other Seam SDKs. + * options. */ final class Options { diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index a3465a8d..3c446642 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AccessCode; class AccessCodesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class AccessCodesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -134,10 +135,10 @@ public function create( ] = $use_offline_access_code; } - $res = $this->client->request( - "POST", - "/access_codes/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/create", [ + "json" => (object) $request_payload, + ]), ); return AccessCode::from_json($res->access_code); @@ -239,10 +240,10 @@ public function create_multiple( ] = $use_backup_access_code_pool; } - $res = $this->client->request( - "POST", - "/access_codes/create_multiple", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/create_multiple", [ + "json" => (object) $request_payload, + ]), ); return array_map( @@ -269,11 +270,9 @@ public function delete( $request_payload["device_id"] = $device_id; } - $this->client->request( - "POST", - "/access_codes/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_codes/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -288,10 +287,10 @@ public function generate_code(string $device_id): AccessCode $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/access_codes/generate_code", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/generate_code", [ + "json" => (object) $request_payload, + ]), ); return AccessCode::from_json($res->generated_code); @@ -324,10 +323,10 @@ public function get( $request_payload["device_id"] = $device_id; } - $res = $this->client->request( - "POST", - "/access_codes/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/get", [ + "json" => (object) $request_payload, + ]), ); return AccessCode::from_json($res->access_code); @@ -397,10 +396,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/access_codes/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -433,10 +432,12 @@ public function pull_backup_access_code(string $access_code_id): AccessCode $request_payload["access_code_id"] = $access_code_id; - $res = $this->client->request( - "POST", - "/access_codes/pull_backup_access_code", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/access_codes/pull_backup_access_code", + ["json" => (object) $request_payload], + ), ); return AccessCode::from_json($res->access_code); @@ -477,7 +478,7 @@ public function report_device_constraints( $this->client->request( "POST", "/access_codes/report_device_constraints", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -601,11 +602,9 @@ public function update( ] = $use_offline_access_code; } - $this->client->request( - "POST", - "/access_codes/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_codes/update", [ + "json" => (object) $request_payload, + ]); } /** @@ -646,10 +645,8 @@ public function update_multiple( $request_payload["starts_at"] = $starts_at; } - $this->client->request( - "POST", - "/access_codes/update_multiple", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_codes/update_multiple", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AccessCodesSimulateClient.php b/src/Routes/AccessCodesSimulateClient.php index 0750e822..a2897660 100644 --- a/src/Routes/AccessCodesSimulateClient.php +++ b/src/Routes/AccessCodesSimulateClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\UnmanagedAccessCode; class AccessCodesSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class AccessCodesSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -42,10 +43,12 @@ public function create_unmanaged_access_code( $request_payload["device_id"] = $device_id; $request_payload["name"] = $name; - $res = $this->client->request( - "POST", - "/access_codes/simulate/create_unmanaged_access_code", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/access_codes/simulate/create_unmanaged_access_code", + ["json" => (object) $request_payload], + ), ); return UnmanagedAccessCode::from_json($res->access_code); diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index e18b0ab3..75a59b15 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\UnmanagedAccessCode; class AccessCodesUnmanagedClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class AccessCodesUnmanagedClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -62,7 +63,7 @@ public function convert_to_managed( $this->client->request( "POST", "/access_codes/unmanaged/convert_to_managed", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -78,11 +79,9 @@ public function delete(string $access_code_id): void $request_payload["access_code_id"] = $access_code_id; - $this->client->request( - "POST", - "/access_codes/unmanaged/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_codes/unmanaged/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -112,10 +111,10 @@ public function get( $request_payload["device_id"] = $device_id; } - $res = $this->client->request( - "POST", - "/access_codes/unmanaged/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/unmanaged/get", [ + "json" => (object) $request_payload, + ]), ); return UnmanagedAccessCode::from_json($res->access_code); @@ -156,10 +155,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/access_codes/unmanaged/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_codes/unmanaged/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -207,10 +206,8 @@ public function update( ] = $is_external_modification_allowed; } - $this->client->request( - "POST", - "/access_codes/unmanaged/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_codes/unmanaged/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index 188412e5..e6a8a8b8 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AccessGrant; use Seam\Resources\Batch; class AccessGrantsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class AccessGrantsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -112,10 +113,10 @@ public function create( $request_payload["starts_at"] = $starts_at; } - $res = $this->client->request( - "POST", - "/access_grants/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_grants/create", [ + "json" => (object) $request_payload, + ]), ); return AccessGrant::from_json($res->access_grant); @@ -133,11 +134,9 @@ public function delete(string $access_grant_id): void $request_payload["access_grant_id"] = $access_grant_id; - $this->client->request( - "POST", - "/access_grants/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_grants/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -160,10 +159,10 @@ public function get( $request_payload["access_grant_key"] = $access_grant_key; } - $res = $this->client->request( - "POST", - "/access_grants/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_grants/get", [ + "json" => (object) $request_payload, + ]), ); return AccessGrant::from_json($res->access_grant); @@ -199,10 +198,10 @@ public function get_related( $request_payload["include"] = $include; } - $res = $this->client->request( - "POST", - "/access_grants/get_related", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_grants/get_related", [ + "json" => (object) $request_payload, + ]), ); return Batch::from_json($res->batch); @@ -285,10 +284,10 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/access_grants/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_grants/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -319,10 +318,12 @@ public function request_access_methods( "requested_access_methods" ] = $requested_access_methods; - $res = $this->client->request( - "POST", - "/access_grants/request_access_methods", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/access_grants/request_access_methods", + ["json" => (object) $request_payload], + ), ); return AccessGrant::from_json($res->access_grant); @@ -363,10 +364,8 @@ public function update( $request_payload["starts_at"] = $starts_at; } - $this->client->request( - "POST", - "/access_grants/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_grants/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index 72955f56..dfe5df2a 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\UnmanagedAccessGrant; class AccessGrantsUnmanagedClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class AccessGrantsUnmanagedClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -35,10 +36,10 @@ public function get(string $access_grant_id): UnmanagedAccessGrant $request_payload["access_grant_id"] = $access_grant_id; - $res = $this->client->request( - "POST", - "/access_grants/unmanaged/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_grants/unmanaged/get", [ + "json" => (object) $request_payload, + ]), ); return UnmanagedAccessGrant::from_json($res->access_grant); @@ -86,10 +87,10 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/access_grants/unmanaged/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_grants/unmanaged/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -127,10 +128,8 @@ public function update( $request_payload["access_grant_key"] = $access_grant_key; } - $this->client->request( - "POST", - "/access_grants/unmanaged/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_grants/unmanaged/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index 09421e8e..a038f4f9 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -2,15 +2,16 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\AccessMethod; use Seam\Resources\ActionAttempt; use Seam\Resources\Batch; class AccessMethodsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -20,7 +21,7 @@ class AccessMethodsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -45,10 +46,10 @@ public function assign_card( $request_payload["access_method_id"] = $access_method_id; $request_payload["card_number"] = $card_number; - $res = $this->client->request( - "POST", - "/access_methods/assign_card", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/assign_card", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -84,11 +85,9 @@ public function delete( $request_payload["reservation_key"] = $reservation_key; } - $this->client->request( - "POST", - "/access_methods/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/access_methods/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -109,10 +108,10 @@ public function encode( $request_payload["access_method_id"] = $access_method_id; $request_payload["acs_encoder_id"] = $acs_encoder_id; - $res = $this->client->request( - "POST", - "/access_methods/encode", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/encode", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -135,10 +134,10 @@ public function get(string $access_method_id): AccessMethod $request_payload["access_method_id"] = $access_method_id; - $res = $this->client->request( - "POST", - "/access_methods/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/get", [ + "json" => (object) $request_payload, + ]), ); return AccessMethod::from_json($res->access_method); @@ -167,10 +166,10 @@ public function get_related( $request_payload["include"] = $include; } - $res = $this->client->request( - "POST", - "/access_methods/get_related", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/get_related", [ + "json" => (object) $request_payload, + ]), ); return Batch::from_json($res->batch); @@ -228,10 +227,10 @@ public function list( $request_payload["space_id"] = $space_id; } - $res = $this->client->request( - "POST", - "/access_methods/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -262,10 +261,10 @@ public function unlock_door( $request_payload["access_method_id"] = $access_method_id; $request_payload["acs_entrance_id"] = $acs_entrance_id; - $res = $this->client->request( - "POST", - "/access_methods/unlock_door", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/unlock_door", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/AccessMethodsUnmanagedClient.php b/src/Routes/AccessMethodsUnmanagedClient.php index 4599a2b4..0aaa359c 100644 --- a/src/Routes/AccessMethodsUnmanagedClient.php +++ b/src/Routes/AccessMethodsUnmanagedClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\UnmanagedAccessMethod; class AccessMethodsUnmanagedClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class AccessMethodsUnmanagedClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -35,10 +36,10 @@ public function get(string $access_method_id): UnmanagedAccessMethod $request_payload["access_method_id"] = $access_method_id; - $res = $this->client->request( - "POST", - "/access_methods/unmanaged/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/unmanaged/get", [ + "json" => (object) $request_payload, + ]), ); return UnmanagedAccessMethod::from_json($res->access_method); @@ -72,10 +73,10 @@ public function list( $request_payload["space_id"] = $space_id; } - $res = $this->client->request( - "POST", - "/access_methods/unmanaged/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/access_methods/unmanaged/list", [ + "json" => (object) $request_payload, + ]), ); return array_map( diff --git a/src/Routes/AcsAccessGroupsClient.php b/src/Routes/AcsAccessGroupsClient.php index 8fea3af4..109f7141 100644 --- a/src/Routes/AcsAccessGroupsClient.php +++ b/src/Routes/AcsAccessGroupsClient.php @@ -2,14 +2,15 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AcsAccessGroup; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsUser; class AcsAccessGroupsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -19,7 +20,7 @@ class AcsAccessGroupsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -48,11 +49,9 @@ public function add_user( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/access_groups/add_user", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/access_groups/add_user", [ + "json" => (object) $request_payload, + ]); } /** @@ -67,11 +66,9 @@ public function delete(string $acs_access_group_id): void $request_payload["acs_access_group_id"] = $acs_access_group_id; - $this->client->request( - "POST", - "/acs/access_groups/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/access_groups/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -86,10 +83,10 @@ public function get(string $acs_access_group_id): AcsAccessGroup $request_payload["acs_access_group_id"] = $acs_access_group_id; - $res = $this->client->request( - "POST", - "/acs/access_groups/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/access_groups/get", [ + "json" => (object) $request_payload, + ]), ); return AcsAccessGroup::from_json($res->acs_access_group); @@ -125,10 +122,10 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/acs/access_groups/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/access_groups/list", [ + "json" => (object) $request_payload, + ]), ); return array_map( @@ -150,10 +147,12 @@ public function list_accessible_entrances( $request_payload["acs_access_group_id"] = $acs_access_group_id; - $res = $this->client->request( - "POST", - "/acs/access_groups/list_accessible_entrances", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/acs/access_groups/list_accessible_entrances", + ["json" => (object) $request_payload], + ), ); return array_map( @@ -174,10 +173,10 @@ public function list_users(string $acs_access_group_id): array $request_payload["acs_access_group_id"] = $acs_access_group_id; - $res = $this->client->request( - "POST", - "/acs/access_groups/list_users", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/access_groups/list_users", [ + "json" => (object) $request_payload, + ]), ); return array_map(fn($r) => AcsUser::from_json($r), $res->acs_users); @@ -206,10 +205,8 @@ public function remove_user( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/access_groups/remove_user", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/access_groups/remove_user", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AcsClient.php b/src/Routes/AcsClient.php index 1d5f11e0..bee3e615 100644 --- a/src/Routes/AcsClient.php +++ b/src/Routes/AcsClient.php @@ -2,11 +2,11 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; class AcsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -21,7 +21,7 @@ class AcsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index 43b41e38..4dc13187 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AcsCredential; use Seam\Resources\AcsEntrance; class AcsCredentialsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class AcsCredentialsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -47,11 +48,9 @@ public function assign( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/credentials/assign", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/credentials/assign", [ + "json" => (object) $request_payload, + ]); } /** @@ -135,10 +134,10 @@ public function create( $request_payload["visionline_metadata"] = $visionline_metadata; } - $res = $this->client->request( - "POST", - "/acs/credentials/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/credentials/create", [ + "json" => (object) $request_payload, + ]), ); return AcsCredential::from_json($res->acs_credential); @@ -156,11 +155,9 @@ public function delete(string $acs_credential_id): void $request_payload["acs_credential_id"] = $acs_credential_id; - $this->client->request( - "POST", - "/acs/credentials/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/credentials/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -175,10 +172,10 @@ public function get(string $acs_credential_id): AcsCredential $request_payload["acs_credential_id"] = $acs_credential_id; - $res = $this->client->request( - "POST", - "/acs/credentials/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/credentials/get", [ + "json" => (object) $request_payload, + ]), ); return AcsCredential::from_json($res->acs_credential); @@ -238,10 +235,10 @@ public function list( $request_payload["search"] = $search; } - $res = $this->client->request( - "POST", - "/acs/credentials/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/credentials/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -266,10 +263,12 @@ public function list_accessible_entrances(string $acs_credential_id): array $request_payload["acs_credential_id"] = $acs_credential_id; - $res = $this->client->request( - "POST", - "/acs/credentials/list_accessible_entrances", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/acs/credentials/list_accessible_entrances", + ["json" => (object) $request_payload], + ), ); return array_map( @@ -301,11 +300,9 @@ public function unassign( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/credentials/unassign", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/credentials/unassign", [ + "json" => (object) $request_payload, + ]); } /** @@ -331,10 +328,8 @@ public function update( $request_payload["ends_at"] = $ends_at; } - $this->client->request( - "POST", - "/acs/credentials/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/credentials/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index dd12962a..6383100f 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -2,14 +2,15 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\AcsEncoder; use Seam\Resources\ActionAttempt; class AcsEncodersClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -19,7 +20,7 @@ class AcsEncodersClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -51,10 +52,10 @@ public function encode_credential( $request_payload["acs_credential_id"] = $acs_credential_id; } - $res = $this->client->request( - "POST", - "/acs/encoders/encode_credential", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/encoders/encode_credential", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -77,10 +78,10 @@ public function get(string $acs_encoder_id): AcsEncoder $request_payload["acs_encoder_id"] = $acs_encoder_id; - $res = $this->client->request( - "POST", - "/acs/encoders/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/encoders/get", [ + "json" => (object) $request_payload, + ]), ); return AcsEncoder::from_json($res->acs_encoder); @@ -123,10 +124,10 @@ public function list( $request_payload["page_cursor"] = $page_cursor; } - $res = $this->client->request( - "POST", - "/acs/encoders/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/encoders/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -159,10 +160,10 @@ public function scan_credential( $request_payload["salto_ks_metadata"] = $salto_ks_metadata; } - $res = $this->client->request( - "POST", - "/acs/encoders/scan_credential", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/encoders/scan_credential", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -203,10 +204,12 @@ public function scan_to_assign_credential( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/acs/encoders/scan_to_assign_credential", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/acs/encoders/scan_to_assign_credential", + ["json" => (object) $request_payload], + ), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/AcsEncodersSimulateClient.php b/src/Routes/AcsEncodersSimulateClient.php index 54ce9aaf..f1616fea 100644 --- a/src/Routes/AcsEncodersSimulateClient.php +++ b/src/Routes/AcsEncodersSimulateClient.php @@ -2,11 +2,11 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; class AcsEncodersSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -16,7 +16,7 @@ class AcsEncodersSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -48,7 +48,7 @@ public function next_credential_encode_will_fail( $this->client->request( "POST", "/acs/encoders/simulate/next_credential_encode_will_fail", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -73,7 +73,7 @@ public function next_credential_encode_will_succeed( $this->client->request( "POST", "/acs/encoders/simulate/next_credential_encode_will_succeed", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -105,7 +105,7 @@ public function next_credential_scan_will_fail( $this->client->request( "POST", "/acs/encoders/simulate/next_credential_scan_will_fail", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -137,7 +137,7 @@ public function next_credential_scan_will_succeed( $this->client->request( "POST", "/acs/encoders/simulate/next_credential_scan_will_succeed", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } } diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index 270cfb24..0c6a65e5 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -2,15 +2,16 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\AcsCredential; use Seam\Resources\AcsEntrance; use Seam\Resources\ActionAttempt; class AcsEntrancesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -20,7 +21,7 @@ class AcsEntrancesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -38,10 +39,10 @@ public function get(string $acs_entrance_id): AcsEntrance $request_payload["acs_entrance_id"] = $acs_entrance_id; - $res = $this->client->request( - "POST", - "/acs/entrances/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/entrances/get", [ + "json" => (object) $request_payload, + ]), ); return AcsEntrance::from_json($res->acs_entrance); @@ -70,11 +71,9 @@ public function grant_access( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/entrances/grant_access", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/entrances/grant_access", [ + "json" => (object) $request_payload, + ]); } /** @@ -144,10 +143,10 @@ public function list( $request_payload["space_id"] = $space_id; } - $res = $this->client->request( - "POST", - "/acs/entrances/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/entrances/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -178,10 +177,12 @@ public function list_credentials_with_access( $request_payload["include_if"] = $include_if; } - $res = $this->client->request( - "POST", - "/acs/entrances/list_credentials_with_access", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/acs/entrances/list_credentials_with_access", + ["json" => (object) $request_payload], + ), ); return array_map( @@ -208,10 +209,10 @@ public function unlock( $request_payload["acs_credential_id"] = $acs_credential_id; $request_payload["acs_entrance_id"] = $acs_entrance_id; - $res = $this->client->request( - "POST", - "/acs/entrances/unlock", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/entrances/unlock", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index cc519bf5..18863753 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AcsSystem; class AcsSystemsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class AcsSystemsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -35,10 +36,10 @@ public function get(string $acs_system_id): AcsSystem $request_payload["acs_system_id"] = $acs_system_id; - $res = $this->client->request( - "POST", - "/acs/systems/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/systems/get", [ + "json" => (object) $request_payload, + ]), ); return AcsSystem::from_json($res->acs_system); @@ -71,10 +72,10 @@ public function list( $request_payload["search"] = $search; } - $res = $this->client->request( - "POST", - "/acs/systems/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/systems/list", [ + "json" => (object) $request_payload, + ]), ); return array_map(fn($r) => AcsSystem::from_json($r), $res->acs_systems); @@ -95,10 +96,12 @@ public function list_compatible_credential_manager_acs_systems( $request_payload["acs_system_id"] = $acs_system_id; - $res = $this->client->request( - "POST", - "/acs/systems/list_compatible_credential_manager_acs_systems", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/acs/systems/list_compatible_credential_manager_acs_systems", + ["json" => (object) $request_payload], + ), ); return array_map(fn($r) => AcsSystem::from_json($r), $res->acs_systems); @@ -127,10 +130,8 @@ public function report_devices( $request_payload["acs_entrances"] = $acs_entrances; } - $this->client->request( - "POST", - "/acs/systems/report_devices", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/systems/report_devices", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 422a0502..65f72306 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsUser; class AcsUsersClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class AcsUsersClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -40,11 +41,9 @@ public function add_to_access_group( $request_payload["acs_access_group_id"] = $acs_access_group_id; $request_payload["acs_user_id"] = $acs_user_id; - $this->client->request( - "POST", - "/acs/users/add_to_access_group", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/users/add_to_access_group", [ + "json" => (object) $request_payload, + ]); } /** @@ -93,10 +92,10 @@ public function create( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/acs/users/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/users/create", [ + "json" => (object) $request_payload, + ]), ); return AcsUser::from_json($res->acs_user); @@ -127,11 +126,9 @@ public function delete( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/users/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/users/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -159,10 +156,10 @@ public function get( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/acs/users/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/users/get", [ + "json" => (object) $request_payload, + ]), ); return AcsUser::from_json($res->acs_user); @@ -224,10 +221,10 @@ public function list( ] = $user_identity_phone_number; } - $res = $this->client->request( - "POST", - "/acs/users/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/acs/users/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -262,10 +259,12 @@ public function list_accessible_entrances( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/acs/users/list_accessible_entrances", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/acs/users/list_accessible_entrances", + ["json" => (object) $request_payload], + ), ); return array_map( @@ -297,11 +296,9 @@ public function remove_from_access_group( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/users/remove_from_access_group", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/users/remove_from_access_group", [ + "json" => (object) $request_payload, + ]); } /** @@ -332,7 +329,7 @@ public function revoke_access_to_all_entrances( $this->client->request( "POST", "/acs/users/revoke_access_to_all_entrances", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -361,11 +358,9 @@ public function suspend( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/users/suspend", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/users/suspend", [ + "json" => (object) $request_payload, + ]); } /** @@ -393,11 +388,9 @@ public function unsuspend( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/users/unsuspend", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/users/unsuspend", [ + "json" => (object) $request_payload, + ]); } /** @@ -455,10 +448,8 @@ public function update( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request( - "POST", - "/acs/users/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/acs/users/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index 971a286e..3f03a968 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; class ActionAttemptsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class ActionAttemptsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -39,10 +40,10 @@ public function get( $request_payload["action_attempt_id"] = $action_attempt_id; - $res = $this->client->request( - "POST", - "/action_attempts/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/action_attempts/get", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -87,10 +88,10 @@ public function list( $request_payload["page_cursor"] = $page_cursor; } - $res = $this->client->request( - "POST", - "/action_attempts/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/action_attempts/list", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index 93b81d8c..99d86e76 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\ClientSession; class ClientSessionsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class ClientSessionsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -73,10 +74,10 @@ public function create( $request_payload["user_identity_ids"] = $user_identity_ids; } - $res = $this->client->request( - "POST", - "/client_sessions/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/client_sessions/create", [ + "json" => (object) $request_payload, + ]), ); return ClientSession::from_json($res->client_session); @@ -94,11 +95,9 @@ public function delete(string $client_session_id): void $request_payload["client_session_id"] = $client_session_id; - $this->client->request( - "POST", - "/client_sessions/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/client_sessions/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -121,10 +120,10 @@ public function get( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/client_sessions/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/client_sessions/get", [ + "json" => (object) $request_payload, + ]), ); return ClientSession::from_json($res->client_session); @@ -170,10 +169,10 @@ public function get_or_create( $request_payload["user_identity_ids"] = $user_identity_ids; } - $res = $this->client->request( - "POST", - "/client_sessions/get_or_create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/client_sessions/get_or_create", [ + "json" => (object) $request_payload, + ]), ); return ClientSession::from_json($res->client_session); @@ -219,11 +218,9 @@ public function grant_access( $request_payload["user_identity_ids"] = $user_identity_ids; } - $this->client->request( - "POST", - "/client_sessions/grant_access", - json: (object) $request_payload, - ); + $this->client->request("POST", "/client_sessions/grant_access", [ + "json" => (object) $request_payload, + ]); } /** @@ -263,10 +260,10 @@ public function list( ] = $without_user_identifier_key; } - $res = $this->client->request( - "POST", - "/client_sessions/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/client_sessions/list", [ + "json" => (object) $request_payload, + ]), ); return array_map( @@ -289,10 +286,8 @@ public function revoke(string $client_session_id): void $request_payload["client_session_id"] = $client_session_id; - $this->client->request( - "POST", - "/client_sessions/revoke", - json: (object) $request_payload, - ); + $this->client->request("POST", "/client_sessions/revoke", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index 760d8edb..22549171 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\ConnectWebview; class ConnectWebviewsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class ConnectWebviewsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -95,10 +96,10 @@ public function create( ] = $wait_for_device_creation; } - $res = $this->client->request( - "POST", - "/connect_webviews/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/connect_webviews/create", [ + "json" => (object) $request_payload, + ]), ); return ConnectWebview::from_json($res->connect_webview); @@ -118,11 +119,9 @@ public function delete(string $connect_webview_id): void $request_payload["connect_webview_id"] = $connect_webview_id; - $this->client->request( - "POST", - "/connect_webviews/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/connect_webviews/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -139,10 +138,10 @@ public function get(string $connect_webview_id): ConnectWebview $request_payload["connect_webview_id"] = $connect_webview_id; - $res = $this->client->request( - "POST", - "/connect_webviews/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/connect_webviews/get", [ + "json" => (object) $request_payload, + ]), ); return ConnectWebview::from_json($res->connect_webview); @@ -190,10 +189,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/connect_webviews/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/connect_webviews/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index 9725597a..14b2b6f0 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\ConnectedAccount; class ConnectedAccountsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class ConnectedAccountsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -43,11 +44,9 @@ public function delete(string $connected_account_id): void $request_payload["connected_account_id"] = $connected_account_id; - $this->client->request( - "POST", - "/connected_accounts/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/connected_accounts/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -70,10 +69,10 @@ public function get( $request_payload["email"] = $email; } - $res = $this->client->request( - "POST", - "/connected_accounts/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/connected_accounts/get", [ + "json" => (object) $request_payload, + ]), ); return ConnectedAccount::from_json($res->connected_account); @@ -126,10 +125,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/connected_accounts/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/connected_accounts/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -154,11 +153,9 @@ public function sync(string $connected_account_id): void $request_payload["connected_account_id"] = $connected_account_id; - $this->client->request( - "POST", - "/connected_accounts/sync", - json: (object) $request_payload, - ); + $this->client->request("POST", "/connected_accounts/sync", [ + "json" => (object) $request_payload, + ]); } /** @@ -201,10 +198,8 @@ public function update( $request_payload["display_name"] = $display_name; } - $this->client->request( - "POST", - "/connected_accounts/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/connected_accounts/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/ConnectedAccountsSimulateClient.php b/src/Routes/ConnectedAccountsSimulateClient.php index 4eef7cb8..85e0612e 100644 --- a/src/Routes/ConnectedAccountsSimulateClient.php +++ b/src/Routes/ConnectedAccountsSimulateClient.php @@ -2,11 +2,11 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; class ConnectedAccountsSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -16,7 +16,7 @@ class ConnectedAccountsSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -37,7 +37,7 @@ public function disconnect(string $connected_account_id): void $this->client->request( "POST", "/connected_accounts/simulate/disconnect", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } } diff --git a/src/Routes/CustomersClient.php b/src/Routes/CustomersClient.php index 3fe55788..3f26137a 100644 --- a/src/Routes/CustomersClient.php +++ b/src/Routes/CustomersClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\CustomerPortal; class CustomersClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class CustomersClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -92,10 +93,10 @@ public function create_portal( $request_payload["customer_data"] = $customer_data; } - $res = $this->client->request( - "POST", - "/customers/create_portal", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/customers/create_portal", [ + "json" => (object) $request_payload, + ]), ); return CustomerPortal::from_json($res->customer_portal); @@ -207,11 +208,9 @@ public function delete_data( $request_payload["user_keys"] = $user_keys; } - $this->client->request( - "POST", - "/customers/delete_data", - json: (object) $request_payload, - ); + $this->client->request("POST", "/customers/delete_data", [ + "json" => (object) $request_payload, + ]); } /** @@ -322,10 +321,8 @@ public function push_data( $request_payload["users"] = $users; } - $this->client->request( - "POST", - "/customers/push_data", - json: (object) $request_payload, - ); + $this->client->request("POST", "/customers/push_data", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 61ed5303..32be9949 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Device; use Seam\Resources\DeviceProvider; class DevicesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -19,7 +20,7 @@ class DevicesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -47,10 +48,10 @@ public function get(?string $device_id = null, ?string $name = null): Device $request_payload["name"] = $name; } - $res = $this->client->request( - "POST", - "/devices/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/devices/get", [ + "json" => (object) $request_payload, + ]), ); return Device::from_json($res->device); @@ -148,10 +149,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/devices/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/devices/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -180,10 +181,10 @@ public function list_device_providers( $request_payload["provider_category"] = $provider_category; } - $res = $this->client->request( - "POST", - "/devices/list_device_providers", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/devices/list_device_providers", [ + "json" => (object) $request_payload, + ]), ); return array_map( @@ -204,11 +205,9 @@ public function report_provider_metadata(array $devices): void $request_payload["devices"] = $devices; - $this->client->request( - "POST", - "/devices/report_provider_metadata", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/report_provider_metadata", [ + "json" => (object) $request_payload, + ]); } /** @@ -253,10 +252,8 @@ public function update( $request_payload["properties"] = $properties; } - $this->client->request( - "POST", - "/devices/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/DevicesSimulateClient.php b/src/Routes/DevicesSimulateClient.php index 2a14d57d..b33bceac 100644 --- a/src/Routes/DevicesSimulateClient.php +++ b/src/Routes/DevicesSimulateClient.php @@ -2,11 +2,11 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; class DevicesSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -16,7 +16,7 @@ class DevicesSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -34,11 +34,9 @@ public function connect(string $device_id): void $request_payload["device_id"] = $device_id; - $this->client->request( - "POST", - "/devices/simulate/connect", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/simulate/connect", [ + "json" => (object) $request_payload, + ]); } /** @@ -56,11 +54,9 @@ public function connect_to_hub(string $device_id): void $request_payload["device_id"] = $device_id; - $this->client->request( - "POST", - "/devices/simulate/connect_to_hub", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/simulate/connect_to_hub", [ + "json" => (object) $request_payload, + ]); } /** @@ -75,11 +71,9 @@ public function disconnect(string $device_id): void $request_payload["device_id"] = $device_id; - $this->client->request( - "POST", - "/devices/simulate/disconnect", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/simulate/disconnect", [ + "json" => (object) $request_payload, + ]); } /** @@ -101,7 +95,7 @@ public function disconnect_from_hub(string $device_id): void $this->client->request( "POST", "/devices/simulate/disconnect_from_hub", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -121,11 +115,9 @@ public function paid_subscription(string $device_id, bool $is_expired): void $request_payload["device_id"] = $device_id; $request_payload["is_expired"] = $is_expired; - $this->client->request( - "POST", - "/devices/simulate/paid_subscription", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/simulate/paid_subscription", [ + "json" => (object) $request_payload, + ]); } /** @@ -140,10 +132,8 @@ public function remove(string $device_id): void $request_payload["device_id"] = $device_id; - $this->client->request( - "POST", - "/devices/simulate/remove", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/simulate/remove", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index 541be58e..9b2007f4 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\UnmanagedDevice; class DevicesUnmanagedClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class DevicesUnmanagedClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -47,10 +48,10 @@ public function get( $request_payload["name"] = $name; } - $res = $this->client->request( - "POST", - "/devices/unmanaged/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/devices/unmanaged/get", [ + "json" => (object) $request_payload, + ]), ); return UnmanagedDevice::from_json($res->device); @@ -150,10 +151,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/devices/unmanaged/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/devices/unmanaged/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -191,10 +192,8 @@ public function update( $request_payload["is_managed"] = $is_managed; } - $this->client->request( - "POST", - "/devices/unmanaged/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/devices/unmanaged/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index f2f1f65c..0bf6a533 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Event; class EventsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class EventsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -48,10 +49,10 @@ public function get( $request_payload["event_type"] = $event_type; } - $res = $this->client->request( - "POST", - "/events/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/events/get", [ + "json" => (object) $request_payload, + ]), ); return Event::from_json($res->event); @@ -207,10 +208,10 @@ public function list( $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/events/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/events/list", [ + "json" => (object) $request_payload, + ]), ); return array_map(fn($r) => Event::from_json($r), $res->events); diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index e91f4f30..895a9351 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\InstantKey; class InstantKeysClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class InstantKeysClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -35,11 +36,9 @@ public function delete(string $instant_key_id): void $request_payload["instant_key_id"] = $instant_key_id; - $this->client->request( - "POST", - "/instant_keys/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/instant_keys/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -62,10 +61,10 @@ public function get( $request_payload["instant_key_url"] = $instant_key_url; } - $res = $this->client->request( - "POST", - "/instant_keys/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/instant_keys/get", [ + "json" => (object) $request_payload, + ]), ); return InstantKey::from_json($res->instant_key); @@ -85,10 +84,10 @@ public function list(?string $user_identity_id = null): array $request_payload["user_identity_id"] = $user_identity_id; } - $res = $this->client->request( - "POST", - "/instant_keys/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/instant_keys/list", [ + "json" => (object) $request_payload, + ]), ); return array_map( diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index d54afadf..c4b2c7b4 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -2,14 +2,15 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\Device; class LocksClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -19,7 +20,7 @@ class LocksClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -51,10 +52,10 @@ public function configure_auto_lock( ] = $auto_lock_delay_seconds; } - $res = $this->client->request( - "POST", - "/locks/configure_auto_lock", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/locks/configure_auto_lock", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -84,10 +85,10 @@ public function get(?string $device_id = null, ?string $name = null): Device $request_payload["name"] = $name; } - $res = $this->client->request( - "POST", - "/locks/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/locks/get", [ + "json" => (object) $request_payload, + ]), ); return Device::from_json($res->device); @@ -185,10 +186,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/locks/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/locks/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -213,10 +214,10 @@ public function lock_door( $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/locks/lock_door", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/locks/lock_door", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -242,10 +243,10 @@ public function unlock_door( $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/locks/unlock_door", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/locks/unlock_door", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/LocksSimulateClient.php b/src/Routes/LocksSimulateClient.php index ca91968c..70a5f08d 100644 --- a/src/Routes/LocksSimulateClient.php +++ b/src/Routes/LocksSimulateClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; class LocksSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class LocksSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -42,10 +43,12 @@ public function keypad_code_entry( $request_payload["code"] = $code; $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/locks/simulate/keypad_code_entry", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/locks/simulate/keypad_code_entry", + ["json" => (object) $request_payload], + ), ); return ResolveActionAttempt::resolve_action_attempt( @@ -71,10 +74,12 @@ public function manual_lock_via_keypad( $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/locks/simulate/manual_lock_via_keypad", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/locks/simulate/manual_lock_via_keypad", + ["json" => (object) $request_payload], + ), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/NoiseSensorsClient.php b/src/Routes/NoiseSensorsClient.php index 3f199835..59025cba 100644 --- a/src/Routes/NoiseSensorsClient.php +++ b/src/Routes/NoiseSensorsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Device; class NoiseSensorsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class NoiseSensorsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -121,10 +122,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/noise_sensors/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/noise_sensors/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { diff --git a/src/Routes/NoiseSensorsNoiseThresholdsClient.php b/src/Routes/NoiseSensorsNoiseThresholdsClient.php index af7ad0de..39723a23 100644 --- a/src/Routes/NoiseSensorsNoiseThresholdsClient.php +++ b/src/Routes/NoiseSensorsNoiseThresholdsClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\NoiseThreshold; class NoiseSensorsNoiseThresholdsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class NoiseSensorsNoiseThresholdsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -59,10 +60,12 @@ public function create( $request_payload["noise_threshold_nrs"] = $noise_threshold_nrs; } - $res = $this->client->request( - "POST", - "/noise_sensors/noise_thresholds/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/noise_sensors/noise_thresholds/create", + ["json" => (object) $request_payload], + ), ); return NoiseThreshold::from_json($res->noise_threshold); @@ -85,7 +88,7 @@ public function delete(string $device_id, string $noise_threshold_id): void $this->client->request( "POST", "/noise_sensors/noise_thresholds/delete", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -101,10 +104,12 @@ public function get(string $noise_threshold_id): NoiseThreshold $request_payload["noise_threshold_id"] = $noise_threshold_id; - $res = $this->client->request( - "POST", - "/noise_sensors/noise_thresholds/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/noise_sensors/noise_thresholds/get", + ["json" => (object) $request_payload], + ), ); return NoiseThreshold::from_json($res->noise_threshold); @@ -122,10 +127,12 @@ public function list(string $device_id): array $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/noise_sensors/noise_thresholds/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/noise_sensors/noise_thresholds/list", + ["json" => (object) $request_payload], + ), ); return array_map( @@ -180,7 +187,7 @@ public function update( $this->client->request( "POST", "/noise_sensors/noise_thresholds/update", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } } diff --git a/src/Routes/NoiseSensorsSimulateClient.php b/src/Routes/NoiseSensorsSimulateClient.php index 7388b50b..7d387ece 100644 --- a/src/Routes/NoiseSensorsSimulateClient.php +++ b/src/Routes/NoiseSensorsSimulateClient.php @@ -2,11 +2,11 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; class NoiseSensorsSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -16,7 +16,7 @@ class NoiseSensorsSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -37,7 +37,7 @@ public function trigger_noise_threshold(string $device_id): void $this->client->request( "POST", "/noise_sensors/simulate/trigger_noise_threshold", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } } diff --git a/src/Routes/PhonesClient.php b/src/Routes/PhonesClient.php index b97d5243..b710c78c 100644 --- a/src/Routes/PhonesClient.php +++ b/src/Routes/PhonesClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Phone; class PhonesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class PhonesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -36,11 +37,9 @@ public function deactivate(string $device_id): void $request_payload["device_id"] = $device_id; - $this->client->request( - "POST", - "/phones/deactivate", - json: (object) $request_payload, - ); + $this->client->request("POST", "/phones/deactivate", [ + "json" => (object) $request_payload, + ]); } /** @@ -55,10 +54,10 @@ public function get(string $device_id): Phone $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/phones/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/phones/get", [ + "json" => (object) $request_payload, + ]), ); return Phone::from_json($res->phone); @@ -86,10 +85,10 @@ public function list( ] = $owner_user_identity_id; } - $res = $this->client->request( - "POST", - "/phones/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/phones/list", [ + "json" => (object) $request_payload, + ]), ); return array_map(fn($r) => Phone::from_json($r), $res->phones); diff --git a/src/Routes/PhonesSimulateClient.php b/src/Routes/PhonesSimulateClient.php index 85c5e1ff..ba99c52e 100644 --- a/src/Routes/PhonesSimulateClient.php +++ b/src/Routes/PhonesSimulateClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Phone; class PhonesSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class PhonesSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -53,10 +54,12 @@ public function create_sandbox_phone( $request_payload["phone_metadata"] = $phone_metadata; } - $res = $this->client->request( - "POST", - "/phones/simulate/create_sandbox_phone", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/phones/simulate/create_sandbox_phone", + ["json" => (object) $request_payload], + ), ); return Phone::from_json($res->phone); diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 46db212d..633e115a 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -2,13 +2,14 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Batch; use Seam\Resources\Space; class SpacesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -18,7 +19,7 @@ class SpacesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -40,11 +41,9 @@ public function add_acs_entrances( $request_payload["acs_entrance_ids"] = $acs_entrance_ids; $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/add_acs_entrances", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/add_acs_entrances", [ + "json" => (object) $request_payload, + ]); } /** @@ -63,11 +62,9 @@ public function add_connected_account( $request_payload["connected_account_id"] = $connected_account_id; $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/add_connected_account", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/add_connected_account", [ + "json" => (object) $request_payload, + ]); } /** @@ -84,11 +81,9 @@ public function add_devices(array $device_ids, string $space_id): void $request_payload["device_ids"] = $device_ids; $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/add_devices", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/add_devices", [ + "json" => (object) $request_payload, + ]); } /** @@ -134,10 +129,10 @@ public function create( $request_payload["space_key"] = $space_key; } - $res = $this->client->request( - "POST", - "/spaces/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/spaces/create", [ + "json" => (object) $request_payload, + ]), ); return Space::from_json($res->space); @@ -155,11 +150,9 @@ public function delete(string $space_id): void $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -182,10 +175,10 @@ public function get( $request_payload["space_key"] = $space_key; } - $res = $this->client->request( - "POST", - "/spaces/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/spaces/get", [ + "json" => (object) $request_payload, + ]), ); return Space::from_json($res->space); @@ -221,10 +214,10 @@ public function get_related( $request_payload["space_keys"] = $space_keys; } - $res = $this->client->request( - "POST", - "/spaces/get_related", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/spaces/get_related", [ + "json" => (object) $request_payload, + ]), ); return Batch::from_json($res->batch); @@ -267,10 +260,10 @@ public function list( $request_payload["space_key"] = $space_key; } - $res = $this->client->request( - "POST", - "/spaces/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/spaces/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -296,11 +289,9 @@ public function remove_acs_entrances( $request_payload["acs_entrance_ids"] = $acs_entrance_ids; $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/remove_acs_entrances", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/remove_acs_entrances", [ + "json" => (object) $request_payload, + ]); } /** @@ -319,11 +310,9 @@ public function remove_connected_account( $request_payload["connected_account_id"] = $connected_account_id; $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/remove_connected_account", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/remove_connected_account", [ + "json" => (object) $request_payload, + ]); } /** @@ -340,11 +329,9 @@ public function remove_devices(array $device_ids, string $space_id): void $request_payload["device_ids"] = $device_ids; $request_payload["space_id"] = $space_id; - $this->client->request( - "POST", - "/spaces/remove_devices", - json: (object) $request_payload, - ); + $this->client->request("POST", "/spaces/remove_devices", [ + "json" => (object) $request_payload, + ]); } /** @@ -387,10 +374,10 @@ public function update( $request_payload["space_key"] = $space_key; } - $res = $this->client->request( - "POST", - "/spaces/update", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/spaces/update", [ + "json" => (object) $request_payload, + ]), ); return Space::from_json($res->space); diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index 7d7c4b4d..c73d00de 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -2,14 +2,15 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\Device; class ThermostatsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -21,7 +22,7 @@ class ThermostatsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -51,10 +52,12 @@ public function activate_climate_preset( $request_payload["climate_preset_key"] = $climate_preset_key; $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/thermostats/activate_climate_preset", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/thermostats/activate_climate_preset", + ["json" => (object) $request_payload], + ), ); return ResolveActionAttempt::resolve_action_attempt( @@ -94,10 +97,10 @@ public function cool( ] = $cooling_set_point_fahrenheit; } - $res = $this->client->request( - "POST", - "/thermostats/cool", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/cool", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -184,11 +187,9 @@ public function create_climate_preset( $request_payload["name"] = $name; } - $this->client->request( - "POST", - "/thermostats/create_climate_preset", - json: (object) $request_payload, - ); + $this->client->request("POST", "/thermostats/create_climate_preset", [ + "json" => (object) $request_payload, + ]); } /** @@ -207,11 +208,9 @@ public function delete_climate_preset( $request_payload["climate_preset_key"] = $climate_preset_key; $request_payload["device_id"] = $device_id; - $this->client->request( - "POST", - "/thermostats/delete_climate_preset", - json: (object) $request_payload, - ); + $this->client->request("POST", "/thermostats/delete_climate_preset", [ + "json" => (object) $request_payload, + ]); } /** @@ -243,10 +242,10 @@ public function heat( ] = $heating_set_point_fahrenheit; } - $res = $this->client->request( - "POST", - "/thermostats/heat", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/heat", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -300,10 +299,10 @@ public function heat_cool( ] = $heating_set_point_fahrenheit; } - $res = $this->client->request( - "POST", - "/thermostats/heat_cool", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/heat_cool", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -406,10 +405,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/thermostats/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -434,10 +433,10 @@ public function off( $request_payload["device_id"] = $device_id; - $res = $this->client->request( - "POST", - "/thermostats/off", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/off", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -467,7 +466,7 @@ public function set_fallback_climate_preset( $this->client->request( "POST", "/thermostats/set_fallback_climate_preset", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -496,10 +495,10 @@ public function set_fan_mode( $request_payload["fan_mode_setting"] = $fan_mode_setting; } - $res = $this->client->request( - "POST", - "/thermostats/set_fan_mode", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/set_fan_mode", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -556,10 +555,10 @@ public function set_hvac_mode( ] = $heating_set_point_fahrenheit; } - $res = $this->client->request( - "POST", - "/thermostats/set_hvac_mode", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/set_hvac_mode", [ + "json" => (object) $request_payload, + ]), ); return ResolveActionAttempt::resolve_action_attempt( @@ -610,7 +609,7 @@ public function set_temperature_threshold( $this->client->request( "POST", "/thermostats/set_temperature_threshold", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -690,11 +689,9 @@ public function update_climate_preset( $request_payload["name"] = $name; } - $this->client->request( - "POST", - "/thermostats/update_climate_preset", - json: (object) $request_payload, - ); + $this->client->request("POST", "/thermostats/update_climate_preset", [ + "json" => (object) $request_payload, + ]); } /** @@ -747,10 +744,12 @@ public function update_weekly_program( $request_payload["wednesday_program_id"] = $wednesday_program_id; } - $res = $this->client->request( - "POST", - "/thermostats/update_weekly_program", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/thermostats/update_weekly_program", + ["json" => (object) $request_payload], + ), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index 19f8196f..9fc26e01 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -2,14 +2,15 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\ThermostatDailyProgram; class ThermostatsDailyProgramsClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -19,7 +20,7 @@ class ThermostatsDailyProgramsClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -44,10 +45,12 @@ public function create( $request_payload["name"] = $name; $request_payload["periods"] = $periods; - $res = $this->client->request( - "POST", - "/thermostats/daily_programs/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/thermostats/daily_programs/create", + ["json" => (object) $request_payload], + ), ); return ThermostatDailyProgram::from_json( @@ -69,11 +72,9 @@ public function delete(string $thermostat_daily_program_id): void "thermostat_daily_program_id" ] = $thermostat_daily_program_id; - $this->client->request( - "POST", - "/thermostats/daily_programs/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/thermostats/daily_programs/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -99,10 +100,12 @@ public function update( "thermostat_daily_program_id" ] = $thermostat_daily_program_id; - $res = $this->client->request( - "POST", - "/thermostats/daily_programs/update", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/thermostats/daily_programs/update", + ["json" => (object) $request_payload], + ), ); return ResolveActionAttempt::resolve_action_attempt( diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index 51864c69..4e857221 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\ThermostatSchedule; class ThermostatsSchedulesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class ThermostatsSchedulesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -62,10 +63,10 @@ public function create( $request_payload["name"] = $name; } - $res = $this->client->request( - "POST", - "/thermostats/schedules/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/schedules/create", [ + "json" => (object) $request_payload, + ]), ); return ThermostatSchedule::from_json($res->thermostat_schedule); @@ -83,11 +84,9 @@ public function delete(string $thermostat_schedule_id): void $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; - $this->client->request( - "POST", - "/thermostats/schedules/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/thermostats/schedules/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -102,10 +101,10 @@ public function get(string $thermostat_schedule_id): ThermostatSchedule $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; - $res = $this->client->request( - "POST", - "/thermostats/schedules/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/schedules/get", [ + "json" => (object) $request_payload, + ]), ); return ThermostatSchedule::from_json($res->thermostat_schedule); @@ -129,10 +128,10 @@ public function list( $request_payload["user_identifier_key"] = $user_identifier_key; } - $res = $this->client->request( - "POST", - "/thermostats/schedules/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/thermostats/schedules/list", [ + "json" => (object) $request_payload, + ]), ); return array_map( @@ -186,10 +185,8 @@ public function update( $request_payload["starts_at"] = $starts_at; } - $this->client->request( - "POST", - "/thermostats/schedules/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/thermostats/schedules/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/ThermostatsSimulateClient.php b/src/Routes/ThermostatsSimulateClient.php index 9b00648f..244539cd 100644 --- a/src/Routes/ThermostatsSimulateClient.php +++ b/src/Routes/ThermostatsSimulateClient.php @@ -2,11 +2,11 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; class ThermostatsSimulateClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -16,7 +16,7 @@ class ThermostatsSimulateClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -69,7 +69,7 @@ public function hvac_mode_adjusted( $this->client->request( "POST", "/thermostats/simulate/hvac_mode_adjusted", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -101,7 +101,7 @@ public function temperature_reached( $this->client->request( "POST", "/thermostats/simulate/temperature_reached", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } } diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index e70ad961..3260b177 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -2,7 +2,8 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\AcsEntrance; use Seam\Resources\AcsSystem; use Seam\Resources\AcsUser; @@ -12,7 +13,7 @@ class UserIdentitiesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -22,7 +23,7 @@ class UserIdentitiesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -59,11 +60,9 @@ public function add_acs_user( $request_payload["user_identity_key"] = $user_identity_key; } - $this->client->request( - "POST", - "/user_identities/add_acs_user", - json: (object) $request_payload, - ); + $this->client->request("POST", "/user_identities/add_acs_user", [ + "json" => (object) $request_payload, + ]); } /** @@ -101,10 +100,10 @@ public function create( $request_payload["user_identity_key"] = $user_identity_key; } - $res = $this->client->request( - "POST", - "/user_identities/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/user_identities/create", [ + "json" => (object) $request_payload, + ]), ); return UserIdentity::from_json($res->user_identity); @@ -122,11 +121,9 @@ public function delete(string $user_identity_id): void $request_payload["user_identity_id"] = $user_identity_id; - $this->client->request( - "POST", - "/user_identities/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/user_identities/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -154,10 +151,12 @@ public function generate_instant_key( $request_payload["max_use_count"] = $max_use_count; } - $res = $this->client->request( - "POST", - "/user_identities/generate_instant_key", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/user_identities/generate_instant_key", + ["json" => (object) $request_payload], + ), ); return InstantKey::from_json($res->instant_key); @@ -183,10 +182,10 @@ public function get( $request_payload["user_identity_key"] = $user_identity_key; } - $res = $this->client->request( - "POST", - "/user_identities/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/user_identities/get", [ + "json" => (object) $request_payload, + ]), ); return UserIdentity::from_json($res->user_identity); @@ -211,7 +210,7 @@ public function grant_access_to_device( $this->client->request( "POST", "/user_identities/grant_access_to_device", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -259,10 +258,10 @@ public function list( $request_payload["user_identity_ids"] = $user_identity_ids; } - $res = $this->client->request( - "POST", - "/user_identities/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/user_identities/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -287,10 +286,12 @@ public function list_accessible_devices(string $user_identity_id): array $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->client->request( - "POST", - "/user_identities/list_accessible_devices", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/user_identities/list_accessible_devices", + ["json" => (object) $request_payload], + ), ); return array_map(fn($r) => Device::from_json($r), $res->devices); @@ -308,10 +309,12 @@ public function list_accessible_entrances(string $user_identity_id): array $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->client->request( - "POST", - "/user_identities/list_accessible_entrances", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/user_identities/list_accessible_entrances", + ["json" => (object) $request_payload], + ), ); return array_map( @@ -332,10 +335,12 @@ public function list_acs_systems(string $user_identity_id): array $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->client->request( - "POST", - "/user_identities/list_acs_systems", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request( + "POST", + "/user_identities/list_acs_systems", + ["json" => (object) $request_payload], + ), ); return array_map(fn($r) => AcsSystem::from_json($r), $res->acs_systems); @@ -353,10 +358,10 @@ public function list_acs_users(string $user_identity_id): array $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->client->request( - "POST", - "/user_identities/list_acs_users", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/user_identities/list_acs_users", [ + "json" => (object) $request_payload, + ]), ); return array_map(fn($r) => AcsUser::from_json($r), $res->acs_users); @@ -378,11 +383,9 @@ public function remove_acs_user( $request_payload["acs_user_id"] = $acs_user_id; $request_payload["user_identity_id"] = $user_identity_id; - $this->client->request( - "POST", - "/user_identities/remove_acs_user", - json: (object) $request_payload, - ); + $this->client->request("POST", "/user_identities/remove_acs_user", [ + "json" => (object) $request_payload, + ]); } /** @@ -404,7 +407,7 @@ public function revoke_access_to_device( $this->client->request( "POST", "/user_identities/revoke_access_to_device", - json: (object) $request_payload, + ["json" => (object) $request_payload], ); } @@ -441,10 +444,8 @@ public function update( $request_payload["user_identity_key"] = $user_identity_key; } - $this->client->request( - "POST", - "/user_identities/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/user_identities/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index 6683b8a2..4190e614 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\UnmanagedUserIdentity; class UserIdentitiesUnmanagedClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class UserIdentitiesUnmanagedClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -35,10 +36,10 @@ public function get(string $user_identity_id): UnmanagedUserIdentity $request_payload["user_identity_id"] = $user_identity_id; - $res = $this->client->request( - "POST", - "/user_identities/unmanaged/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/user_identities/unmanaged/get", [ + "json" => (object) $request_payload, + ]), ); return UnmanagedUserIdentity::from_json($res->user_identity); @@ -76,10 +77,10 @@ public function list( $request_payload["search"] = $search; } - $res = $this->client->request( - "POST", - "/user_identities/unmanaged/list", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/user_identities/unmanaged/list", [ + "json" => (object) $request_payload, + ]), ); if ($on_response !== null) { @@ -115,10 +116,8 @@ public function update( $request_payload["user_identity_key"] = $user_identity_key; } - $this->client->request( - "POST", - "/user_identities/unmanaged/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/user_identities/unmanaged/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index 4d95a1b7..efe061fa 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -2,12 +2,13 @@ namespace Seam\Routes; -use Seam\Http\SeamHttpClient; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Resources\Webhook; class WebhooksClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -17,7 +18,7 @@ class WebhooksClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -39,10 +40,10 @@ public function create(string $url, ?array $event_types = null): Webhook $request_payload["event_types"] = $event_types; } - $res = $this->client->request( - "POST", - "/webhooks/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/webhooks/create", [ + "json" => (object) $request_payload, + ]), ); return Webhook::from_json($res->webhook); @@ -60,11 +61,9 @@ public function delete(string $webhook_id): void $request_payload["webhook_id"] = $webhook_id; - $this->client->request( - "POST", - "/webhooks/delete", - json: (object) $request_payload, - ); + $this->client->request("POST", "/webhooks/delete", [ + "json" => (object) $request_payload, + ]); } /** @@ -79,10 +78,10 @@ public function get(string $webhook_id): Webhook $request_payload["webhook_id"] = $webhook_id; - $res = $this->client->request( - "POST", - "/webhooks/get", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/webhooks/get", [ + "json" => (object) $request_payload, + ]), ); return Webhook::from_json($res->webhook); @@ -95,7 +94,7 @@ public function get(string $webhook_id): Webhook */ public function list(): array { - $res = $this->client->request("POST", "/webhooks/list"); + $res = Body::decode($this->client->request("POST", "/webhooks/list")); return array_map(fn($r) => Webhook::from_json($r), $res->webhooks); } @@ -114,10 +113,8 @@ public function update(array $event_types, string $webhook_id): void $request_payload["event_types"] = $event_types; $request_payload["webhook_id"] = $webhook_id; - $this->client->request( - "POST", - "/webhooks/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/webhooks/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Routes/WorkspacesClient.php b/src/Routes/WorkspacesClient.php index 42a52ec5..84462b8a 100644 --- a/src/Routes/WorkspacesClient.php +++ b/src/Routes/WorkspacesClient.php @@ -2,14 +2,15 @@ namespace Seam\Routes; +use GuzzleHttp\ClientInterface; +use Seam\Http\Body; use Seam\Http\ResolveActionAttempt; -use Seam\Http\SeamHttpClient; use Seam\Resources\ActionAttempt; use Seam\Resources\Workspace; class WorkspacesClient { - private SeamHttpClient $client; + private ClientInterface $client; /** * @var array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} @@ -19,7 +20,7 @@ class WorkspacesClient /** * @param array{wait_for_action_attempt: bool|array{timeout?: float, polling_interval?: float}} $defaults */ - public function __construct(SeamHttpClient $client, array $defaults) + public function __construct(ClientInterface $client, array $defaults) { $this->client = $client; $this->defaults = $defaults; @@ -91,10 +92,10 @@ public function create( ] = $webview_success_message; } - $res = $this->client->request( - "POST", - "/workspaces/create", - json: (object) $request_payload, + $res = Body::decode( + $this->client->request("POST", "/workspaces/create", [ + "json" => (object) $request_payload, + ]), ); return Workspace::from_json($res->workspace); @@ -107,7 +108,7 @@ public function create( */ public function get(): Workspace { - $res = $this->client->request("POST", "/workspaces/get"); + $res = Body::decode($this->client->request("POST", "/workspaces/get")); return Workspace::from_json($res->workspace); } @@ -119,7 +120,7 @@ public function get(): Workspace */ public function list(): array { - $res = $this->client->request("POST", "/workspaces/list"); + $res = Body::decode($this->client->request("POST", "/workspaces/list")); return array_map(fn($r) => Workspace::from_json($r), $res->workspaces); } @@ -133,7 +134,9 @@ public function list(): array public function reset_sandbox( bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - $res = $this->client->request("POST", "/workspaces/reset_sandbox"); + $res = Body::decode( + $this->client->request("POST", "/workspaces/reset_sandbox"), + ); return ResolveActionAttempt::resolve_action_attempt( ActionAttempt::from_json($res->action_attempt), @@ -187,10 +190,8 @@ public function update( $request_payload["organization_id"] = $organization_id; } - $this->client->request( - "POST", - "/workspaces/update", - json: (object) $request_payload, - ); + $this->client->request("POST", "/workspaces/update", [ + "json" => (object) $request_payload, + ]); } } diff --git a/src/Seam.php b/src/Seam.php index 588c7617..cecd0646 100644 --- a/src/Seam.php +++ b/src/Seam.php @@ -23,8 +23,9 @@ use Seam\Routes\WebhooksClient; use Seam\Routes\WorkspacesClient; +use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; -use Seam\Http\SeamHttpClient; +use Seam\Http\ClientFactory; /** * Client for the Seam API. @@ -61,9 +62,12 @@ class Seam /** * The long term support version of the Seam API this SDK targets. */ - public const LTS_VERSION = SeamHttpClient::LTS_VERSION; + public const LTS_VERSION = ClientFactory::LTS_VERSION; - public SeamHttpClient $client; + /** + * The Guzzle client this instance makes its requests with. + */ + public ClientInterface $client; /** * Default request options applied to every call, currently just @@ -75,9 +79,10 @@ class Seam /** * @param bool|array{timeout?: float, polling_interval?: float}|null $wait_for_action_attempt Whether to wait for action attempts to finish, optionally with timeout and polling_interval in seconds. Defaults to true. - * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. timeout or headers. + * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. headers or proxy. * @param int|null $retries How many times to retry a failed request. Defaults to 2; pass 0 to disable. - * @param ClientInterface|null $client A preconfigured Guzzle client. It carries its own endpoint and authorization, so no other authentication option may be given with it. + * @param float|null $timeout Request timeout in seconds, covering connecting and reading. Defaults to 30; pass 0 to disable. + * @param ClientInterface|null $client A preconfigured Guzzle client, used as is. It carries its own endpoint and authorization, so no other authentication option applies to it. */ public function __construct( ?string $api_key = null, @@ -87,6 +92,7 @@ public function __construct( bool|array|null $wait_for_action_attempt = null, array $guzzle_options = [], ?int $retries = null, + ?float $timeout = null, ?ClientInterface $client = null, ) { $this->defaults = [ @@ -96,18 +102,18 @@ public function __construct( // A client carries its own endpoint and authorization, so the // authentication options are only read when one has to be built. $this->client = - $client !== null - ? SeamHttpClient::from_client($client) - : new SeamHttpClient( - Options::get_endpoint($endpoint), - Auth::get_auth_headers( - $api_key, - $personal_access_token, - $workspace_id, - ), - $guzzle_options, - $retries, - ); + $client ?? + ClientFactory::create( + Options::get_endpoint($endpoint), + Auth::get_auth_headers( + $api_key, + $personal_access_token, + $workspace_id, + ), + $guzzle_options, + $retries, + $timeout, + ); $this->access_codes = new AccessCodesClient( $this->client, @@ -176,6 +182,7 @@ public static function from_api_key( bool|array|null $wait_for_action_attempt = null, array $guzzle_options = [], ?int $retries = null, + ?float $timeout = null, ): static { return new static( api_key: $api_key, @@ -183,6 +190,7 @@ public static function from_api_key( wait_for_action_attempt: $wait_for_action_attempt, guzzle_options: $guzzle_options, retries: $retries, + timeout: $timeout, ); } @@ -197,6 +205,7 @@ public static function from_personal_access_token( bool|array|null $wait_for_action_attempt = null, array $guzzle_options = [], ?int $retries = null, + ?float $timeout = null, ): static { return new static( personal_access_token: $personal_access_token, @@ -204,6 +213,7 @@ public static function from_personal_access_token( endpoint: $endpoint, wait_for_action_attempt: $wait_for_action_attempt, retries: $retries, + timeout: $timeout, guzzle_options: $guzzle_options, ); } @@ -226,21 +236,6 @@ public function lts_version(): string return self::LTS_VERSION; } - /** - * Makes a request against the Seam API with this client's authorization. - * - * @param array|object|null $json - * @param array|null $query - */ - public function request( - string $method, - string $path, - mixed $json = null, - ?array $query = null, - ): mixed { - return $this->client->request($method, $path, $json, $query); - } - /** * Creates a paginator for a list endpoint. * diff --git a/src/SeamClient.php b/src/SeamClient.php deleted file mode 100644 index c43d7e1b..00000000 --- a/src/SeamClient.php +++ /dev/null @@ -1,9 +0,0 @@ -client = SeamHttpClient::from_client($client); + $this->client = $client; } else { if ($personal_access_token === null) { throw new InvalidOptionsError( @@ -41,13 +45,14 @@ public function __construct( ); } - $this->client = new SeamHttpClient( + $this->client = ClientFactory::create( Options::get_endpoint($endpoint), Auth::get_auth_headers_for_personal_access_token_without_workspace( $personal_access_token, ), $guzzle_options, $retries, + $timeout, ); } @@ -67,12 +72,14 @@ public static function from_personal_access_token( ?string $endpoint = null, array $guzzle_options = [], ?int $retries = null, + ?float $timeout = null, ): static { return new static( personal_access_token: $personal_access_token, endpoint: $endpoint, guzzle_options: $guzzle_options, retries: $retries, + timeout: $timeout, ); } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 739c263b..46c143c2 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -4,7 +4,8 @@ namespace Tests; -use Seam\Http\SeamHttpClient; +use Seam\Http\Body; +use Seam\Http\ClientFactory; use Seam\Seam; use Tests\Support\FakeSeamConnectTestCase; @@ -14,12 +15,12 @@ public function testClientCanMakeRequests(): void { $seam = $this->seam(); - $res = $seam->client->request( - "POST", - "/devices/get", - (object) [ - "device_id" => $this->seed["august_device_1"], - ], + $res = Body::decode( + $seam->client->request("POST", "/devices/get", [ + "json" => (object) [ + "device_id" => $this->seed["august_device_1"], + ], + ]), ); $this->assertSame( @@ -32,21 +33,11 @@ public function testClientCanMakeRequests(): void ); } - public function testRequestDelegatesToTheClient(): void + public function testClientIsTheGuzzleClient(): void { - $seam = $this->seam(); - - $res = $seam->request( - "POST", - "/devices/get", - (object) [ - "device_id" => $this->seed["august_device_1"], - ], - ); - - $this->assertSame( - $this->seed["august_device_1"], - $res->device->device_id, + $this->assertInstanceOf( + \GuzzleHttp\ClientInterface::class, + $this->seam()->client, ); } @@ -58,7 +49,7 @@ public function testFromClientNeedsNoCredentials(): void { $authorized = $this->seam(); - $seam = Seam::from_client($authorized->client->get_client()); + $seam = Seam::from_client($authorized->client); $device = $seam->devices->get($this->seed["august_device_1"]); @@ -71,7 +62,7 @@ public function testFromClientNeedsNoCredentials(): void public function testClientOptionReusesAnotherInstancesClient(): void { - $seam = new Seam(client: $this->seam()->client->get_client()); + $seam = new Seam(client: $this->seam()->client); $device = $seam->devices->get($this->seed["august_device_1"]); @@ -87,7 +78,7 @@ public function testGuzzleOptionsAreMergedIntoTheClient(): void ], ); - $config = $seam->client->get_client()->getConfig(); + $config = $seam->client->getConfig(); $this->assertSame(30, $config["timeout"]); $this->assertSame("Test-Value", $config["headers"]["Custom-Header"]); @@ -111,11 +102,27 @@ public function testGuzzleOptionsStillAuthorizeRequests(): void $this->assertSame($this->seed["august_device_1"], $device->device_id); } - public function testTimeoutDefaultsToSixtySeconds(): void + public function testTimeoutDefaultsToThirtySeconds(): void + { + $config = $this->seam()->client->getConfig(); + + $this->assertSame(30.0, ClientFactory::DEFAULT_TIMEOUT); + $this->assertSame(30.0, $config["timeout"]); + $this->assertSame(30.0, $config["connect_timeout"]); + } + + public function testTimeoutCanBeSet(): void { - $config = $this->seam()->client->get_client()->getConfig(); + $seam = Seam::from_api_key( + $this->seed["seam_apikey1_token"], + endpoint: $this->endpoint, + timeout: 5.0, + ); - $this->assertSame(SeamHttpClient::DEFAULT_TIMEOUT, $config["timeout"]); + $config = $seam->client->getConfig(); + + $this->assertSame(5.0, $config["timeout"]); + $this->assertSame(5.0, $config["connect_timeout"]); } /** @@ -140,9 +147,7 @@ public function testWaitForActionAttemptDefaultsToTrueEverywhere(): void $this->seed["seed_workspace_1"], endpoint: $this->endpoint, ), - "from_client" => Seam::from_client( - $this->seam()->client->get_client(), - ), + "from_client" => Seam::from_client($this->seam()->client), ]; foreach ($clients as $name => $seam) { diff --git a/tests/HeadersTest.php b/tests/HeadersTest.php index 3f8cf4c7..d752ad4f 100644 --- a/tests/HeadersTest.php +++ b/tests/HeadersTest.php @@ -5,7 +5,7 @@ namespace Tests; use PHPUnit\Framework\TestCase; -use Seam\Http\SeamHttpClient; +use Seam\Http\ClientFactory; use Seam\Seam; use Seam\Utils\PackageVersion; use Tests\Support\RecordingClient; @@ -50,7 +50,7 @@ public function testSendsDefaultHeaders(): void $request->getHeaderLine("seam-sdk-version"), ); $this->assertSame( - SeamHttpClient::LTS_VERSION, + ClientFactory::LTS_VERSION, $request->getHeaderLine("seam-lts-version"), ); $this->assertSame( diff --git a/tests/HttpErrorTest.php b/tests/HttpErrorTest.php index d445defc..25af1c50 100644 --- a/tests/HttpErrorTest.php +++ b/tests/HttpErrorTest.php @@ -50,13 +50,9 @@ public function testThrowsApiErrorOnStandardErrorResponse(): void public function testThrowsInvalidInputErrorWithValidationMessages(): void { try { - $this->seam()->client->request( - "POST", - "/devices/list", - (object) [ - "device_ids" => 4242, - ], - ); + $this->seam()->client->request("POST", "/devices/list", [ + "json" => (object) ["device_ids" => 4242], + ]); $this->fail("Expected HttpInvalidInputError"); } catch (HttpInvalidInputError $error) { $this->assertSame(400, $error->getStatusCode()); @@ -75,13 +71,9 @@ public function testThrowsInvalidInputErrorWithValidationMessages(): void public function testValidationMessagesAreEmptyForAnUnknownParam(): void { try { - $this->seam()->client->request( - "POST", - "/devices/list", - (object) [ - "device_ids" => 4242, - ], - ); + $this->seam()->client->request("POST", "/devices/list", [ + "json" => (object) ["device_ids" => 4242], + ]); $this->fail("Expected HttpInvalidInputError"); } catch (HttpInvalidInputError $error) { $this->assertSame( @@ -94,20 +86,18 @@ public function testValidationMessagesAreEmptyForAnUnknownParam(): void /** * A workspace outage answers with a 503 that is not a Seam error * envelope, so it surfaces as the underlying transport error rather than - * a Seam exception, which is what the other Seam SDKs do too. + * a Seam exception. */ public function testWorkspaceOutageSurfacesTheTransportError(): void { $seam = $this->seam(retries: 0); - $seam->client->request( - "POST", - "/_fake/simulate_workspace_outage", - (object) [ + $seam->client->request("POST", "/_fake/simulate_workspace_outage", [ + "json" => (object) [ "workspace_id" => $this->seed["seed_workspace_1"], "routes" => ["/devices/list"], ], - ); + ]); try { $seam->devices->list(); diff --git a/tests/PackageVersionTest.php b/tests/PackageVersionTest.php index 7b1d7f9a..7d7d9fd3 100644 --- a/tests/PackageVersionTest.php +++ b/tests/PackageVersionTest.php @@ -29,7 +29,7 @@ public function testVersionMatchesPackageJson(): void public function testVersionIsUsedAsTheSdkVersionHeader(): void { $seam = new \Seam\Seam("seam_apikey1_token"); - $headers = $seam->client->get_client()->getConfig("headers"); + $headers = $seam->client->getConfig("headers"); $this->assertSame(PackageVersion::get(), $headers["seam-sdk-version"]); } diff --git a/tests/RetryTest.php b/tests/RetryTest.php index 322697e7..a001e125 100644 --- a/tests/RetryTest.php +++ b/tests/RetryTest.php @@ -7,6 +7,7 @@ use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Psr7\Request; use PHPUnit\Framework\TestCase; +use Seam\Http\Body; use Seam\Seam; use Tests\Support\RecordingClient; @@ -41,9 +42,8 @@ private function seam(RecordingClient $recorder, ?int $retries = null): Seam } /** - * Every Seam endpoint is a POST. Retrying one the server may already have - * processed could duplicate a write, so a status code never triggers a - * retry. The Ruby SDK asserts the same thing. + * A POST the server may already have processed could duplicate a write if + * repeated, so a status code never triggers a retry for one. */ public function testDoesNotRetryPostOnServiceUnavailable(): void { @@ -131,7 +131,9 @@ public function testRetriesIdempotentRequestsOnServiceUnavailable(): void self::devices(), ]); - $res = $this->seam($recorder)->client->request("GET", "/devices/list"); + $res = Body::decode( + $this->seam($recorder)->client->request("GET", "/devices/list"), + ); $this->assertSame([], $res->devices); $this->assertSame(3, $recorder->attempt_count()); diff --git a/tests/Support/FakeSeamConnect.php b/tests/Support/FakeSeamConnect.php index 93976532..7a8cbe2e 100644 --- a/tests/Support/FakeSeamConnect.php +++ b/tests/Support/FakeSeamConnect.php @@ -6,8 +6,7 @@ * Runs a fake Seam Connect server for the duration of a single test. * * Prefer this over stubbing HTTP responses: the fake exercises the SDK against - * a real server and seeded records, which is how the other Seam SDKs are - * tested. + * a real server and seeded records. */ class FakeSeamConnect { diff --git a/tests/WaitForActionAttemptTest.php b/tests/WaitForActionAttemptTest.php index 32dae081..4df89ea2 100644 --- a/tests/WaitForActionAttemptTest.php +++ b/tests/WaitForActionAttemptTest.php @@ -32,15 +32,13 @@ private function set_status( string $status, ?array $error = null, ): void { - $seam->client->request( - "POST", - "/_fake/update_action_attempt", - (object) array_filter([ + $seam->client->request("POST", "/_fake/update_action_attempt", [ + "json" => (object) array_filter([ "action_attempt_id" => $action_attempt->action_attempt_id, "status" => $status, "error" => $error, ]), - ); + ]); } public function testWaitsByDefault(): void From 795ff59ff943cc4a857a1e3407fd37d6848e0ce1 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Aug 2026 22:55:57 +0000 Subject: [PATCH 10/35] ci: construct the client the install check smoke tests The install check builds the package, requires it from a scratch project and constructs a client to prove the published archive autoloads. It still named Seam\SeamClient, which no longer exists, so the check failed on every PHP version once the alias went away. Construct Seam\Seam instead, by name, matching how the README documents calls. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index e9772866..54db6700 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -121,7 +121,7 @@ jobs: contents: | Date: Thu, 13 Aug 2026 00:04:56 +0000 Subject: [PATCH 11/35] feat: read the personal access token and workspace id from the environment SEAM_PERSONAL_ACCESS_TOKEN and SEAM_WORKSPACE_ID now fall back into place the way SEAM_API_KEY already did, so a client can be constructed with no arguments under either authentication method. Defining both credential variables at once is ambiguous and raises an InvalidOptionsError. SeamWithoutWorkspace reads SEAM_PERSONAL_ACCESS_TOKEN as well. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- README.md | 7 +++ codegen/layouts/seam-client.hbs | 3 +- src/Auth.php | 42 ++++++++++++- src/Seam.php | 3 +- src/SeamWithoutWorkspace.php | 19 +++--- tests/EnvTest.php | 102 +++++++++++++++++++++++++++++++- 6 files changed, 158 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 5b8ab1b3..71a5fc55 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,9 @@ A Personal Access Token is scoped to a Seam Console user. It must be used with a workspace id. ```php +// Set the SEAM_PERSONAL_ACCESS_TOKEN and SEAM_WORKSPACE_ID environment variables +$seam = new Seam\Seam(); + // Pass as options to the constructor $seam = new Seam\Seam( personal_access_token: "your-personal-access-token", @@ -265,6 +268,10 @@ Some endpoints are not scoped to a workspace. Use `SeamWithoutWorkspace` with a personal access token to reach them. ```php +// Set the SEAM_PERSONAL_ACCESS_TOKEN environment variable +$seam = new Seam\SeamWithoutWorkspace(); + +// Use the factory method $seam = Seam\SeamWithoutWorkspace::from_personal_access_token( "your-personal-access-token" ); diff --git a/codegen/layouts/seam-client.hbs b/codegen/layouts/seam-client.hbs index c07c5d63..215622ad 100644 --- a/codegen/layouts/seam-client.hbs +++ b/codegen/layouts/seam-client.hbs @@ -15,7 +15,8 @@ use Seam\Http\ClientFactory; * * Authenticate with an API key, which is scoped to a single workspace, or with * a personal access token together with the id of the workspace to act on. - * When neither is given, the SEAM_API_KEY environment variable is used. + * When neither is given, the SEAM_API_KEY or SEAM_PERSONAL_ACCESS_TOKEN and + * SEAM_WORKSPACE_ID environment variables are used. * * @see https://docs.seam.co/ */ diff --git a/src/Auth.php b/src/Auth.php index 6023be71..55151b89 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -24,8 +24,20 @@ public static function get_auth_headers( // a stray SEAM_API_KEY. if ($api_key === null && $personal_access_token === null) { $api_key = Options::get_env("SEAM_API_KEY"); + $personal_access_token = Options::get_env( + "SEAM_PERSONAL_ACCESS_TOKEN", + ); + + if ($api_key !== null && $personal_access_token !== null) { + throw new InvalidOptionsError( + "Both SEAM_API_KEY and SEAM_PERSONAL_ACCESS_TOKEN environment variables are defined. " . + "Please use only one authentication method.", + ); + } } + $workspace_id ??= Options::get_env("SEAM_WORKSPACE_ID"); + if ( Options::is_seam_options_with_api_key( $api_key, @@ -50,8 +62,34 @@ public static function get_auth_headers( throw new InvalidOptionsError( "Must specify an api_key or personal_access_token. " . - "Attempted reading configuration from the environment, " . - "but the environment variable SEAM_API_KEY is not set.", + "Attempted reading configuration from the environment, but neither the " . + "SEAM_API_KEY nor the SEAM_PERSONAL_ACCESS_TOKEN environment variable is set.", + ); + } + + /** + * Builds the headers for a client that is not scoped to a workspace, + * falling back to the environment when no token is given. + * + * @return array + */ + public static function get_auth_headers_without_workspace( + ?string $personal_access_token = null, + ): array { + $personal_access_token ??= Options::get_env( + "SEAM_PERSONAL_ACCESS_TOKEN", + ); + + if ($personal_access_token === null) { + throw new InvalidOptionsError( + "Must specify a personal_access_token. " . + "Attempted reading configuration from the environment, " . + "but the environment variable SEAM_PERSONAL_ACCESS_TOKEN is not set.", + ); + } + + return self::get_auth_headers_for_personal_access_token_without_workspace( + $personal_access_token, ); } diff --git a/src/Seam.php b/src/Seam.php index cecd0646..855b1d4e 100644 --- a/src/Seam.php +++ b/src/Seam.php @@ -32,7 +32,8 @@ * * Authenticate with an API key, which is scoped to a single workspace, or with * a personal access token together with the id of the workspace to act on. - * When neither is given, the SEAM_API_KEY environment variable is used. + * When neither is given, the SEAM_API_KEY or SEAM_PERSONAL_ACCESS_TOKEN and + * SEAM_WORKSPACE_ID environment variables are used. * * @see https://docs.seam.co/ */ diff --git a/src/SeamWithoutWorkspace.php b/src/SeamWithoutWorkspace.php index e8981729..0423e5eb 100644 --- a/src/SeamWithoutWorkspace.php +++ b/src/SeamWithoutWorkspace.php @@ -11,6 +11,9 @@ * * Authenticates with a personal access token and exposes only the workspace * listing and creation endpoints. Use Seam\Seam for everything else. + * + * When no token is given, the SEAM_PERSONAL_ACCESS_TOKEN environment variable + * is used. */ class SeamWithoutWorkspace { @@ -36,25 +39,17 @@ public function __construct( ) { // A client carries its own endpoint and authorization, so the // authentication options are only read when one has to be built. - if ($client !== null) { - $this->client = $client; - } else { - if ($personal_access_token === null) { - throw new InvalidOptionsError( - "Must specify a personal_access_token", - ); - } - - $this->client = ClientFactory::create( + $this->client = + $client ?? + ClientFactory::create( Options::get_endpoint($endpoint), - Auth::get_auth_headers_for_personal_access_token_without_workspace( + Auth::get_auth_headers_without_workspace( $personal_access_token, ), $guzzle_options, $retries, $timeout, ); - } $this->workspaces = new WorkspacesProxy( new WorkspacesClient($this->client, [ diff --git a/tests/EnvTest.php b/tests/EnvTest.php index 3c0f92a7..79876cce 100644 --- a/tests/EnvTest.php +++ b/tests/EnvTest.php @@ -7,11 +7,18 @@ use Seam\InvalidOptionsError; use Seam\Options; use Seam\Seam; +use Seam\SeamWithoutWorkspace; use Tests\Support\FakeSeamConnectTestCase; final class EnvTest extends FakeSeamConnectTestCase { - private const VARIABLES = ["SEAM_API_KEY", "SEAM_ENDPOINT", "SEAM_API_URL"]; + private const VARIABLES = [ + "SEAM_API_KEY", + "SEAM_PERSONAL_ACCESS_TOKEN", + "SEAM_WORKSPACE_ID", + "SEAM_ENDPOINT", + "SEAM_API_URL", + ]; /** @var array */ private array $saved_env = []; @@ -118,10 +125,68 @@ public function testDeprecatedSeamApiUrlWarns(): void } } + public function testReadsThePersonalAccessTokenAndWorkspaceIdFromTheEnvironment(): void + { + putenv("SEAM_PERSONAL_ACCESS_TOKEN=" . $this->seed["seam_at1_token"]); + putenv("SEAM_WORKSPACE_ID=" . $this->seed["seed_workspace_1"]); + + $seam = new Seam(endpoint: $this->endpoint); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testReadsOnlyTheWorkspaceIdFromTheEnvironment(): void + { + putenv("SEAM_WORKSPACE_ID=" . $this->seed["seed_workspace_1"]); + + $seam = new Seam( + personal_access_token: $this->seed["seam_at1_token"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testWorkspaceIdOptionWinsOverTheEnvironment(): void + { + putenv("SEAM_WORKSPACE_ID=workspace-from-the-environment"); + + $seam = new Seam( + personal_access_token: $this->seed["seam_at1_token"], + workspace_id: $this->seed["seed_workspace_1"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + /** + * Two credentials in the environment are ambiguous, so neither is picked. + */ + public function testFailsWhenBothCredentialEnvironmentVariablesAreSet(): void + { + putenv("SEAM_API_KEY=" . $this->seed["seam_apikey1_token"]); + putenv("SEAM_PERSONAL_ACCESS_TOKEN=" . $this->seed["seam_at1_token"]); + putenv("SEAM_WORKSPACE_ID=" . $this->seed["seed_workspace_1"]); + + $this->expectException(InvalidOptionsError::class); + $this->expectExceptionMessage( + "Both SEAM_API_KEY and SEAM_PERSONAL_ACCESS_TOKEN", + ); + + new Seam(endpoint: $this->endpoint); + } + public function testFailsWhenNoCredentialsAreAvailable(): void { $this->expectException(InvalidOptionsError::class); - $this->expectExceptionMessage("SEAM_API_KEY is not set"); + $this->expectExceptionMessage("SEAM_API_KEY"); new Seam(endpoint: $this->endpoint); } @@ -140,4 +205,37 @@ public function testApiKeyEnvironmentVariableIsIgnoredForAPersonalAccessToken(): $this->assertSame($this->seed["august_device_1"], $device->device_id); } + + public function testPersonalAccessTokenEnvironmentVariableIsIgnoredForAnApiKey(): void + { + putenv("SEAM_PERSONAL_ACCESS_TOKEN=" . $this->seed["seam_at1_token"]); + + $seam = new Seam( + api_key: $this->seed["seam_apikey1_token"], + endpoint: $this->endpoint, + ); + + $device = $seam->devices->get($this->seed["august_device_1"]); + + $this->assertSame($this->seed["august_device_1"], $device->device_id); + } + + public function testWithoutWorkspaceReadsThePersonalAccessTokenFromTheEnvironment(): void + { + putenv("SEAM_PERSONAL_ACCESS_TOKEN=" . $this->seed["seam_at1_token"]); + + $seam = new SeamWithoutWorkspace(endpoint: $this->endpoint); + + $workspaces = $seam->workspaces->list(); + + $this->assertNotEmpty($workspaces); + } + + public function testWithoutWorkspaceFailsWhenNoTokenIsAvailable(): void + { + $this->expectException(InvalidOptionsError::class); + $this->expectExceptionMessage("SEAM_PERSONAL_ACCESS_TOKEN is not set"); + + new SeamWithoutWorkspace(endpoint: $this->endpoint); + } } From aba2adfabfaba9daecdbc31e9956acd4fd1135cf Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 03:55:53 +0000 Subject: [PATCH 12/35] fix: return a list of action attempts as a list Whether a method resolves its result as an action attempt was keyed on the return resource alone, so the list endpoint wrapped its entire action_attempts array in a single ActionAttempt and piped it through the resolver, which broke every call to it: an empty list decoded to null and a populated one polled an attempt with a null id. The array response now generates like any other list endpoint, which also restores the on_response hook the resolver branch skipped, so the paginator gets its pagination metadata. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- codegen/lib/layouts/route.ts | 9 +++++++-- src/Routes/ActionAttemptsClient.php | 14 +++++++------- tests/WaitForActionAttemptTest.php | 22 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 8dc8c110..8450dff8 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -74,7 +74,10 @@ const getMethodLayoutContext = ( ): MethodLayoutContext => { const { methodName, path, parameters, returnResource, returnPath } = method - const usesActionAttempt = returnResource === 'ActionAttempt' + // A method returning a list of action attempts is an ordinary list + // endpoint: only a single returned attempt is resolved. + const usesActionAttempt = + returnResource === 'ActionAttempt' && !method.isArrayResponse const usesOnResponse = parameters.some((p) => p.name === 'page_cursor') && methodName === 'list' const returnsVoid = returnResource === '' @@ -146,7 +149,9 @@ const getUseStatements = (client: PhpClient): string[] => { .filter((resourceName) => resourceName !== ''), ) - const usesActionAttempt = resourceNames.has('ActionAttempt') + const usesActionAttempt = client.methods.some( + (m) => m.returnResource === 'ActionAttempt' && !m.isArrayResponse, + ) // Void endpoints never read the response, so they do not decode it. const readsBody = client.methods.some((m) => m.returnResource !== '') diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index 3f03a968..c1a656d4 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -61,7 +61,6 @@ public function get( * @param string $device_id ID of the device to filter action attempts by. * @param int $limit Maximum number of records to return per page. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. - * @param bool|array|null $wait_for_action_attempt Whether to wait for the action attempt to finish, optionally with timeout and polling_interval in seconds. Defaults to the value set on the client. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ @@ -70,7 +69,6 @@ public function list( ?string $device_id = null, ?int $limit = null, ?string $page_cursor = null, - bool|array|null $wait_for_action_attempt = null, ?callable $on_response = null, ): array { $request_payload = []; @@ -94,11 +92,13 @@ public function list( ]), ); - return ResolveActionAttempt::resolve_action_attempt( - ActionAttempt::from_json($res->action_attempts), - $this->client, - $wait_for_action_attempt ?? - $this->defaults["wait_for_action_attempt"], + if ($on_response !== null) { + $on_response($res); + } + + return array_map( + fn($r) => ActionAttempt::from_json($r), + $res->action_attempts, ); } } diff --git a/tests/WaitForActionAttemptTest.php b/tests/WaitForActionAttemptTest.php index 4df89ea2..7f4696ed 100644 --- a/tests/WaitForActionAttemptTest.php +++ b/tests/WaitForActionAttemptTest.php @@ -26,6 +26,28 @@ private function pending_action_attempt(Seam $seam): ActionAttempt return $action_attempt; } + /** + * A list of action attempts is returned as is: only a single returned + * attempt is ever resolved, so listing must not poll pending attempts. + */ + public function testListReturnsActionAttemptsWithoutResolvingThem(): void + { + $seam = $this->seam(wait_for_action_attempt: false); + $pending = $this->pending_action_attempt($seam); + + $attempts = $seam->action_attempts->list( + action_attempt_ids: [$pending->action_attempt_id], + ); + + $this->assertContainsOnlyInstancesOf(ActionAttempt::class, $attempts); + $this->assertCount(1, $attempts); + $this->assertSame( + $pending->action_attempt_id, + $attempts[0]->action_attempt_id, + ); + $this->assertSame("pending", $attempts[0]->status); + } + private function set_status( Seam $seam, ActionAttempt $action_attempt, From 0dfb88619c58db2f497624e59120bac65bab60ff Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 03:56:10 +0000 Subject: [PATCH 13/35] fix: keep a retried request from duplicating a write A timeout can fire while waiting on a response to a request the server received and is still processing, so retrying it can repeat a write, such as unlocking a door twice. Transport errors on a method that is not safe to repeat now go through a dedicated middleware that skips anything that looks like a timeout, while idempotent methods keep retrying every transport error. Connection resets keep retrying either way. The error mapping middleware also sat inside the redirect middleware, where raising on a 3xx made following redirects impossible even when asked for. It is unshifted to the outside of the stack, so a redirect is followed rather than raised and only an unfollowed one is an error. The handler stack a caller passes in is cloned rather than mutated, so building a second client from the same options no longer stacks the middleware twice and multiplies the retries. A bare handler, such as a MockHandler, is wrapped in a stack so the error mapping and retries apply to it instead of being silently dropped. And a response body that cannot seek, such as a streamed response, is read as is rather than failing on rewind. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- src/Http/Body.php | 9 +- src/Http/ClientFactory.php | 73 +++----------- src/Http/ErrorMiddleware.php | 6 +- src/Http/RetryMiddleware.php | 190 +++++++++++++++++++++++++++++++++++ tests/RedirectTest.php | 72 +++++++++++++ tests/RetryTest.php | 129 ++++++++++++++++++++++++ 6 files changed, 416 insertions(+), 63 deletions(-) create mode 100644 src/Http/RetryMiddleware.php create mode 100644 tests/RedirectTest.php diff --git a/src/Http/Body.php b/src/Http/Body.php index a63321a7..47b36eef 100644 --- a/src/Http/Body.php +++ b/src/Http/Body.php @@ -15,12 +15,17 @@ final class Body * * A PSR-7 body is a stream that can only be read once, so it is rewound * first: the middleware reads it to look for a Seam error before the - * caller ever sees the response. + * caller ever sees the response. A body that cannot seek, such as a + * streamed response, is simply read from where it stands. */ public static function decode(ResponseInterface $response): mixed { $body = $response->getBody(); - $body->rewind(); + + if ($body->isSeekable()) { + $body->rewind(); + } + $contents = $body->getContents(); if ($contents === "") { diff --git a/src/Http/ClientFactory.php b/src/Http/ClientFactory.php index b51f3136..a479e305 100644 --- a/src/Http/ClientFactory.php +++ b/src/Http/ClientFactory.php @@ -4,9 +4,6 @@ use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; -use GuzzleHttp\Promise\PromiseInterface; -use GuzzleRetry\GuzzleRetryMiddleware; -use Psr\Http\Message\RequestInterface; use Seam\Utils\PackageVersion; /** @@ -28,22 +25,6 @@ final class ClientFactory */ public const DEFAULT_TIMEOUT = 30.0; - private const RETRYABLE_STATUS_CODES = [429, 500, 502, 503, 504]; - - /** - * Retrying a request the server may already have processed can duplicate a - * write, so a status code only triggers a retry for a method that is safe - * to repeat. A request that never reached the server is retried whatever - * the method, since it cannot have had an effect. - */ - private const IDEMPOTENT_METHODS = [ - "GET", - "HEAD", - "OPTIONS", - "PUT", - "DELETE", - ]; - /** * @param array $auth_headers * @param array $guzzle_options @@ -63,25 +44,22 @@ public static function create( $handler = $guzzle_options["handler"] ?? HandlerStack::create(); if ($handler instanceof HandlerStack) { - // Pushed first, so it wraps the retry middleware and only sees the - // response a request finally settled on. - $handler->push(ErrorMiddleware::create(), "seam_error"); - $handler->push( - self::retry_options_middleware(), - "seam_retry_options", - ); - $handler->push( - GuzzleRetryMiddleware::factory([ - "retry_enabled" => $retries > 0, - "max_retry_attempts" => $retries, - "retry_on_timeout" => true, - // Set per request by the middleware above. - "retry_on_status" => [], - ]), - "seam_retry", - ); + // Cloned so that building a client does not mutate a stack the + // caller may reuse, which would stack this middleware twice. + $handler = clone $handler; + } else { + // A bare handler, such as a MockHandler, is wrapped so the + // middleware below still applies to it. + $handler = HandlerStack::create($handler); } + // Unshifted so it sits outside every other middleware and only sees + // a response none of them could act on: a redirect is followed + // rather than raised, and a retried request is judged by the + // response it finally settled on. + $handler->unshift(ErrorMiddleware::create(), "seam_error"); + RetryMiddleware::add($handler, $retries); + $headers = array_merge( $auth_headers, $guzzle_options["headers"] ?? [], @@ -107,29 +85,6 @@ public static function create( ); } - /** - * Opts a request into status based retries when repeating it is safe. - */ - private static function retry_options_middleware(): callable - { - return static fn(callable $handler): callable => static function ( - RequestInterface $request, - array $options, - ) use ($handler): PromiseInterface { - if ( - in_array( - strtoupper($request->getMethod()), - self::IDEMPOTENT_METHODS, - true, - ) - ) { - $options["retry_on_status"] = self::RETRYABLE_STATUS_CODES; - } - - return $handler($request, $options); - }; - } - /** * @return array */ diff --git a/src/Http/ErrorMiddleware.php b/src/Http/ErrorMiddleware.php index a709af15..d4c4c506 100644 --- a/src/Http/ErrorMiddleware.php +++ b/src/Http/ErrorMiddleware.php @@ -14,8 +14,10 @@ * Guzzle middleware that turns a Seam error response into a Seam exception. * * The client is built with `http_errors` disabled so this runs instead, and it - * sits outside the retry middleware so it only sees the response a request - * finally settled on. + * sits outside every other middleware so it only sees a response none of them + * could act on: a redirect this deep has already been followed or was opted + * out of, and a retried request is judged by the response it finally settled + * on. * * A response that is not a Seam error envelope, such as a gateway returning * HTML, raises the transport error Guzzle would have raised on its own. diff --git a/src/Http/RetryMiddleware.php b/src/Http/RetryMiddleware.php new file mode 100644 index 00000000..6411a132 --- /dev/null +++ b/src/Http/RetryMiddleware.php @@ -0,0 +1,190 @@ +push(self::retry_options_middleware(), "seam_retry_options"); + $handler->push( + GuzzleRetryMiddleware::factory([ + "retry_enabled" => $retries > 0, + "max_retry_attempts" => $retries, + "retry_on_timeout" => true, + // Set per request by the middleware above. + "retry_on_status" => [], + ]), + "seam_retry", + ); + // Pushed last, so it sits inside the middleware above and its + // retries are invisible to it: each mechanism covers the failures + // the other does not, and their budgets never combine. + $handler->push( + self::transport_retry_middleware($retries), + "seam_transport_retry", + ); + } + + /** + * Configures the retry middleware per request: a method that is safe to + * repeat opts into status based retries, any other method opts out of + * its transport error handling, which would retry timeouts as well, and + * is covered by the transport retry middleware instead. + */ + private static function retry_options_middleware(): callable + { + return static fn(callable $handler): callable => static function ( + RequestInterface $request, + array $options, + ) use ($handler): PromiseInterface { + if (self::is_idempotent($request)) { + $options["retry_on_status"] = self::RETRYABLE_STATUS_CODES; + } else { + $options["retry_on_timeout"] = false; + } + + return $handler($request, $options); + }; + } + + /** + * Retries the transport errors for the methods that are not safe to + * repeat, skipping anything that looks like a timeout. + */ + private static function transport_retry_middleware(int $retries): callable + { + return static fn( + callable $handler, + ): callable => static fn(RequestInterface $request, array $options): PromiseInterface => self::dispatch($handler, $request, $options, $retries); + } + + private static function dispatch( + callable $handler, + RequestInterface $request, + array $options, + int $retries, + ): PromiseInterface { + return $handler($request, $options)->otherwise(static function ( + \Throwable $reason, + ) use ($handler, $request, $options, $retries): PromiseInterface { + $attempts = $options["seam_transport_retries"] ?? 0; + + if ( + !is_int($attempts) || + $attempts >= $retries || + self::is_idempotent($request) || + !self::is_retryable_transport_error($reason) + ) { + return Create::rejectionFor($reason); + } + + $options["seam_transport_retries"] = $attempts + 1; + $options["delay"] = + (int) (self::RETRY_DELAY_SECONDS * + (float) ($attempts + 1) * + 1000.0); + + return self::dispatch($handler, $request, $options, $retries); + }); + } + + private static function is_retryable_transport_error( + \Throwable $reason, + ): bool { + if ($reason instanceof ConnectException) { + return !self::is_timeout($reason); + } + + // A response was received, so this is a status error covered by the + // retry middleware, not a transport error. + if ($reason instanceof BadResponseException) { + return false; + } + + if ($reason instanceof RequestException) { + $errno = $reason->getHandlerContext()["errno"] ?? null; + + return $errno === self::ECONNRESET; + } + + return false; + } + + /** + * Whether a connection exception is a timeout. The curl errno is + * decisive when present; a handler without one, such as the stream + * handler, is judged by its message. + */ + private static function is_timeout(ConnectException $reason): bool + { + $errno = $reason->getHandlerContext()["errno"] ?? null; + + if ($errno !== null) { + return $errno === self::CURLE_OPERATION_TIMEDOUT; + } + + return preg_match("/timed?\s*out/i", $reason->getMessage()) === 1; + } + + private static function is_idempotent(RequestInterface $request): bool + { + return in_array( + strtoupper($request->getMethod()), + self::IDEMPOTENT_METHODS, + true, + ); + } +} diff --git a/tests/RedirectTest.php b/tests/RedirectTest.php new file mode 100644 index 00000000..0be0105d --- /dev/null +++ b/tests/RedirectTest.php @@ -0,0 +1,72 @@ +guzzle_options(), + $guzzle_options, + ), + ); + } + + public function testFollowsARedirectByDefault(): void + { + $recorder = new RecordingClient([ + new Response(302, [ + "location" => "https://example.com/devices/list_moved", + ]), + RecordingClient::json(200, ["devices" => []]), + ]); + + $devices = $this->seam($recorder)->devices->list(); + + $this->assertSame([], $devices); + $this->assertSame(2, $recorder->attempt_count()); + } + + /** + * With following disabled, a redirect is a response outside the success + * range like any other, and must not be silently decoded as a success. + */ + public function testARedirectIsAnErrorWhenFollowingIsDisabled(): void + { + $recorder = new RecordingClient([ + new Response(302, [ + "location" => "https://example.com/devices/list_moved", + ]), + ]); + + $seam = $this->seam($recorder, ["allow_redirects" => false]); + + try { + $seam->devices->list(); + $this->fail("Expected the redirect to surface as an error"); + } catch (RequestException $error) { + $this->assertSame(302, $error->getResponse()?->getStatusCode()); + } + + $this->assertSame(1, $recorder->attempt_count()); + } +} diff --git a/tests/RetryTest.php b/tests/RetryTest.php index a001e125..c033f905 100644 --- a/tests/RetryTest.php +++ b/tests/RetryTest.php @@ -5,6 +5,7 @@ namespace Tests; use GuzzleHttp\Exception\ConnectException; +use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Psr7\Request; use PHPUnit\Framework\TestCase; use Seam\Http\Body; @@ -82,6 +83,101 @@ public function testRetriesPostOnConnectionFailure(): void $this->assertSame(3, $recorder->attempt_count()); } + /** + * A timeout may have fired while waiting on a response to a request the + * server received and is still processing, so repeating the POST could + * duplicate a write. + */ + public function testDoesNotRetryPostOnTimeout(): void + { + $timeout = new ConnectException( + "cURL error 28: Operation timed out after 30001 milliseconds with 0 bytes received", + new Request("POST", "/devices/list"), + null, + ["errno" => 28], + ); + + $recorder = RecordingClient::repeating_throwable($timeout); + + try { + $this->seam($recorder)->devices->list(); + $this->fail("Expected the timeout to surface"); + } catch (ConnectException) { + // Expected. + } + + $this->assertSame(1, $recorder->attempt_count()); + } + + /** + * A handler other than curl reports no errno, so the timeout is + * recognized by its message. + */ + public function testDoesNotRetryPostOnTimeoutWithoutAnErrno(): void + { + $timeout = new ConnectException( + "Connection timed out", + new Request("POST", "/devices/list"), + ); + + $recorder = RecordingClient::repeating_throwable($timeout); + + try { + $this->seam($recorder)->devices->list(); + $this->fail("Expected the timeout to surface"); + } catch (ConnectException) { + // Expected. + } + + $this->assertSame(1, $recorder->attempt_count()); + } + + /** + * Repeating an idempotent request is safe even when the server may have + * received it, so a timeout does get retried there. + */ + public function testRetriesIdempotentRequestsOnTimeout(): void + { + $timeout = new ConnectException( + "cURL error 28: Operation timed out after 30001 milliseconds with 0 bytes received", + new Request("GET", "/devices/list"), + null, + ["errno" => 28], + ); + + $recorder = new RecordingClient([$timeout, $timeout, self::devices()]); + + $res = Body::decode( + $this->seam($recorder)->client->request("GET", "/devices/list"), + ); + + $this->assertSame([], $res->devices); + $this->assertSame(3, $recorder->attempt_count()); + } + + /** + * A connection reset by the peer surfaces as a request exception with + * errno 104 rather than a connection exception, but is a transport + * failure all the same. + */ + public function testRetriesPostOnConnectionReset(): void + { + $reset = new RequestException( + "Connection reset by peer", + new Request("POST", "/devices/list"), + null, + null, + ["errno" => 104], + ); + + $recorder = new RecordingClient([$reset, self::devices()]); + + $devices = $this->seam($recorder)->devices->list(); + + $this->assertSame([], $devices); + $this->assertSame(2, $recorder->attempt_count()); + } + public function testStopsRetryingOnceRetriesAreExhausted(): void { $connect_error = new ConnectException( @@ -119,6 +215,39 @@ public function testDoesNotRetryWhenRetriesAreDisabled(): void $this->assertSame(1, $recorder->attempt_count()); } + /** + * Building a client must not mutate a handler stack the caller may + * reuse: a second client built from the same options would otherwise + * stack the retry middleware twice and multiply the retries. + */ + public function testBuildingASecondClientDoesNotStackRetries(): void + { + $recorder = RecordingClient::repeating(self::service_unavailable()); + $options = $recorder->guzzle_options(); + + $first = new Seam( + api_key: self::API_KEY, + endpoint: "https://example.com", + guzzle_options: $options, + ); + $second = new Seam( + api_key: self::API_KEY, + endpoint: "https://example.com", + guzzle_options: $options, + ); + + $this->assertNotSame($first->client, $second->client); + + try { + Body::decode($second->client->request("GET", "/devices/list")); + $this->fail("Expected the 503 to surface"); + } catch (\Throwable) { + // The error mapping is covered in HttpErrorTest. + } + + $this->assertSame(3, $recorder->attempt_count()); + } + /** * The SDK itself only issues POSTs, but a caller reaching for the client * directly with an idempotent method does get status based retries. From c83b3edab02921dd9753c47ef27c347deee7cfc4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 03:56:23 +0000 Subject: [PATCH 14/35] fix: reject an option that a preconfigured client would silently discard A credential or endpoint passed beside a client was dropped without a sound, since the client carries its own, so requests went out with whatever authorization the client held. The combination now raises an InvalidOptionsError naming the offending option, on both Seam and SeamWithoutWorkspace; wait_for_action_attempt stays allowed since it does not configure the client. Also swept up along the way: - The paginator replaced a caller's on_response callback with its own instead of chaining the two, so the caller's silently never fired. - WorkspacesProxy forwarded to the generated create positionally, the exact silent mis-binding the README warns about, and now uses named arguments. - The reserved name check in codegen only knew PHP's type names, so a property named list or default would have generated a class that cannot parse; it now covers the reserved keywords too. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- README.md | 3 +- codegen/layouts/seam-client.hbs | 16 +++++-- codegen/lib/resource-model.ts | 73 +++++++++++++++++++++++++++++-- src/Options.php | 25 +++++++++++ src/Paginator.php | 16 +++++-- src/Seam.php | 16 +++++-- src/SeamWithoutWorkspace.php | 12 +++++- src/WorkspacesProxy.php | 23 +++++----- tests/ClientTest.php | 76 +++++++++++++++++++++++++++++++++ tests/PaginatorTest.php | 21 +++++++++ 10 files changed, 256 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 71a5fc55..313ac7cb 100644 --- a/README.md +++ b/README.md @@ -374,7 +374,8 @@ $devices = Seam\Http\Body::decode($response)->devices; #### Overriding the client Pass an already configured Guzzle client. It carries its own endpoint and -authorization, so no other authentication option may be given alongside it. +authorization, so it cannot be combined with any option other than +`wait_for_action_attempt`. ```php $client = new GuzzleHttp\Client([ diff --git a/codegen/layouts/seam-client.hbs b/codegen/layouts/seam-client.hbs index 215622ad..2f0b0b92 100644 --- a/codegen/layouts/seam-client.hbs +++ b/codegen/layouts/seam-client.hbs @@ -49,7 +49,7 @@ class Seam * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. headers or proxy. * @param int|null $retries How many times to retry a failed request. Defaults to 2; pass 0 to disable. * @param float|null $timeout Request timeout in seconds, covering connecting and reading. Defaults to 30; pass 0 to disable. - * @param ClientInterface|null $client A preconfigured Guzzle client, used as is. It carries its own endpoint and authorization, so no other authentication option applies to it. + * @param ClientInterface|null $client A preconfigured Guzzle client, used as is. It carries its own endpoint and authorization, so it cannot be combined with any option other than wait_for_action_attempt. */ public function __construct( ?string $api_key = null, @@ -66,8 +66,18 @@ class Seam "wait_for_action_attempt" => $wait_for_action_attempt ?? true, ]; - // A client carries its own endpoint and authorization, so the - // authentication options are only read when one has to be built. + // A client carries its own endpoint and authorization, so no option + // that would configure one can be combined with it. + Options::check_client_options($client, [ + "api_key" => $api_key, + "personal_access_token" => $personal_access_token, + "workspace_id" => $workspace_id, + "endpoint" => $endpoint, + "guzzle_options" => $guzzle_options, + "retries" => $retries, + "timeout" => $timeout, + ]); + $this->client = $client ?? ClientFactory::create( Options::get_endpoint($endpoint), Auth::get_auth_headers($api_key, $personal_access_token, $workspace_id), diff --git a/codegen/lib/resource-model.ts b/codegen/lib/resource-model.ts index 2d9bccd3..6c277f86 100644 --- a/codegen/lib/resource-model.ts +++ b/codegen/lib/resource-model.ts @@ -61,9 +61,10 @@ const rootNamespace = 'Seam\\Resources' // deep, so exceeding it means the blueprint is cyclic rather than nested. const maxDepth = 16 -// PHP reserves these as type names, so a class cannot be called any of them. -// Class names are case insensitive, so the check has to be too. +// PHP reserves these as type names or keywords, so a class cannot be called +// any of them. Class names are case insensitive, so the check has to be too. const reservedClassNames = new Set([ + // Type names. 'array', 'bool', 'callable', @@ -82,6 +83,72 @@ const reservedClassNames = new Set([ 'string', 'true', 'void', + // Keywords. + 'abstract', + 'and', + 'as', + 'break', + 'case', + 'catch', + 'class', + 'clone', + 'const', + 'continue', + 'declare', + 'default', + 'die', + 'do', + 'echo', + 'else', + 'elseif', + 'empty', + 'enddeclare', + 'endfor', + 'endforeach', + 'endif', + 'endswitch', + 'endwhile', + 'exit', + 'extends', + 'final', + 'finally', + 'fn', + 'for', + 'foreach', + 'function', + 'global', + 'goto', + 'if', + 'implements', + 'include', + 'include_once', + 'instanceof', + 'insteadof', + 'interface', + 'isset', + 'list', + 'match', + 'namespace', + 'new', + 'or', + 'print', + 'private', + 'protected', + 'public', + 'readonly', + 'require', + 'require_once', + 'return', + 'switch', + 'throw', + 'trait', + 'try', + 'unset', + 'use', + 'var', + 'while', + 'xor', + 'yield', ]) interface ClassDocs { @@ -209,7 +276,7 @@ const buildClass = ( if (reservedClassNames.has(nestedClassName.toLowerCase())) { throw new Error( - `Cannot generate ${nestedPath}: ${nestedClassName} is a reserved PHP type name`, + `Cannot generate ${nestedPath}: ${nestedClassName} is reserved in PHP`, ) } diff --git a/src/Options.php b/src/Options.php index e3d22aa3..287adfa0 100644 --- a/src/Options.php +++ b/src/Options.php @@ -79,6 +79,31 @@ public static function is_seam_options_with_personal_access_token( return true; } + /** + * A preconfigured client carries its own endpoint and authorization, so an + * option that would configure one is a mistake to combine with it, and is + * rejected rather than silently ignored. + * + * @param array $options The other options by name, where + * null (or, for an options array, empty) means the option was not given. + */ + public static function check_client_options( + ?object $client, + array $options, + ): void { + if ($client === null) { + return; + } + + foreach ($options as $name => $value) { + if ($value !== null && $value !== []) { + throw new InvalidOptionsError( + "The {$name} option cannot be used with the client option", + ); + } + } + } + /** * Reads an environment variable, treating an empty value as unset so that * an exported-but-blank variable does not override the default. diff --git a/src/Paginator.php b/src/Paginator.php index 5b86e6db..812012d8 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -55,10 +55,20 @@ private function fetchPage(string $cursor): array $params["page_cursor"] = $cursor; } - $params["on_response"] = fn($response) => $this->cachePagination( - $response, + // Chained rather than replaced, so a callback the caller passed in + // through the params still fires. + $on_response = $params["on_response"] ?? null; + + $params["on_response"] = function ($response) use ( $cursor, - ); + $on_response, + ): void { + $this->cachePagination($response, $cursor); + + if (is_callable($on_response)) { + $on_response($response); + } + }; $data = $request($params); diff --git a/src/Seam.php b/src/Seam.php index 855b1d4e..76dcda31 100644 --- a/src/Seam.php +++ b/src/Seam.php @@ -83,7 +83,7 @@ class Seam * @param array $guzzle_options Options merged into the underlying Guzzle client, e.g. headers or proxy. * @param int|null $retries How many times to retry a failed request. Defaults to 2; pass 0 to disable. * @param float|null $timeout Request timeout in seconds, covering connecting and reading. Defaults to 30; pass 0 to disable. - * @param ClientInterface|null $client A preconfigured Guzzle client, used as is. It carries its own endpoint and authorization, so no other authentication option applies to it. + * @param ClientInterface|null $client A preconfigured Guzzle client, used as is. It carries its own endpoint and authorization, so it cannot be combined with any option other than wait_for_action_attempt. */ public function __construct( ?string $api_key = null, @@ -100,8 +100,18 @@ public function __construct( "wait_for_action_attempt" => $wait_for_action_attempt ?? true, ]; - // A client carries its own endpoint and authorization, so the - // authentication options are only read when one has to be built. + // A client carries its own endpoint and authorization, so no option + // that would configure one can be combined with it. + Options::check_client_options($client, [ + "api_key" => $api_key, + "personal_access_token" => $personal_access_token, + "workspace_id" => $workspace_id, + "endpoint" => $endpoint, + "guzzle_options" => $guzzle_options, + "retries" => $retries, + "timeout" => $timeout, + ]); + $this->client = $client ?? ClientFactory::create( diff --git a/src/SeamWithoutWorkspace.php b/src/SeamWithoutWorkspace.php index 0423e5eb..91b9874a 100644 --- a/src/SeamWithoutWorkspace.php +++ b/src/SeamWithoutWorkspace.php @@ -37,8 +37,16 @@ public function __construct( ?float $timeout = null, ?ClientInterface $client = null, ) { - // A client carries its own endpoint and authorization, so the - // authentication options are only read when one has to be built. + // A client carries its own endpoint and authorization, so no option + // that would configure one can be combined with it. + Options::check_client_options($client, [ + "personal_access_token" => $personal_access_token, + "endpoint" => $endpoint, + "guzzle_options" => $guzzle_options, + "retries" => $retries, + "timeout" => $timeout, + ]); + $this->client = $client ?? ClientFactory::create( diff --git a/src/WorkspacesProxy.php b/src/WorkspacesProxy.php index 0c596e60..e1d64fb1 100644 --- a/src/WorkspacesProxy.php +++ b/src/WorkspacesProxy.php @@ -38,17 +38,20 @@ public function create( ?string $webview_primary_button_text_color = null, ?string $webview_success_message = null, ): Workspace { + // Forwarded by name: the generated parameter order follows the API + // definition, so a positional call could silently shift after a + // regeneration. return $this->workspaces->create( - $name, - $company_name, - $connect_partner_name, - $connect_webview_customization, - $is_sandbox, - $organization_id, - $webview_logo_shape, - $webview_primary_button_color, - $webview_primary_button_text_color, - $webview_success_message, + name: $name, + company_name: $company_name, + connect_partner_name: $connect_partner_name, + connect_webview_customization: $connect_webview_customization, + is_sandbox: $is_sandbox, + organization_id: $organization_id, + webview_logo_shape: $webview_logo_shape, + webview_primary_button_color: $webview_primary_button_color, + webview_primary_button_text_color: $webview_primary_button_text_color, + webview_success_message: $webview_success_message, ); } } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 46c143c2..81e727a5 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -4,10 +4,15 @@ namespace Tests; +use GuzzleHttp\Handler\MockHandler; use Seam\Http\Body; use Seam\Http\ClientFactory; +use Seam\HttpApiError; +use Seam\InvalidOptionsError; use Seam\Seam; +use Seam\SeamWithoutWorkspace; use Tests\Support\FakeSeamConnectTestCase; +use Tests\Support\RecordingClient; final class ClientTest extends FakeSeamConnectTestCase { @@ -69,6 +74,77 @@ public function testClientOptionReusesAnotherInstancesClient(): void $this->assertSame($this->seed["august_device_1"], $device->device_id); } + /** + * A credential passed beside a client would be silently discarded, since + * the client carries its own authorization, so the mix-up is rejected. + */ + public function testClientOptionRejectsAnyOtherOption(): void + { + $client = $this->seam()->client; + + $this->expectException(InvalidOptionsError::class); + $this->expectExceptionMessage( + "The api_key option cannot be used with the client option", + ); + + new Seam(api_key: $this->seed["seam_apikey1_token"], client: $client); + } + + /** + * wait_for_action_attempt does not configure the client, so it is the + * one option a client can be combined with. + */ + public function testClientOptionStillTakesAWaitForActionAttemptDefault(): void + { + $seam = new Seam( + client: $this->seam()->client, + wait_for_action_attempt: false, + ); + + $this->assertFalse($seam->defaults["wait_for_action_attempt"]); + } + + public function testWithoutWorkspaceClientOptionRejectsAnyOtherOption(): void + { + $client = $this->seam()->client; + + $this->expectException(InvalidOptionsError::class); + $this->expectExceptionMessage( + "The endpoint option cannot be used with the client option", + ); + + new SeamWithoutWorkspace(endpoint: $this->endpoint, client: $client); + } + + /** + * A bare handler, such as a MockHandler, is wrapped in a handler stack + * so the error mapping and retries still apply to it. + */ + public function testABareHandlerStillGetsTheErrorMapping(): void + { + $mock = new MockHandler([ + RecordingClient::json(404, [ + "error" => [ + "type" => "device_not_found", + "message" => "Device not found", + ], + ]), + ]); + + $seam = new Seam( + api_key: $this->seed["seam_apikey1_token"], + endpoint: "https://example.com", + guzzle_options: ["handler" => $mock], + ); + + try { + $seam->devices->list(); + $this->fail("Expected the error to be mapped"); + } catch (HttpApiError $error) { + $this->assertSame("device_not_found", $error->getErrorCode()); + } + } + public function testGuzzleOptionsAreMergedIntoTheClient(): void { $seam = $this->seam( diff --git a/tests/PaginatorTest.php b/tests/PaginatorTest.php index 8442318a..4fbd2c39 100644 --- a/tests/PaginatorTest.php +++ b/tests/PaginatorTest.php @@ -56,6 +56,27 @@ public function testNextPageReturnsTheNextPage(): void $this->assertEmpty(array_intersect($first_ids, $second_ids)); } + /** + * The paginator reads the pagination metadata through on_response, but + * must not swallow a callback the caller passed in themselves. + */ + public function testOnResponseParamIsChainedNotReplaced(): void + { + $seen = 0; + + [, $pagination] = $this->paginator([ + "limit" => 2, + "on_response" => function ($response) use (&$seen): void { + $seen++; + $this->assertObjectHasProperty("pagination", $response); + }, + ])->firstPage(); + + $this->assertSame(1, $seen); + // The paginator's own callback has to keep working as well. + $this->assertTrue($pagination->has_next_page); + } + public function testNextPageRequiresACursor(): void { $this->expectException(\InvalidArgumentException::class); From c2602db9ab3490f5400131277b8d82723af2a7e5 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 04:09:30 +0000 Subject: [PATCH 15/35] feat: remove the lts_version method It only echoed the LTS_VERSION constant, and its snake_case sat oddly beside createPaginator on the same class. Use Seam::LTS_VERSION. BREAKING CHANGE: lts_version() is removed from Seam and SeamWithoutWorkspace. Read the LTS_VERSION class constant instead. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- codegen/layouts/seam-client.hbs | 5 ----- src/Seam.php | 5 ----- src/SeamWithoutWorkspace.php | 5 ----- tests/ClientTest.php | 1 - 4 files changed, 16 deletions(-) diff --git a/codegen/layouts/seam-client.hbs b/codegen/layouts/seam-client.hbs index 2f0b0b92..24c40fbd 100644 --- a/codegen/layouts/seam-client.hbs +++ b/codegen/layouts/seam-client.hbs @@ -149,11 +149,6 @@ class Seam ); } - public function lts_version(): string - { - return self::LTS_VERSION; - } - /** * Creates a paginator for a list endpoint. * diff --git a/src/Seam.php b/src/Seam.php index 76dcda31..d6109e6b 100644 --- a/src/Seam.php +++ b/src/Seam.php @@ -242,11 +242,6 @@ public static function from_client( ); } - public function lts_version(): string - { - return self::LTS_VERSION; - } - /** * Creates a paginator for a list endpoint. * diff --git a/src/SeamWithoutWorkspace.php b/src/SeamWithoutWorkspace.php index 91b9874a..a75dd841 100644 --- a/src/SeamWithoutWorkspace.php +++ b/src/SeamWithoutWorkspace.php @@ -93,9 +93,4 @@ public static function from_client(ClientInterface $client): static { return new static(client: $client); } - - public function lts_version(): string - { - return self::LTS_VERSION; - } } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 81e727a5..b289232a 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -244,6 +244,5 @@ public function testWaitForActionAttemptDefaultCanBeDisabled(): void public function testLtsVersionIsExposed(): void { $this->assertSame("1.0.0", Seam::LTS_VERSION); - $this->assertSame("1.0.0", $this->seam()->lts_version()); } } From de21c4d6d5896af0d19014b0e507162d3df89cec Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 04:17:19 +0000 Subject: [PATCH 16/35] test: specify that a 503 on a read should be retried Repeating a read is safe, but every SDK call currently goes over POST, where a status based retry never is, so the SDK's own reads get none. The test asserts today's behavior and marks itself incomplete; once the SDK issues GET for the endpoints that support it, planned for a followup PR, the incomplete branch stops matching and the real assertions take over. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Fwgphpc9YhtxBbxuxppy8m --- tests/RetryTest.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/RetryTest.php b/tests/RetryTest.php index c033f905..ba5ea9f8 100644 --- a/tests/RetryTest.php +++ b/tests/RetryTest.php @@ -215,6 +215,36 @@ public function testDoesNotRetryWhenRetriesAreDisabled(): void $this->assertSame(1, $recorder->attempt_count()); } + /** + * Repeating a read is safe, so a 503 on one should be retried. + * + * TODO: Every SDK call currently goes over POST, where a status based + * retry is never safe, so the SDK's own reads get none and this test + * marks itself incomplete. Once the SDK issues GET for the endpoints + * that support it, planned for a followup PR, the incomplete branch + * stops matching and the real assertions take over. + */ + public function testSdkReadsAreRetriedOnServiceUnavailable(): void + { + $recorder = new RecordingClient([ + self::service_unavailable(), + self::service_unavailable(), + self::devices(), + ]); + + try { + $devices = $this->seam($recorder)->devices->list(); + } catch (\Throwable) { + $this->assertSame(1, $recorder->attempt_count()); + $this->markTestIncomplete( + "The SDK does not use GET yet, so its reads get no status based retries.", + ); + } + + $this->assertSame([], $devices); + $this->assertSame(3, $recorder->attempt_count()); + } + /** * Building a client must not mutate a handler stack the caller may * reuse: a second client built from the same options would otherwise From 2c9220899692a448b48af9e39aeaa507e13c8f16 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 04:29:05 +0000 Subject: [PATCH 17/35] 4.0.0-beta.1 --- package-lock.json | 4 ++-- package.json | 2 +- src/Utils/PackageVersion.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 35c7c127..86c0cac8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "3.5.1", + "version": "4.0.0-beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "3.5.1", + "version": "4.0.0-beta.1", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index c1f2a9ed..953c62f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "3.5.1", + "version": "4.0.0-beta.1", "type": "module", "private": true, "license": "MIT", diff --git a/src/Utils/PackageVersion.php b/src/Utils/PackageVersion.php index 66fbf871..fecc898a 100644 --- a/src/Utils/PackageVersion.php +++ b/src/Utils/PackageVersion.php @@ -10,7 +10,7 @@ class PackageVersion * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "3.5.1"; + public const VERSION = "4.0.0-beta.1"; public static function get(): string { From 16d3b5cd3cd3d56654120f7e184922225097081e Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 21:50:30 -0700 Subject: [PATCH 18/35] Fix prune workflow job name --- .github/workflows/prune.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prune.yml b/.github/workflows/prune.yml index 562d16cc..1c484837 100644 --- a/.github/workflows/prune.yml +++ b/.github/workflows/prune.yml @@ -7,7 +7,7 @@ on: - cron: '0 15 * * 3' jobs: - tag: + branches: name: Prune Branches runs-on: 'ubuntu-latest' timeout-minutes: 30 From cecb150c00e80ad39579c8aa789f46c97b6ff871 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 22:38:43 -0700 Subject: [PATCH 19/35] Make retry behavior explicit --- README.md | 14 +-- src/Http/RetryMiddleware.php | 190 +++++++---------------------------- tests/RetryTest.php | 63 ++++++++---- 3 files changed, 84 insertions(+), 183 deletions(-) diff --git a/README.md b/README.md index 313ac7cb..5366cad2 100644 --- a/README.md +++ b/README.md @@ -341,12 +341,14 @@ $seam = new Seam\Seam(timeout: 60.0); #### Retries -Failed requests are retried twice by default, with exponential backoff. - -A request that never reached the server, such as a connection failure, is -always retried, since it cannot have had an effect. A request the server may -already have processed is retried only when repeating it is safe, so a retry -never risks duplicating a write. +By default, the SDK makes up to three attempts: the initial request and two +retries. Retries are limited to `GET`, `HEAD`, `OPTIONS`, `PUT`, and `DELETE` +requests that fail because of a transport error, timeout, HTTP 429 response, or +HTTP 5xx response. `POST` and `PATCH` requests are not retried. + +Retries use exponential backoff with jitter: approximately 200–240 ms before +the first retry and 400–480 ms before the second. A longer `Retry-After` header +is honored. The request timeout is reset for each attempt. ```php // Retry more times diff --git a/src/Http/RetryMiddleware.php b/src/Http/RetryMiddleware.php index 6411a132..792d887f 100644 --- a/src/Http/RetryMiddleware.php +++ b/src/Http/RetryMiddleware.php @@ -2,33 +2,18 @@ namespace Seam\Http; -use GuzzleHttp\Exception\BadResponseException; -use GuzzleHttp\Exception\ConnectException; -use GuzzleHttp\Exception\RequestException; use GuzzleHttp\HandlerStack; -use GuzzleHttp\Promise\Create; -use GuzzleHttp\Promise\PromiseInterface; use GuzzleRetry\GuzzleRetryMiddleware; use Psr\Http\Message\RequestInterface; /** - * Retries failed requests. + * Retries transient failures for idempotent requests. * - * Retrying a request the server may already have processed can duplicate a - * write, so what is retried depends on the method: - * - * - A method that is safe to repeat is retried on a retryable status code, - * and on any transport error. - * - Any other method is only retried on a transport error that occurred - * before the request could have reached the server, such as a failed - * connection. A timeout in particular is not retried, because it may have - * fired while waiting on a response to a request the server received and - * is still processing. + * Guzzle re-applies the request timeout to every retry, so the timeout covers + * each individual attempt rather than the complete sequence of attempts. */ final class RetryMiddleware { - private const RETRYABLE_STATUS_CODES = [429, 500, 502, 503, 504]; - private const IDEMPOTENT_METHODS = [ "GET", "HEAD", @@ -37,154 +22,47 @@ final class RetryMiddleware "DELETE", ]; - /** - * The linear backoff applied per attempt already made, matching the - * default of the retry middleware handling the idempotent methods. - */ - private const RETRY_DELAY_SECONDS = 1.5; + private const INITIAL_DELAY_SECONDS = 0.2; - /** - * The curl error for a request that hit a timeout. The timeout may have - * fired while waiting for the response, after the server received the - * request. - */ - private const CURLE_OPERATION_TIMEDOUT = 28; - - /** - * The errno for a connection reset by the peer, which the retry - * middleware below also treats as retryable. - */ - private const ECONNRESET = 104; + private const JITTER_MULTIPLIER = 1.2; public static function add(HandlerStack $handler, int $retries): void { - $handler->push(self::retry_options_middleware(), "seam_retry_options"); $handler->push( GuzzleRetryMiddleware::factory([ "retry_enabled" => $retries > 0, - "max_retry_attempts" => $retries, + "max_retry_attempts" => max(0, $retries), + "retry_on_methods" => self::IDEMPOTENT_METHODS, "retry_on_timeout" => true, - // Set per request by the middleware above. - "retry_on_status" => [], + "retry_on_status" => array_merge([429], range(500, 599)), + // Delay is applied by the callback below so Retry-After can + // be compared with, rather than replace, the jittered delay. + "default_retry_multiplier" => 0.0, + "on_retry_callback" => static function ( + int $retry_count, + float $retry_after, + RequestInterface &$request, + array &$options, + ): void { + $backoff = + self::INITIAL_DELAY_SECONDS * + 2.0 ** (float) ($retry_count - 1); + $jittered_backoff = random_int( + (int) ($backoff * 1000.0), + (int) ( + $backoff * + self::JITTER_MULTIPLIER * + 1000.0 + ), + ); + + $options["delay"] = max( + $jittered_backoff, + (int) ceil($retry_after * 1000.0), + ); + }, ]), "seam_retry", ); - // Pushed last, so it sits inside the middleware above and its - // retries are invisible to it: each mechanism covers the failures - // the other does not, and their budgets never combine. - $handler->push( - self::transport_retry_middleware($retries), - "seam_transport_retry", - ); - } - - /** - * Configures the retry middleware per request: a method that is safe to - * repeat opts into status based retries, any other method opts out of - * its transport error handling, which would retry timeouts as well, and - * is covered by the transport retry middleware instead. - */ - private static function retry_options_middleware(): callable - { - return static fn(callable $handler): callable => static function ( - RequestInterface $request, - array $options, - ) use ($handler): PromiseInterface { - if (self::is_idempotent($request)) { - $options["retry_on_status"] = self::RETRYABLE_STATUS_CODES; - } else { - $options["retry_on_timeout"] = false; - } - - return $handler($request, $options); - }; - } - - /** - * Retries the transport errors for the methods that are not safe to - * repeat, skipping anything that looks like a timeout. - */ - private static function transport_retry_middleware(int $retries): callable - { - return static fn( - callable $handler, - ): callable => static fn(RequestInterface $request, array $options): PromiseInterface => self::dispatch($handler, $request, $options, $retries); - } - - private static function dispatch( - callable $handler, - RequestInterface $request, - array $options, - int $retries, - ): PromiseInterface { - return $handler($request, $options)->otherwise(static function ( - \Throwable $reason, - ) use ($handler, $request, $options, $retries): PromiseInterface { - $attempts = $options["seam_transport_retries"] ?? 0; - - if ( - !is_int($attempts) || - $attempts >= $retries || - self::is_idempotent($request) || - !self::is_retryable_transport_error($reason) - ) { - return Create::rejectionFor($reason); - } - - $options["seam_transport_retries"] = $attempts + 1; - $options["delay"] = - (int) (self::RETRY_DELAY_SECONDS * - (float) ($attempts + 1) * - 1000.0); - - return self::dispatch($handler, $request, $options, $retries); - }); - } - - private static function is_retryable_transport_error( - \Throwable $reason, - ): bool { - if ($reason instanceof ConnectException) { - return !self::is_timeout($reason); - } - - // A response was received, so this is a status error covered by the - // retry middleware, not a transport error. - if ($reason instanceof BadResponseException) { - return false; - } - - if ($reason instanceof RequestException) { - $errno = $reason->getHandlerContext()["errno"] ?? null; - - return $errno === self::ECONNRESET; - } - - return false; - } - - /** - * Whether a connection exception is a timeout. The curl errno is - * decisive when present; a handler without one, such as the stream - * handler, is judged by its message. - */ - private static function is_timeout(ConnectException $reason): bool - { - $errno = $reason->getHandlerContext()["errno"] ?? null; - - if ($errno !== null) { - return $errno === self::CURLE_OPERATION_TIMEDOUT; - } - - return preg_match("/timed?\s*out/i", $reason->getMessage()) === 1; - } - - private static function is_idempotent(RequestInterface $request): bool - { - return in_array( - strtoupper($request->getMethod()), - self::IDEMPOTENT_METHODS, - true, - ); } } diff --git a/tests/RetryTest.php b/tests/RetryTest.php index ba5ea9f8..41f3cec0 100644 --- a/tests/RetryTest.php +++ b/tests/RetryTest.php @@ -61,26 +61,26 @@ public function testDoesNotRetryPostOnServiceUnavailable(): void } /** - * A connection failure never reached the server, so retrying it cannot - * duplicate anything. + * Non-idempotent methods are never retried, even when the failure is + * known to have happened before the request reached the server. */ - public function testRetriesPostOnConnectionFailure(): void + public function testDoesNotRetryPostOnConnectionFailure(): void { $connect_error = new ConnectException( "Could not resolve host", new Request("POST", "/devices/list"), ); - $recorder = new RecordingClient([ - $connect_error, - $connect_error, - self::devices(), - ]); + $recorder = RecordingClient::repeating_throwable($connect_error); - $devices = $this->seam($recorder)->devices->list(); + try { + $this->seam($recorder)->devices->list(); + $this->fail("Expected the connection failure to surface"); + } catch (ConnectException) { + // Expected. + } - $this->assertSame([], $devices); - $this->assertSame(3, $recorder->attempt_count()); + $this->assertSame(1, $recorder->attempt_count()); } /** @@ -135,6 +135,8 @@ public function testDoesNotRetryPostOnTimeoutWithoutAnErrno(): void /** * Repeating an idempotent request is safe even when the server may have * received it, so a timeout does get retried there. + * + * TODO: Use the SDK route once generated reads use GET instead of POST. */ public function testRetriesIdempotentRequestsOnTimeout(): void { @@ -156,11 +158,10 @@ public function testRetriesIdempotentRequestsOnTimeout(): void } /** - * A connection reset by the peer surfaces as a request exception with - * errno 104 rather than a connection exception, but is a transport - * failure all the same. + * A connection reset is a transport error, but POST is still not safe to + * repeat. */ - public function testRetriesPostOnConnectionReset(): void + public function testDoesNotRetryPostOnConnectionReset(): void { $reset = new RequestException( "Connection reset by peer", @@ -170,14 +171,21 @@ public function testRetriesPostOnConnectionReset(): void ["errno" => 104], ); - $recorder = new RecordingClient([$reset, self::devices()]); + $recorder = RecordingClient::repeating_throwable($reset); - $devices = $this->seam($recorder)->devices->list(); + try { + $this->seam($recorder)->devices->list(); + $this->fail("Expected the connection reset to surface"); + } catch (RequestException) { + // Expected. + } - $this->assertSame([], $devices); - $this->assertSame(2, $recorder->attempt_count()); + $this->assertSame(1, $recorder->attempt_count()); } + /** + * TODO: Use the SDK route once generated reads use GET instead of POST. + */ public function testStopsRetryingOnceRetriesAreExhausted(): void { $connect_error = new ConnectException( @@ -190,12 +198,18 @@ public function testStopsRetryingOnceRetriesAreExhausted(): void $this->expectException(ConnectException::class); try { - $this->seam($recorder, retries: 1)->devices->list(); + $this->seam($recorder, retries: 1)->client->request( + "GET", + "/devices/list", + ); } finally { $this->assertSame(2, $recorder->attempt_count()); } } + /** + * TODO: Use the SDK route once generated reads use GET instead of POST. + */ public function testDoesNotRetryWhenRetriesAreDisabled(): void { $connect_error = new ConnectException( @@ -206,7 +220,10 @@ public function testDoesNotRetryWhenRetriesAreDisabled(): void $recorder = RecordingClient::repeating_throwable($connect_error); try { - $this->seam($recorder, retries: 0)->devices->list(); + $this->seam($recorder, retries: 0)->client->request( + "GET", + "/devices/list", + ); $this->fail("Expected the connection failure to surface"); } catch (ConnectException) { // Expected. @@ -249,6 +266,8 @@ public function testSdkReadsAreRetriedOnServiceUnavailable(): void * Building a client must not mutate a handler stack the caller may * reuse: a second client built from the same options would otherwise * stack the retry middleware twice and multiply the retries. + * + * TODO: Use the SDK route once generated reads use GET instead of POST. */ public function testBuildingASecondClientDoesNotStackRetries(): void { @@ -281,6 +300,8 @@ public function testBuildingASecondClientDoesNotStackRetries(): void /** * The SDK itself only issues POSTs, but a caller reaching for the client * directly with an idempotent method does get status based retries. + * + * TODO: Use the SDK route once generated reads use GET instead of POST. */ public function testRetriesIdempotentRequestsOnServiceUnavailable(): void { From c410de55408359ba98a3ef7963e186de4554abf5 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 05:39:40 +0000 Subject: [PATCH 20/35] ci: Format code --- src/Http/RetryMiddleware.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Http/RetryMiddleware.php b/src/Http/RetryMiddleware.php index 792d887f..7d32f4b7 100644 --- a/src/Http/RetryMiddleware.php +++ b/src/Http/RetryMiddleware.php @@ -49,11 +49,7 @@ public static function add(HandlerStack $handler, int $retries): void 2.0 ** (float) ($retry_count - 1); $jittered_backoff = random_int( (int) ($backoff * 1000.0), - (int) ( - $backoff * - self::JITTER_MULTIPLIER * - 1000.0 - ), + (int) ($backoff * self::JITTER_MULTIPLIER * 1000.0), ); $options["delay"] = max( From 93490e0ffa20376ec2e59cb5011fee639269c204 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:19:46 -0700 Subject: [PATCH 21/35] Update blueprint and types --- package-lock.json | 22 +++++++++++----------- package.json | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 86c0cac8..da2a2607 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,10 @@ "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", - "@seamapi/blueprint": "^1.2.0", + "@seamapi/blueprint": "^1.5.1", "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", - "@seamapi/types": "1.985.0", + "@seamapi/types": "1.1001.0", "change-case": "^5.4.4", "execa": "^10.0.1", "prettier": "^3.9.6" @@ -806,9 +806,9 @@ "license": "MIT" }, "node_modules/@seamapi/blueprint": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.2.0.tgz", - "integrity": "sha512-+fomtVD/VGJavEpOtVZD+3ZxUYgTXAS4DreNqbsNl0R1qS0wiIdJ40AmE9dgLnY1cHCfPNBmf1z//FdvCqHhrw==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.5.1.tgz", + "integrity": "sha512-rXHMNDGmaE7OK+tg3e64DnpMHQypN4BuSS+PkVqGUoYc9f+xBMONqPscsHAGi9iU/iyZOJZKEOKOXvMmWaLoLA==", "dev": true, "license": "MIT", "dependencies": { @@ -816,7 +816,7 @@ "zod": "^3.23.8" }, "engines": { - "node": ">=22.11.0", + "node": ">=22.12.0", "npm": ">=10.0.0" } }, @@ -890,14 +890,14 @@ } }, "node_modules/@seamapi/types": { - "version": "1.985.0", - "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.985.0.tgz", - "integrity": "sha512-3+aFZXav6zQ9mPsl6FLR0TuMBbu3u3kC16OoEjNHZbTASj1eOYTAjb2lUR8VjFAaR1iZi/rNcIPBRfX/xaWbfg==", + "version": "1.1001.0", + "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.1001.0.tgz", + "integrity": "sha512-pwIEqMYCdOLlIHUzzLlMq/4K6QwAM3kWXooSxNWOlPyCeWk21SvrbTN/joXwZtky504g3unLPKMbFad1mlQyfQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=22.11.0", - "npm": ">=10.9.4" + "node": ">=22.12.0", + "npm": ">=10.0.0" }, "peerDependencies": { "zod": "^3.24.0" diff --git a/package.json b/package.json index 953c62f4..2bce7fc9 100644 --- a/package.json +++ b/package.json @@ -32,10 +32,10 @@ }, "devDependencies": { "@prettier/plugin-php": "^0.25.0", - "@seamapi/blueprint": "^1.2.0", + "@seamapi/blueprint": "^1.5.1", "@seamapi/fake-seam-connect": "1.86.0", "@seamapi/smith": "^1.1.0", - "@seamapi/types": "1.985.0", + "@seamapi/types": "1.1001.0", "change-case": "^5.4.4", "execa": "^10.0.1", "prettier": "^3.9.6" From 97f655d4ae4a235517a0da5e3d3ea88e2f8ce607 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 06:20:30 +0000 Subject: [PATCH 22/35] ci: Generate code --- src/Resources/AcsCredential.php | 10 ++++ src/Resources/ActionAttempt.php | 20 ++++++++ src/Resources/Device.php | 66 +++++++++++++++++++++++++-- src/Resources/Event.php | 13 +++--- src/Resources/UnmanagedDevice.php | 5 ++ src/Routes/AccessCodesClient.php | 43 ----------------- src/Routes/DevicesUnmanagedClient.php | 20 -------- src/Routes/LocksClient.php | 56 ----------------------- src/Routes/NoiseSensorsClient.php | 56 ----------------------- src/Routes/ThermostatsClient.php | 56 ----------------------- 10 files changed, 104 insertions(+), 241 deletions(-) diff --git a/src/Resources/AcsCredential.php b/src/Resources/AcsCredential.php index f62fceda..a63289f1 100644 --- a/src/Resources/AcsCredential.php +++ b/src/Resources/AcsCredential.php @@ -367,6 +367,8 @@ public static function from_json(mixed $json): Warnings|null return new self( created_at: $json->created_at ?? null, message: $json->message ?? null, + new_code: $json->new_code ?? null, + original_code: $json->original_code ?? null, warning_code: $json->warning_code ?? null, ); } @@ -380,6 +382,14 @@ public function __construct( * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, + /** + * The PIN code that was assigned instead. + */ + public string|null $new_code, + /** + * The originally requested PIN code that could not be used. + */ + public string|null $original_code, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 2af52ae4..04af6d8c 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -762,8 +762,10 @@ public static function from_json(mixed $json): Warnings|null return new self( created_at: $json->created_at ?? null, message: $json->message ?? null, + new_code: $json->new_code ?? null, original_access_method_id: $json->original_access_method_id ?? null, + original_code: $json->original_code ?? null, warning_code: $json->warning_code ?? null, warning_message: $json->warning_message ?? null, ); @@ -778,10 +780,18 @@ public function __construct( * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, + /** + * The PIN code that was assigned instead. + */ + public string|null $new_code, /** * ID of the original access method from which this backup access method was split, if applicable. */ public string|null $original_access_method_id, + /** + * The originally requested PIN code that could not be used. + */ + public string|null $original_code, public string|null $warning_code, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. @@ -1042,6 +1052,8 @@ public static function from_json(mixed $json): Warnings|null return new self( created_at: $json->created_at ?? null, message: $json->message ?? null, + new_code: $json->new_code ?? null, + original_code: $json->original_code ?? null, warning_code: $json->warning_code ?? null, ); } @@ -1055,6 +1067,14 @@ public function __construct( * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ public string|null $message, + /** + * The PIN code that was assigned instead. + */ + public string|null $new_code, + /** + * The originally requested PIN code that could not be used. + */ + public string|null $original_code, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/Device.php b/src/Resources/Device.php index 9b01f398..e7d7964e 100644 --- a/src/Resources/Device.php +++ b/src/Resources/Device.php @@ -358,6 +358,7 @@ public static function from_json(mixed $json): Location|null } return new self( location_name: $json->location_name ?? null, + room_name: $json->room_name ?? null, time_zone: $json->time_zone ?? null, timezone: $json->timezone ?? null, ); @@ -368,6 +369,10 @@ public function __construct( * Name of the device location. */ public string|null $location_name, + /** + * Name of the room within the device location, when the provider reports one. + */ + public string|null $room_name, /** * Time zone of the device location. */ @@ -714,6 +719,9 @@ public static function from_json(mixed $json): Properties|null wyze_metadata: isset($json->wyze_metadata) ? Properties\WyzeMetadata::from_json($json->wyze_metadata) : null, + yacan_metadata: isset($json->yacan_metadata) + ? Properties\YacanMetadata::from_json($json->yacan_metadata) + : null, ); } @@ -1049,7 +1057,7 @@ public function __construct( /** * Metada for a Salto device. * - * @deprecated Use `salto_ks_metadata ` instead. + * @deprecated Use `salto_ks_metadata` instead. */ public Properties\SaltoMetadata|null $salto_metadata, /** @@ -1144,6 +1152,10 @@ public function __construct( * Metadata for a Wyze device. */ public Properties\WyzeMetadata|null $wyze_metadata, + /** + * Metadata for a Yacan device. + */ + public Properties\YacanMetadata|null $yacan_metadata, ) {} } @@ -1709,7 +1721,7 @@ public function __construct( /** * Device ID for a dormakaba Oracode device. */ - public mixed $device_id, + public string|null $device_id, /** * Door ID for a dormakaba Oracode device. */ @@ -2279,6 +2291,8 @@ public static function from_json(mixed $json): NestMetadata|null device_name: $json->device_name ?? null, display_name: $json->display_name ?? null, nest_device_id: $json->nest_device_id ?? null, + nest_structure_id: $json->nest_structure_id ?? null, + structure_name: $json->structure_name ?? null, ); } @@ -2299,6 +2313,14 @@ public function __construct( * Device ID for a Google Nest device. */ public string|null $nest_device_id, + /** + * ID of the Google Nest structure containing the device. + */ + public string|null $nest_structure_id, + /** + * Name of the Google Nest structure containing the device. The device owner sets this value. + */ + public string|null $structure_name, ) {} } @@ -2536,7 +2558,7 @@ public function __construct( /** * Metada for a Salto device. * - * @deprecated Use `salto_ks_metadata ` instead. + * @deprecated Use `salto_ks_metadata` instead. */ class SaltoMetadata { @@ -3026,6 +3048,44 @@ public function __construct( ) {} } + /** + * Metadata for a Yacan device. + */ + class YacanMetadata + { + public static function from_json(mixed $json): YacanMetadata|null + { + if (!$json) { + return null; + } + return new self( + device_id: $json->device_id ?? null, + device_name: $json->device_name ?? null, + device_type: $json->device_type ?? null, + serial_number: $json->serial_number ?? null, + ); + } + + public function __construct( + /** + * Device ID for a Yacan device. + */ + public string|null $device_id, + /** + * Device name for a Yacan device. + */ + public string|null $device_name, + /** + * Device type for a Yacan device. + */ + public string|null $device_type, + /** + * Serial number for a Yacan device. + */ + public string|null $serial_number, + ) {} + } + /** * Constraints on access codes for the device. Seam represents each constraint as an object with a `constraint_type` property. Depending on the constraint type, there may also be additional properties. Note that some constraints are manufacturer- or device-specific. */ diff --git a/src/Resources/Event.php b/src/Resources/Event.php index f11c863e..73a16b05 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -61,7 +61,6 @@ public static function from_json(mixed $json): Event|null $json->connected_account_errors ?? [], ), connected_account_id: $json->connected_account_id ?? null, - connected_account_type: $json->connected_account_type ?? null, connected_account_warnings: array_map( fn($c) => Event\ConnectedAccountWarnings::from_json($c), $json->connected_account_warnings ?? [], @@ -139,7 +138,6 @@ public static function from_json(mixed $json): Event|null to: isset($json->to) ? Event\To::from_json($json->to) : null, upper_limit_celsius: $json->upper_limit_celsius ?? null, upper_limit_fahrenheit: $json->upper_limit_fahrenheit ?? null, - user_identity_id: $json->user_identity_id ?? null, video_url: $json->video_url ?? null, workspace_id: $json->workspace_id ?? null, ); @@ -197,11 +195,17 @@ public function __construct( * Errors associated with the access control system. */ public array $acs_system_errors, + /** + * ID of the access system. + */ public string|null $acs_system_id, /** * Warnings associated with the access control system. */ public array $acs_system_warnings, + /** + * ID of the affected access system user. + */ public string|null $acs_user_id, public string|null $action_attempt_id, /** @@ -248,10 +252,6 @@ public function __construct( */ public array $connected_account_errors, public string|null $connected_account_id, - /** - * undocumented: Unreleased. - */ - public string|null $connected_account_type, /** * Warnings associated with the connected account. */ @@ -435,7 +435,6 @@ public function __construct( * Upper temperature limit, in °F, defined by the set threshold. */ public float|null $upper_limit_fahrenheit, - public string|null $user_identity_id, public string|null $video_url, /** * ID of the workspace associated with the event. diff --git a/src/Resources/UnmanagedDevice.php b/src/Resources/UnmanagedDevice.php index 25d35b1e..87649de0 100644 --- a/src/Resources/UnmanagedDevice.php +++ b/src/Resources/UnmanagedDevice.php @@ -256,6 +256,7 @@ public static function from_json(mixed $json): Location|null } return new self( location_name: $json->location_name ?? null, + room_name: $json->room_name ?? null, time_zone: $json->time_zone ?? null, timezone: $json->timezone ?? null, ); @@ -266,6 +267,10 @@ public function __construct( * Name of the device location. */ public string|null $location_name, + /** + * Name of the room within the device location, when the provider reports one. + */ + public string|null $room_name, /** * Time zone of the device location. */ diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index 3c446642..e0dc3632 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -495,9 +495,6 @@ public function report_device_constraints( * @param string $ends_at Date and time at which the validity of the new access code ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. * @param bool $is_external_modification_allowed Indicates whether [external modification](https://docs.seam.co/low-level-apis/smart-locks/access-codes#external-modification) of the code is allowed. Default: `false`. * @param bool $is_managed Indicates whether the access code is managed through Seam. Note that to convert an unmanaged access code into a managed access code, use `/access_codes/unmanaged/convert_to_managed`. - * @param bool $is_offline_access_code Indicates whether the access code is an [offline access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/offline-access-codes). - * @param bool $is_one_time_use Indicates whether the [offline access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/offline-access-codes) is a single-use access code. - * @param string $max_time_rounding Maximum rounding adjustment. To create a daily-bound [offline access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/offline-access-codes) for devices that support this feature, set this parameter to `1d`. * @param string $name Name of the new access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. @@ -505,12 +502,8 @@ public function report_device_constraints( To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). - * @param bool $prefer_native_scheduling Indicates whether [native scheduling](https://docs.seam.co/low-level-apis/smart-locks/access-codes#native-scheduling) should be used for time-bound codes when supported by the provider. Default: `true`. - * @param float $preferred_code_length Preferred code length. Only applicable if you do not specify a `code`. If the affected device does not support the preferred code length, Seam reverts to using the shortest supported code length. * @param string $starts_at Date and time at which the validity of the new access code starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. * @param string $type Type to which you want to convert the access code. To convert a time-bound access code to an ongoing access code, set `type` to `ongoing`. See also [Changing a time-bound access code to permanent access](https://docs.seam.co/low-level-apis/smart-locks/access-codes/modifying-access-codes#special-case-2-changing-a-time-bound-access-code-to-permanent-access). - * @param bool $use_backup_access_code_pool Indicates whether to use a [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) provided by Seam. If `true`, you can use [`/access_codes/pull_backup_access_code`](https://docs.seam.co/api/access_codes/pull_backup_access_code). - * @param bool $use_offline_access_code * @return void OK */ public function update( @@ -522,16 +515,9 @@ public function update( ?string $ends_at = null, ?bool $is_external_modification_allowed = null, ?bool $is_managed = null, - ?bool $is_offline_access_code = null, - ?bool $is_one_time_use = null, - ?string $max_time_rounding = null, ?string $name = null, - ?bool $prefer_native_scheduling = null, - ?float $preferred_code_length = null, ?string $starts_at = null, ?string $type = null, - ?bool $use_backup_access_code_pool = null, - ?bool $use_offline_access_code = null, ): void { $request_payload = []; @@ -563,44 +549,15 @@ public function update( if ($is_managed !== null) { $request_payload["is_managed"] = $is_managed; } - if ($is_offline_access_code !== null) { - $request_payload[ - "is_offline_access_code" - ] = $is_offline_access_code; - } - if ($is_one_time_use !== null) { - $request_payload["is_one_time_use"] = $is_one_time_use; - } - if ($max_time_rounding !== null) { - $request_payload["max_time_rounding"] = $max_time_rounding; - } if ($name !== null) { $request_payload["name"] = $name; } - if ($prefer_native_scheduling !== null) { - $request_payload[ - "prefer_native_scheduling" - ] = $prefer_native_scheduling; - } - if ($preferred_code_length !== null) { - $request_payload["preferred_code_length"] = $preferred_code_length; - } if ($starts_at !== null) { $request_payload["starts_at"] = $starts_at; } if ($type !== null) { $request_payload["type"] = $type; } - if ($use_backup_access_code_pool !== null) { - $request_payload[ - "use_backup_access_code_pool" - ] = $use_backup_access_code_pool; - } - if ($use_offline_access_code !== null) { - $request_payload[ - "use_offline_access_code" - ] = $use_offline_access_code; - } $this->client->request("POST", "/access_codes/update", [ "json" => (object) $request_payload, diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index 9b2007f4..1d10eb6f 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -66,7 +66,6 @@ public function get( * @param string $connected_account_id ID of the connected account for which you want to list devices. * @param array $connected_account_ids Array of IDs of the connected accounts for which you want to list devices. * @param string $created_before Timestamp by which to limit returned devices. Returns devices created before this timestamp. - * @param mixed $custom_metadata_has Set of key:value [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) pairs for which you want to list devices. * @param string $customer_key Customer key for which you want to list devices. * @param array $device_ids Array of device IDs for which you want to list devices. * @param string $device_type Device type for which you want to list devices. @@ -75,9 +74,6 @@ public function get( * @param string $manufacturer Manufacturer for which you want to list devices. * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. * @param string $search String for which to search. Filters returned devices to include all records that satisfy a partial match using `device_id` (full or partial UUID prefix, minimum 4 characters), `connected_account_id`, `display_name`, `custom_metadata` or `location.location_name`. - * @param string $space_id ID of the space for which you want to list devices. - * @param string $unstable_location_id - * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ @@ -86,7 +82,6 @@ public function list( ?string $connected_account_id = null, ?array $connected_account_ids = null, ?string $created_before = null, - mixed $custom_metadata_has = null, ?string $customer_key = null, ?array $device_ids = null, ?string $device_type = null, @@ -95,9 +90,6 @@ public function list( ?string $manufacturer = null, ?string $page_cursor = null, ?string $search = null, - ?string $space_id = null, - ?string $unstable_location_id = null, - ?string $user_identifier_key = null, ?callable $on_response = null, ): array { $request_payload = []; @@ -114,9 +106,6 @@ public function list( if ($created_before !== null) { $request_payload["created_before"] = $created_before; } - if ($custom_metadata_has !== null) { - $request_payload["custom_metadata_has"] = $custom_metadata_has; - } if ($customer_key !== null) { $request_payload["customer_key"] = $customer_key; } @@ -141,15 +130,6 @@ public function list( if ($search !== null) { $request_payload["search"] = $search; } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } - if ($unstable_location_id !== null) { - $request_payload["unstable_location_id"] = $unstable_location_id; - } - if ($user_identifier_key !== null) { - $request_payload["user_identifier_key"] = $user_identifier_key; - } $res = Body::decode( $this->client->request("POST", "/devices/unmanaged/list", [ diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index c4b2c7b4..5f12b842 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -99,41 +99,19 @@ public function get(?string $device_id = null, ?string $name = null): Device * * @param string $connect_webview_id ID of the Connect Webview for which you want to list devices. * @param string $connected_account_id ID of the connected account for which you want to list devices. - * @param array $connected_account_ids Array of IDs of the connected accounts for which you want to list devices. - * @param string $created_before Timestamp by which to limit returned devices. Returns devices created before this timestamp. - * @param mixed $custom_metadata_has Set of key:value [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) pairs for which you want to list devices. * @param string $customer_key Customer key for which you want to list devices. - * @param array $device_ids Array of device IDs for which you want to list devices. * @param string $device_type Device type of the locks that you want to list. * @param array $device_types Device types of the locks that you want to list. - * @param float $limit Numerical limit on the number of devices to return. * @param string $manufacturer Manufacturer of the locks that you want to list. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. - * @param string $search String for which to search. Filters returned devices to include all records that satisfy a partial match using `device_id` (full or partial UUID prefix, minimum 4 characters), `connected_account_id`, `display_name`, `custom_metadata` or `location.location_name`. - * @param string $space_id ID of the space for which you want to list devices. - * @param string $unstable_location_id - * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. - * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( ?string $connect_webview_id = null, ?string $connected_account_id = null, - ?array $connected_account_ids = null, - ?string $created_before = null, - mixed $custom_metadata_has = null, ?string $customer_key = null, - ?array $device_ids = null, ?string $device_type = null, ?array $device_types = null, - ?float $limit = null, ?string $manufacturer = null, - ?string $page_cursor = null, - ?string $search = null, - ?string $space_id = null, - ?string $unstable_location_id = null, - ?string $user_identifier_key = null, - ?callable $on_response = null, ): array { $request_payload = []; @@ -143,48 +121,18 @@ public function list( if ($connected_account_id !== null) { $request_payload["connected_account_id"] = $connected_account_id; } - if ($connected_account_ids !== null) { - $request_payload["connected_account_ids"] = $connected_account_ids; - } - if ($created_before !== null) { - $request_payload["created_before"] = $created_before; - } - if ($custom_metadata_has !== null) { - $request_payload["custom_metadata_has"] = $custom_metadata_has; - } if ($customer_key !== null) { $request_payload["customer_key"] = $customer_key; } - if ($device_ids !== null) { - $request_payload["device_ids"] = $device_ids; - } if ($device_type !== null) { $request_payload["device_type"] = $device_type; } if ($device_types !== null) { $request_payload["device_types"] = $device_types; } - if ($limit !== null) { - $request_payload["limit"] = $limit; - } if ($manufacturer !== null) { $request_payload["manufacturer"] = $manufacturer; } - if ($page_cursor !== null) { - $request_payload["page_cursor"] = $page_cursor; - } - if ($search !== null) { - $request_payload["search"] = $search; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } - if ($unstable_location_id !== null) { - $request_payload["unstable_location_id"] = $unstable_location_id; - } - if ($user_identifier_key !== null) { - $request_payload["user_identifier_key"] = $user_identifier_key; - } $res = Body::decode( $this->client->request("POST", "/locks/list", [ @@ -192,10 +140,6 @@ public function list( ]), ); - if ($on_response !== null) { - $on_response($res); - } - return array_map(fn($r) => Device::from_json($r), $res->devices); } diff --git a/src/Routes/NoiseSensorsClient.php b/src/Routes/NoiseSensorsClient.php index 59025cba..b1450aea 100644 --- a/src/Routes/NoiseSensorsClient.php +++ b/src/Routes/NoiseSensorsClient.php @@ -35,41 +35,19 @@ public function __construct(ClientInterface $client, array $defaults) * * @param string $connect_webview_id ID of the Connect Webview for which you want to list devices. * @param string $connected_account_id ID of the connected account for which you want to list devices. - * @param array $connected_account_ids Array of IDs of the connected accounts for which you want to list devices. - * @param string $created_before Timestamp by which to limit returned devices. Returns devices created before this timestamp. - * @param mixed $custom_metadata_has Set of key:value [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) pairs for which you want to list devices. * @param string $customer_key Customer key for which you want to list devices. - * @param array $device_ids Array of device IDs for which you want to list devices. * @param string $device_type Device type of the noise sensors that you want to list. * @param array $device_types Device types of the noise sensors that you want to list. - * @param float $limit Numerical limit on the number of devices to return. * @param string $manufacturer Manufacturers of the noise sensors that you want to list. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. - * @param string $search String for which to search. Filters returned devices to include all records that satisfy a partial match using `device_id` (full or partial UUID prefix, minimum 4 characters), `connected_account_id`, `display_name`, `custom_metadata` or `location.location_name`. - * @param string $space_id ID of the space for which you want to list devices. - * @param string $unstable_location_id - * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. - * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( ?string $connect_webview_id = null, ?string $connected_account_id = null, - ?array $connected_account_ids = null, - ?string $created_before = null, - mixed $custom_metadata_has = null, ?string $customer_key = null, - ?array $device_ids = null, ?string $device_type = null, ?array $device_types = null, - ?float $limit = null, ?string $manufacturer = null, - ?string $page_cursor = null, - ?string $search = null, - ?string $space_id = null, - ?string $unstable_location_id = null, - ?string $user_identifier_key = null, - ?callable $on_response = null, ): array { $request_payload = []; @@ -79,48 +57,18 @@ public function list( if ($connected_account_id !== null) { $request_payload["connected_account_id"] = $connected_account_id; } - if ($connected_account_ids !== null) { - $request_payload["connected_account_ids"] = $connected_account_ids; - } - if ($created_before !== null) { - $request_payload["created_before"] = $created_before; - } - if ($custom_metadata_has !== null) { - $request_payload["custom_metadata_has"] = $custom_metadata_has; - } if ($customer_key !== null) { $request_payload["customer_key"] = $customer_key; } - if ($device_ids !== null) { - $request_payload["device_ids"] = $device_ids; - } if ($device_type !== null) { $request_payload["device_type"] = $device_type; } if ($device_types !== null) { $request_payload["device_types"] = $device_types; } - if ($limit !== null) { - $request_payload["limit"] = $limit; - } if ($manufacturer !== null) { $request_payload["manufacturer"] = $manufacturer; } - if ($page_cursor !== null) { - $request_payload["page_cursor"] = $page_cursor; - } - if ($search !== null) { - $request_payload["search"] = $search; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } - if ($unstable_location_id !== null) { - $request_payload["unstable_location_id"] = $unstable_location_id; - } - if ($user_identifier_key !== null) { - $request_payload["user_identifier_key"] = $user_identifier_key; - } $res = Body::decode( $this->client->request("POST", "/noise_sensors/list", [ @@ -128,10 +76,6 @@ public function list( ]), ); - if ($on_response !== null) { - $on_response($res); - } - return array_map(fn($r) => Device::from_json($r), $res->devices); } } diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index c73d00de..a1dfbc5d 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -318,41 +318,19 @@ public function heat_cool( * * @param string $connect_webview_id ID of the Connect Webview for which you want to list devices. * @param string $connected_account_id ID of the connected account for which you want to list devices. - * @param array $connected_account_ids Array of IDs of the connected accounts for which you want to list devices. - * @param string $created_before Timestamp by which to limit returned devices. Returns devices created before this timestamp. - * @param mixed $custom_metadata_has Set of key:value [custom metadata](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device) pairs for which you want to list devices. * @param string $customer_key Customer key for which you want to list devices. - * @param array $device_ids Array of device IDs for which you want to list devices. * @param string $device_type Device type by which you want to filter thermostat devices. * @param array $device_types Array of device types by which you want to filter thermostat devices. - * @param float $limit Numerical limit on the number of devices to return. * @param string $manufacturer Manufacturer by which you want to filter thermostat devices. - * @param string $page_cursor Identifies the specific page of results to return, obtained from the previous page's `next_page_cursor`. - * @param string $search String for which to search. Filters returned devices to include all records that satisfy a partial match using `device_id` (full or partial UUID prefix, minimum 4 characters), `connected_account_id`, `display_name`, `custom_metadata` or `location.location_name`. - * @param string $space_id ID of the space for which you want to list devices. - * @param string $unstable_location_id - * @param string $user_identifier_key Your own internal user ID for the user for which you want to list devices. - * @param callable|null $on_response Called with the raw response envelope, used by the paginator to read the pagination metadata. * @return array OK */ public function list( ?string $connect_webview_id = null, ?string $connected_account_id = null, - ?array $connected_account_ids = null, - ?string $created_before = null, - mixed $custom_metadata_has = null, ?string $customer_key = null, - ?array $device_ids = null, ?string $device_type = null, ?array $device_types = null, - ?float $limit = null, ?string $manufacturer = null, - ?string $page_cursor = null, - ?string $search = null, - ?string $space_id = null, - ?string $unstable_location_id = null, - ?string $user_identifier_key = null, - ?callable $on_response = null, ): array { $request_payload = []; @@ -362,48 +340,18 @@ public function list( if ($connected_account_id !== null) { $request_payload["connected_account_id"] = $connected_account_id; } - if ($connected_account_ids !== null) { - $request_payload["connected_account_ids"] = $connected_account_ids; - } - if ($created_before !== null) { - $request_payload["created_before"] = $created_before; - } - if ($custom_metadata_has !== null) { - $request_payload["custom_metadata_has"] = $custom_metadata_has; - } if ($customer_key !== null) { $request_payload["customer_key"] = $customer_key; } - if ($device_ids !== null) { - $request_payload["device_ids"] = $device_ids; - } if ($device_type !== null) { $request_payload["device_type"] = $device_type; } if ($device_types !== null) { $request_payload["device_types"] = $device_types; } - if ($limit !== null) { - $request_payload["limit"] = $limit; - } if ($manufacturer !== null) { $request_payload["manufacturer"] = $manufacturer; } - if ($page_cursor !== null) { - $request_payload["page_cursor"] = $page_cursor; - } - if ($search !== null) { - $request_payload["search"] = $search; - } - if ($space_id !== null) { - $request_payload["space_id"] = $space_id; - } - if ($unstable_location_id !== null) { - $request_payload["unstable_location_id"] = $unstable_location_id; - } - if ($user_identifier_key !== null) { - $request_payload["user_identifier_key"] = $user_identifier_key; - } $res = Body::decode( $this->client->request("POST", "/thermostats/list", [ @@ -411,10 +359,6 @@ public function list( ]), ); - if ($on_response !== null) { - $on_response($res); - } - return array_map(fn($r) => Device::from_json($r), $res->devices); } From b60e4c15cac2f969585e6ed43f317e1d9ccd74a7 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:23:49 -0700 Subject: [PATCH 23/35] Use blueprint preferred HTTP methods --- codegen/layouts/partials/route-method.hbs | 2 +- codegen/lib/class-model.ts | 1 + codegen/lib/layouts/route.ts | 2 ++ codegen/lib/routes.ts | 1 + src/Http/ResolveActionAttempt.php | 2 +- src/Routes/AccessCodesClient.php | 12 ++++---- src/Routes/AccessCodesUnmanagedClient.php | 10 +++---- src/Routes/AccessGrantsClient.php | 6 ++-- src/Routes/AccessGrantsUnmanagedClient.php | 6 ++-- src/Routes/AccessMethodsClient.php | 6 ++-- src/Routes/AccessMethodsUnmanagedClient.php | 4 +-- src/Routes/AcsAccessGroupsClient.php | 14 +++++----- src/Routes/AcsCredentialsClient.php | 14 +++++----- src/Routes/AcsEncodersClient.php | 2 +- src/Routes/AcsEntrancesClient.php | 2 +- src/Routes/AcsSystemsClient.php | 6 ++-- src/Routes/AcsUsersClient.php | 22 +++++++++------ src/Routes/ActionAttemptsClient.php | 2 +- src/Routes/ClientSessionsClient.php | 10 +++---- src/Routes/ConnectWebviewsClient.php | 4 +-- src/Routes/ConnectedAccountsClient.php | 6 ++-- src/Routes/DevicesClient.php | 6 ++-- src/Routes/DevicesUnmanagedClient.php | 4 +-- src/Routes/EventsClient.php | 2 +- src/Routes/InstantKeysClient.php | 6 ++-- src/Routes/LocksClient.php | 2 +- .../NoiseSensorsNoiseThresholdsClient.php | 8 +++--- src/Routes/PhonesClient.php | 6 ++-- src/Routes/SpacesClient.php | 16 +++++------ src/Routes/ThermostatsClient.php | 6 ++-- src/Routes/ThermostatsDailyProgramsClient.php | 4 +-- src/Routes/ThermostatsSchedulesClient.php | 8 +++--- src/Routes/UserIdentitiesClient.php | 28 +++++++++---------- src/Routes/UserIdentitiesUnmanagedClient.php | 6 ++-- src/Routes/WebhooksClient.php | 8 +++--- src/Routes/WorkspacesClient.php | 6 ++-- 36 files changed, 128 insertions(+), 122 deletions(-) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 95efcd7d..cf40c56b 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -15,7 +15,7 @@ {{/if}} {{#unless returnsVoid}}$res = Body::decode({{/unless}}$this->client->request( - "POST", + "{{httpMethod}}", "{{path}}", {{#if hasParams}} ["json" => (object) $request_payload], diff --git a/codegen/lib/class-model.ts b/codegen/lib/class-model.ts index 93356d5c..ab9f8022 100644 --- a/codegen/lib/class-model.ts +++ b/codegen/lib/class-model.ts @@ -11,6 +11,7 @@ export interface PhpClientMethodParameter { export interface PhpClientMethod { methodName: string + httpMethod: string path: string description: string responseDescription: string diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 8450dff8..de8c4693 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -15,6 +15,7 @@ const resourcesNamespace = 'Seam\\Resources' export interface MethodLayoutContext { methodName: string + httpMethod: string description: string responseDescription: string isDeprecated: boolean @@ -113,6 +114,7 @@ const getMethodLayoutContext = ( return { methodName, + httpMethod: method.httpMethod, description: method.description, responseDescription: method.responseDescription, isDeprecated: method.isDeprecated, diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index e49bb63b..60deb72a 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -121,6 +121,7 @@ const createClientMethod = (endpoint: Endpoint): PhpClientMethod => { return { methodName: endpoint.name, + httpMethod: endpoint.request.preferredMethod, path: endpoint.path, description: endpoint.description, responseDescription: response.description, diff --git a/src/Http/ResolveActionAttempt.php b/src/Http/ResolveActionAttempt.php index b0bb60fd..3d51c6c1 100644 --- a/src/Http/ResolveActionAttempt.php +++ b/src/Http/ResolveActionAttempt.php @@ -78,7 +78,7 @@ private static function get_action_attempt( string $action_attempt_id, ): ActionAttempt { $res = Body::decode( - $client->request("POST", "/action_attempts/get", [ + $client->request("GET", "/action_attempts/get", [ "json" => (object) ["action_attempt_id" => $action_attempt_id], ]), ); diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index e0dc3632..31d04bc3 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -241,7 +241,7 @@ public function create_multiple( } $res = Body::decode( - $this->client->request("POST", "/access_codes/create_multiple", [ + $this->client->request("PUT", "/access_codes/create_multiple", [ "json" => (object) $request_payload, ]), ); @@ -270,7 +270,7 @@ public function delete( $request_payload["device_id"] = $device_id; } - $this->client->request("POST", "/access_codes/delete", [ + $this->client->request("DELETE", "/access_codes/delete", [ "json" => (object) $request_payload, ]); } @@ -288,7 +288,7 @@ public function generate_code(string $device_id): AccessCode $request_payload["device_id"] = $device_id; $res = Body::decode( - $this->client->request("POST", "/access_codes/generate_code", [ + $this->client->request("GET", "/access_codes/generate_code", [ "json" => (object) $request_payload, ]), ); @@ -324,7 +324,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/access_codes/get", [ + $this->client->request("GET", "/access_codes/get", [ "json" => (object) $request_payload, ]), ); @@ -559,7 +559,7 @@ public function update( $request_payload["type"] = $type; } - $this->client->request("POST", "/access_codes/update", [ + $this->client->request("PUT", "/access_codes/update", [ "json" => (object) $request_payload, ]); } @@ -602,7 +602,7 @@ public function update_multiple( $request_payload["starts_at"] = $starts_at; } - $this->client->request("POST", "/access_codes/update_multiple", [ + $this->client->request("PATCH", "/access_codes/update_multiple", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index 75a59b15..e823dc0c 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -61,7 +61,7 @@ public function convert_to_managed( } $this->client->request( - "POST", + "PATCH", "/access_codes/unmanaged/convert_to_managed", ["json" => (object) $request_payload], ); @@ -79,7 +79,7 @@ public function delete(string $access_code_id): void $request_payload["access_code_id"] = $access_code_id; - $this->client->request("POST", "/access_codes/unmanaged/delete", [ + $this->client->request("DELETE", "/access_codes/unmanaged/delete", [ "json" => (object) $request_payload, ]); } @@ -112,7 +112,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/access_codes/unmanaged/get", [ + $this->client->request("GET", "/access_codes/unmanaged/get", [ "json" => (object) $request_payload, ]), ); @@ -156,7 +156,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/access_codes/unmanaged/list", [ + $this->client->request("GET", "/access_codes/unmanaged/list", [ "json" => (object) $request_payload, ]), ); @@ -206,7 +206,7 @@ public function update( ] = $is_external_modification_allowed; } - $this->client->request("POST", "/access_codes/unmanaged/update", [ + $this->client->request("PATCH", "/access_codes/unmanaged/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index e6a8a8b8..2ec4a198 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -134,7 +134,7 @@ public function delete(string $access_grant_id): void $request_payload["access_grant_id"] = $access_grant_id; - $this->client->request("POST", "/access_grants/delete", [ + $this->client->request("DELETE", "/access_grants/delete", [ "json" => (object) $request_payload, ]); } @@ -160,7 +160,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/access_grants/get", [ + $this->client->request("GET", "/access_grants/get", [ "json" => (object) $request_payload, ]), ); @@ -364,7 +364,7 @@ public function update( $request_payload["starts_at"] = $starts_at; } - $this->client->request("POST", "/access_grants/update", [ + $this->client->request("PATCH", "/access_grants/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index dfe5df2a..06d2974c 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -37,7 +37,7 @@ public function get(string $access_grant_id): UnmanagedAccessGrant $request_payload["access_grant_id"] = $access_grant_id; $res = Body::decode( - $this->client->request("POST", "/access_grants/unmanaged/get", [ + $this->client->request("GET", "/access_grants/unmanaged/get", [ "json" => (object) $request_payload, ]), ); @@ -88,7 +88,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/access_grants/unmanaged/list", [ + $this->client->request("GET", "/access_grants/unmanaged/list", [ "json" => (object) $request_payload, ]), ); @@ -128,7 +128,7 @@ public function update( $request_payload["access_grant_key"] = $access_grant_key; } - $this->client->request("POST", "/access_grants/unmanaged/update", [ + $this->client->request("PATCH", "/access_grants/unmanaged/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index a038f4f9..e972d500 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -85,7 +85,7 @@ public function delete( $request_payload["reservation_key"] = $reservation_key; } - $this->client->request("POST", "/access_methods/delete", [ + $this->client->request("DELETE", "/access_methods/delete", [ "json" => (object) $request_payload, ]); } @@ -135,7 +135,7 @@ public function get(string $access_method_id): AccessMethod $request_payload["access_method_id"] = $access_method_id; $res = Body::decode( - $this->client->request("POST", "/access_methods/get", [ + $this->client->request("GET", "/access_methods/get", [ "json" => (object) $request_payload, ]), ); @@ -228,7 +228,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/access_methods/list", [ + $this->client->request("GET", "/access_methods/list", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/AccessMethodsUnmanagedClient.php b/src/Routes/AccessMethodsUnmanagedClient.php index 0aaa359c..3f958727 100644 --- a/src/Routes/AccessMethodsUnmanagedClient.php +++ b/src/Routes/AccessMethodsUnmanagedClient.php @@ -37,7 +37,7 @@ public function get(string $access_method_id): UnmanagedAccessMethod $request_payload["access_method_id"] = $access_method_id; $res = Body::decode( - $this->client->request("POST", "/access_methods/unmanaged/get", [ + $this->client->request("GET", "/access_methods/unmanaged/get", [ "json" => (object) $request_payload, ]), ); @@ -74,7 +74,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/access_methods/unmanaged/list", [ + $this->client->request("GET", "/access_methods/unmanaged/list", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/AcsAccessGroupsClient.php b/src/Routes/AcsAccessGroupsClient.php index 109f7141..900e7dc4 100644 --- a/src/Routes/AcsAccessGroupsClient.php +++ b/src/Routes/AcsAccessGroupsClient.php @@ -49,7 +49,7 @@ public function add_user( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/access_groups/add_user", [ + $this->client->request("PUT", "/acs/access_groups/add_user", [ "json" => (object) $request_payload, ]); } @@ -66,7 +66,7 @@ public function delete(string $acs_access_group_id): void $request_payload["acs_access_group_id"] = $acs_access_group_id; - $this->client->request("POST", "/acs/access_groups/delete", [ + $this->client->request("DELETE", "/acs/access_groups/delete", [ "json" => (object) $request_payload, ]); } @@ -84,7 +84,7 @@ public function get(string $acs_access_group_id): AcsAccessGroup $request_payload["acs_access_group_id"] = $acs_access_group_id; $res = Body::decode( - $this->client->request("POST", "/acs/access_groups/get", [ + $this->client->request("GET", "/acs/access_groups/get", [ "json" => (object) $request_payload, ]), ); @@ -123,7 +123,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/acs/access_groups/list", [ + $this->client->request("GET", "/acs/access_groups/list", [ "json" => (object) $request_payload, ]), ); @@ -149,7 +149,7 @@ public function list_accessible_entrances( $res = Body::decode( $this->client->request( - "POST", + "GET", "/acs/access_groups/list_accessible_entrances", ["json" => (object) $request_payload], ), @@ -174,7 +174,7 @@ public function list_users(string $acs_access_group_id): array $request_payload["acs_access_group_id"] = $acs_access_group_id; $res = Body::decode( - $this->client->request("POST", "/acs/access_groups/list_users", [ + $this->client->request("GET", "/acs/access_groups/list_users", [ "json" => (object) $request_payload, ]), ); @@ -205,7 +205,7 @@ public function remove_user( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/access_groups/remove_user", [ + $this->client->request("DELETE", "/acs/access_groups/remove_user", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index 4dc13187..2c74d537 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -48,7 +48,7 @@ public function assign( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/credentials/assign", [ + $this->client->request("PATCH", "/acs/credentials/assign", [ "json" => (object) $request_payload, ]); } @@ -155,7 +155,7 @@ public function delete(string $acs_credential_id): void $request_payload["acs_credential_id"] = $acs_credential_id; - $this->client->request("POST", "/acs/credentials/delete", [ + $this->client->request("DELETE", "/acs/credentials/delete", [ "json" => (object) $request_payload, ]); } @@ -173,7 +173,7 @@ public function get(string $acs_credential_id): AcsCredential $request_payload["acs_credential_id"] = $acs_credential_id; $res = Body::decode( - $this->client->request("POST", "/acs/credentials/get", [ + $this->client->request("GET", "/acs/credentials/get", [ "json" => (object) $request_payload, ]), ); @@ -236,7 +236,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/acs/credentials/list", [ + $this->client->request("GET", "/acs/credentials/list", [ "json" => (object) $request_payload, ]), ); @@ -265,7 +265,7 @@ public function list_accessible_entrances(string $acs_credential_id): array $res = Body::decode( $this->client->request( - "POST", + "GET", "/acs/credentials/list_accessible_entrances", ["json" => (object) $request_payload], ), @@ -300,7 +300,7 @@ public function unassign( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/credentials/unassign", [ + $this->client->request("PATCH", "/acs/credentials/unassign", [ "json" => (object) $request_payload, ]); } @@ -328,7 +328,7 @@ public function update( $request_payload["ends_at"] = $ends_at; } - $this->client->request("POST", "/acs/credentials/update", [ + $this->client->request("PATCH", "/acs/credentials/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index 6383100f..3feb5c4a 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -79,7 +79,7 @@ public function get(string $acs_encoder_id): AcsEncoder $request_payload["acs_encoder_id"] = $acs_encoder_id; $res = Body::decode( - $this->client->request("POST", "/acs/encoders/get", [ + $this->client->request("GET", "/acs/encoders/get", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index 0c6a65e5..c0d97a6e 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -40,7 +40,7 @@ public function get(string $acs_entrance_id): AcsEntrance $request_payload["acs_entrance_id"] = $acs_entrance_id; $res = Body::decode( - $this->client->request("POST", "/acs/entrances/get", [ + $this->client->request("GET", "/acs/entrances/get", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index 18863753..432e9a5c 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -37,7 +37,7 @@ public function get(string $acs_system_id): AcsSystem $request_payload["acs_system_id"] = $acs_system_id; $res = Body::decode( - $this->client->request("POST", "/acs/systems/get", [ + $this->client->request("GET", "/acs/systems/get", [ "json" => (object) $request_payload, ]), ); @@ -73,7 +73,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/acs/systems/list", [ + $this->client->request("GET", "/acs/systems/list", [ "json" => (object) $request_payload, ]), ); @@ -98,7 +98,7 @@ public function list_compatible_credential_manager_acs_systems( $res = Body::decode( $this->client->request( - "POST", + "GET", "/acs/systems/list_compatible_credential_manager_acs_systems", ["json" => (object) $request_payload], ), diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 65f72306..b6d3340a 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -41,7 +41,7 @@ public function add_to_access_group( $request_payload["acs_access_group_id"] = $acs_access_group_id; $request_payload["acs_user_id"] = $acs_user_id; - $this->client->request("POST", "/acs/users/add_to_access_group", [ + $this->client->request("PUT", "/acs/users/add_to_access_group", [ "json" => (object) $request_payload, ]); } @@ -126,7 +126,7 @@ public function delete( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/users/delete", [ + $this->client->request("DELETE", "/acs/users/delete", [ "json" => (object) $request_payload, ]); } @@ -157,7 +157,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/acs/users/get", [ + $this->client->request("GET", "/acs/users/get", [ "json" => (object) $request_payload, ]), ); @@ -222,7 +222,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/acs/users/list", [ + $this->client->request("GET", "/acs/users/list", [ "json" => (object) $request_payload, ]), ); @@ -261,7 +261,7 @@ public function list_accessible_entrances( $res = Body::decode( $this->client->request( - "POST", + "GET", "/acs/users/list_accessible_entrances", ["json" => (object) $request_payload], ), @@ -296,9 +296,13 @@ public function remove_from_access_group( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/users/remove_from_access_group", [ - "json" => (object) $request_payload, - ]); + $this->client->request( + "DELETE", + "/acs/users/remove_from_access_group", + [ + "json" => (object) $request_payload, + ], + ); } /** @@ -448,7 +452,7 @@ public function update( $request_payload["user_identity_id"] = $user_identity_id; } - $this->client->request("POST", "/acs/users/update", [ + $this->client->request("PATCH", "/acs/users/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index c1a656d4..74003d53 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -41,7 +41,7 @@ public function get( $request_payload["action_attempt_id"] = $action_attempt_id; $res = Body::decode( - $this->client->request("POST", "/action_attempts/get", [ + $this->client->request("GET", "/action_attempts/get", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index 99d86e76..9f235068 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -75,7 +75,7 @@ public function create( } $res = Body::decode( - $this->client->request("POST", "/client_sessions/create", [ + $this->client->request("PUT", "/client_sessions/create", [ "json" => (object) $request_payload, ]), ); @@ -95,7 +95,7 @@ public function delete(string $client_session_id): void $request_payload["client_session_id"] = $client_session_id; - $this->client->request("POST", "/client_sessions/delete", [ + $this->client->request("DELETE", "/client_sessions/delete", [ "json" => (object) $request_payload, ]); } @@ -121,7 +121,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/client_sessions/get", [ + $this->client->request("GET", "/client_sessions/get", [ "json" => (object) $request_payload, ]), ); @@ -218,7 +218,7 @@ public function grant_access( $request_payload["user_identity_ids"] = $user_identity_ids; } - $this->client->request("POST", "/client_sessions/grant_access", [ + $this->client->request("PATCH", "/client_sessions/grant_access", [ "json" => (object) $request_payload, ]); } @@ -261,7 +261,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/client_sessions/list", [ + $this->client->request("GET", "/client_sessions/list", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index 22549171..1a01bd70 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -119,7 +119,7 @@ public function delete(string $connect_webview_id): void $request_payload["connect_webview_id"] = $connect_webview_id; - $this->client->request("POST", "/connect_webviews/delete", [ + $this->client->request("DELETE", "/connect_webviews/delete", [ "json" => (object) $request_payload, ]); } @@ -139,7 +139,7 @@ public function get(string $connect_webview_id): ConnectWebview $request_payload["connect_webview_id"] = $connect_webview_id; $res = Body::decode( - $this->client->request("POST", "/connect_webviews/get", [ + $this->client->request("GET", "/connect_webviews/get", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index 14b2b6f0..9525c04a 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -44,7 +44,7 @@ public function delete(string $connected_account_id): void $request_payload["connected_account_id"] = $connected_account_id; - $this->client->request("POST", "/connected_accounts/delete", [ + $this->client->request("DELETE", "/connected_accounts/delete", [ "json" => (object) $request_payload, ]); } @@ -70,7 +70,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/connected_accounts/get", [ + $this->client->request("GET", "/connected_accounts/get", [ "json" => (object) $request_payload, ]), ); @@ -198,7 +198,7 @@ public function update( $request_payload["display_name"] = $display_name; } - $this->client->request("POST", "/connected_accounts/update", [ + $this->client->request("PATCH", "/connected_accounts/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 32be9949..4ff3970d 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -49,7 +49,7 @@ public function get(?string $device_id = null, ?string $name = null): Device } $res = Body::decode( - $this->client->request("POST", "/devices/get", [ + $this->client->request("GET", "/devices/get", [ "json" => (object) $request_payload, ]), ); @@ -182,7 +182,7 @@ public function list_device_providers( } $res = Body::decode( - $this->client->request("POST", "/devices/list_device_providers", [ + $this->client->request("GET", "/devices/list_device_providers", [ "json" => (object) $request_payload, ]), ); @@ -252,7 +252,7 @@ public function update( $request_payload["properties"] = $properties; } - $this->client->request("POST", "/devices/update", [ + $this->client->request("PATCH", "/devices/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index 1d10eb6f..ae1d1917 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -49,7 +49,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/devices/unmanaged/get", [ + $this->client->request("GET", "/devices/unmanaged/get", [ "json" => (object) $request_payload, ]), ); @@ -172,7 +172,7 @@ public function update( $request_payload["is_managed"] = $is_managed; } - $this->client->request("POST", "/devices/unmanaged/update", [ + $this->client->request("PATCH", "/devices/unmanaged/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index 0bf6a533..df5a0174 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -50,7 +50,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/events/get", [ + $this->client->request("GET", "/events/get", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index 895a9351..d4d383cd 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -36,7 +36,7 @@ public function delete(string $instant_key_id): void $request_payload["instant_key_id"] = $instant_key_id; - $this->client->request("POST", "/instant_keys/delete", [ + $this->client->request("DELETE", "/instant_keys/delete", [ "json" => (object) $request_payload, ]); } @@ -62,7 +62,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/instant_keys/get", [ + $this->client->request("GET", "/instant_keys/get", [ "json" => (object) $request_payload, ]), ); @@ -85,7 +85,7 @@ public function list(?string $user_identity_id = null): array } $res = Body::decode( - $this->client->request("POST", "/instant_keys/list", [ + $this->client->request("GET", "/instant_keys/list", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index 5f12b842..c30b958b 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -86,7 +86,7 @@ public function get(?string $device_id = null, ?string $name = null): Device } $res = Body::decode( - $this->client->request("POST", "/locks/get", [ + $this->client->request("GET", "/locks/get", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/NoiseSensorsNoiseThresholdsClient.php b/src/Routes/NoiseSensorsNoiseThresholdsClient.php index 39723a23..810b839e 100644 --- a/src/Routes/NoiseSensorsNoiseThresholdsClient.php +++ b/src/Routes/NoiseSensorsNoiseThresholdsClient.php @@ -86,7 +86,7 @@ public function delete(string $device_id, string $noise_threshold_id): void $request_payload["noise_threshold_id"] = $noise_threshold_id; $this->client->request( - "POST", + "DELETE", "/noise_sensors/noise_thresholds/delete", ["json" => (object) $request_payload], ); @@ -106,7 +106,7 @@ public function get(string $noise_threshold_id): NoiseThreshold $res = Body::decode( $this->client->request( - "POST", + "GET", "/noise_sensors/noise_thresholds/get", ["json" => (object) $request_payload], ), @@ -129,7 +129,7 @@ public function list(string $device_id): array $res = Body::decode( $this->client->request( - "POST", + "GET", "/noise_sensors/noise_thresholds/list", ["json" => (object) $request_payload], ), @@ -185,7 +185,7 @@ public function update( } $this->client->request( - "POST", + "PUT", "/noise_sensors/noise_thresholds/update", ["json" => (object) $request_payload], ); diff --git a/src/Routes/PhonesClient.php b/src/Routes/PhonesClient.php index b710c78c..7b49612d 100644 --- a/src/Routes/PhonesClient.php +++ b/src/Routes/PhonesClient.php @@ -37,7 +37,7 @@ public function deactivate(string $device_id): void $request_payload["device_id"] = $device_id; - $this->client->request("POST", "/phones/deactivate", [ + $this->client->request("DELETE", "/phones/deactivate", [ "json" => (object) $request_payload, ]); } @@ -55,7 +55,7 @@ public function get(string $device_id): Phone $request_payload["device_id"] = $device_id; $res = Body::decode( - $this->client->request("POST", "/phones/get", [ + $this->client->request("GET", "/phones/get", [ "json" => (object) $request_payload, ]), ); @@ -86,7 +86,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/phones/list", [ + $this->client->request("GET", "/phones/list", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 633e115a..1a5dbec1 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -41,7 +41,7 @@ public function add_acs_entrances( $request_payload["acs_entrance_ids"] = $acs_entrance_ids; $request_payload["space_id"] = $space_id; - $this->client->request("POST", "/spaces/add_acs_entrances", [ + $this->client->request("PUT", "/spaces/add_acs_entrances", [ "json" => (object) $request_payload, ]); } @@ -62,7 +62,7 @@ public function add_connected_account( $request_payload["connected_account_id"] = $connected_account_id; $request_payload["space_id"] = $space_id; - $this->client->request("POST", "/spaces/add_connected_account", [ + $this->client->request("PUT", "/spaces/add_connected_account", [ "json" => (object) $request_payload, ]); } @@ -81,7 +81,7 @@ public function add_devices(array $device_ids, string $space_id): void $request_payload["device_ids"] = $device_ids; $request_payload["space_id"] = $space_id; - $this->client->request("POST", "/spaces/add_devices", [ + $this->client->request("PUT", "/spaces/add_devices", [ "json" => (object) $request_payload, ]); } @@ -150,7 +150,7 @@ public function delete(string $space_id): void $request_payload["space_id"] = $space_id; - $this->client->request("POST", "/spaces/delete", [ + $this->client->request("DELETE", "/spaces/delete", [ "json" => (object) $request_payload, ]); } @@ -176,7 +176,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/spaces/get", [ + $this->client->request("GET", "/spaces/get", [ "json" => (object) $request_payload, ]), ); @@ -261,7 +261,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/spaces/list", [ + $this->client->request("GET", "/spaces/list", [ "json" => (object) $request_payload, ]), ); @@ -310,7 +310,7 @@ public function remove_connected_account( $request_payload["connected_account_id"] = $connected_account_id; $request_payload["space_id"] = $space_id; - $this->client->request("POST", "/spaces/remove_connected_account", [ + $this->client->request("DELETE", "/spaces/remove_connected_account", [ "json" => (object) $request_payload, ]); } @@ -375,7 +375,7 @@ public function update( } $res = Body::decode( - $this->client->request("POST", "/spaces/update", [ + $this->client->request("PATCH", "/spaces/update", [ "json" => (object) $request_payload, ]), ); diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index a1dfbc5d..7d41d63c 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -208,7 +208,7 @@ public function delete_climate_preset( $request_payload["climate_preset_key"] = $climate_preset_key; $request_payload["device_id"] = $device_id; - $this->client->request("POST", "/thermostats/delete_climate_preset", [ + $this->client->request("DELETE", "/thermostats/delete_climate_preset", [ "json" => (object) $request_payload, ]); } @@ -551,7 +551,7 @@ public function set_temperature_threshold( } $this->client->request( - "POST", + "PATCH", "/thermostats/set_temperature_threshold", ["json" => (object) $request_payload], ); @@ -633,7 +633,7 @@ public function update_climate_preset( $request_payload["name"] = $name; } - $this->client->request("POST", "/thermostats/update_climate_preset", [ + $this->client->request("PATCH", "/thermostats/update_climate_preset", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index 9fc26e01..c16100fb 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -72,7 +72,7 @@ public function delete(string $thermostat_daily_program_id): void "thermostat_daily_program_id" ] = $thermostat_daily_program_id; - $this->client->request("POST", "/thermostats/daily_programs/delete", [ + $this->client->request("DELETE", "/thermostats/daily_programs/delete", [ "json" => (object) $request_payload, ]); } @@ -102,7 +102,7 @@ public function update( $res = Body::decode( $this->client->request( - "POST", + "PATCH", "/thermostats/daily_programs/update", ["json" => (object) $request_payload], ), diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index 4e857221..a5faaa6c 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -84,7 +84,7 @@ public function delete(string $thermostat_schedule_id): void $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; - $this->client->request("POST", "/thermostats/schedules/delete", [ + $this->client->request("DELETE", "/thermostats/schedules/delete", [ "json" => (object) $request_payload, ]); } @@ -102,7 +102,7 @@ public function get(string $thermostat_schedule_id): ThermostatSchedule $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; $res = Body::decode( - $this->client->request("POST", "/thermostats/schedules/get", [ + $this->client->request("GET", "/thermostats/schedules/get", [ "json" => (object) $request_payload, ]), ); @@ -129,7 +129,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/thermostats/schedules/list", [ + $this->client->request("GET", "/thermostats/schedules/list", [ "json" => (object) $request_payload, ]), ); @@ -185,7 +185,7 @@ public function update( $request_payload["starts_at"] = $starts_at; } - $this->client->request("POST", "/thermostats/schedules/update", [ + $this->client->request("PATCH", "/thermostats/schedules/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index 3260b177..b37dbd5d 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -60,7 +60,7 @@ public function add_acs_user( $request_payload["user_identity_key"] = $user_identity_key; } - $this->client->request("POST", "/user_identities/add_acs_user", [ + $this->client->request("PUT", "/user_identities/add_acs_user", [ "json" => (object) $request_payload, ]); } @@ -121,7 +121,7 @@ public function delete(string $user_identity_id): void $request_payload["user_identity_id"] = $user_identity_id; - $this->client->request("POST", "/user_identities/delete", [ + $this->client->request("DELETE", "/user_identities/delete", [ "json" => (object) $request_payload, ]); } @@ -183,7 +183,7 @@ public function get( } $res = Body::decode( - $this->client->request("POST", "/user_identities/get", [ + $this->client->request("GET", "/user_identities/get", [ "json" => (object) $request_payload, ]), ); @@ -208,7 +208,7 @@ public function grant_access_to_device( $request_payload["user_identity_id"] = $user_identity_id; $this->client->request( - "POST", + "PUT", "/user_identities/grant_access_to_device", ["json" => (object) $request_payload], ); @@ -288,7 +288,7 @@ public function list_accessible_devices(string $user_identity_id): array $res = Body::decode( $this->client->request( - "POST", + "GET", "/user_identities/list_accessible_devices", ["json" => (object) $request_payload], ), @@ -311,7 +311,7 @@ public function list_accessible_entrances(string $user_identity_id): array $res = Body::decode( $this->client->request( - "POST", + "GET", "/user_identities/list_accessible_entrances", ["json" => (object) $request_payload], ), @@ -336,11 +336,9 @@ public function list_acs_systems(string $user_identity_id): array $request_payload["user_identity_id"] = $user_identity_id; $res = Body::decode( - $this->client->request( - "POST", - "/user_identities/list_acs_systems", - ["json" => (object) $request_payload], - ), + $this->client->request("GET", "/user_identities/list_acs_systems", [ + "json" => (object) $request_payload, + ]), ); return array_map(fn($r) => AcsSystem::from_json($r), $res->acs_systems); @@ -359,7 +357,7 @@ public function list_acs_users(string $user_identity_id): array $request_payload["user_identity_id"] = $user_identity_id; $res = Body::decode( - $this->client->request("POST", "/user_identities/list_acs_users", [ + $this->client->request("GET", "/user_identities/list_acs_users", [ "json" => (object) $request_payload, ]), ); @@ -383,7 +381,7 @@ public function remove_acs_user( $request_payload["acs_user_id"] = $acs_user_id; $request_payload["user_identity_id"] = $user_identity_id; - $this->client->request("POST", "/user_identities/remove_acs_user", [ + $this->client->request("DELETE", "/user_identities/remove_acs_user", [ "json" => (object) $request_payload, ]); } @@ -405,7 +403,7 @@ public function revoke_access_to_device( $request_payload["user_identity_id"] = $user_identity_id; $this->client->request( - "POST", + "DELETE", "/user_identities/revoke_access_to_device", ["json" => (object) $request_payload], ); @@ -444,7 +442,7 @@ public function update( $request_payload["user_identity_key"] = $user_identity_key; } - $this->client->request("POST", "/user_identities/update", [ + $this->client->request("PATCH", "/user_identities/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index 4190e614..6cea5c93 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -37,7 +37,7 @@ public function get(string $user_identity_id): UnmanagedUserIdentity $request_payload["user_identity_id"] = $user_identity_id; $res = Body::decode( - $this->client->request("POST", "/user_identities/unmanaged/get", [ + $this->client->request("GET", "/user_identities/unmanaged/get", [ "json" => (object) $request_payload, ]), ); @@ -78,7 +78,7 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/user_identities/unmanaged/list", [ + $this->client->request("GET", "/user_identities/unmanaged/list", [ "json" => (object) $request_payload, ]), ); @@ -116,7 +116,7 @@ public function update( $request_payload["user_identity_key"] = $user_identity_key; } - $this->client->request("POST", "/user_identities/unmanaged/update", [ + $this->client->request("PATCH", "/user_identities/unmanaged/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index efe061fa..d34a9660 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -61,7 +61,7 @@ public function delete(string $webhook_id): void $request_payload["webhook_id"] = $webhook_id; - $this->client->request("POST", "/webhooks/delete", [ + $this->client->request("DELETE", "/webhooks/delete", [ "json" => (object) $request_payload, ]); } @@ -79,7 +79,7 @@ public function get(string $webhook_id): Webhook $request_payload["webhook_id"] = $webhook_id; $res = Body::decode( - $this->client->request("POST", "/webhooks/get", [ + $this->client->request("GET", "/webhooks/get", [ "json" => (object) $request_payload, ]), ); @@ -94,7 +94,7 @@ public function get(string $webhook_id): Webhook */ public function list(): array { - $res = Body::decode($this->client->request("POST", "/webhooks/list")); + $res = Body::decode($this->client->request("GET", "/webhooks/list")); return array_map(fn($r) => Webhook::from_json($r), $res->webhooks); } @@ -113,7 +113,7 @@ public function update(array $event_types, string $webhook_id): void $request_payload["event_types"] = $event_types; $request_payload["webhook_id"] = $webhook_id; - $this->client->request("POST", "/webhooks/update", [ + $this->client->request("PUT", "/webhooks/update", [ "json" => (object) $request_payload, ]); } diff --git a/src/Routes/WorkspacesClient.php b/src/Routes/WorkspacesClient.php index 84462b8a..752b182b 100644 --- a/src/Routes/WorkspacesClient.php +++ b/src/Routes/WorkspacesClient.php @@ -108,7 +108,7 @@ public function create( */ public function get(): Workspace { - $res = Body::decode($this->client->request("POST", "/workspaces/get")); + $res = Body::decode($this->client->request("GET", "/workspaces/get")); return Workspace::from_json($res->workspace); } @@ -120,7 +120,7 @@ public function get(): Workspace */ public function list(): array { - $res = Body::decode($this->client->request("POST", "/workspaces/list")); + $res = Body::decode($this->client->request("GET", "/workspaces/list")); return array_map(fn($r) => Workspace::from_json($r), $res->workspaces); } @@ -190,7 +190,7 @@ public function update( $request_payload["organization_id"] = $organization_id; } - $this->client->request("POST", "/workspaces/update", [ + $this->client->request("PATCH", "/workspaces/update", [ "json" => (object) $request_payload, ]); } From f3f9616960b99d70e214c01dafe5b8609a652260 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 06:29:58 +0000 Subject: [PATCH 24/35] ci: Generate code --- src/Routes/AcsUsersClient.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index b6d3340a..e26c38f8 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -299,9 +299,7 @@ public function remove_from_access_group( $this->client->request( "DELETE", "/acs/users/remove_from_access_group", - [ - "json" => (object) $request_payload, - ], + ["json" => (object) $request_payload], ); } From 045751eb8908d67d6db8d24bbfd3504f0ddc240b Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:40:28 -0700 Subject: [PATCH 25/35] Use params for GET / DELETE --- codegen/layouts/partials/route-method.hbs | 2 +- codegen/lib/layouts/route.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index cf40c56b..06c547d5 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -18,7 +18,7 @@ "{{httpMethod}}", "{{path}}", {{#if hasParams}} - ["json" => (object) $request_payload], + ["{{#if usesQueryParams}}query{{else}}json{{/if}}" => {{#unless usesQueryParams}}(object) {{/unless}}$request_payload], {{/if}} ){{#unless returnsVoid}}){{/unless}}; {{#if usesActionAttempt}} diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index de8c4693..6104d390 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -16,6 +16,7 @@ const resourcesNamespace = 'Seam\\Resources' export interface MethodLayoutContext { methodName: string httpMethod: string + usesQueryParams: boolean description: string responseDescription: string isDeprecated: boolean @@ -115,6 +116,7 @@ const getMethodLayoutContext = ( return { methodName, httpMethod: method.httpMethod, + usesQueryParams: ['GET', 'DELETE'].includes(method.httpMethod), description: method.description, responseDescription: method.responseDescription, isDeprecated: method.isDeprecated, From 1478dd9609d12b2b52f66b094c115752a16d2ec9 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 06:41:09 +0000 Subject: [PATCH 26/35] ci: Generate code --- src/Routes/AccessCodesClient.php | 6 +++--- src/Routes/AccessCodesUnmanagedClient.php | 6 +++--- src/Routes/AccessGrantsClient.php | 4 ++-- src/Routes/AccessGrantsUnmanagedClient.php | 4 ++-- src/Routes/AccessMethodsClient.php | 6 +++--- src/Routes/AccessMethodsUnmanagedClient.php | 4 ++-- src/Routes/AcsAccessGroupsClient.php | 12 ++++++------ src/Routes/AcsCredentialsClient.php | 8 ++++---- src/Routes/AcsEncodersClient.php | 2 +- src/Routes/AcsEntrancesClient.php | 2 +- src/Routes/AcsSystemsClient.php | 6 +++--- src/Routes/AcsUsersClient.php | 10 +++++----- src/Routes/ActionAttemptsClient.php | 2 +- src/Routes/ClientSessionsClient.php | 6 +++--- src/Routes/ConnectWebviewsClient.php | 4 ++-- src/Routes/ConnectedAccountsClient.php | 4 ++-- src/Routes/DevicesClient.php | 4 ++-- src/Routes/DevicesUnmanagedClient.php | 2 +- src/Routes/EventsClient.php | 2 +- src/Routes/InstantKeysClient.php | 6 +++--- src/Routes/LocksClient.php | 2 +- src/Routes/NoiseSensorsNoiseThresholdsClient.php | 6 +++--- src/Routes/PhonesClient.php | 6 +++--- src/Routes/SpacesClient.php | 8 ++++---- src/Routes/ThermostatsClient.php | 2 +- src/Routes/ThermostatsDailyProgramsClient.php | 2 +- src/Routes/ThermostatsSchedulesClient.php | 6 +++--- src/Routes/UserIdentitiesClient.php | 16 ++++++++-------- src/Routes/UserIdentitiesUnmanagedClient.php | 4 ++-- src/Routes/WebhooksClient.php | 4 ++-- 30 files changed, 78 insertions(+), 78 deletions(-) diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index 31d04bc3..a648f967 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -271,7 +271,7 @@ public function delete( } $this->client->request("DELETE", "/access_codes/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -289,7 +289,7 @@ public function generate_code(string $device_id): AccessCode $res = Body::decode( $this->client->request("GET", "/access_codes/generate_code", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -325,7 +325,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/access_codes/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index e823dc0c..8662ce2f 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -80,7 +80,7 @@ public function delete(string $access_code_id): void $request_payload["access_code_id"] = $access_code_id; $this->client->request("DELETE", "/access_codes/unmanaged/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -113,7 +113,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/access_codes/unmanaged/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -157,7 +157,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/access_codes/unmanaged/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index 2ec4a198..a8c23f9c 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -135,7 +135,7 @@ public function delete(string $access_grant_id): void $request_payload["access_grant_id"] = $access_grant_id; $this->client->request("DELETE", "/access_grants/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -161,7 +161,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/access_grants/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index 06d2974c..3b3d6658 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -38,7 +38,7 @@ public function get(string $access_grant_id): UnmanagedAccessGrant $res = Body::decode( $this->client->request("GET", "/access_grants/unmanaged/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -89,7 +89,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/access_grants/unmanaged/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index e972d500..c8bb36c9 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -86,7 +86,7 @@ public function delete( } $this->client->request("DELETE", "/access_methods/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -136,7 +136,7 @@ public function get(string $access_method_id): AccessMethod $res = Body::decode( $this->client->request("GET", "/access_methods/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -229,7 +229,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/access_methods/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessMethodsUnmanagedClient.php b/src/Routes/AccessMethodsUnmanagedClient.php index 3f958727..9bc0a43a 100644 --- a/src/Routes/AccessMethodsUnmanagedClient.php +++ b/src/Routes/AccessMethodsUnmanagedClient.php @@ -38,7 +38,7 @@ public function get(string $access_method_id): UnmanagedAccessMethod $res = Body::decode( $this->client->request("GET", "/access_methods/unmanaged/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -75,7 +75,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/access_methods/unmanaged/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AcsAccessGroupsClient.php b/src/Routes/AcsAccessGroupsClient.php index 900e7dc4..74cb4208 100644 --- a/src/Routes/AcsAccessGroupsClient.php +++ b/src/Routes/AcsAccessGroupsClient.php @@ -67,7 +67,7 @@ public function delete(string $acs_access_group_id): void $request_payload["acs_access_group_id"] = $acs_access_group_id; $this->client->request("DELETE", "/acs/access_groups/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -85,7 +85,7 @@ public function get(string $acs_access_group_id): AcsAccessGroup $res = Body::decode( $this->client->request("GET", "/acs/access_groups/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -124,7 +124,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/acs/access_groups/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -151,7 +151,7 @@ public function list_accessible_entrances( $this->client->request( "GET", "/acs/access_groups/list_accessible_entrances", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); @@ -175,7 +175,7 @@ public function list_users(string $acs_access_group_id): array $res = Body::decode( $this->client->request("GET", "/acs/access_groups/list_users", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -206,7 +206,7 @@ public function remove_user( } $this->client->request("DELETE", "/acs/access_groups/remove_user", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } } diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index 2c74d537..86a884c8 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -156,7 +156,7 @@ public function delete(string $acs_credential_id): void $request_payload["acs_credential_id"] = $acs_credential_id; $this->client->request("DELETE", "/acs/credentials/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -174,7 +174,7 @@ public function get(string $acs_credential_id): AcsCredential $res = Body::decode( $this->client->request("GET", "/acs/credentials/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -237,7 +237,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/acs/credentials/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -267,7 +267,7 @@ public function list_accessible_entrances(string $acs_credential_id): array $this->client->request( "GET", "/acs/credentials/list_accessible_entrances", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index 3feb5c4a..b7cd6c15 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -80,7 +80,7 @@ public function get(string $acs_encoder_id): AcsEncoder $res = Body::decode( $this->client->request("GET", "/acs/encoders/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index c0d97a6e..1cd2c1c1 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -41,7 +41,7 @@ public function get(string $acs_entrance_id): AcsEntrance $res = Body::decode( $this->client->request("GET", "/acs/entrances/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index 432e9a5c..af1d681d 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -38,7 +38,7 @@ public function get(string $acs_system_id): AcsSystem $res = Body::decode( $this->client->request("GET", "/acs/systems/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -74,7 +74,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/acs/systems/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -100,7 +100,7 @@ public function list_compatible_credential_manager_acs_systems( $this->client->request( "GET", "/acs/systems/list_compatible_credential_manager_acs_systems", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index e26c38f8..5b72d157 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -127,7 +127,7 @@ public function delete( } $this->client->request("DELETE", "/acs/users/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -158,7 +158,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/acs/users/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -223,7 +223,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/acs/users/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -263,7 +263,7 @@ public function list_accessible_entrances( $this->client->request( "GET", "/acs/users/list_accessible_entrances", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); @@ -299,7 +299,7 @@ public function remove_from_access_group( $this->client->request( "DELETE", "/acs/users/remove_from_access_group", - ["json" => (object) $request_payload], + ["query" => $request_payload], ); } diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index 74003d53..11454f3d 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -42,7 +42,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/action_attempts/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index 9f235068..313d2dd6 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -96,7 +96,7 @@ public function delete(string $client_session_id): void $request_payload["client_session_id"] = $client_session_id; $this->client->request("DELETE", "/client_sessions/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -122,7 +122,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/client_sessions/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -262,7 +262,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/client_sessions/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index 1a01bd70..b5f171b2 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -120,7 +120,7 @@ public function delete(string $connect_webview_id): void $request_payload["connect_webview_id"] = $connect_webview_id; $this->client->request("DELETE", "/connect_webviews/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -140,7 +140,7 @@ public function get(string $connect_webview_id): ConnectWebview $res = Body::decode( $this->client->request("GET", "/connect_webviews/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index 9525c04a..abbb5914 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -45,7 +45,7 @@ public function delete(string $connected_account_id): void $request_payload["connected_account_id"] = $connected_account_id; $this->client->request("DELETE", "/connected_accounts/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -71,7 +71,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/connected_accounts/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 4ff3970d..f7807686 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -50,7 +50,7 @@ public function get(?string $device_id = null, ?string $name = null): Device $res = Body::decode( $this->client->request("GET", "/devices/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -183,7 +183,7 @@ public function list_device_providers( $res = Body::decode( $this->client->request("GET", "/devices/list_device_providers", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index ae1d1917..e6c222ea 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -50,7 +50,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/devices/unmanaged/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index df5a0174..f787e91b 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -51,7 +51,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/events/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index d4d383cd..b04a987c 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -37,7 +37,7 @@ public function delete(string $instant_key_id): void $request_payload["instant_key_id"] = $instant_key_id; $this->client->request("DELETE", "/instant_keys/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -63,7 +63,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/instant_keys/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -86,7 +86,7 @@ public function list(?string $user_identity_id = null): array $res = Body::decode( $this->client->request("GET", "/instant_keys/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index c30b958b..313a499f 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -87,7 +87,7 @@ public function get(?string $device_id = null, ?string $name = null): Device $res = Body::decode( $this->client->request("GET", "/locks/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/NoiseSensorsNoiseThresholdsClient.php b/src/Routes/NoiseSensorsNoiseThresholdsClient.php index 810b839e..88558830 100644 --- a/src/Routes/NoiseSensorsNoiseThresholdsClient.php +++ b/src/Routes/NoiseSensorsNoiseThresholdsClient.php @@ -88,7 +88,7 @@ public function delete(string $device_id, string $noise_threshold_id): void $this->client->request( "DELETE", "/noise_sensors/noise_thresholds/delete", - ["json" => (object) $request_payload], + ["query" => $request_payload], ); } @@ -108,7 +108,7 @@ public function get(string $noise_threshold_id): NoiseThreshold $this->client->request( "GET", "/noise_sensors/noise_thresholds/get", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); @@ -131,7 +131,7 @@ public function list(string $device_id): array $this->client->request( "GET", "/noise_sensors/noise_thresholds/list", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); diff --git a/src/Routes/PhonesClient.php b/src/Routes/PhonesClient.php index 7b49612d..f56953e8 100644 --- a/src/Routes/PhonesClient.php +++ b/src/Routes/PhonesClient.php @@ -38,7 +38,7 @@ public function deactivate(string $device_id): void $request_payload["device_id"] = $device_id; $this->client->request("DELETE", "/phones/deactivate", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -56,7 +56,7 @@ public function get(string $device_id): Phone $res = Body::decode( $this->client->request("GET", "/phones/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -87,7 +87,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/phones/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 1a5dbec1..88d64988 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -151,7 +151,7 @@ public function delete(string $space_id): void $request_payload["space_id"] = $space_id; $this->client->request("DELETE", "/spaces/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -177,7 +177,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/spaces/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -262,7 +262,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/spaces/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -311,7 +311,7 @@ public function remove_connected_account( $request_payload["space_id"] = $space_id; $this->client->request("DELETE", "/spaces/remove_connected_account", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index 7d41d63c..af0f5e09 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -209,7 +209,7 @@ public function delete_climate_preset( $request_payload["device_id"] = $device_id; $this->client->request("DELETE", "/thermostats/delete_climate_preset", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index c16100fb..3d64dcbc 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -73,7 +73,7 @@ public function delete(string $thermostat_daily_program_id): void ] = $thermostat_daily_program_id; $this->client->request("DELETE", "/thermostats/daily_programs/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index a5faaa6c..38ad4f07 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -85,7 +85,7 @@ public function delete(string $thermostat_schedule_id): void $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; $this->client->request("DELETE", "/thermostats/schedules/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -103,7 +103,7 @@ public function get(string $thermostat_schedule_id): ThermostatSchedule $res = Body::decode( $this->client->request("GET", "/thermostats/schedules/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -130,7 +130,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/thermostats/schedules/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index b37dbd5d..c30cf586 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -122,7 +122,7 @@ public function delete(string $user_identity_id): void $request_payload["user_identity_id"] = $user_identity_id; $this->client->request("DELETE", "/user_identities/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -184,7 +184,7 @@ public function get( $res = Body::decode( $this->client->request("GET", "/user_identities/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -290,7 +290,7 @@ public function list_accessible_devices(string $user_identity_id): array $this->client->request( "GET", "/user_identities/list_accessible_devices", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); @@ -313,7 +313,7 @@ public function list_accessible_entrances(string $user_identity_id): array $this->client->request( "GET", "/user_identities/list_accessible_entrances", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); @@ -337,7 +337,7 @@ public function list_acs_systems(string $user_identity_id): array $res = Body::decode( $this->client->request("GET", "/user_identities/list_acs_systems", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -358,7 +358,7 @@ public function list_acs_users(string $user_identity_id): array $res = Body::decode( $this->client->request("GET", "/user_identities/list_acs_users", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -382,7 +382,7 @@ public function remove_acs_user( $request_payload["user_identity_id"] = $user_identity_id; $this->client->request("DELETE", "/user_identities/remove_acs_user", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -405,7 +405,7 @@ public function revoke_access_to_device( $this->client->request( "DELETE", "/user_identities/revoke_access_to_device", - ["json" => (object) $request_payload], + ["query" => $request_payload], ); } diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index 6cea5c93..92a77c06 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -38,7 +38,7 @@ public function get(string $user_identity_id): UnmanagedUserIdentity $res = Body::decode( $this->client->request("GET", "/user_identities/unmanaged/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); @@ -79,7 +79,7 @@ public function list( $res = Body::decode( $this->client->request("GET", "/user_identities/unmanaged/list", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index d34a9660..29cbb648 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -62,7 +62,7 @@ public function delete(string $webhook_id): void $request_payload["webhook_id"] = $webhook_id; $this->client->request("DELETE", "/webhooks/delete", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]); } @@ -80,7 +80,7 @@ public function get(string $webhook_id): Webhook $res = Body::decode( $this->client->request("GET", "/webhooks/get", [ - "json" => (object) $request_payload, + "query" => $request_payload, ]), ); From 8bdc5fb34e028c3e8438d13ba6b528c2634dc5b9 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:49:41 -0700 Subject: [PATCH 27/35] Impliment isOptional / isNullable --- codegen/lib/class-model.ts | 5 +++-- codegen/lib/layouts/resource.ts | 11 ++++++++--- codegen/lib/layouts/route.ts | 10 +++++++--- codegen/lib/resource-model.ts | 4 ++++ codegen/lib/routes.ts | 3 ++- tests/HeadersTest.php | 2 +- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/codegen/lib/class-model.ts b/codegen/lib/class-model.ts index ab9f8022..c335114e 100644 --- a/codegen/lib/class-model.ts +++ b/codegen/lib/class-model.ts @@ -5,7 +5,8 @@ export interface PhpClientMethodParameter { name: string type: string description: string - required?: boolean | undefined + isOptional: boolean + isNullable: boolean position?: number | undefined } @@ -44,4 +45,4 @@ export const sortPhpClientMethodParameters = ( [...parameters].sort((a, b) => getParameterRank(a) - getParameterRank(b)) const getParameterRank = (parameter: PhpClientMethodParameter): number => - parameter.position ?? ((parameter.required ?? false) ? 1000 : 9999) + parameter.position ?? (parameter.isOptional ? 9999 : 1000) diff --git a/codegen/lib/layouts/resource.ts b/codegen/lib/layouts/resource.ts index 6ebd0ee6..72b424d1 100644 --- a/codegen/lib/layouts/resource.ts +++ b/codegen/lib/layouts/resource.ts @@ -57,19 +57,24 @@ const generateConstructorParam = ( property: ResourceClassProperty, ): ConstructorParamLayoutContext => { let declaration: string + // Resource decoding remains tolerant of sparse response envelopes. Optional + // properties can also be omitted when constructing a resource directly; + // nullable properties retain the same null-safe representation. + const defaultValue = property.isOptional ? ' = null' : '' + switch (property.kind) { case 'objectReference': - declaration = `public ${property.referenceName}|null $${property.name},` + declaration = `public ${property.referenceName}|null $${property.name}${defaultValue},` break case 'listReference': - declaration = `public array $${property.name},` + declaration = `public array${property.isOptional ? '|null' : ''} $${property.name}${defaultValue},` break case 'value': { const { phpType } = property const nullSuffix = phpType === 'mixed' ? '' : '|null' - declaration = `public ${phpType}${nullSuffix} $${property.name},` + declaration = `public ${phpType}${nullSuffix} $${property.name}${defaultValue},` break } } diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 6104d390..fbe11da4 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -26,6 +26,8 @@ export interface MethodLayoutContext { type: string description: string required: boolean + isOptional: boolean + isNullable: boolean }> documentedParameters: Array<{ name: string @@ -94,7 +96,7 @@ const getMethodLayoutContext = ( const signatureParams = sortedParameters .map( (p) => - `${!(p.required ?? false) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${(p.required ?? false) ? '' : ' = null'}`, + `${(p.isNullable || p.isOptional) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${p.isOptional ? ' = null' : ''}`, ) .concat( usesActionAttempt @@ -105,11 +107,13 @@ const getMethodLayoutContext = ( .join(', ') const documentedEndpointParameters = sortedParameters.map( - ({ name, type, description, required }) => ({ + ({ name, type, description, isOptional, isNullable }) => ({ name, type, description, - required: required ?? false, + required: !isOptional, + isOptional, + isNullable, }), ) diff --git a/codegen/lib/resource-model.ts b/codegen/lib/resource-model.ts index 6c277f86..c09688d7 100644 --- a/codegen/lib/resource-model.ts +++ b/codegen/lib/resource-model.ts @@ -32,6 +32,8 @@ export type ResourceClassProperty = interface ResourceClassPropertyMetadata { name: string description: string + isOptional: boolean + isNullable: boolean isDeprecated: boolean deprecationMessage: string } @@ -258,6 +260,8 @@ const buildClass = ( const metadata = { name: property.name, description: property.description, + isOptional: property.isOptional, + isNullable: property.isNullable, isDeprecated: property.isDeprecated, deprecationMessage: property.deprecationMessage, } diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index 60deb72a..d81cb9f7 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -131,7 +131,8 @@ const createClientMethod = (endpoint: Endpoint): PhpClientMethod => { name: parameter.name, type: getPhpType(parameter), description: parameter.description, - required: parameter.isRequired, + isOptional: !parameter.isRequired, + isNullable: parameter.isNullable, // The primary identifier of a get endpoint always sorts first in the // method signature. position: diff --git a/tests/HeadersTest.php b/tests/HeadersTest.php index d752ad4f..6afecd90 100644 --- a/tests/HeadersTest.php +++ b/tests/HeadersTest.php @@ -35,7 +35,7 @@ public function testSendsDefaultHeaders(): void $request = $recorder->request(); $this->assertSame("/devices/get", $request->getUri()->getPath()); - $this->assertEquals((object) ["device_id" => "d1"], $recorder->body()); + $this->assertSame("device_id=d1", $request->getUri()->getQuery()); $this->assertSame( "Bearer seam_apikey_token", From 3c2a719ccaf31851e929ce6a936bac2991f070ff Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 06:50:26 +0000 Subject: [PATCH 28/35] ci: Generate code --- src/Resources/AccessCode.php | 48 +- src/Resources/AccessGrant.php | 20 +- src/Resources/AccessMethod.php | 18 +- src/Resources/AcsAccessGroup.php | 6 +- src/Resources/AcsCredential.php | 68 +- src/Resources/AcsEntrance.php | 132 ++-- src/Resources/AcsSystem.php | 26 +- src/Resources/AcsUser.php | 52 +- src/Resources/ActionAttempt.php | 188 ++--- src/Resources/Batch.php | 48 +- src/Resources/ClientSession.php | 4 +- src/Resources/ConnectWebview.php | 2 +- src/Resources/ConnectedAccount.php | 54 +- src/Resources/Device.php | 868 ++++++++++++------------ src/Resources/DeviceProvider.php | 40 +- src/Resources/Event.php | 76 +-- src/Resources/InstantKey.php | 10 +- src/Resources/NoiseThreshold.php | 2 +- src/Resources/Phone.php | 16 +- src/Resources/Space.php | 16 +- src/Resources/ThermostatSchedule.php | 4 +- src/Resources/UnmanagedAccessCode.php | 44 +- src/Resources/UnmanagedAccessGrant.php | 14 +- src/Resources/UnmanagedAccessMethod.php | 12 +- src/Resources/UnmanagedDevice.php | 80 +-- src/Resources/Webhook.php | 4 +- src/Resources/Workspace.php | 12 +- 27 files changed, 932 insertions(+), 932 deletions(-) diff --git a/src/Resources/AccessCode.php b/src/Resources/AccessCode.php index d2c1a118..ad39cb12 100644 --- a/src/Resources/AccessCode.php +++ b/src/Resources/AccessCode.php @@ -90,11 +90,11 @@ public function __construct( /** * Metadata for a dormakaba Oracode managed access code. Only present for access codes from dormakaba Oracode devices. */ - public AccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata, + public AccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, /** * Date and time after which the time-bound access code becomes inactive. */ - public string|null $ends_at, + public string|null $ends_at = null, /** * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). */ @@ -102,7 +102,7 @@ public function __construct( /** * Indicates whether the access code is a backup code. */ - public bool|null $is_backup, + public bool|null $is_backup = null, /** * Indicates whether a backup access code is available for use if the primary access code is lost or compromised. */ @@ -126,11 +126,11 @@ public function __construct( /** * Indicates whether the code is set on the device according to a preconfigured schedule. */ - public bool|null $is_scheduled_on_device, + public bool|null $is_scheduled_on_device = null, /** * Indicates whether the access code is waiting for a code assignment. */ - public bool|null $is_waiting_for_code_assignment, + public bool|null $is_waiting_for_code_assignment = null, /** * Name of the access code. Enables administrators and users to identify the access code easily, especially when there are numerous access codes. Note that the name provided on Seam is used to identify the code on Seam and is not necessarily the name that will appear in the lock provider's app or on the device. This is because lock providers may have constraints on names, such as length, uniqueness, or characters that can be used. In addition, some lock providers may break down names into components such as `first_name` and `last_name`. To provide a consistent experience, Seam identifies the code on Seam by its name but may modify the name that appears on the lock provider's app or on the device. For example, Seam may add additional characters or truncate the name to meet provider constraints. To help your users identify codes set by Seam, Seam provides the name exactly as it appears on the lock provider's app or on the device as a separate property called `appearance`. This is an object with a `name` property and, optionally, `first_name` and `last_name` properties (for providers that break down a name into components). */ @@ -142,11 +142,11 @@ public function __construct( /** * Identifier of the pulled backup access code. Used to associate the pulled backup access code with the original access code. */ - public string|null $pulled_backup_access_code_id, + public string|null $pulled_backup_access_code_id = null, /** * Date and time at which the time-bound access code becomes active. */ - public string|null $starts_at, + public string|null $starts_at = null, /** * Current status of the access code within the operational lifecycle. Values are `setting`, a transitional phase that indicates that the code is being configured or activated; `set`, which indicates that the code is active and operational; `unset`, which indicates a deactivated or unused state, either before activation or after deliberate deactivation; `removing`, which indicates a transitional period in which the code is being deleted or made inactive; and `unknown`, which indicates an indeterminate state, due to reasons such as system errors or incomplete data, that highlights a potential need for system review or troubleshooting. See also [Lifecycle of Access Codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes/lifecycle-of-access-codes). */ @@ -195,35 +195,35 @@ public function __construct( /** * Indicates whether the stay can be cancelled via the Dormakaba Oracode API. */ - public bool|null $is_cancellable, + public bool|null $is_cancellable = null, /** * Indicates whether early check-in is available for this stay. */ - public bool|null $is_early_checkin_able, + public bool|null $is_early_checkin_able = null, /** * Indicates whether the stay can be extended via the Dormakaba Oracode API. */ - public bool|null $is_extendable, + public bool|null $is_extendable = null, /** * Indicates whether the access code can be overridden. When false, the maximum number of overrides has been reached. */ - public bool|null $is_overridable, + public bool|null $is_overridable = null, /** * Dormakaba Oracode site name associated with this access code. */ - public string|null $site_name, + public string|null $site_name = null, /** * Dormakaba Oracode stay ID associated with this access code. */ - public float|null $stay_id, + public float|null $stay_id = null, /** * Dormakaba Oracode user level ID associated with this access code. */ - public string|null $user_level_id, + public string|null $user_level_id = null, /** * Dormakaba Oracode user level name associated with this access code. */ - public string|null $user_level_name, + public string|null $user_level_name = null, ) {} } @@ -261,11 +261,11 @@ public function __construct( /** * Indicates the type of external modification. `modified` means the code's PIN or schedule was changed. `removed` means the code was deleted from the device. */ - public string|null $change_type, + public string|null $change_type = null, /** * Date and time at which Seam created the error. */ - public string|null $created_at, + public string|null $created_at = null, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ @@ -277,13 +277,13 @@ public function __construct( /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ - public bool|null $is_bridge_error, + public bool|null $is_bridge_error = null, public bool|null $is_connected_account_error, public bool|null $is_device_error, /** * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. */ - public string|null $managed_access_code_id, + public string|null $managed_access_code_id = null, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ @@ -291,11 +291,11 @@ public function __construct( /** * List of fields that were changed externally, with their previous and new values. */ - public array $modified_fields, + public array|null $modified_fields = null, /** * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. */ - public string|null $unmanaged_access_code_id, + public string|null $unmanaged_access_code_id = null, ) {} } @@ -368,11 +368,11 @@ public function __construct( /** * Indicates the type of external modification. `modified` means the code's PIN or schedule was changed. `removed` means the code was deleted from the device. */ - public string|null $change_type, + public string|null $change_type = null, /** * Date and time at which Seam created the warning. */ - public string|null $created_at, + public string|null $created_at = null, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ @@ -380,7 +380,7 @@ public function __construct( /** * List of fields that were changed externally, with their previous and new values. */ - public array $modified_fields, + public array|null $modified_fields = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/AccessGrant.php b/src/Resources/AccessGrant.php index 6df3876a..9d2d9448 100644 --- a/src/Resources/AccessGrant.php +++ b/src/Resources/AccessGrant.php @@ -56,7 +56,7 @@ public function __construct( /** * Unique key for the access grant within the workspace. */ - public string|null $access_grant_key, + public string|null $access_grant_key = null, /** * IDs of the access methods created for the Access Grant. */ @@ -64,7 +64,7 @@ public function __construct( /** * Client Session Token. Only returned if the Access Grant has a mobile_key access method. */ - public string|null $client_session_token, + public string|null $client_session_token = null, /** * Date and time at which the Access Grant was created. */ @@ -72,7 +72,7 @@ public function __construct( /** * ID of the customization profile associated with the Access Grant. */ - public string|null $customization_profile_id, + public string|null $customization_profile_id = null, /** * Display name of the Access Grant. */ @@ -88,7 +88,7 @@ public function __construct( /** * Instant Key URL. Only returned if the Access Grant has a single mobile_key access_method. */ - public string|null $instant_key_url, + public string|null $instant_key_url = null, /** * @deprecated Use `space_ids`. */ @@ -108,7 +108,7 @@ public function __construct( /** * Reservation key for the access grant. */ - public string|null $reservation_key, + public string|null $reservation_key = null, /** * IDs of the spaces to which the Access Grant gives access. */ @@ -168,7 +168,7 @@ public function __construct( /** * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. */ - public array|null $missing_device_ids, + public array|null $missing_device_ids = null, ) {} } @@ -242,7 +242,7 @@ public function __construct( /** * Specific PIN code to use for this access method. Only applicable when mode is 'code'. */ - public string|null $code, + public string|null $code = null, /** * IDs of the access methods created for the requested access method. */ @@ -258,7 +258,7 @@ public function __construct( /** * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. */ - public int|null $instant_key_max_use_count, + public int|null $instant_key_max_use_count = null, /** * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. */ @@ -305,7 +305,7 @@ public function __construct( /** * Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). */ - public array $failed_devices, + public array|null $failed_devices = null, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ @@ -380,7 +380,7 @@ public function __construct( /** * Common code key to ensure PIN code reuse across devices. */ - public string|null $common_code_key, + public string|null $common_code_key = null, /** * New device IDs where access codes should be created. */ diff --git a/src/Resources/AccessMethod.php b/src/Resources/AccessMethod.php index 7745b2f6..f7946c5b 100644 --- a/src/Resources/AccessMethod.php +++ b/src/Resources/AccessMethod.php @@ -51,11 +51,11 @@ public function __construct( /** * Token of the client session associated with the access method. */ - public string|null $client_session_token, + public string|null $client_session_token = null, /** * The actual PIN code for code access methods. */ - public string|null $code, + public string|null $code = null, /** * Date and time at which the access method was created. */ @@ -63,7 +63,7 @@ public function __construct( /** * ID of the customization profile associated with the access method. */ - public string|null $customization_profile_id, + public string|null $customization_profile_id = null, /** * Display name of the access method. */ @@ -75,15 +75,15 @@ public function __construct( /** * URL of the Instant Key for mobile key access methods. */ - public string|null $instant_key_url, + public string|null $instant_key_url = null, /** * Indicates whether an existing card credential must be assigned to this access method before it can be issued. Only applies to card-mode access methods on systems that support credential assignment. */ - public bool|null $is_assignment_required, + public bool|null $is_assignment_required = null, /** * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. */ - public bool|null $is_encoding_required, + public bool|null $is_encoding_required = null, /** * Indicates whether the access method has been issued. */ @@ -91,11 +91,11 @@ public function __construct( /** * Indicates whether the access method is ready for card assignment. This is true when the access method is in card mode, has not yet been issued, and the system supports credential assignment. */ - public bool|null $is_ready_for_assignment, + public bool|null $is_ready_for_assignment = null, /** * Indicates whether the access method is ready to be encoded. This is true when the credential has been created and the card has not yet been issued. */ - public bool|null $is_ready_for_encoding, + public bool|null $is_ready_for_encoding = null, /** * Date and time at which the access method was issued. */ @@ -223,7 +223,7 @@ public function __construct( /** * ID of the original access method from which this backup access method was split, if applicable. */ - public string|null $original_access_method_id, + public string|null $original_access_method_id = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/AcsAccessGroup.php b/src/Resources/AcsAccessGroup.php index b51f042d..387acf79 100644 --- a/src/Resources/AcsAccessGroup.php +++ b/src/Resources/AcsAccessGroup.php @@ -62,7 +62,7 @@ public function __construct( /** * `starts_at` and `ends_at` timestamps for the access group's access. */ - public AcsAccessGroup\AccessSchedule|null $access_schedule, + public AcsAccessGroup\AccessSchedule|null $access_schedule = null, /** * ID of the access group. */ @@ -296,7 +296,7 @@ public function __construct( /** * Name of the access group. */ - public string|null $name, + public string|null $name = null, /** * Starting time for the access schedule. */ @@ -336,7 +336,7 @@ public function __construct( /** * Name of the access group. */ - public string|null $name, + public string|null $name = null, /** * Starting time for the access schedule. */ diff --git a/src/Resources/AcsCredential.php b/src/Resources/AcsCredential.php index a63289f1..51f87ee3 100644 --- a/src/Resources/AcsCredential.php +++ b/src/Resources/AcsCredential.php @@ -87,7 +87,7 @@ public function __construct( /** * ID of the credential pool to which the credential belongs. */ - public string|null $acs_credential_pool_id, + public string|null $acs_credential_pool_id = null, /** * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -95,23 +95,23 @@ public function __construct( /** * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ - public string|null $acs_user_id, + public string|null $acs_user_id = null, /** * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredential\AkilesMetadata|null $akiles_metadata, + public AcsCredential\AkilesMetadata|null $akiles_metadata = null, /** * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredential\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + public AcsCredential\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $card_number, + public string|null $card_number = null, /** * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $code, + public string|null $code = null, /** * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ @@ -127,7 +127,7 @@ public function __construct( /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. */ - public string|null $ends_at, + public string|null $ends_at = null, /** * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -135,19 +135,19 @@ public function __construct( /** * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. */ - public string|null $external_type, + public string|null $external_type = null, /** * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. */ - public string|null $external_type_display_name, + public string|null $external_type_display_name = null, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. */ - public bool|null $is_issued, + public bool|null $is_issued = null, /** * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. */ - public bool|null $is_latest_desired_state_synced_with_provider, + public bool|null $is_latest_desired_state_synced_with_provider = null, /** * Indicates whether Seam manages the credential. */ @@ -155,35 +155,35 @@ public function __construct( /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). */ - public bool|null $is_multi_phone_sync_credential, + public bool|null $is_multi_phone_sync_credential = null, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. */ - public bool|null $is_one_time_use, + public bool|null $is_one_time_use = null, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. */ - public string|null $issued_at, + public string|null $issued_at = null, /** * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. */ - public string|null $latest_desired_state_synced_with_provider_at, + public string|null $latest_desired_state_synced_with_provider_at = null, /** * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $parent_acs_credential_id, + public string|null $parent_acs_credential_id = null, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. */ - public string|null $starts_at, + public string|null $starts_at = null, /** * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ - public string|null $user_identity_id, + public string|null $user_identity_id = null, /** * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredential\VisionlineMetadata|null $visionline_metadata, + public AcsCredential\VisionlineMetadata|null $visionline_metadata = null, /** * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -214,7 +214,7 @@ public function __construct( /** * ID of the Akiles member PIN. */ - public string|null $member_pin_id, + public string|null $member_pin_id = null, ) {} } @@ -244,27 +244,27 @@ public function __construct( /** * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. */ - public bool|null $auto_join, + public bool|null $auto_join = null, /** * Names of the doors to which to grant access in the Vostio access system. */ - public array|null $door_names, + public array|null $door_names = null, /** * Endpoint ID in the Vostio access system. */ - public string|null $endpoint_id, + public string|null $endpoint_id = null, /** * Key ID in the Vostio access system. */ - public string|null $key_id, + public string|null $key_id = null, /** * Key issuing request ID in the Vostio access system. */ - public string|null $key_issuing_request_id, + public string|null $key_issuing_request_id = null, /** * IDs of the guest entrances to override in the Vostio access system. */ - public array|null $override_guest_acs_entrance_ids, + public array|null $override_guest_acs_entrance_ids = null, ) {} } @@ -322,35 +322,35 @@ public function __construct( /** * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. */ - public bool|null $auto_join, + public bool|null $auto_join = null, /** * Card function type in the Visionline access system. */ - public string|null $card_function_type, + public string|null $card_function_type = null, /** * ID of the card in the Visionline access system. */ - public string|null $card_id, + public string|null $card_id = null, /** * Common entrance IDs in the Visionline access system. */ - public array|null $common_acs_entrance_ids, + public array|null $common_acs_entrance_ids = null, /** * ID of the credential in the Visionline access system. */ - public string|null $credential_id, + public string|null $credential_id = null, /** * Guest entrance IDs in the Visionline access system. */ - public array|null $guest_acs_entrance_ids, + public array|null $guest_acs_entrance_ids = null, /** * Indicates whether the credential is valid. */ - public bool|null $is_valid, + public bool|null $is_valid = null, /** * IDs of the credentials to which you want to join. */ - public array|null $joiner_acs_credential_ids, + public array|null $joiner_acs_credential_ids = null, ) {} } diff --git a/src/Resources/AcsEntrance.php b/src/Resources/AcsEntrance.php index 59fdad0d..9c20b698 100644 --- a/src/Resources/AcsEntrance.php +++ b/src/Resources/AcsEntrance.php @@ -113,39 +113,39 @@ public function __construct( /** * Akiles-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\AkilesMetadata|null $akiles_metadata, + public AcsEntrance\AkilesMetadata|null $akiles_metadata = null, /** * ASSA ABLOY Vostio-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + public AcsEntrance\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Avigilon Alta-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\AvigilonAltaMetadata|null $avigilon_alta_metadata, + public AcsEntrance\AvigilonAltaMetadata|null $avigilon_alta_metadata = null, /** * Brivo-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\BrivoMetadata|null $brivo_metadata, + public AcsEntrance\BrivoMetadata|null $brivo_metadata = null, /** * Indicates whether the ACS entrance can belong to a reservation via an access_grant.reservation_key. */ - public bool|null $can_belong_to_reservation, + public bool|null $can_belong_to_reservation = null, /** * Indicates whether the ACS entrance can be unlocked with card credentials. */ - public bool|null $can_unlock_with_card, + public bool|null $can_unlock_with_card = null, /** * Indicates whether the ACS entrance can be unlocked with cloud key credentials. */ - public bool|null $can_unlock_with_cloud_key, + public bool|null $can_unlock_with_cloud_key = null, /** * Indicates whether the ACS entrance can be unlocked with pin codes. */ - public bool|null $can_unlock_with_code, + public bool|null $can_unlock_with_code = null, /** * Indicates whether the ACS entrance can be unlocked with mobile key credentials. */ - public bool|null $can_unlock_with_mobile_key, + public bool|null $can_unlock_with_mobile_key = null, /** * ID of the [connected account](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ @@ -161,11 +161,11 @@ public function __construct( /** * dormakaba Ambiance-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\DormakabaAmbianceMetadata|null $dormakaba_ambiance_metadata, + public AcsEntrance\DormakabaAmbianceMetadata|null $dormakaba_ambiance_metadata = null, /** * dormakaba Community-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\DormakabaCommunityMetadata|null $dormakaba_community_metadata, + public AcsEntrance\DormakabaCommunityMetadata|null $dormakaba_community_metadata = null, /** * Errors associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ @@ -173,23 +173,23 @@ public function __construct( /** * Hotek-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\HotekMetadata|null $hotek_metadata, + public AcsEntrance\HotekMetadata|null $hotek_metadata = null, /** * Indicates whether the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) is currently locked. */ - public bool|null $is_locked, + public bool|null $is_locked = null, /** * Latch-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\LatchMetadata|null $latch_metadata, + public AcsEntrance\LatchMetadata|null $latch_metadata = null, /** * Salto KS-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\SaltoKsMetadata|null $salto_ks_metadata, + public AcsEntrance\SaltoKsMetadata|null $salto_ks_metadata = null, /** * Salto Space-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\SaltoSpaceMetadata|null $salto_space_metadata, + public AcsEntrance\SaltoSpaceMetadata|null $salto_space_metadata = null, /** * IDs of the spaces that the entrance is in. */ @@ -197,7 +197,7 @@ public function __construct( /** * Visionline-specific metadata associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ - public AcsEntrance\VisionlineMetadata|null $visionline_metadata, + public AcsEntrance\VisionlineMetadata|null $visionline_metadata = null, /** * Warnings associated with the [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). */ @@ -232,19 +232,19 @@ public function __construct( /** * Actions the gadget exposes (for example, open). */ - public array $actions, + public array|null $actions = null, /** * ID of the Akiles gadget. */ - public string|null $gadget_id, + public string|null $gadget_id = null, /** * ID of the Akiles site the gadget belongs to. */ - public string|null $site_id, + public string|null $site_id = null, /** * Name of the Akiles site the gadget belongs to. */ - public string|null $site_name, + public string|null $site_name = null, ) {} } @@ -272,23 +272,23 @@ public function __construct( /** * Name of the door in the Vostio access system. */ - public string|null $door_name, + public string|null $door_name = null, /** * Number of the door in the Vostio access system. */ - public float|null $door_number, + public float|null $door_number = null, /** * Type of the door in the Vostio access system. */ - public string|null $door_type, + public string|null $door_type = null, /** * PMS ID of the door in the Vostio access system. */ - public string|null $pms_id, + public string|null $pms_id = null, /** * Indicates whether keys are allowed to set the door in stand open mode in the Vostio access system. */ - public bool|null $stand_open, + public bool|null $stand_open = null, ) {} } @@ -318,31 +318,31 @@ public function __construct( /** * Entry name for an Avigilon Alta system. */ - public string|null $entry_name, + public string|null $entry_name = null, /** * Total count of entry relays for an Avigilon Alta system. */ - public float|null $entry_relays_total_count, + public float|null $entry_relays_total_count = null, /** * Organization name for an Avigilon Alta system. */ - public string|null $org_name, + public string|null $org_name = null, /** * Site ID for an Avigilon Alta system. */ - public float|null $site_id, + public float|null $site_id = null, /** * Site name for an Avigilon Alta system. */ - public string|null $site_name, + public string|null $site_name = null, /** * Zone ID for an Avigilon Alta system. */ - public float|null $zone_id, + public float|null $zone_id = null, /** * Zone name for an Avigilon Alta system. */ - public string|null $zone_name, + public string|null $zone_name = null, ) {} } @@ -367,15 +367,15 @@ public function __construct( /** * ID of the access point in the Brivo access system. */ - public string|null $access_point_id, + public string|null $access_point_id = null, /** * ID of the site that the access point belongs to. */ - public float|null $site_id, + public float|null $site_id = null, /** * Name of the site that the access point belongs to. */ - public string|null $site_name, + public string|null $site_name = null, ) {} } @@ -399,7 +399,7 @@ public function __construct( /** * Name of the access point in the dormakaba Ambiance access system. */ - public string|null $access_point_name, + public string|null $access_point_name = null, ) {} } @@ -423,7 +423,7 @@ public function __construct( /** * Type of access point profile in the dormakaba Community access system. */ - public string|null $access_point_profile, + public string|null $access_point_profile = null, ) {} } @@ -481,15 +481,15 @@ public function __construct( /** * Display name of the entrance. */ - public string|null $common_area_name, + public string|null $common_area_name = null, /** * Display name of the entrance. */ - public string|null $common_area_number, + public string|null $common_area_number = null, /** * Room number of the entrance. */ - public string|null $room_number, + public string|null $room_number = null, ) {} } @@ -515,19 +515,19 @@ public function __construct( /** * Accessibility type in the Latch access system. */ - public string|null $accessibility_type, + public string|null $accessibility_type = null, /** * Name of the door in the Latch access system. */ - public string|null $door_name, + public string|null $door_name = null, /** * Type of the door in the Latch access system. */ - public string|null $door_type, + public string|null $door_type = null, /** * Indicates whether the entrance is connected. */ - public bool|null $is_connected, + public bool|null $is_connected = null, ) {} } @@ -557,35 +557,35 @@ public function __construct( /** * Battery level of the door access device. */ - public string|null $battery_level, + public string|null $battery_level = null, /** * Name of the door in the Salto KS access system. */ - public string|null $door_name, + public string|null $door_name = null, /** * Indicates whether an intrusion alarm is active on the door. */ - public bool|null $intrusion_alarm, + public bool|null $intrusion_alarm = null, /** * Indicates whether the door is left open. */ - public bool|null $left_open_alarm, + public bool|null $left_open_alarm = null, /** * Type of the lock in the Salto KS access system. */ - public string|null $lock_type, + public string|null $lock_type = null, /** * Locked state of the door in the Salto KS access system. */ - public string|null $locked_state, + public string|null $locked_state = null, /** * Indicates whether the door access device is online. */ - public bool|null $online, + public bool|null $online = null, /** * Indicates whether privacy mode is enabled for the lock. */ - public bool|null $privacy_mode, + public bool|null $privacy_mode = null, ) {} } @@ -613,27 +613,27 @@ public function __construct( /** * Indicates whether AuditOnKeys is enabled for the door in the Salto Space access system. */ - public bool|null $audit_on_keys, + public bool|null $audit_on_keys = null, /** * Description of the door in the Salto Space access system. */ - public string|null $door_description, + public string|null $door_description = null, /** * Door ID in the Salto Space access system. */ - public string|null $door_id, + public string|null $door_id = null, /** * Name of the door in the Salto Space access system. */ - public string|null $door_name, + public string|null $door_name = null, /** * Description of the room in the Salto Space access system. */ - public string|null $room_description, + public string|null $room_description = null, /** * Name of the room in the Salto Space access system. */ - public string|null $room_name, + public string|null $room_name = null, ) {} } @@ -661,15 +661,15 @@ public function __construct( /** * Category of the door in the Visionline access system. */ - public string|null $door_category, + public string|null $door_category = null, /** * Name of the door in the Visionline access system. */ - public string|null $door_name, + public string|null $door_name = null, /** * Profile for the door in the Visionline access system. */ - public array $profiles, + public array|null $profiles = null, ) {} } @@ -725,11 +725,11 @@ public function __construct( /** * ID of the gadget action. */ - public string|null $id, + public string|null $id = null, /** * Name of the gadget action. */ - public string|null $name, + public string|null $name = null, ) {} } } @@ -757,11 +757,11 @@ public function __construct( /** * Door profile ID in the Visionline access system. */ - public string|null $visionline_door_profile_id, + public string|null $visionline_door_profile_id = null, /** * Door profile type in the Visionline access system. */ - public string|null $visionline_door_profile_type, + public string|null $visionline_door_profile_type = null, ) {} } } diff --git a/src/Resources/AcsSystem.php b/src/Resources/AcsSystem.php index b00ec137..642d242d 100644 --- a/src/Resources/AcsSystem.php +++ b/src/Resources/AcsSystem.php @@ -58,7 +58,7 @@ public function __construct( /** * Number of access groups in the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ - public float|null $acs_access_group_count, + public float|null $acs_access_group_count = null, /** * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ @@ -66,7 +66,7 @@ public function __construct( /** * Number of users in the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ - public float|null $acs_user_count, + public float|null $acs_user_count = null, /** * ID of the connected account associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ @@ -84,7 +84,7 @@ public function __construct( /** * ID of the default credential manager `acs_system` for this [access control system](https://docs.seam.co/low-level-apis/access-systems). */ - public string|null $default_credential_manager_acs_system_id, + public string|null $default_credential_manager_acs_system_id = null, /** * Errors associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ @@ -92,11 +92,11 @@ public function __construct( /** * Brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. */ - public string|null $external_type, + public string|null $external_type = null, /** * Display name that corresponds to the brand-specific terminology for the [access control system](https://docs.seam.co/low-level-apis/access-systems) type. */ - public string|null $external_type_display_name, + public string|null $external_type_display_name = null, /** * Alternative text for the [access control system](https://docs.seam.co/low-level-apis/access-systems) image. */ @@ -120,15 +120,15 @@ public function __construct( /** * @deprecated Use `external_type`. */ - public string|null $system_type, + public string|null $system_type = null, /** * @deprecated Use `external_type_display_name`. */ - public string|null $system_type_display_name, + public string|null $system_type_display_name = null, /** * Visionline-specific metadata for the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ - public AcsSystem\VisionlineMetadata|null $visionline_metadata, + public AcsSystem\VisionlineMetadata|null $visionline_metadata = null, /** * Warnings associated with the [access control system](https://docs.seam.co/low-level-apis/access-systems). */ @@ -172,7 +172,7 @@ public function __construct( /** * Indicates whether the error is related to the [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ - public bool|null $is_bridge_error, + public bool|null $is_bridge_error = null, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ @@ -222,15 +222,15 @@ public function __construct( /** * IP address or hostname of the main Visionline server relative to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge) on the local network. */ - public string|null $lan_address, + public string|null $lan_address = null, /** * Keyset loaded into a reader. Mobile keys and reader administration tools securely authenticate only with readers programmed with a matching keyset. */ - public string|null $mobile_access_uuid, + public string|null $mobile_access_uuid = null, /** * Unique ID assigned by the ASSA ABLOY licensing team that identifies each hotel in your credential manager. */ - public string|null $system_id, + public string|null $system_id = null, ) {} } @@ -265,7 +265,7 @@ public function __construct( /** * @deprecated this field is deprecated. */ - public array|null $misconfigured_acs_entrance_ids, + public array|null $misconfigured_acs_entrance_ids = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/AcsUser.php b/src/Resources/AcsUser.php index e2bc4fbf..880d5331 100644 --- a/src/Resources/AcsUser.php +++ b/src/Resources/AcsUser.php @@ -70,7 +70,7 @@ public function __construct( /** * `starts_at` and `ends_at` timestamps for the [access system user's](https://docs.seam.co/low-level-apis/access-systems/user-management) access. */ - public AcsUser\AccessSchedule|null $access_schedule, + public AcsUser\AccessSchedule|null $access_schedule = null, /** * ID of the [access system](https://docs.seam.co/low-level-apis/access-systems) that contains the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ @@ -94,11 +94,11 @@ public function __construct( /** * @deprecated use email_address. */ - public string|null $email, + public string|null $email = null, /** * Email address of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public string|null $email_address, + public string|null $email_address = null, /** * Errors associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ @@ -106,19 +106,19 @@ public function __construct( /** * Brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. */ - public string|null $external_type, + public string|null $external_type = null, /** * Display name that corresponds to the brand-specific terminology for the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) type. */ - public string|null $external_type_display_name, + public string|null $external_type_display_name = null, /** * Full name of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public string|null $full_name, + public string|null $full_name = null, /** * ID of the HID access control system associated with the user. */ - public string|null $hid_acs_system_id, + public string|null $hid_acs_system_id = null, /** * Indicates whether Seam manages the access system user. */ @@ -126,39 +126,39 @@ public function __construct( /** * Indicates whether the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) is currently [suspended](https://docs.seam.co/low-level-apis/access-systems/user-management/suspending-and-unsuspending-users). */ - public bool|null $is_suspended, + public bool|null $is_suspended = null, /** * Pending mutations associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). Seam is in the process of pushing these mutations to the integrated access system. */ - public array $pending_mutations, + public array|null $pending_mutations = null, /** * Phone number of the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). */ - public string|null $phone_number, + public string|null $phone_number = null, /** * Salto KS-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public AcsUser\SaltoKsMetadata|null $salto_ks_metadata, + public AcsUser\SaltoKsMetadata|null $salto_ks_metadata = null, /** * Salto Space-specific metadata associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public AcsUser\SaltoSpaceMetadata|null $salto_space_metadata, + public AcsUser\SaltoSpaceMetadata|null $salto_space_metadata = null, /** * Email address of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public string|null $user_identity_email_address, + public string|null $user_identity_email_address = null, /** * Full name of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public string|null $user_identity_full_name, + public string|null $user_identity_full_name = null, /** * ID of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ - public string|null $user_identity_id, + public string|null $user_identity_id = null, /** * Phone number of the user identity associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management) in E.164 format (for example, `+15555550100`). */ - public string|null $user_identity_phone_number, + public string|null $user_identity_phone_number = null, /** * Warnings associated with the [access system user](https://docs.seam.co/low-level-apis/access-systems/user-management). */ @@ -274,7 +274,7 @@ public function __construct( /** * Optional: When the user creation is scheduled to occur. */ - public string|null $scheduled_at, + public string|null $scheduled_at = null, public PendingMutations\To|null $to, /** * Whether the user is scheduled to be added to or removed from the access group. @@ -300,7 +300,7 @@ public function __construct( /** * Indicates whether the user holds an active subscription slot on the Salto KS site. Only subscribed users can unlock doors and count against the site's user-subscription limit. A user may not be subscribed because their access schedule has not started or has ended, the site has reached its subscription limit, or they were manually unsubscribed. This is distinct from `is_suspended`, which reflects whether the user has been explicitly blocked. */ - public bool|null $is_subscribed, + public bool|null $is_subscribed = null, ) {} } @@ -324,11 +324,11 @@ public function __construct( /** * Indicates whether AuditOpenings is enabled for the user in the Salto Space access system. */ - public bool|null $audit_openings, + public bool|null $audit_openings = null, /** * User ID in the Salto Space access system. */ - public string|null $user_id, + public string|null $user_id = null, ) {} } @@ -395,7 +395,7 @@ public function __construct( /** * Email address of the access system user. */ - public string|null $email_address, + public string|null $email_address = null, /** * Starting time for the access schedule. */ @@ -403,12 +403,12 @@ public function __construct( /** * Full name of the access system user. */ - public string|null $full_name, + public string|null $full_name = null, public bool|null $is_suspended, /** * Phone number of the access system user. */ - public string|null $phone_number, + public string|null $phone_number = null, /** * Starting time for the access schedule. */ @@ -447,7 +447,7 @@ public function __construct( /** * Email address of the access system user. */ - public string|null $email_address, + public string|null $email_address = null, /** * Starting time for the access schedule. */ @@ -455,12 +455,12 @@ public function __construct( /** * Full name of the access system user. */ - public string|null $full_name, + public string|null $full_name = null, public bool|null $is_suspended, /** * Phone number of the access system user. */ - public string|null $phone_number, + public string|null $phone_number = null, /** * Starting time for the access schedule. */ diff --git a/src/Resources/ActionAttempt.php b/src/Resources/ActionAttempt.php index 04af6d8c..009c6529 100644 --- a/src/Resources/ActionAttempt.php +++ b/src/Resources/ActionAttempt.php @@ -183,7 +183,7 @@ public function __construct( /** * ID of the credential pool to which the credential belongs. */ - public string|null $acs_credential_pool_id, + public string|null $acs_credential_pool_id = null, /** * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -191,24 +191,24 @@ public function __construct( /** * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ - public string|null $acs_user_id, + public string|null $acs_user_id = null, /** * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public Result\AkilesMetadata|null $akiles_metadata, + public Result\AkilesMetadata|null $akiles_metadata = null, /** * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public Result\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + public Result\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $card_number, + public string|null $card_number = null, /** * Token of the client session associated with the access method. */ - public string|null $client_session_token, - public string|null $code, + public string|null $client_session_token = null, + public string|null $code = null, /** * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ @@ -217,38 +217,38 @@ public function __construct( /** * ID of the customization profile associated with the access method. */ - public string|null $customization_profile_id, + public string|null $customization_profile_id = null, public string|null $display_name, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. */ - public string|null $ends_at, + public string|null $ends_at = null, public array $errors, /** * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. */ - public string|null $external_type, + public string|null $external_type = null, /** * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. */ - public string|null $external_type_display_name, + public string|null $external_type_display_name = null, /** * URL of the Instant Key for mobile key access methods. */ - public string|null $instant_key_url, + public string|null $instant_key_url = null, /** * Indicates whether an existing card credential must be assigned to this access method before it can be issued. Only applies to card-mode access methods on systems that support credential assignment. */ - public bool|null $is_assignment_required, + public bool|null $is_assignment_required = null, /** * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. */ - public bool|null $is_encoding_required, - public bool|null $is_issued, + public bool|null $is_encoding_required = null, + public bool|null $is_issued = null, /** * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. */ - public bool|null $is_latest_desired_state_synced_with_provider, + public bool|null $is_latest_desired_state_synced_with_provider = null, /** * Indicates whether Seam manages the credential. */ @@ -256,24 +256,24 @@ public function __construct( /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). */ - public bool|null $is_multi_phone_sync_credential, + public bool|null $is_multi_phone_sync_credential = null, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. */ - public bool|null $is_one_time_use, + public bool|null $is_one_time_use = null, /** * Indicates whether the access method is ready for card assignment. This is true when the access method is in card mode, has not yet been issued, and the system supports credential assignment. */ - public bool|null $is_ready_for_assignment, + public bool|null $is_ready_for_assignment = null, /** * Indicates whether the access method is ready to be encoded. This is true when the credential has been created and the card has not yet been issued. */ - public bool|null $is_ready_for_encoding, - public string|null $issued_at, + public bool|null $is_ready_for_encoding = null, + public string|null $issued_at = null, /** * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. */ - public string|null $latest_desired_state_synced_with_provider_at, + public string|null $latest_desired_state_synced_with_provider_at = null, /** * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. */ @@ -282,7 +282,7 @@ public function __construct( /** * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $parent_acs_credential_id, + public string|null $parent_acs_credential_id = null, /** * Pending mutations for the [access method](https://docs.seam.co/use-cases/granting-access/creating-an-access-grant). Indicates operations that are in progress. */ @@ -290,17 +290,17 @@ public function __construct( /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. */ - public string|null $starts_at, + public string|null $starts_at = null, /** * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ - public string|null $user_identity_id, + public string|null $user_identity_id = null, /** * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public Result\VisionlineMetadata|null $visionline_metadata, + public Result\VisionlineMetadata|null $visionline_metadata = null, public array $warnings, - public bool|null $was_confirmed_by_device, + public bool|null $was_confirmed_by_device = null, public string|null $workspace_id, ) {} } @@ -356,7 +356,7 @@ public function __construct( /** * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredentialOnEncoder\VisionlineMetadata|null $visionline_metadata, + public AcsCredentialOnEncoder\VisionlineMetadata|null $visionline_metadata = null, ) {} } @@ -440,7 +440,7 @@ public function __construct( /** * ID of the credential pool to which the credential belongs. */ - public string|null $acs_credential_pool_id, + public string|null $acs_credential_pool_id = null, /** * ID of the [access control system](https://docs.seam.co/low-level-apis/access-systems) that contains the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -448,23 +448,23 @@ public function __construct( /** * ID of the [ACS user](https://docs.seam.co/low-level-apis/access-systems/user-management) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ - public string|null $acs_user_id, + public string|null $acs_user_id = null, /** * Akiles-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredentialOnSeam\AkilesMetadata|null $akiles_metadata, + public AcsCredentialOnSeam\AkilesMetadata|null $akiles_metadata = null, /** * Vostio-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredentialOnSeam\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + public AcsCredentialOnSeam\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Number of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $card_number, + public string|null $card_number = null, /** * Access (PIN) code for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $code, + public string|null $code = null, /** * ID of the [connected account](https://docs.seam.co/core-concepts/connected-accounts) to which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ @@ -480,7 +480,7 @@ public function __construct( /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity ends, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Must be a time in the future and after `starts_at`. */ - public string|null $ends_at, + public string|null $ends_at = null, /** * Errors associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -488,52 +488,52 @@ public function __construct( /** * Brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. Supported values: `pti_card`, `brivo_credential`, `hid_credential`, `visionline_card`. */ - public string|null $external_type, + public string|null $external_type = null, /** * Display name that corresponds to the brand-specific terminology for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) type. */ - public string|null $external_type_display_name, + public string|null $external_type_display_name = null, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been encoded onto a card. */ - public bool|null $is_issued, + public bool|null $is_issued = null, /** * Indicates whether the latest state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) has been synced from Seam to the provider. */ - public bool|null $is_latest_desired_state_synced_with_provider, + public bool|null $is_latest_desired_state_synced_with_provider = null, public bool|null $is_managed, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is a [multi-phone sync credential](https://docs.seam.co/capability-guides/mobile-access/issuing-mobile-credentials-from-an-access-control-system#what-are-multi-phone-sync-credentials). */ - public bool|null $is_multi_phone_sync_credential, + public bool|null $is_multi_phone_sync_credential = null, /** * Indicates whether the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) can only be used once. If `true`, the code becomes invalid after the first use. */ - public bool|null $is_one_time_use, + public bool|null $is_one_time_use = null, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was encoded onto a card. */ - public string|null $issued_at, + public string|null $issued_at = null, /** * Date and time at which the state of the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) was most recently synced from Seam to the provider. */ - public string|null $latest_desired_state_synced_with_provider_at, + public string|null $latest_desired_state_synced_with_provider_at = null, /** * ID of the parent [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $parent_acs_credential_id, + public string|null $parent_acs_credential_id = null, /** * Date and time at which the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) validity starts, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. */ - public string|null $starts_at, + public string|null $starts_at = null, /** * ID of the [user identity](https://docs.seam.co/api/user_identities) to whom the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) belongs. */ - public string|null $user_identity_id, + public string|null $user_identity_id = null, /** * Visionline-specific metadata for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public AcsCredentialOnSeam\VisionlineMetadata|null $visionline_metadata, + public AcsCredentialOnSeam\VisionlineMetadata|null $visionline_metadata = null, /** * Warnings associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ @@ -562,7 +562,7 @@ public function __construct( /** * ID of the Akiles member PIN. */ - public string|null $member_pin_id, + public string|null $member_pin_id = null, ) {} } @@ -592,27 +592,27 @@ public function __construct( /** * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. */ - public bool|null $auto_join, + public bool|null $auto_join = null, /** * Names of the doors to which to grant access in the Vostio access system. */ - public array|null $door_names, + public array|null $door_names = null, /** * Endpoint ID in the Vostio access system. */ - public string|null $endpoint_id, + public string|null $endpoint_id = null, /** * Key ID in the Vostio access system. */ - public string|null $key_id, + public string|null $key_id = null, /** * Key issuing request ID in the Vostio access system. */ - public string|null $key_issuing_request_id, + public string|null $key_issuing_request_id = null, /** * IDs of the guest entrances to override in the Vostio access system. */ - public array|null $override_guest_acs_entrance_ids, + public array|null $override_guest_acs_entrance_ids = null, ) {} } @@ -720,35 +720,35 @@ public function __construct( /** * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. */ - public bool|null $auto_join, + public bool|null $auto_join = null, /** * Card function type in the Visionline access system. */ - public string|null $card_function_type, + public string|null $card_function_type = null, /** * ID of the card in the Visionline access system. */ - public string|null $card_id, + public string|null $card_id = null, /** * Common entrance IDs in the Visionline access system. */ - public array|null $common_acs_entrance_ids, + public array|null $common_acs_entrance_ids = null, /** * ID of the credential in the Visionline access system. */ - public string|null $credential_id, + public string|null $credential_id = null, /** * Guest entrance IDs in the Visionline access system. */ - public array|null $guest_acs_entrance_ids, + public array|null $guest_acs_entrance_ids = null, /** * Indicates whether the credential is valid. */ - public bool|null $is_valid, + public bool|null $is_valid = null, /** * IDs of the credentials to which you want to join. */ - public array|null $joiner_acs_credential_ids, + public array|null $joiner_acs_credential_ids = null, ) {} } @@ -783,15 +783,15 @@ public function __construct( /** * The PIN code that was assigned instead. */ - public string|null $new_code, + public string|null $new_code = null, /** * ID of the original access method from which this backup access method was split, if applicable. */ - public string|null $original_access_method_id, + public string|null $original_access_method_id = null, /** * The originally requested PIN code that could not be used. */ - public string|null $original_code, + public string|null $original_code = null, public string|null $warning_code, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. @@ -832,51 +832,51 @@ public function __construct( /** * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is cancelled. */ - public bool|null $cancelled, + public bool|null $cancelled = null, /** * Format of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $card_format, + public string|null $card_format = null, /** * Holder of the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $card_holder, + public string|null $card_holder = null, /** * Card ID for the Visionline card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public string|null $card_id, + public string|null $card_id = null, /** * IDs of the common [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public array|null $common_acs_entrance_ids, + public array|null $common_acs_entrance_ids = null, /** * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is discarded. */ - public bool|null $discarded, + public bool|null $discarded = null, /** * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is expired. */ - public bool|null $expired, + public bool|null $expired = null, /** * IDs of the guest [entrances](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) for the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public array|null $guest_acs_entrance_ids, + public array|null $guest_acs_entrance_ids = null, /** * Number of issued cards associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials). */ - public float|null $number_of_issued_cards, + public float|null $number_of_issued_cards = null, /** * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overridden. */ - public bool|null $overridden, + public bool|null $overridden = null, /** * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is overwritten. */ - public bool|null $overwritten, + public bool|null $overwritten = null, /** * Indicates whether the card associated with the [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) is pending auto-update. */ - public bool|null $pending_auto_update, + public bool|null $pending_auto_update = null, ) {} } } @@ -899,7 +899,7 @@ public function __construct( /** * ID of the Akiles member PIN. */ - public string|null $member_pin_id, + public string|null $member_pin_id = null, ) {} } @@ -929,27 +929,27 @@ public function __construct( /** * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. */ - public bool|null $auto_join, + public bool|null $auto_join = null, /** * Names of the doors to which to grant access in the Vostio access system. */ - public array|null $door_names, + public array|null $door_names = null, /** * Endpoint ID in the Vostio access system. */ - public string|null $endpoint_id, + public string|null $endpoint_id = null, /** * Key ID in the Vostio access system. */ - public string|null $key_id, + public string|null $key_id = null, /** * Key issuing request ID in the Vostio access system. */ - public string|null $key_issuing_request_id, + public string|null $key_issuing_request_id = null, /** * IDs of the guest entrances to override in the Vostio access system. */ - public array|null $override_guest_acs_entrance_ids, + public array|null $override_guest_acs_entrance_ids = null, ) {} } @@ -1007,35 +1007,35 @@ public function __construct( /** * Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. */ - public bool|null $auto_join, + public bool|null $auto_join = null, /** * Card function type in the Visionline access system. */ - public string|null $card_function_type, + public string|null $card_function_type = null, /** * ID of the card in the Visionline access system. */ - public string|null $card_id, + public string|null $card_id = null, /** * Common entrance IDs in the Visionline access system. */ - public array|null $common_acs_entrance_ids, + public array|null $common_acs_entrance_ids = null, /** * ID of the credential in the Visionline access system. */ - public string|null $credential_id, + public string|null $credential_id = null, /** * Guest entrance IDs in the Visionline access system. */ - public array|null $guest_acs_entrance_ids, + public array|null $guest_acs_entrance_ids = null, /** * Indicates whether the credential is valid. */ - public bool|null $is_valid, + public bool|null $is_valid = null, /** * IDs of the credentials to which you want to join. */ - public array|null $joiner_acs_credential_ids, + public array|null $joiner_acs_credential_ids = null, ) {} } @@ -1070,11 +1070,11 @@ public function __construct( /** * The PIN code that was assigned instead. */ - public string|null $new_code, + public string|null $new_code = null, /** * The originally requested PIN code that could not be used. */ - public string|null $original_code, + public string|null $original_code = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/Batch.php b/src/Resources/Batch.php index 29a7b2da..7f5c92a0 100644 --- a/src/Resources/Batch.php +++ b/src/Resources/Batch.php @@ -52,15 +52,15 @@ public function __construct( * * For granting a person access to a space, [Access Grants](https://docs.seam.co/use-cases/granting-access) are the default and recommended approach and work across both standalone smart locks and access systems. Use the lower-level Access Codes API directly only when you specifically need to manage individual PIN codes. */ - public mixed $access_codes, + public mixed $access_codes = null, /** * Represents an Access Grant. Access Grants enable you to grant a user identity access to spaces, entrances, and devices through one or more access methods, such as mobile keys, plastic cards, and PIN codes. You can create an Access Grant for an existing user identity, or you can create a new user identity *while* creating the new Access Grant. */ - public mixed $access_grants, + public mixed $access_grants = null, /** * Represents an access method for an Access Grant. Access methods describe the modes of access, such as PIN codes, plastic cards, and mobile keys. For a mobile key, the access method also stores the URL for the associated Instant Key. */ - public mixed $access_methods, + public mixed $access_methods = null, /** * Group that defines the entrances to which a set of users has access and, in some cases, the access schedule for these entrances and users. * @@ -68,7 +68,7 @@ public function __construct( * * To learn whether your access control system supports access groups, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). */ - public mixed $acs_access_groups, + public mixed $acs_access_groups = null, /** * Means by which an [access control system user](https://docs.seam.co/low-level-apis/access-systems/user-management) gains access at an [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details). The `acs_credential` object represents a [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) that provides an ACS user access within an [access control system](https://docs.seam.co/low-level-apis/access-systems). * @@ -78,7 +78,7 @@ public function __construct( * * For granting a person access to a space, [Access Grants](https://docs.seam.co/use-cases/granting-access) are the default and recommended approach. Use the lower-level ACS credential API directly only when you specifically need to manage individual credentials. */ - public mixed $acs_credentials, + public mixed $acs_credentials = null, /** * Represents a hardware device that encodes [credential](https://docs.seam.co/low-level-apis/access-systems/managing-credentials) data onto physical cards within an [access control system](https://docs.seam.co/low-level-apis/access-systems). * @@ -95,13 +95,13 @@ public function __construct( * * To verify if your access control system requires a card encoder, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). */ - public mixed $acs_encoders, + public mixed $acs_encoders = null, /** * Represents an [entrance](https://docs.seam.co/low-level-apis/access-systems/retrieving-entrance-details) within an [access control system](https://docs.seam.co/low-level-apis/access-systems). * * In an access control system, an entrance is a secured door, gate, zone, or other method of entry. You can list details for all the `acs_entrance` resources in your workspace or get these details for a specific `acs_entrance`. You can also list all entrances associated with a specific credential, and you can list all credentials associated with a specific entrance. */ - public mixed $acs_entrances, + public mixed $acs_entrances = null, /** * Represents an [access control system](https://docs.seam.co/low-level-apis/access-systems). * @@ -109,7 +109,7 @@ public function __construct( * * For details about the resources associated with an access control system, see the [access control systems namespace](https://docs.seam.co/api/acs). */ - public mixed $acs_systems, + public mixed $acs_systems = null, /** * Represents a [user](https://docs.seam.co/low-level-apis/access-systems/user-management) in an [access system](https://docs.seam.co/low-level-apis/access-systems). * @@ -117,7 +117,7 @@ public function __construct( * * For details about how to configure users in your access system, see the corresponding [system integration guide](https://docs.seam.co/device-and-system-integration-guides#access-control-systems). */ - public mixed $acs_users, + public mixed $acs_users = null, /** * Represents an action attempt that enables you to keep track of the progress of your action that affects a physical device or system.actions against a device. Action attempts are useful because the physical world is intrinsically asynchronous. * @@ -125,7 +125,7 @@ public function __construct( * * See also [Action Attempts](https://docs.seam.co/core-concepts/action-attempts). */ - public mixed $action_attempts, + public mixed $action_attempts = null, /** * Represents a [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). If you want to restrict your users' access to their own devices, use client sessions. * @@ -137,7 +137,7 @@ public function __construct( * * See also [Get Started with React](https://docs.seam.co/ui-components/overview/getting-started-with-seam-components/get-started-with-react-components-and-client-session-tokens). */ - public mixed $client_sessions, + public mixed $client_sessions = null, /** * Represents a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews). * @@ -151,43 +151,43 @@ public function __construct( * * To list all providers within a category, use `/devices/list_device_providers` with the desired `provider_category` filter. To list all provider keys, use `/devices/list_device_providers` with no filters. */ - public mixed $connect_webviews, + public mixed $connect_webviews = null, /** * Represents a [connected account](https://docs.seam.co/core-concepts/connected-accounts). A connected account is an external third-party account to which your user has authorized Seam to get access, for example, an August account with a list of door locks. */ - public mixed $connected_accounts, + public mixed $connected_accounts = null, /** * Represents a [device](https://docs.seam.co/core-concepts/devices) that has been connected to Seam. */ - public mixed $devices, + public mixed $devices = null, /** * Represents an event. Events let you know when something interesting happens in your workspace. For example, when a lock is unlocked, Seam creates a `lock.unlocked` event. When a device's battery level is low, Seam creates a `device.battery_low` event. * * As with other API resources, you can retrieve an individual event or a list of events. Seam also provides a separate webhook system for sending the event objects directly to an endpoint on your sever. Manage webhooks through [Seam Console](https://console.seam.co). You can also use the webhooks sandbox in Seam Console to see the different payloads for each event and test them against your own endpoints. */ - public mixed $events, + public mixed $events = null, /** * Represents a Seam Instant Key. For issuing Bluetooth mobile keys, Instant Keys are the fastest way to share access. With a single API call, you can create a mobile key and send it through text or email or embed it in your own app. * * There’s no app to install, nor account to create. Your user just taps a link and gets a lightweight, native-feeling experience using iOS App Clip or Instant Apps on Android. Further, Instant Keys work offline, so even in areas with poor cellular or Wi-Fi, like elevator banks or concrete-walled hallways, the Instant Keys still work. */ - public mixed $instant_keys, + public mixed $instant_keys = null, /** * Represents a [noise threshold](https://docs.seam.co/capability-guides/noise-sensors/configure-noise-threshold-settings) for a [noise sensor](https://docs.seam.co/capability-guides/noise-sensors). Thresholds represent the limits of noise tolerated at a property, which can be customized for each hour of the day. Each device has its own default thresholds, but you can use the Seam API to modify them. */ - public mixed $noise_thresholds, + public mixed $noise_thresholds = null, /** * Represents a space that is a logical grouping of devices and entrances. You can assign access to an entire space, thereby making granting access more efficient. */ - public mixed $spaces, + public mixed $spaces = null, /** * Represents a thermostat daily program, consisting of a set of periods, each of which has a starting time and the key that identifies the climate preset to apply at the starting time. */ - public mixed $thermostat_daily_programs, + public mixed $thermostat_daily_programs = null, /** * Represents a [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) that activates a configured [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) on a [thermostat](https://docs.seam.co/capability-guides/thermostats) at a specified starting time and deactivates the climate preset at a specified ending time. */ - public mixed $thermostat_schedules, + public mixed $thermostat_schedules = null, /** * Represents an [unmanaged smart lock access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes). * @@ -201,19 +201,19 @@ public function __construct( * * - [Kwikset](https://docs.seam.co/device-and-system-integration-guides/kwikset-locks) */ - public mixed $unmanaged_access_codes, + public mixed $unmanaged_access_codes = null, /** * Represents an [unmanaged device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices). An unmanaged device has a limited set of visible properties and a subset of supported events. You cannot control an unmanaged device. Any [access codes](https://docs.seam.co/low-level-apis/smart-locks/access-codes/migrating-existing-access-codes) on an unmanaged device are unmanaged. To control an unmanaged device with Seam, [convert it to a managed device](https://docs.seam.co/core-concepts/devices/managed-and-unmanaged-devices#convert-an-unmanaged-device-to-managed). */ - public mixed $unmanaged_devices, + public mixed $unmanaged_devices = null, /** * Represents a [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with an application user account. */ - public mixed $user_identities, + public mixed $user_identities = null, /** * Represents a Seam [workspace](https://docs.seam.co/core-concepts/workspaces). A workspace is a top-level entity that encompasses all other resources below it, such as devices, connected accounts, and Connect Webviews. Seam provides two types of workspaces. A [sandbox workspace](https://docs.seam.co/core-concepts/workspaces#sandbox-workspaces) is a special type of workspace designed for testing code. Sandbox workspaces offer test device accounts and virtual devices that you can connect and control. This ability to work with virtual devices is quite handy because it removes the need to own physical devices from multiple brands. To connect real devices and systems to Seam, use a [production workspace](https://docs.seam.co/core-concepts/workspaces#production-workspaces). */ - public mixed $workspaces, + public mixed $workspaces = null, ) {} } } diff --git a/src/Resources/ClientSession.php b/src/Resources/ClientSession.php index 53ec9f7e..88083915 100644 --- a/src/Resources/ClientSession.php +++ b/src/Resources/ClientSession.php @@ -55,7 +55,7 @@ public function __construct( /** * Customer key associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). */ - public string|null $customer_key, + public string|null $customer_key = null, /** * Number of devices associated with the [client session](https://docs.seam.co/core-concepts/authentication/client-session-tokens). */ @@ -75,7 +75,7 @@ public function __construct( /** * ID of the [user identity](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. */ - public string|null $user_identity_id, + public string|null $user_identity_id = null, /** * IDs of the [user identities](https://docs.seam.co/capability-guides/mobile-access/managing-mobile-app-user-accounts-with-user-identities#what-is-a-user-identity) associated with the client session. * diff --git a/src/Resources/ConnectWebview.php b/src/Resources/ConnectWebview.php index 224eb740..c857baa0 100644 --- a/src/Resources/ConnectWebview.php +++ b/src/Resources/ConnectWebview.php @@ -95,7 +95,7 @@ public function __construct( /** * The customer key associated with this webview, if any. */ - public string|null $customer_key, + public string|null $customer_key = null, /** * Device selection mode of the Connect Webview. Supported values: `none`, `single`, `multiple`. */ diff --git a/src/Resources/ConnectedAccount.php b/src/Resources/ConnectedAccount.php index 3f6e237b..0bd1b645 100644 --- a/src/Resources/ConnectedAccount.php +++ b/src/Resources/ConnectedAccount.php @@ -53,7 +53,7 @@ public function __construct( /** * Type of connected account. */ - public string|null $account_type, + public string|null $account_type = null, /** * Display name for the connected account type. */ @@ -69,7 +69,7 @@ public function __construct( /** * Date and time at which the connected account was created. */ - public string|null $created_at, + public string|null $created_at = null, /** * Set of key:value pairs. Adding custom metadata to a resource, such as a [Connect Webview](https://docs.seam.co/core-concepts/connect-webviews/attaching-custom-data-to-the-connect-webview), [connected account](https://docs.seam.co/core-concepts/connected-accounts/adding-custom-metadata-to-a-connected-account), or [device](https://docs.seam.co/core-concepts/devices/adding-custom-metadata-to-a-device), enables you to store custom information, like customer details or internal IDs from your application. */ @@ -77,15 +77,15 @@ public function __construct( /** * Your unique key for the customer associated with this connected account. */ - public string|null $customer_key, + public string|null $customer_key = null, /** * Default reservation check-in time for this connected account, as `HH:mm` (24-hour). Sourced from the connector configuration — set during the connect_webview for providers like Lodgify whose API does not expose check-in times. */ - public string|null $default_checkin_time, + public string|null $default_checkin_time = null, /** * Default reservation check-out time for this connected account, as `HH:mm` (24-hour). Sourced from the connector configuration. */ - public string|null $default_checkout_time, + public string|null $default_checkout_time = null, /** * Display name for the connected account. */ @@ -97,25 +97,25 @@ public function __construct( /** * For iCal connected accounts, the platform that produced the feed (for example, `airbnb`, `vrbo`, or `booking`), or `unknown` when it could not be determined. Intended for rendering the source platform's logo. */ - public string|null $ical_feed_origin, + public string|null $ical_feed_origin = null, /** * For iCal connected accounts, the feed URL for the connection. Sourced from the connector configuration. */ - public string|null $ical_url, + public string|null $ical_url = null, /** * Logo URL for the connected account provider. */ - public string|null $image_url, + public string|null $image_url = null, /** * IANA time zone (e.g. America/Los_Angeles) for this connected account. Sourced from the connector configuration. */ - public string|null $time_zone, + public string|null $time_zone = null, /** * User identifier associated with the connected account. * * @deprecated Use `display_name` instead. */ - public ConnectedAccount\UserIdentifier|null $user_identifier, + public ConnectedAccount\UserIdentifier|null $user_identifier = null, /** * Warnings associated with the connected account. */ @@ -162,11 +162,11 @@ public function __construct( /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ - public bool|null $is_bridge_error, + public bool|null $is_bridge_error = null, /** * Indicates whether the error is related specifically to the connected account. */ - public bool|null $is_connected_account_error, + public bool|null $is_connected_account_error = null, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ @@ -203,23 +203,23 @@ public function __construct( /** * API URL for the user identifier associated with the connected account. */ - public string|null $api_url, + public string|null $api_url = null, /** * Email address of the user identifier associated with the connected account. */ - public string|null $email, + public string|null $email = null, /** * Indicates whether the user identifier associated with the connected account is exclusive. */ - public bool|null $exclusive, + public bool|null $exclusive = null, /** * Phone number of the user identifier associated with the connected account. */ - public string|null $phone, + public string|null $phone = null, /** * Username of the user identifier associated with the connected account. */ - public string|null $username, + public string|null $username = null, ) {} } @@ -289,7 +289,7 @@ public function __construct( /** * Salto sites associated with the connected account that has an error. */ - public array $sites, + public array|null $sites = null, ) {} } } @@ -319,19 +319,19 @@ public function __construct( /** * ID of a Salto site associated with the connected account that has an error. */ - public string|null $site_id, + public string|null $site_id = null, /** * Name of a Salto site associated with the connected account that has an error. */ - public string|null $site_name, + public string|null $site_name = null, /** * Subscription limit of site users for a Salto site associated with the connected account that has an error. */ - public int|null $site_user_subscription_limit, + public int|null $site_user_subscription_limit = null, /** * Count of subscribed site users for a Salto site associated with the connected account that has an error. */ - public int|null $subscribed_site_user_count, + public int|null $subscribed_site_user_count = null, ) {} } } @@ -359,7 +359,7 @@ public function __construct( /** * Salto sites associated with the connected account that has a warning. */ - public array $sites, + public array|null $sites = null, ) {} } } @@ -389,19 +389,19 @@ public function __construct( /** * ID of a Salto site associated with the connected account that has a warning. */ - public string|null $site_id, + public string|null $site_id = null, /** * Name of a Salto site associated with the connected account that has a warning. */ - public string|null $site_name, + public string|null $site_name = null, /** * Subscription limit of site users for a Salto site associated with the connected account that has a warning. */ - public int|null $site_user_subscription_limit, + public int|null $site_user_subscription_limit = null, /** * Count of subscribed site users for a Salto site associated with the connected account that has a warning. */ - public int|null $subscribed_site_user_count, + public int|null $subscribed_site_user_count = null, ) {} } } diff --git a/src/Resources/Device.php b/src/Resources/Device.php index e7d7964e..4e960104 100644 --- a/src/Resources/Device.php +++ b/src/Resources/Device.php @@ -82,83 +82,83 @@ public function __construct( /** * Indicates whether the lock supports configuring automatic locking. */ - public bool|null $can_configure_auto_lock, + public bool|null $can_configure_auto_lock = null, /** * Indicates whether the thermostat supports cooling. */ - public bool|null $can_hvac_cool, + public bool|null $can_hvac_cool = null, /** * Indicates whether the thermostat supports heating. */ - public bool|null $can_hvac_heat, + public bool|null $can_hvac_heat = null, /** * Indicates whether the thermostat supports simultaneous heating and cooling. */ - public bool|null $can_hvac_heat_cool, + public bool|null $can_hvac_heat_cool = null, /** * Indicates whether the device supports programming offline access codes. */ - public bool|null $can_program_offline_access_codes, + public bool|null $can_program_offline_access_codes = null, /** * Indicates whether the device supports programming online access codes. */ - public bool|null $can_program_online_access_codes, + public bool|null $can_program_online_access_codes = null, /** * Indicates whether the thermostat supports different climate programs for each day of the week. */ - public bool|null $can_program_thermostat_programs_as_different_each_day, + public bool|null $can_program_thermostat_programs_as_different_each_day = null, /** * Indicates whether the thermostat supports a single climate program applied to every day. */ - public bool|null $can_program_thermostat_programs_as_same_each_day, + public bool|null $can_program_thermostat_programs_as_same_each_day = null, /** * Indicates whether the thermostat supports weekday/weekend climate programs. */ - public bool|null $can_program_thermostat_programs_as_weekday_weekend, + public bool|null $can_program_thermostat_programs_as_weekday_weekend = null, /** * Indicates whether the device supports remote locking. */ - public bool|null $can_remotely_lock, + public bool|null $can_remotely_lock = null, /** * Indicates whether the device supports remote unlocking. */ - public bool|null $can_remotely_unlock, + public bool|null $can_remotely_unlock = null, /** * Indicates whether the thermostat supports running climate programs. */ - public bool|null $can_run_thermostat_programs, + public bool|null $can_run_thermostat_programs = null, /** * Indicates whether the device supports simulating connection in a sandbox. */ - public bool|null $can_simulate_connection, + public bool|null $can_simulate_connection = null, /** * Indicates whether the device supports simulating disconnection in a sandbox. */ - public bool|null $can_simulate_disconnection, + public bool|null $can_simulate_disconnection = null, /** * Indicates whether the hub supports simulating connection in a sandbox. */ - public bool|null $can_simulate_hub_connection, + public bool|null $can_simulate_hub_connection = null, /** * Indicates whether the hub supports simulating disconnection in a sandbox. */ - public bool|null $can_simulate_hub_disconnection, + public bool|null $can_simulate_hub_disconnection = null, /** * Indicates whether the device supports simulating a paid subscription in a sandbox. */ - public bool|null $can_simulate_paid_subscription, + public bool|null $can_simulate_paid_subscription = null, /** * Indicates whether the device supports simulating removal in a sandbox. */ - public bool|null $can_simulate_removal, + public bool|null $can_simulate_removal = null, /** * Indicates whether the thermostat can be turned off. */ - public bool|null $can_turn_off_hvac, + public bool|null $can_turn_off_hvac = null, /** * Indicates whether the lock supports unlocking with an access code. */ - public bool|null $can_unlock_with_code, + public bool|null $can_unlock_with_code = null, /** * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). */ @@ -182,11 +182,11 @@ public function __construct( /** * Manufacturer of the device. Represents the hardware brand, which may differ from the provider. */ - public Device\DeviceManufacturer|null $device_manufacturer, + public Device\DeviceManufacturer|null $device_manufacturer = null, /** * Provider of the device. Represents the third-party service through which the device is controlled. */ - public Device\DeviceProvider|null $device_provider, + public Device\DeviceProvider|null $device_provider = null, /** * Type of the device. */ @@ -206,11 +206,11 @@ public function __construct( /** * Location information for the device. */ - public Device\Location|null $location, + public Device\Location|null $location = null, /** * Optional nickname to describe the device, settable through Seam. */ - public string|null $nickname, + public string|null $nickname = null, /** * Properties of the device. */ @@ -257,7 +257,7 @@ public function __construct( /** * Image URL for the manufacturer logo. */ - public string|null $image_url, + public string|null $image_url = null, /** * Manufacturer identifier, such as `august`, `yale`, `salto`, and so on. */ @@ -295,7 +295,7 @@ public function __construct( /** * Image URL for the device provider. */ - public string|null $image_url, + public string|null $image_url = null, /** * Provider category. Indicates the third-party provider type, such as `stable`, for stable integrations, or `internal`, for internal integrations. */ @@ -336,7 +336,7 @@ public function __construct( /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ - public bool|null $is_bridge_error, + public bool|null $is_bridge_error = null, public bool|null $is_connected_account_error, public bool|null $is_device_error, /** @@ -368,21 +368,21 @@ public function __construct( /** * Name of the device location. */ - public string|null $location_name, + public string|null $location_name = null, /** * Name of the room within the device location, when the provider reports one. */ - public string|null $room_name, + public string|null $room_name = null, /** * Time zone of the device location. */ - public string|null $time_zone, + public string|null $time_zone = null, /** * Time zone of the device location. * * @deprecated Use `time_zone` instead. */ - public string|null $timezone, + public string|null $timezone = null, ) {} } @@ -729,21 +729,21 @@ public function __construct( /** * Accessory keypad properties and state. */ - public Properties\AccessoryKeypad|null $accessory_keypad, + public Properties\AccessoryKeypad|null $accessory_keypad = null, /** * Active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). * * @deprecated Use `active_thermostat_schedule_id` with `/thermostats/schedules/get` instead. */ - public Properties\ActiveThermostatSchedule|null $active_thermostat_schedule, + public Properties\ActiveThermostatSchedule|null $active_thermostat_schedule = null, /** * ID of the active [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). */ - public string|null $active_thermostat_schedule_id, + public string|null $active_thermostat_schedule_id = null, /** * Metadata for an Akiles device. */ - public Properties\AkilesMetadata|null $akiles_metadata, + public Properties\AkilesMetadata|null $akiles_metadata = null, /** * Appearance-related properties, as reported by the device. */ @@ -751,243 +751,243 @@ public function __construct( /** * Metadata for an Aqara device. */ - public Properties\AqaraMetadata|null $aqara_metadata, + public Properties\AqaraMetadata|null $aqara_metadata = null, /** * ASSA ABLOY Credential Service metadata for the phone. */ - public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata, + public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata = null, /** * Metadata for an ASSA ABLOY Vostio system. */ - public Properties\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata, + public Properties\AssaAbloyVostioMetadata|null $assa_abloy_vostio_metadata = null, /** * Metadata for an August device. */ - public Properties\AugustMetadata|null $august_metadata, + public Properties\AugustMetadata|null $august_metadata = null, /** * The delay in seconds before the lock automatically locks after being unlocked. */ - public float|null $auto_lock_delay_seconds, + public float|null $auto_lock_delay_seconds = null, /** * Indicates whether automatic locking is enabled. */ - public bool|null $auto_lock_enabled, + public bool|null $auto_lock_enabled = null, /** * Climate preset modes that the thermostat supports, such as "home", "away", "wake", "sleep", "occupied", and "unoccupied". */ - public array|null $available_climate_preset_modes, + public array|null $available_climate_preset_modes = null, /** * Available [climate presets](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) for the thermostat. */ - public array $available_climate_presets, + public array|null $available_climate_presets = null, /** * Fan mode settings that the thermostat supports. */ - public array|null $available_fan_mode_settings, + public array|null $available_fan_mode_settings = null, /** * HVAC mode settings that the thermostat supports. */ - public array|null $available_hvac_mode_settings, + public array|null $available_hvac_mode_settings = null, /** * Metadata for an Avigilon Alta system. */ - public Properties\AvigilonAltaMetadata|null $avigilon_alta_metadata, + public Properties\AvigilonAltaMetadata|null $avigilon_alta_metadata = null, /** * Indicates whether the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is currently enabled for the device. To disable it, set this to `false` using [/devices/update](https://docs.seam.co/api/devices/update). */ - public bool|null $backup_access_code_pool_enabled, + public bool|null $backup_access_code_pool_enabled = null, /** * Represents the current status of the battery charge level. */ - public Properties\Battery|null $battery, + public Properties\Battery|null $battery = null, /** * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. */ - public float|null $battery_level, + public float|null $battery_level = null, /** * Metadata for a Brivo device. */ - public Properties\BrivoMetadata|null $brivo_metadata, + public Properties\BrivoMetadata|null $brivo_metadata = null, /** * Constraints on access codes for the device. Seam represents each constraint as an object with a `constraint_type` property. Depending on the constraint type, there may also be additional properties. Note that some constraints are manufacturer- or device-specific. */ - public array $code_constraints, + public array|null $code_constraints = null, /** * Metadata for a ControlByWeb device. */ - public Properties\ControlbywebMetadata|null $controlbyweb_metadata, + public Properties\ControlbywebMetadata|null $controlbyweb_metadata = null, /** * Current climate setting. */ - public Properties\CurrentClimateSetting|null $current_climate_setting, + public Properties\CurrentClimateSetting|null $current_climate_setting = null, /** * Array of noise threshold IDs that are currently triggering. */ - public array|null $currently_triggering_noise_threshold_ids, + public array|null $currently_triggering_noise_threshold_ids = null, /** * @deprecated use fallback_climate_preset_key to specify a fallback climate preset instead. */ - public Properties\DefaultClimateSetting|null $default_climate_setting, + public Properties\DefaultClimateSetting|null $default_climate_setting = null, /** * Indicates whether the door is open. */ - public bool|null $door_open, + public bool|null $door_open = null, /** * Metadata for a dormakaba Oracode device. */ - public Properties\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata, + public Properties\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, /** * Metadata for an ecobee device. */ - public Properties\EcobeeMetadata|null $ecobee_metadata, + public Properties\EcobeeMetadata|null $ecobee_metadata = null, /** * Key of the [fallback climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets/setting-the-fallback-climate-preset) for the thermostat. */ - public string|null $fallback_climate_preset_key, + public string|null $fallback_climate_preset_key = null, /** * @deprecated Use `current_climate_setting.fan_mode_setting` instead. */ - public string|null $fan_mode_setting, + public string|null $fan_mode_setting = null, /** * Metadata for a 4SUITES device. */ - public Properties\FourSuitesMetadata|null $four_suites_metadata, + public Properties\FourSuitesMetadata|null $four_suites_metadata = null, /** * Metadata for a Genie device. */ - public Properties\GenieMetadata|null $genie_metadata, + public Properties\GenieMetadata|null $genie_metadata = null, /** * Indicates whether the device has direct power. */ - public bool|null $has_direct_power, + public bool|null $has_direct_power = null, /** * Indicates whether the device supports native entry events. */ - public bool|null $has_native_entry_events, + public bool|null $has_native_entry_events = null, /** * Metadata for a Honeywell Resideo device. */ - public Properties\HoneywellResideoMetadata|null $honeywell_resideo_metadata, + public Properties\HoneywellResideoMetadata|null $honeywell_resideo_metadata = null, /** * Metadata for an igloo device. */ - public Properties\IglooMetadata|null $igloo_metadata, + public Properties\IglooMetadata|null $igloo_metadata = null, /** * Metadata for an igloohome device. */ - public Properties\IgloohomeMetadata|null $igloohome_metadata, + public Properties\IgloohomeMetadata|null $igloohome_metadata = null, /** * Alt text for the device image. */ - public string|null $image_alt_text, + public string|null $image_alt_text = null, /** * Image URL for the device. */ - public string|null $image_url, + public string|null $image_url = null, /** * Indicates whether the connected HVAC system is currently cooling, as reported by the thermostat. */ - public bool|null $is_cooling, + public bool|null $is_cooling = null, /** * Indicates whether the fan in the connected HVAC system is currently running, as reported by the thermostat. */ - public bool|null $is_fan_running, + public bool|null $is_fan_running = null, /** * Indicates whether the connected HVAC system is currently heating, as reported by the thermostat. */ - public bool|null $is_heating, + public bool|null $is_heating = null, /** * Indicates whether the current thermostat settings differ from the most recent active program or schedule that Seam activated. For this condition to occur, `current_climate_setting.manual_override_allowed` must also be `true`. */ - public bool|null $is_temporary_manual_override_active, + public bool|null $is_temporary_manual_override_active = null, /** * Metadata for a KeyNest device. */ - public Properties\KeynestMetadata|null $keynest_metadata, + public Properties\KeynestMetadata|null $keynest_metadata = null, /** * Keypad battery status. */ - public Properties\KeypadBattery|null $keypad_battery, + public Properties\KeypadBattery|null $keypad_battery = null, /** * Metadata for a Kisi device. */ - public Properties\KisiMetadata|null $kisi_metadata, + public Properties\KisiMetadata|null $kisi_metadata = null, /** * Metadata for a Korelock device. */ - public Properties\KorelockMetadata|null $korelock_metadata, + public Properties\KorelockMetadata|null $korelock_metadata = null, /** * Metadata for a Kwikset device. */ - public Properties\KwiksetMetadata|null $kwikset_metadata, + public Properties\KwiksetMetadata|null $kwikset_metadata = null, /** * Indicates whether the lock is locked. */ - public bool|null $locked, + public bool|null $locked = null, /** * Metadata for a Lockly device. */ - public Properties\LocklyMetadata|null $lockly_metadata, + public Properties\LocklyMetadata|null $lockly_metadata = null, /** * Manufacturer of the device. When a device, such as a smart lock, is connected through a smart hub, the manufacturer of the device might be different from that of the smart hub. */ - public string|null $manufacturer, + public string|null $manufacturer = null, /** * Maximum number of active access codes that the device supports. */ - public float|null $max_active_codes_supported, + public float|null $max_active_codes_supported = null, /** * Maximum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °C. */ - public float|null $max_cooling_set_point_celsius, + public float|null $max_cooling_set_point_celsius = null, /** * Maximum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °F. */ - public float|null $max_cooling_set_point_fahrenheit, + public float|null $max_cooling_set_point_fahrenheit = null, /** * Maximum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °C. */ - public float|null $max_heating_set_point_celsius, + public float|null $max_heating_set_point_celsius = null, /** * Maximum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °F. */ - public float|null $max_heating_set_point_fahrenheit, + public float|null $max_heating_set_point_fahrenheit = null, /** * Maximum number of periods that the thermostat can support per day. For example, if the thermostat supports 4 periods per day, this value is 4. */ - public float|null $max_thermostat_daily_program_periods_per_day, + public float|null $max_thermostat_daily_program_periods_per_day = null, /** * Maximum number of climate presets that the thermostat can support for weekly programming. */ - public float|null $max_unique_climate_presets_per_thermostat_weekly_program, + public float|null $max_unique_climate_presets_per_thermostat_weekly_program = null, /** * Minimum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °C. */ - public float|null $min_cooling_set_point_celsius, + public float|null $min_cooling_set_point_celsius = null, /** * Minimum [cooling set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#cooling-set-point) in °F. */ - public float|null $min_cooling_set_point_fahrenheit, + public float|null $min_cooling_set_point_fahrenheit = null, /** * Minimum [temperature difference](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#minimum-heating-cooling-temperature-delta) in °C between the cooling and heating set points when in heat-cool (auto) mode. */ - public float|null $min_heating_cooling_delta_celsius, + public float|null $min_heating_cooling_delta_celsius = null, /** * Minimum [temperature difference](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#minimum-heating-cooling-temperature-delta) in °F between the cooling and heating set points when in heat-cool (auto) mode. */ - public float|null $min_heating_cooling_delta_fahrenheit, + public float|null $min_heating_cooling_delta_fahrenheit = null, /** * Minimum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °C. */ - public float|null $min_heating_set_point_celsius, + public float|null $min_heating_set_point_celsius = null, /** * Minimum [heating set point](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points#heating-set-point) in °F. */ - public float|null $min_heating_set_point_fahrenheit, + public float|null $min_heating_set_point_fahrenheit = null, /** * Metadata for a Minut device. */ - public Properties\MinutMetadata|null $minut_metadata, + public Properties\MinutMetadata|null $minut_metadata = null, /** * Device model-related properties. */ @@ -1001,33 +1001,33 @@ public function __construct( /** * Metadata for a Google Nest device. */ - public Properties\NestMetadata|null $nest_metadata, + public Properties\NestMetadata|null $nest_metadata = null, /** * Indicates current noise level in decibels, if the device supports noise detection. */ - public float|null $noise_level_decibels, + public float|null $noise_level_decibels = null, /** * Metadata for a NoiseAware device. */ - public Properties\NoiseawareMetadata|null $noiseaware_metadata, + public Properties\NoiseawareMetadata|null $noiseaware_metadata = null, /** * Metadata for a Nuki device. */ - public Properties\NukiMetadata|null $nuki_metadata, + public Properties\NukiMetadata|null $nuki_metadata = null, /** * Indicates whether it is currently possible to use offline access codes for the device. * * @deprecated use device.can_program_offline_access_codes */ - public bool|null $offline_access_codes_enabled, + public bool|null $offline_access_codes_enabled = null, /** * Time frames that may be requested when creating an offline access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by `display_name` when they do) and satisfies that one option's rules. When `undefined`, any time frame works. */ - public array $offline_time_frame_options, + public array|null $offline_time_frame_options = null, /** * Metadata for an Omnitec device. */ - public Properties\OmnitecMetadata|null $omnitec_metadata, + public Properties\OmnitecMetadata|null $omnitec_metadata = null, /** * Indicates whether the device is online. */ @@ -1037,125 +1037,125 @@ public function __construct( * * @deprecated use device.can_program_online_access_codes */ - public bool|null $online_access_codes_enabled, + public bool|null $online_access_codes_enabled = null, /** * Time frames that may be requested when creating an online access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by `display_name` when they do) and satisfies that one option's rules. When `undefined`, any time frame works. */ - public array $online_time_frame_options, + public array|null $online_time_frame_options = null, /** * Reported relative humidity, as a value between 0 and 1, inclusive. */ - public float|null $relative_humidity, + public float|null $relative_humidity = null, /** * Metadata for a Ring device. */ - public Properties\RingMetadata|null $ring_metadata, + public Properties\RingMetadata|null $ring_metadata = null, /** * Metadata for a Salto KS device. */ - public Properties\SaltoKsMetadata|null $salto_ks_metadata, + public Properties\SaltoKsMetadata|null $salto_ks_metadata = null, /** * Metada for a Salto device. * * @deprecated Use `salto_ks_metadata` instead. */ - public Properties\SaltoMetadata|null $salto_metadata, + public Properties\SaltoMetadata|null $salto_metadata = null, /** * Salto Space credential service metadata for the phone. */ - public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata, + public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata = null, /** * Metadata for a Schlage device. */ - public Properties\SchlageMetadata|null $schlage_metadata, + public Properties\SchlageMetadata|null $schlage_metadata = null, /** * Metadata for Seam Bridge. */ - public Properties\SeamBridgeMetadata|null $seam_bridge_metadata, + public Properties\SeamBridgeMetadata|null $seam_bridge_metadata = null, /** * Metadata for a Sensi device. */ - public Properties\SensiMetadata|null $sensi_metadata, + public Properties\SensiMetadata|null $sensi_metadata = null, /** * Serial number of the device. */ - public string|null $serial_number, + public string|null $serial_number = null, /** * Metadata for a SmartThings device. */ - public Properties\SmartthingsMetadata|null $smartthings_metadata, + public Properties\SmartthingsMetadata|null $smartthings_metadata = null, /** * Supported code lengths for access codes. */ - public array|null $supported_code_lengths, + public array|null $supported_code_lengths = null, /** * @deprecated use device.properties.model.can_connect_accessory_keypad */ - public bool|null $supports_accessory_keypad, + public bool|null $supports_accessory_keypad = null, /** * Indicates whether the device supports a [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes). */ - public bool|null $supports_backup_access_code_pool, + public bool|null $supports_backup_access_code_pool = null, /** * @deprecated use offline_access_codes_enabled */ - public bool|null $supports_offline_access_codes, + public bool|null $supports_offline_access_codes = null, /** * Metadata for a tado° device. */ - public Properties\TadoMetadata|null $tado_metadata, + public Properties\TadoMetadata|null $tado_metadata = null, /** * Metadata for a Tedee device. */ - public Properties\TedeeMetadata|null $tedee_metadata, + public Properties\TedeeMetadata|null $tedee_metadata = null, /** * Reported temperature in °C. */ - public float|null $temperature_celsius, + public float|null $temperature_celsius = null, /** * Reported temperature in °F. */ - public float|null $temperature_fahrenheit, + public float|null $temperature_fahrenheit = null, /** * Current [temperature threshold](https://docs.seam.co/capability-guides/thermostats/setting-and-monitoring-temperature-thresholds) set for the thermostat. */ - public Properties\TemperatureThreshold|null $temperature_threshold, + public Properties\TemperatureThreshold|null $temperature_threshold = null, /** * Precision of the thermostat's period in minutes. For example, if the thermostat supports 15-minute periods, this value is 15. All values are relative to the top of the hour, so for 15 minutes, the periods would be 0, 15, 30, and 45 minutes past the hour. */ - public float|null $thermostat_daily_program_period_precision_minutes, + public float|null $thermostat_daily_program_period_precision_minutes = null, /** * Configured [daily programs](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. */ - public array $thermostat_daily_programs, + public array|null $thermostat_daily_programs = null, /** * Current [weekly program](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-programs) for the thermostat. */ - public Properties\ThermostatWeeklyProgram|null $thermostat_weekly_program, + public Properties\ThermostatWeeklyProgram|null $thermostat_weekly_program = null, /** * Metadata for a TTLock device. */ - public Properties\TtlockMetadata|null $ttlock_metadata, + public Properties\TtlockMetadata|null $ttlock_metadata = null, /** * Metadata for a 2N device. */ - public Properties\TwoNMetadata|null $two_n_metadata, + public Properties\TwoNMetadata|null $two_n_metadata = null, /** * Metadata for an Ultraloq device. */ - public Properties\UltraloqMetadata|null $ultraloq_metadata, + public Properties\UltraloqMetadata|null $ultraloq_metadata = null, /** * Metadata for an ASSA ABLOY Visionline system. */ - public Properties\VisionlineMetadata|null $visionline_metadata, + public Properties\VisionlineMetadata|null $visionline_metadata = null, /** * Metadata for a Wyze device. */ - public Properties\WyzeMetadata|null $wyze_metadata, + public Properties\WyzeMetadata|null $wyze_metadata = null, /** * Metadata for a Yacan device. */ - public Properties\YacanMetadata|null $yacan_metadata, + public Properties\YacanMetadata|null $yacan_metadata = null, ) {} } @@ -1228,7 +1228,7 @@ public function __construct( /** * Keypad battery properties. */ - public AccessoryKeypad\Battery|null $battery, + public AccessoryKeypad\Battery|null $battery = null, /** * Indicates if an accessory keypad is connected to the device. */ @@ -1315,11 +1315,11 @@ public function __construct( /** * @deprecated use device.properties.model.can_connect_accessory_keypad */ - public bool|null $accessory_keypad_supported, + public bool|null $accessory_keypad_supported = null, /** * Indicates whether the device can connect a accessory keypad. */ - public bool|null $can_connect_accessory_keypad, + public bool|null $can_connect_accessory_keypad = null, /** * Display name of the device model. */ @@ -1327,7 +1327,7 @@ public function __construct( /** * Indicates whether the device has a built in accessory keypad. */ - public bool|null $has_built_in_keypad, + public bool|null $has_built_in_keypad = null, /** * Display name that corresponds to the manufacturer-specific terminology for the device. */ @@ -1335,11 +1335,11 @@ public function __construct( /** * @deprecated use device.can_program_offline_access_codes. */ - public bool|null $offline_access_codes_supported, + public bool|null $offline_access_codes_supported = null, /** * @deprecated use device.can_program_online_access_codes. */ - public bool|null $online_access_codes_supported, + public bool|null $online_access_codes_supported = null, ) {} } @@ -1371,11 +1371,11 @@ public function __construct( /** * Endpoints associated with the phone. */ - public array $endpoints, + public array|null $endpoints = null, /** * Indicates whether the credential service has active endpoints associated with the phone. */ - public bool|null $has_active_endpoint, + public bool|null $has_active_endpoint = null, ) {} } @@ -1397,7 +1397,7 @@ public function __construct( /** * Indicates whether the credential service has an active associated phone. */ - public bool|null $has_active_phone, + public bool|null $has_active_phone = null, ) {} } @@ -1423,19 +1423,19 @@ public function __construct( /** * Group ID to which to add users for an Akiles device. */ - public string|null $_member_group_id, + public string|null $_member_group_id = null, /** * Gadget ID for an Akiles device. */ - public string|null $gadget_id, + public string|null $gadget_id = null, /** * Gadget name for an Akiles device. */ - public string|null $gadget_name, + public string|null $gadget_name = null, /** * Product name for an Akiles device. */ - public string|null $product_name, + public string|null $product_name = null, ) {} } @@ -1465,35 +1465,35 @@ public function __construct( /** * Device name for an Aqara device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Device ID (did) for an Aqara device. */ - public string|null $did, + public string|null $did = null, /** * Firmware version for an Aqara device. */ - public string|null $firmware_version, + public string|null $firmware_version = null, /** * Model identifier for an Aqara device. */ - public string|null $model, + public string|null $model = null, /** * Model type for an Aqara device. */ - public float|null $model_type, + public float|null $model_type = null, /** * Parent gateway device ID for an Aqara device. */ - public string|null $parent_did, + public string|null $parent_did = null, /** * Position (room) ID for an Aqara device. */ - public string|null $position_id, + public string|null $position_id = null, /** * Time zone reported for an Aqara device (e.g. GMT-07:00). */ - public string|null $time_zone, + public string|null $time_zone = null, ) {} } @@ -1515,7 +1515,7 @@ public function __construct( /** * Encoder name for an ASSA ABLOY Vostio system. */ - public string|null $encoder_name, + public string|null $encoder_name = null, ) {} } @@ -1544,31 +1544,31 @@ public function __construct( /** * Indicates whether an August device has a keypad. */ - public bool|null $has_keypad, + public bool|null $has_keypad = null, /** * House ID for an August device. */ - public string|null $house_id, + public string|null $house_id = null, /** * House name for an August device. */ - public string|null $house_name, + public string|null $house_name = null, /** * Keypad battery level for an August device. */ - public string|null $keypad_battery_level, + public string|null $keypad_battery_level = null, /** * Lock ID for an August device. */ - public string|null $lock_id, + public string|null $lock_id = null, /** * Lock name for an August device. */ - public string|null $lock_name, + public string|null $lock_name = null, /** * Model for an August device. */ - public string|null $model, + public string|null $model = null, ) {} } @@ -1598,31 +1598,31 @@ public function __construct( /** * Entry name for an Avigilon Alta system. */ - public string|null $entry_name, + public string|null $entry_name = null, /** * Total count of entry relays for an Avigilon Alta system. */ - public float|null $entry_relays_total_count, + public float|null $entry_relays_total_count = null, /** * Organization name for an Avigilon Alta system. */ - public string|null $org_name, + public string|null $org_name = null, /** * Site ID for an Avigilon Alta system. */ - public float|null $site_id, + public float|null $site_id = null, /** * Site name for an Avigilon Alta system. */ - public string|null $site_name, + public string|null $site_name = null, /** * Zone ID for an Avigilon Alta system. */ - public float|null $zone_id, + public float|null $zone_id = null, /** * Zone name for an Avigilon Alta system. */ - public string|null $zone_name, + public string|null $zone_name = null, ) {} } @@ -1646,11 +1646,11 @@ public function __construct( /** * Indicates whether the Brivo access point has activation (remote unlock) enabled. */ - public bool|null $activation_enabled, + public bool|null $activation_enabled = null, /** * Device name for a Brivo device. */ - public string|null $device_name, + public string|null $device_name = null, ) {} } @@ -1675,15 +1675,15 @@ public function __construct( /** * Device ID for a ControlByWeb device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a ControlByWeb device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Relay name for a ControlByWeb device. */ - public string|null $relay_name, + public string|null $relay_name = null, ) {} } @@ -1721,37 +1721,37 @@ public function __construct( /** * Device ID for a dormakaba Oracode device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Door ID for a dormakaba Oracode device. */ - public float|null $door_id, + public float|null $door_id = null, /** * Indicates whether a door is wireless for a dormakaba Oracode device. */ - public bool|null $door_is_wireless, + public bool|null $door_is_wireless = null, /** * Door name for a dormakaba Oracode device. */ - public string|null $door_name, + public string|null $door_name = null, /** * IANA time zone for a dormakaba Oracode device. */ - public string|null $iana_timezone, + public string|null $iana_timezone = null, /** * Predefined time slots for a dormakaba Oracode device. */ - public array $predefined_time_slots, + public array|null $predefined_time_slots = null, /** * Site ID for a dormakaba Oracode device. * * @deprecated Previously marked as "@DEPRECATED." */ - public float|null $site_id, + public float|null $site_id = null, /** * Site name for a dormakaba Oracode device. */ - public string|null $site_name, + public string|null $site_name = null, ) {} } @@ -1775,11 +1775,11 @@ public function __construct( /** * Device name for an ecobee device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Device ID for an ecobee device. */ - public string|null $ecobee_device_id, + public string|null $ecobee_device_id = null, ) {} } @@ -1805,15 +1805,15 @@ public function __construct( /** * Device ID for a 4SUITES device. */ - public float|null $device_id, + public float|null $device_id = null, /** * Device name for a 4SUITES device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Reclose delay, in seconds, for a 4SUITES device. */ - public float|null $reclose_delay_in_seconds, + public float|null $reclose_delay_in_seconds = null, ) {} } @@ -1837,11 +1837,11 @@ public function __construct( /** * Lock name for a Genie device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Door name for a Genie device. */ - public string|null $door_name, + public string|null $door_name = null, ) {} } @@ -1867,11 +1867,11 @@ public function __construct( /** * Device name for a Honeywell Resideo device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Device ID for a Honeywell Resideo device. */ - public string|null $honeywell_resideo_device_id, + public string|null $honeywell_resideo_device_id = null, ) {} } @@ -1896,15 +1896,15 @@ public function __construct( /** * Bridge ID for an igloo device. */ - public string|null $bridge_id, + public string|null $bridge_id = null, /** * Device ID for an igloo device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Model for an igloo device. */ - public string|null $model, + public string|null $model = null, ) {} } @@ -1933,27 +1933,27 @@ public function __construct( /** * Bridge ID for an igloohome device. */ - public string|null $bridge_id, + public string|null $bridge_id = null, /** * Bridge name for an igloohome device. */ - public string|null $bridge_name, + public string|null $bridge_name = null, /** * Device ID for an igloohome device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for an igloohome device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Indicates whether a keypad is linked to a bridge for an igloohome device. */ - public bool|null $is_accessory_keypad_linked_to_bridge, + public bool|null $is_accessory_keypad_linked_to_bridge = null, /** * Keypad ID for an igloohome device. */ - public string|null $keypad_id, + public string|null $keypad_id = null, ) {} } @@ -1998,87 +1998,87 @@ public function __construct( /** * Address for a KeyNest device. */ - public string|null $address, + public string|null $address = null, /** * Current or last store ID for a KeyNest device. */ - public float|null $current_or_last_store_id, + public float|null $current_or_last_store_id = null, /** * Current status for a KeyNest device. */ - public string|null $current_status, + public string|null $current_status = null, /** * Current user company for a KeyNest device. */ - public string|null $current_user_company, + public string|null $current_user_company = null, /** * Current user email for a KeyNest device. */ - public string|null $current_user_email, + public string|null $current_user_email = null, /** * Current user name for a KeyNest device. */ - public string|null $current_user_name, + public string|null $current_user_name = null, /** * Current user phone number for a KeyNest device. */ - public string|null $current_user_phone_number, + public string|null $current_user_phone_number = null, /** * Default office ID for a KeyNest device. */ - public float|null $default_office_id, + public float|null $default_office_id = null, /** * Device name for a KeyNest device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Fob ID for a KeyNest device. */ - public float|null $fob_id, + public float|null $fob_id = null, /** * Handover method for a KeyNest device. */ - public string|null $handover_method, + public string|null $handover_method = null, /** * Whether the KeyNest device has a photo. */ - public bool|null $has_photo, + public bool|null $has_photo = null, /** * Whether the key is in a locker that does not support the access codes API. */ - public bool|null $is_quadient_locker, + public bool|null $is_quadient_locker = null, /** * Key ID for a KeyNest device. */ - public string|null $key_id, + public string|null $key_id = null, /** * Key notes for a KeyNest device. */ - public string|null $key_notes, + public string|null $key_notes = null, /** * KeyNest app user for a KeyNest device. */ - public string|null $keynest_app_user, + public string|null $keynest_app_user = null, /** * Last movement timestamp for a KeyNest device. */ - public string|null $last_movement, + public string|null $last_movement = null, /** * Property ID for a KeyNest device. */ - public string|null $property_id, + public string|null $property_id = null, /** * Property postcode for a KeyNest device. */ - public string|null $property_postcode, + public string|null $property_postcode = null, /** * Status type for a KeyNest device. */ - public string|null $status_type, + public string|null $status_type = null, /** * Subscription plan for a KeyNest device. */ - public string|null $subscription_plan, + public string|null $subscription_plan = null, ) {} } @@ -2104,19 +2104,19 @@ public function __construct( /** * Description for a Kisi device. */ - public string|null $description, + public string|null $description = null, /** * Lock ID for a Kisi device. */ - public float|null $lock_id, + public float|null $lock_id = null, /** * Lock name for a Kisi device. */ - public string|null $lock_name, + public string|null $lock_name = null, /** * Place name for a Kisi device. */ - public string|null $place_name, + public string|null $place_name = null, ) {} } @@ -2145,31 +2145,31 @@ public function __construct( /** * Device ID for a Korelock device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Korelock device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Firmware version for a Korelock device. */ - public string|null $firmware_version, + public string|null $firmware_version = null, /** * Location ID for a Korelock device. Required for timebound access codes. */ - public string|null $location_id, + public string|null $location_id = null, /** * Model code for a Korelock device. */ - public string|null $model_code, + public string|null $model_code = null, /** * Serial number for a Korelock device. */ - public string|null $serial_number, + public string|null $serial_number = null, /** * WiFi signal strength (0-1) for a Korelock device. */ - public float|null $wifi_signal_strength, + public float|null $wifi_signal_strength = null, ) {} } @@ -2194,15 +2194,15 @@ public function __construct( /** * Device ID for a Kwikset device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Kwikset device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Model number for a Kwikset device. */ - public string|null $model_number, + public string|null $model_number = null, ) {} } @@ -2227,15 +2227,15 @@ public function __construct( /** * Device ID for a Lockly device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Lockly device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Model for a Lockly device. */ - public string|null $model, + public string|null $model = null, ) {} } @@ -2264,15 +2264,15 @@ public function __construct( /** * Device ID for a Minut device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Minut device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Latest sensor values for a Minut device. */ - public MinutMetadata\LatestSensorValues|null $latest_sensor_values, + public MinutMetadata\LatestSensorValues|null $latest_sensor_values = null, ) {} } @@ -2300,27 +2300,27 @@ public function __construct( /** * Custom device name for a Google Nest device. The device owner sets this value. */ - public string|null $device_custom_name, + public string|null $device_custom_name = null, /** * Device name for a Google Nest device. Google sets this value. */ - public string|null $device_name, + public string|null $device_name = null, /** * Display name for a Google Nest device. */ - public string|null $display_name, + public string|null $display_name = null, /** * Device ID for a Google Nest device. */ - public string|null $nest_device_id, + public string|null $nest_device_id = null, /** * ID of the Google Nest structure containing the device. */ - public string|null $nest_structure_id, + public string|null $nest_structure_id = null, /** * Name of the Google Nest structure containing the device. The device owner sets this value. */ - public string|null $structure_name, + public string|null $structure_name = null, ) {} } @@ -2347,23 +2347,23 @@ public function __construct( /** * Device ID for a NoiseAware device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device model for a NoiseAware device. */ - public string|null $device_model, + public string|null $device_model = null, /** * Device name for a NoiseAware device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Noise level, in decibels, for a NoiseAware device. */ - public float|null $noise_level_decibel, + public float|null $noise_level_decibel = null, /** * Noise level, expressed as a Noise Risk Score (NRS), for a NoiseAware device. */ - public float|null $noise_level_nrs, + public float|null $noise_level_nrs = null, ) {} } @@ -2390,23 +2390,23 @@ public function __construct( /** * Device ID for a Nuki device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Nuki device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Indicates whether keypad 2 is paired for a Nuki device. */ - public bool|null $keypad_2_paired, + public bool|null $keypad_2_paired = null, /** * Indicates whether the keypad battery is in a critical state for a Nuki device. */ - public bool|null $keypad_battery_critical, + public bool|null $keypad_battery_critical = null, /** * Indicates whether the keypad is paired for a Nuki device. */ - public bool|null $keypad_paired, + public bool|null $keypad_paired = null, ) {} } @@ -2435,31 +2435,31 @@ public function __construct( /** * Whether the Omnitec lock has a connected gateway for remote operations. */ - public bool|null $has_gateway, + public bool|null $has_gateway = null, /** * Operator-assigned alias for an Omnitec device. */ - public string|null $lock_alias, + public string|null $lock_alias = null, /** * Lock ID for an Omnitec device. */ - public float|null $lock_id, + public float|null $lock_id = null, /** * Bluetooth MAC address for an Omnitec device. */ - public string|null $lock_mac, + public string|null $lock_mac = null, /** * Lock name for an Omnitec device. */ - public string|null $lock_name, + public string|null $lock_name = null, /** * IANA time zone for the Omnitec device, used to schedule time-bound access codes at the correct local time (accounting for DST). */ - public string|null $time_zone, + public string|null $time_zone = null, /** * Static UTC offset of the Omnitec lock in milliseconds. Does not account for DST. */ - public float|null $timezone_raw_offset_ms, + public float|null $timezone_raw_offset_ms = null, ) {} } @@ -2483,11 +2483,11 @@ public function __construct( /** * Device ID for a Ring device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Ring device. */ - public string|null $device_name, + public string|null $device_name = null, ) {} } @@ -2519,39 +2519,39 @@ public function __construct( /** * Battery level for a Salto KS device. */ - public string|null $battery_level, + public string|null $battery_level = null, /** * Customer reference for a Salto KS device. */ - public string|null $customer_reference, + public string|null $customer_reference = null, /** * Indicates whether the site has a Salto KS subscription that supports custom PINs. */ - public bool|null $has_custom_pin_subscription, + public bool|null $has_custom_pin_subscription = null, /** * Lock ID for a Salto KS device. */ - public string|null $lock_id, + public string|null $lock_id = null, /** * Lock type for a Salto KS device. */ - public string|null $lock_type, + public string|null $lock_type = null, /** * Locked state for a Salto KS device. */ - public string|null $locked_state, + public string|null $locked_state = null, /** * Model for a Salto KS device. */ - public string|null $model, + public string|null $model = null, /** * Site ID for the Salto KS site to which the device belongs. */ - public string|null $site_id, + public string|null $site_id = null, /** * Site name for the Salto KS site to which the device belongs. */ - public string|null $site_name, + public string|null $site_name = null, ) {} } @@ -2583,35 +2583,35 @@ public function __construct( /** * Battery level for a Salto device. */ - public string|null $battery_level, + public string|null $battery_level = null, /** * Customer reference for a Salto device. */ - public string|null $customer_reference, + public string|null $customer_reference = null, /** * Lock ID for a Salto device. */ - public string|null $lock_id, + public string|null $lock_id = null, /** * Lock type for a Salto device. */ - public string|null $lock_type, + public string|null $lock_type = null, /** * Locked state for a Salto device. */ - public string|null $locked_state, + public string|null $locked_state = null, /** * Model for a Salto device. */ - public string|null $model, + public string|null $model = null, /** * Site ID for the Salto KS site to which the device belongs. */ - public string|null $site_id, + public string|null $site_id = null, /** * Site name for the Salto KS site to which the device belongs. */ - public string|null $site_name, + public string|null $site_name = null, ) {} } @@ -2636,15 +2636,15 @@ public function __construct( /** * Device ID for a Schlage device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Schlage device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Model for a Schlage device. */ - public string|null $model, + public string|null $model = null, ) {} } @@ -2669,15 +2669,15 @@ public function __construct( /** * Device number for Seam Bridge. */ - public float|null $device_num, + public float|null $device_num = null, /** * Name for Seam Bridge. */ - public string|null $name, + public string|null $name = null, /** * Unlock method for Seam Bridge. */ - public string|null $unlock_method, + public string|null $unlock_method = null, ) {} } @@ -2706,23 +2706,23 @@ public function __construct( /** * Device ID for a Sensi device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Sensi device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Set to true when the device does not support the /dual-setpoints API endpoint. */ - public bool|null $dual_setpoints_not_supported, + public bool|null $dual_setpoints_not_supported = null, /** * Enforced setpoint range in Celsius for a Sensi device, derived from an OutOfRange API error. */ - public array|null $enforced_setpoint_range_celsius, + public array|null $enforced_setpoint_range_celsius = null, /** * Product type for a Sensi device. */ - public string|null $product_type, + public string|null $product_type = null, ) {} } @@ -2748,19 +2748,19 @@ public function __construct( /** * Device ID for a SmartThings device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a SmartThings device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Location ID for a SmartThings device. */ - public string|null $location_id, + public string|null $location_id = null, /** * Model for a SmartThings device. */ - public string|null $model, + public string|null $model = null, ) {} } @@ -2784,11 +2784,11 @@ public function __construct( /** * Device type for a tado° device. */ - public string|null $device_type, + public string|null $device_type = null, /** * Serial number for a tado° device. */ - public string|null $serial_no, + public string|null $serial_no = null, ) {} } @@ -2817,31 +2817,31 @@ public function __construct( /** * Bridge ID for a Tedee device. */ - public float|null $bridge_id, + public float|null $bridge_id = null, /** * Bridge name for a Tedee device. */ - public string|null $bridge_name, + public string|null $bridge_name = null, /** * Device ID for a Tedee device. */ - public float|null $device_id, + public float|null $device_id = null, /** * Device model for a Tedee device. */ - public string|null $device_model, + public string|null $device_model = null, /** * Device name for a Tedee device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Keypad ID for a Tedee device. */ - public float|null $keypad_id, + public float|null $keypad_id = null, /** * Serial number for a Tedee device. */ - public string|null $serial_number, + public string|null $serial_number = null, ) {} } @@ -2875,31 +2875,31 @@ public function __construct( /** * Feature value for a TTLock device. */ - public string|null $feature_value, + public string|null $feature_value = null, /** * Features for a TTLock device. */ - public TtlockMetadata\Features|null $features, + public TtlockMetadata\Features|null $features = null, /** * Indicates whether a TTLock device has a gateway. */ - public bool|null $has_gateway, + public bool|null $has_gateway = null, /** * Lock alias for a TTLock device. */ - public string|null $lock_alias, + public string|null $lock_alias = null, /** * Lock ID for a TTLock device. */ - public float|null $lock_id, + public float|null $lock_id = null, /** * Lock-side timezone offset in milliseconds east of UTC, as configured in the TTLock app. Source of truth for the lock's wall-clock interpretation of access code start/end times — a misconfigured value here is the typical cause of customer "codes offset by N hours" reports. Diagnostic only; Seam does not convert times based on this value. */ - public float|null $timezone_raw_offset_ms, + public float|null $timezone_raw_offset_ms = null, /** * Wireless keypads for a TTLock device. */ - public array $wireless_keypads, + public array|null $wireless_keypads = null, ) {} } @@ -2923,11 +2923,11 @@ public function __construct( /** * Device ID for a 2N device. */ - public float|null $device_id, + public float|null $device_id = null, /** * Device name for a 2N device. */ - public string|null $device_name, + public string|null $device_name = null, ) {} } @@ -2953,19 +2953,19 @@ public function __construct( /** * Device ID for an Ultraloq device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for an Ultraloq device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Device type for an Ultraloq device. */ - public string|null $device_type, + public string|null $device_type = null, /** * IANA timezone for the Ultraloq device. */ - public string|null $time_zone, + public string|null $time_zone = null, ) {} } @@ -2986,7 +2986,7 @@ public function __construct( /** * Encoder ID for an ASSA ABLOY Visionline system. */ - public string|null $encoder_id, + public string|null $encoder_id = null, ) {} } @@ -3016,35 +3016,35 @@ public function __construct( /** * Device ID for a Wyze device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device information model for a Wyze device. */ - public string|null $device_info_model, + public string|null $device_info_model = null, /** * Device name for a Wyze device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Keypad UUID for a Wyze device. */ - public string|null $keypad_uuid, + public string|null $keypad_uuid = null, /** * Locker status (hardlock) for a Wyze device. */ - public float|null $locker_status_hardlock, + public float|null $locker_status_hardlock = null, /** * Product model for a Wyze device. */ - public string|null $product_model, + public string|null $product_model = null, /** * Product name for a Wyze device. */ - public string|null $product_name, + public string|null $product_name = null, /** * Product type for a Wyze device. */ - public string|null $product_type, + public string|null $product_type = null, ) {} } @@ -3070,19 +3070,19 @@ public function __construct( /** * Device ID for a Yacan device. */ - public string|null $device_id, + public string|null $device_id = null, /** * Device name for a Yacan device. */ - public string|null $device_name, + public string|null $device_name = null, /** * Device type for a Yacan device. */ - public string|null $device_type, + public string|null $device_type = null, /** * Serial number for a Yacan device. */ - public string|null $serial_number, + public string|null $serial_number = null, ) {} } @@ -3108,11 +3108,11 @@ public function __construct( /** * Maximum name length constraint for access codes. */ - public float|null $max_length, + public float|null $max_length = null, /** * Minimum name length constraint for access codes. */ - public float|null $min_length, + public float|null $min_length = null, ) {} } @@ -3173,31 +3173,31 @@ public function __construct( /** * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. */ - public string|null $end_date_recurrence_rule, + public string|null $end_date_recurrence_rule = null, /** * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. */ - public bool|null $matching_start_end_time, + public bool|null $matching_start_end_time = null, /** * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. */ - public string|null $max_duration, + public string|null $max_duration = null, /** * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. */ - public string|null $min_duration, + public string|null $min_duration = null, /** * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. */ - public string|null $start_date_recurrence_rule, + public string|null $start_date_recurrence_rule = null, /** * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. */ - public array $time_pairs, + public array|null $time_pairs = null, /** * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. */ - public string|null $time_zone, + public string|null $time_zone = null, ) {} } @@ -3237,31 +3237,31 @@ public function __construct( /** * iCalendar recurrence rule (RRULE) that the end date must fall on. Constrains which calendar dates are selectable, independent of the time-of-day rules. */ - public string|null $end_date_recurrence_rule, + public string|null $end_date_recurrence_rule = null, /** * When `true`, the start and end must fall at the same time of day (the caller picks which). Mutually exclusive with `time_pairs`. */ - public bool|null $matching_start_end_time, + public bool|null $matching_start_end_time = null, /** * Maximum duration this option covers, as an ISO 8601 duration (for example, `PT672H` or `P367D`). Omitted when there is no maximum. */ - public string|null $max_duration, + public string|null $max_duration = null, /** * Minimum duration this option covers, as an ISO 8601 duration (for example, `PT1H` or `P29D`). Omitted when there is no minimum. */ - public string|null $min_duration, + public string|null $min_duration = null, /** * iCalendar recurrence rule (RRULE) that the start date must fall on (for example, `FREQ=MONTHLY;BYDAY=1MO,3MO`). Constrains which calendar dates are selectable, independent of the time-of-day rules. */ - public string|null $start_date_recurrence_rule, + public string|null $start_date_recurrence_rule = null, /** * Fixed start/end time pairings the caller chooses from. Mutually exclusive with `matching_start_end_time`. */ - public array $time_pairs, + public array|null $time_pairs = null, /** * IANA time zone for interpreting `time_pairs` and the date recurrence rules. Present only when the option fixes times or dates. */ - public string|null $time_zone, + public string|null $time_zone = null, ) {} } @@ -3321,11 +3321,11 @@ public function __construct( /** * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. */ - public bool|null $is_override_allowed, + public bool|null $is_override_allowed = null, /** * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). */ - public int|null $max_override_period_minutes, + public int|null $max_override_period_minutes = null, /** * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). */ @@ -3404,15 +3404,15 @@ public function __construct( /** * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. */ - public string|null $climate_preset_mode, + public string|null $climate_preset_mode = null, /** * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_celsius, + public float|null $cooling_set_point_celsius = null, /** * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_fahrenheit, + public float|null $cooling_set_point_fahrenheit = null, /** * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ @@ -3420,23 +3420,23 @@ public function __construct( /** * Metadata specific to the Ecobee climate, if applicable. */ - public AvailableClimatePresets\EcobeeMetadata|null $ecobee_metadata, + public AvailableClimatePresets\EcobeeMetadata|null $ecobee_metadata = null, /** * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. */ - public string|null $fan_mode_setting, + public string|null $fan_mode_setting = null, /** * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_celsius, + public float|null $heating_set_point_celsius = null, /** * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_fahrenheit, + public float|null $heating_set_point_fahrenheit = null, /** * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. */ - public string|null $hvac_mode_setting, + public string|null $hvac_mode_setting = null, /** * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). * @@ -3446,7 +3446,7 @@ public function __construct( /** * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $name, + public string|null $name = null, ) {} } @@ -3493,65 +3493,65 @@ public function __construct( /** * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. */ - public bool|null $can_delete, + public bool|null $can_delete = null, /** * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. */ - public bool|null $can_edit, + public bool|null $can_edit = null, /** * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. */ - public bool|null $can_use_with_thermostat_daily_programs, + public bool|null $can_use_with_thermostat_daily_programs = null, /** * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $climate_preset_key, + public string|null $climate_preset_key = null, /** * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. */ - public string|null $climate_preset_mode, + public string|null $climate_preset_mode = null, /** * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_celsius, + public float|null $cooling_set_point_celsius = null, /** * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_fahrenheit, + public float|null $cooling_set_point_fahrenheit = null, /** * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $display_name, + public string|null $display_name = null, /** * Metadata specific to the Ecobee climate, if applicable. */ - public CurrentClimateSetting\EcobeeMetadata|null $ecobee_metadata, + public CurrentClimateSetting\EcobeeMetadata|null $ecobee_metadata = null, /** * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. */ - public string|null $fan_mode_setting, + public string|null $fan_mode_setting = null, /** * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_celsius, + public float|null $heating_set_point_celsius = null, /** * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_fahrenheit, + public float|null $heating_set_point_fahrenheit = null, /** * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. */ - public string|null $hvac_mode_setting, + public string|null $hvac_mode_setting = null, /** * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). * * @deprecated Use 'thermostat_schedule.is_override_allowed' */ - public bool|null $manual_override_allowed, + public bool|null $manual_override_allowed = null, /** * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $name, + public string|null $name = null, ) {} } @@ -3598,65 +3598,65 @@ public function __construct( /** * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be deleted. */ - public bool|null $can_delete, + public bool|null $can_delete = null, /** * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be edited. */ - public bool|null $can_edit, + public bool|null $can_edit = null, /** * Indicates whether the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets) key can be programmed in a thermostat daily program. */ - public bool|null $can_use_with_thermostat_daily_programs, + public bool|null $can_use_with_thermostat_daily_programs = null, /** * Unique key to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $climate_preset_key, + public string|null $climate_preset_key = null, /** * The climate preset mode for the thermostat, based on the available climate preset modes reported by the device. */ - public string|null $climate_preset_mode, + public string|null $climate_preset_mode = null, /** * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_celsius, + public float|null $cooling_set_point_celsius = null, /** * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_fahrenheit, + public float|null $cooling_set_point_fahrenheit = null, /** * Display name for the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $display_name, + public string|null $display_name = null, /** * Metadata specific to the Ecobee climate, if applicable. */ - public DefaultClimateSetting\EcobeeMetadata|null $ecobee_metadata, + public DefaultClimateSetting\EcobeeMetadata|null $ecobee_metadata = null, /** * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. */ - public string|null $fan_mode_setting, + public string|null $fan_mode_setting = null, /** * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_celsius, + public float|null $heating_set_point_celsius = null, /** * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_fahrenheit, + public float|null $heating_set_point_fahrenheit = null, /** * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. */ - public string|null $hvac_mode_setting, + public string|null $hvac_mode_setting = null, /** * Indicates whether a person at the thermostat can change the thermostat's settings. See [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). * * @deprecated Use 'thermostat_schedule.is_override_allowed' */ - public bool|null $manual_override_allowed, + public bool|null $manual_override_allowed = null, /** * User-friendly name to identify the [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). */ - public string|null $name, + public string|null $name = null, ) {} } @@ -3850,11 +3850,11 @@ public function __construct( /** * ID of the associated endpoint. */ - public string|null $endpoint_id, + public string|null $endpoint_id = null, /** * Indicated whether the endpoint is active. */ - public bool|null $is_active, + public bool|null $is_active = null, ) {} } } @@ -3890,43 +3890,43 @@ public function __construct( /** * Check in time for a time slot for a dormakaba Oracode device. */ - public string|null $check_in_time, + public string|null $check_in_time = null, /** * Checkout time for a time slot for a dormakaba Oracode device. */ - public string|null $check_out_time, + public string|null $check_out_time = null, /** * ID of a user level for a dormakaba Oracode device. */ - public string|null $dormakaba_oracode_user_level_id, + public string|null $dormakaba_oracode_user_level_id = null, /** * Prefix for a user level for a dormakaba Oracode device. */ - public float|null $dormakaba_oracode_user_level_prefix, + public float|null $dormakaba_oracode_user_level_prefix = null, /** * Indicates whether a time slot for a dormakaba Oracode device is a 24-hour time slot. */ - public bool|null $is_24_hour, + public bool|null $is_24_hour = null, /** * Indicates whether a time slot for a dormakaba Oracode device is in biweekly mode. */ - public bool|null $is_biweekly_mode, + public bool|null $is_biweekly_mode = null, /** * Indicates whether a time slot for a dormakaba Oracode device is a master time slot. */ - public bool|null $is_master, + public bool|null $is_master = null, /** * Indicates whether a time slot for a dormakaba Oracode device is a one-shot time slot. */ - public bool|null $is_one_shot, + public bool|null $is_one_shot = null, /** * Name of a time slot for a dormakaba Oracode device. */ - public string|null $name, + public string|null $name = null, /** * Prefix for a time slot for a dormakaba Oracode device. */ - public float|null $prefix, + public float|null $prefix = null, ) {} } } @@ -3969,23 +3969,23 @@ public function __construct( /** * Latest accelerometer Z-axis reading for a Minut device. */ - public LatestSensorValues\AccelerometerZ|null $accelerometer_z, + public LatestSensorValues\AccelerometerZ|null $accelerometer_z = null, /** * Latest humidity reading for a Minut device. */ - public LatestSensorValues\Humidity|null $humidity, + public LatestSensorValues\Humidity|null $humidity = null, /** * Latest pressure reading for a Minut device. */ - public LatestSensorValues\Pressure|null $pressure, + public LatestSensorValues\Pressure|null $pressure = null, /** * Latest sound reading for a Minut device. */ - public LatestSensorValues\Sound|null $sound, + public LatestSensorValues\Sound|null $sound = null, /** * Latest temperature reading for a Minut device. */ - public LatestSensorValues\Temperature|null $temperature, + public LatestSensorValues\Temperature|null $temperature = null, ) {} } } @@ -4011,11 +4011,11 @@ public function __construct( /** * Time of latest accelerometer Z-axis reading for a Minut device. */ - public string|null $time, + public string|null $time = null, /** * Value of latest accelerometer Z-axis reading for a Minut device. */ - public float|null $value, + public float|null $value = null, ) {} } @@ -4039,11 +4039,11 @@ public function __construct( /** * Time of latest humidity reading for a Minut device. */ - public string|null $time, + public string|null $time = null, /** * Value of latest humidity reading for a Minut device. */ - public float|null $value, + public float|null $value = null, ) {} } @@ -4067,11 +4067,11 @@ public function __construct( /** * Time of latest pressure reading for a Minut device. */ - public string|null $time, + public string|null $time = null, /** * Value of latest pressure reading for a Minut device. */ - public float|null $value, + public float|null $value = null, ) {} } @@ -4095,11 +4095,11 @@ public function __construct( /** * Time of latest sound reading for a Minut device. */ - public string|null $time, + public string|null $time = null, /** * Value of latest sound reading for a Minut device. */ - public float|null $value, + public float|null $value = null, ) {} } @@ -4123,11 +4123,11 @@ public function __construct( /** * Time of latest temperature reading for a Minut device. */ - public string|null $time, + public string|null $time = null, /** * Value of latest temperature reading for a Minut device. */ - public float|null $value, + public float|null $value = null, ) {} } } @@ -4159,31 +4159,31 @@ public function __construct( /** * Indicates whether a TTLock device supports auto-lock time configuration. */ - public bool|null $auto_lock_time_config, + public bool|null $auto_lock_time_config = null, /** * Indicates whether a TTLock device supports an incomplete keyboard passcode. */ - public bool|null $incomplete_keyboard_passcode, + public bool|null $incomplete_keyboard_passcode = null, /** * Indicates whether a TTLock device supports the lock command. */ - public bool|null $lock_command, + public bool|null $lock_command = null, /** * Indicates whether a TTLock device supports a passcode. */ - public bool|null $passcode, + public bool|null $passcode = null, /** * Indicates whether a TTLock device supports passcode management. */ - public bool|null $passcode_management, + public bool|null $passcode_management = null, /** * Indicates whether a TTLock device supports unlock via gateway. */ - public bool|null $unlock_via_gateway, + public bool|null $unlock_via_gateway = null, /** * Indicates whether a TTLock device supports Wi-Fi. */ - public bool|null $wifi, + public bool|null $wifi = null, ) {} } @@ -4207,11 +4207,11 @@ public function __construct( /** * ID for a wireless keypad for a TTLock device. */ - public float|null $wireless_keypad_id, + public float|null $wireless_keypad_id = null, /** * Name for a wireless keypad for a TTLock device. */ - public string|null $wireless_keypad_name, + public string|null $wireless_keypad_name = null, ) {} } } @@ -4343,15 +4343,15 @@ public function __construct( /** * Reference to the Ecobee climate, if applicable. */ - public string|null $climate_ref, + public string|null $climate_ref = null, /** * Indicates if the climate preset is optimized by Ecobee. */ - public bool|null $is_optimized, + public bool|null $is_optimized = null, /** * Indicates whether the climate preset is owned by the user or the system. */ - public string|null $owner, + public string|null $owner = null, ) {} } } @@ -4378,15 +4378,15 @@ public function __construct( /** * Reference to the Ecobee climate, if applicable. */ - public string|null $climate_ref, + public string|null $climate_ref = null, /** * Indicates if the climate preset is optimized by Ecobee. */ - public bool|null $is_optimized, + public bool|null $is_optimized = null, /** * Indicates whether the climate preset is owned by the user or the system. */ - public string|null $owner, + public string|null $owner = null, ) {} } } @@ -4413,15 +4413,15 @@ public function __construct( /** * Reference to the Ecobee climate, if applicable. */ - public string|null $climate_ref, + public string|null $climate_ref = null, /** * Indicates if the climate preset is optimized by Ecobee. */ - public bool|null $is_optimized, + public bool|null $is_optimized = null, /** * Indicates whether the climate preset is owned by the user or the system. */ - public string|null $owner, + public string|null $owner = null, ) {} } } diff --git a/src/Resources/DeviceProvider.php b/src/Resources/DeviceProvider.php index a3276967..044ae59c 100644 --- a/src/Resources/DeviceProvider.php +++ b/src/Resources/DeviceProvider.php @@ -50,83 +50,83 @@ public function __construct( /** * Indicates whether the lock supports configuring automatic locking. */ - public bool|null $can_configure_auto_lock, + public bool|null $can_configure_auto_lock = null, /** * Indicates whether the thermostat supports cooling. */ - public bool|null $can_hvac_cool, + public bool|null $can_hvac_cool = null, /** * Indicates whether the thermostat supports heating. */ - public bool|null $can_hvac_heat, + public bool|null $can_hvac_heat = null, /** * Indicates whether the thermostat supports simultaneous heating and cooling. */ - public bool|null $can_hvac_heat_cool, + public bool|null $can_hvac_heat_cool = null, /** * Indicates whether the device supports programming offline access codes. */ - public bool|null $can_program_offline_access_codes, + public bool|null $can_program_offline_access_codes = null, /** * Indicates whether the device supports programming online access codes. */ - public bool|null $can_program_online_access_codes, + public bool|null $can_program_online_access_codes = null, /** * Indicates whether the thermostat supports different climate programs for each day of the week. */ - public bool|null $can_program_thermostat_programs_as_different_each_day, + public bool|null $can_program_thermostat_programs_as_different_each_day = null, /** * Indicates whether the thermostat supports a single climate program applied to every day. */ - public bool|null $can_program_thermostat_programs_as_same_each_day, + public bool|null $can_program_thermostat_programs_as_same_each_day = null, /** * Indicates whether the thermostat supports weekday/weekend climate programs. */ - public bool|null $can_program_thermostat_programs_as_weekday_weekend, + public bool|null $can_program_thermostat_programs_as_weekday_weekend = null, /** * Indicates whether the device supports remote locking. */ - public bool|null $can_remotely_lock, + public bool|null $can_remotely_lock = null, /** * Indicates whether the device supports remote unlocking. */ - public bool|null $can_remotely_unlock, + public bool|null $can_remotely_unlock = null, /** * Indicates whether the thermostat supports running climate programs. */ - public bool|null $can_run_thermostat_programs, + public bool|null $can_run_thermostat_programs = null, /** * Indicates whether the device supports simulating connection in a sandbox. */ - public bool|null $can_simulate_connection, + public bool|null $can_simulate_connection = null, /** * Indicates whether the device supports simulating disconnection in a sandbox. */ - public bool|null $can_simulate_disconnection, + public bool|null $can_simulate_disconnection = null, /** * Indicates whether the hub supports simulating connection in a sandbox. */ - public bool|null $can_simulate_hub_connection, + public bool|null $can_simulate_hub_connection = null, /** * Indicates whether the hub supports simulating disconnection in a sandbox. */ - public bool|null $can_simulate_hub_disconnection, + public bool|null $can_simulate_hub_disconnection = null, /** * Indicates whether the device supports simulating a paid subscription in a sandbox. */ - public bool|null $can_simulate_paid_subscription, + public bool|null $can_simulate_paid_subscription = null, /** * Indicates whether the device supports simulating removal in a sandbox. */ - public bool|null $can_simulate_removal, + public bool|null $can_simulate_removal = null, /** * Indicates whether the thermostat can be turned off. */ - public bool|null $can_turn_off_hvac, + public bool|null $can_turn_off_hvac = null, /** * Indicates whether the lock supports unlocking with an access code. */ - public bool|null $can_unlock_with_code, + public bool|null $can_unlock_with_code = null, /** * Name of the device provider. */ diff --git a/src/Resources/Event.php b/src/Resources/Event.php index 73a16b05..641212cd 100644 --- a/src/Resources/Event.php +++ b/src/Resources/Event.php @@ -152,7 +152,7 @@ public function __construct( /** * Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. */ - public bool|null $access_code_is_managed, + public bool|null $access_code_is_managed = null, /** * Warnings associated with the access code. */ @@ -168,11 +168,11 @@ public function __construct( /** * Key of the affected Access Grant (if present). */ - public string|null $access_grant_key, + public string|null $access_grant_key = null, /** * Keys of the access grants associated with this access method (if present). */ - public array|null $access_grant_keys, + public array|null $access_grant_keys = null, /** * ID of the affected access method. */ @@ -231,11 +231,11 @@ public function __construct( /** * Human-readable reason for the change (e.g. `ongoing code auto-renewed`). */ - public string|null $change_reason, + public string|null $change_reason = null, /** * List of properties that changed on the access code. */ - public array $changed_properties, + public array|null $changed_properties = null, /** * ID of the affected client session. */ @@ -245,8 +245,8 @@ public function __construct( */ public string|null $climate_preset_key, public string|null $code, - public string|null $connect_webview_id, - public mixed $connected_account_custom_metadata, + public string|null $connect_webview_id = null, + public mixed $connected_account_custom_metadata = null, /** * Errors associated with the connected account. */ @@ -259,16 +259,16 @@ public function __construct( /** * Temperature to which the thermostat should cool (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_celsius, + public float|null $cooling_set_point_celsius = null, /** * Temperature to which the thermostat should cool (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $cooling_set_point_fahrenheit, + public float|null $cooling_set_point_fahrenheit = null, /** * Date and time at which the event was created. */ public string|null $created_at, - public string|null $customer_key, + public string|null $customer_key = null, /** * Human-readable description of the change and its source. */ @@ -276,19 +276,19 @@ public function __construct( /** * Desired temperature, in °C, defined by the affected thermostat's cooling or heating set point. */ - public float|null $desired_temperature_celsius, + public float|null $desired_temperature_celsius = null, /** * Desired temperature, in °F, defined by the affected thermostat's cooling or heating set point. */ - public float|null $desired_temperature_fahrenheit, - public mixed $device_custom_metadata, + public float|null $desired_temperature_fahrenheit = null, + public mixed $device_custom_metadata = null, /** * Errors associated with the device. */ public array $device_errors, public string|null $device_id, public array|null $device_ids, - public string|null $device_name, + public string|null $device_name = null, /** * Warnings associated with the device. */ @@ -296,7 +296,7 @@ public function __construct( /** * The new end time for the access grant. */ - public string|null $ends_at, + public string|null $ends_at = null, /** * Error code associated with the disconnection event, if any. */ @@ -308,7 +308,7 @@ public function __construct( /** * Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. */ - public string|null $event_description, + public string|null $event_description = null, /** * ID of the event. */ @@ -320,31 +320,31 @@ public function __construct( /** * Desired [fan mode setting](https://docs.seam.co/capability-guides/thermostats/configure-current-climate-settings#fan-mode-settings), such as `on`, `auto`, or `circulate`. */ - public string|null $fan_mode_setting, + public string|null $fan_mode_setting = null, public Event\From|null $from, /** * Temperature to which the thermostat should heat (in °C). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_celsius, + public float|null $heating_set_point_celsius = null, /** * Temperature to which the thermostat should heat (in °F). See also [Set Points](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/set-points). */ - public float|null $heating_set_point_fahrenheit, + public float|null $heating_set_point_fahrenheit = null, /** * Desired [HVAC mode](https://docs.seam.co/capability-guides/thermostats/understanding-thermostat-concepts/hvac-mode) setting, such as `heat`, `cool`, `heat_cool`, or `off`. */ - public string|null $hvac_mode_setting, - public string|null $image_url, + public string|null $hvac_mode_setting = null, + public string|null $image_url = null, /** * Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). */ - public bool|null $is_backup_code, + public bool|null $is_backup_code = null, /** * Indicates whether the climate preset that was activated is the fallback climate preset for the thermostat. */ public bool|null $is_fallback_climate_preset, - public bool|null $is_via_bluetooth, - public bool|null $is_via_nfc, + public bool|null $is_via_bluetooth = null, + public bool|null $is_via_nfc = null, /** * Lower temperature limit, in °C, defined by the set threshold. */ @@ -357,35 +357,35 @@ public function __construct( /** * Metadata from Minut. */ - public mixed $minut_metadata, + public mixed $minut_metadata = null, /** * IDs of the devices that did not receive a requested access method. Use these to identify which specific devices failed without having to fetch the Access Grant. */ - public array|null $missing_device_ids, + public array|null $missing_device_ids = null, /** * Sub-type of motion detected, if available. */ - public string|null $motion_sub_type, + public string|null $motion_sub_type = null, /** * Detected noise level in decibels. */ - public float|null $noise_level_decibels, + public float|null $noise_level_decibels = null, /** * Detected noise level in Noiseaware Noise Risk Score (NRS). */ - public float|null $noise_level_nrs, + public float|null $noise_level_nrs = null, /** * ID of the noise threshold that was triggered. */ - public string|null $noise_threshold_id, + public string|null $noise_threshold_id = null, /** * Name of the noise threshold that was triggered. */ - public string|null $noise_threshold_name, + public string|null $noise_threshold_name = null, /** * Metadata from Noiseaware. */ - public mixed $noiseaware_metadata, + public mixed $noiseaware_metadata = null, /** * Date and time at which the event occurred. */ @@ -393,7 +393,7 @@ public function __construct( /** * Why access was denied, when the provider reports a determinable cause. Omitted when unknown. */ - public Event\Reason|null $reason, + public Event\Reason|null $reason = null, /** * Array of mutations requested on the access code, each containing the mutation type and from/to values. */ @@ -405,11 +405,11 @@ public function __construct( /** * Unique key for the space within the workspace. */ - public string|null $space_key, + public string|null $space_key = null, /** * The new start time for the access grant. */ - public string|null $starts_at, + public string|null $starts_at = null, /** * Status of the action. */ @@ -435,7 +435,7 @@ public function __construct( * Upper temperature limit, in °F, defined by the set threshold. */ public float|null $upper_limit_fahrenheit, - public string|null $video_url, + public string|null $video_url = null, /** * ID of the workspace associated with the event. */ @@ -828,7 +828,7 @@ public function __construct( /** * Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. */ - public mixed $from, + public mixed $from = null, /** * Code identifying the type of mutation requested, such as `updating_name`, `updating_code`, `updating_time_frame`, or `deleting`. */ @@ -836,7 +836,7 @@ public function __construct( /** * New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like `deleting`. */ - public mixed $to, + public mixed $to = null, ) {} } diff --git a/src/Resources/InstantKey.php b/src/Resources/InstantKey.php index cbeb2807..f7a93404 100644 --- a/src/Resources/InstantKey.php +++ b/src/Resources/InstantKey.php @@ -41,11 +41,11 @@ public function __construct( /** * Customization applied to the Instant Key UI. */ - public InstantKey\Customization|null $customization, + public InstantKey\Customization|null $customization = null, /** * ID of the customization profile associated with the Instant Key. */ - public string|null $customization_profile_id, + public string|null $customization_profile_id = null, /** * Date and time at which the Instant Key expires. */ @@ -92,15 +92,15 @@ public function __construct( /** * URL of the logo displayed on the Instant Key. */ - public string|null $logo_url, + public string|null $logo_url = null, /** * Primary color used in the Instant Key UI. */ - public string|null $primary_color, + public string|null $primary_color = null, /** * Secondary color used in the Instant Key UI. */ - public string|null $secondary_color, + public string|null $secondary_color = null, ) {} } } diff --git a/src/Resources/NoiseThreshold.php b/src/Resources/NoiseThreshold.php index 765329d2..0545ab0f 100644 --- a/src/Resources/NoiseThreshold.php +++ b/src/Resources/NoiseThreshold.php @@ -47,7 +47,7 @@ public function __construct( /** * Noise level in Noiseaware Noise Risk Score (NRS) for the noise threshold. This parameter is only relevant for [Noiseaware sensors](https://docs.seam.co/device-and-system-integration-guides/noiseaware-sensors). */ - public float|null $noise_threshold_nrs, + public float|null $noise_threshold_nrs = null, /** * Time at which the noise threshold should become active daily. */ diff --git a/src/Resources/Phone.php b/src/Resources/Phone.php index 28f7d0d8..3cb31a93 100644 --- a/src/Resources/Phone.php +++ b/src/Resources/Phone.php @@ -61,7 +61,7 @@ public function __construct( /** * Optional nickname to describe the phone, settable through Seam. */ - public string|null $nickname, + public string|null $nickname = null, /** * Properties of the phone. */ @@ -144,11 +144,11 @@ public function __construct( /** * ASSA ABLOY Credential Service metadata for the phone. */ - public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata, + public Properties\AssaAbloyCredentialServiceMetadata|null $assa_abloy_credential_service_metadata = null, /** * Salto Space credential service metadata for the phone. */ - public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata, + public Properties\SaltoSpaceCredentialServiceMetadata|null $salto_space_credential_service_metadata = null, ) {} } @@ -215,11 +215,11 @@ public function __construct( /** * Endpoints associated with the phone. */ - public array $endpoints, + public array|null $endpoints = null, /** * Indicates whether the credential service has active endpoints associated with the phone. */ - public bool|null $has_active_endpoint, + public bool|null $has_active_endpoint = null, ) {} } @@ -241,7 +241,7 @@ public function __construct( /** * Indicates whether the credential service has an active associated phone. */ - public bool|null $has_active_phone, + public bool|null $has_active_phone = null, ) {} } } @@ -267,11 +267,11 @@ public function __construct( /** * ID of the associated endpoint. */ - public string|null $endpoint_id, + public string|null $endpoint_id = null, /** * Indicated whether the endpoint is active. */ - public bool|null $is_active, + public bool|null $is_active = null, ) {} } } diff --git a/src/Resources/Space.php b/src/Resources/Space.php index eb7c6777..c5acd6e5 100644 --- a/src/Resources/Space.php +++ b/src/Resources/Space.php @@ -42,11 +42,11 @@ public function __construct( /** * Reservation/stay-related defaults for the space. Also carries the provider/PMS-supplied name under a `_name` key (e.g. `guesty_name`), which Seam preserves when you rename the space (read-only — managed by Seam). */ - public Space\CustomerData|null $customer_data, + public Space\CustomerData|null $customer_data = null, /** * Customer key associated with the space. */ - public string|null $customer_key, + public string|null $customer_key = null, /** * Number of devices in the space. */ @@ -58,7 +58,7 @@ public function __construct( /** * Geographic coordinates (latitude and longitude) of the space. */ - public Space\Geolocation|null $geolocation, + public Space\Geolocation|null $geolocation = null, /** * Name of the space. */ @@ -70,7 +70,7 @@ public function __construct( /** * Unique key for the space within the workspace. */ - public string|null $space_key, + public string|null $space_key = null, /** * ID of the workspace associated with the space. */ @@ -102,19 +102,19 @@ public function __construct( /** * Postal address for the space. */ - public string|null $address, + public string|null $address = null, /** * Default check-in time for reservations at the space, as HH:mm or HH:mm:ss. */ - public string|null $default_checkin_time, + public string|null $default_checkin_time = null, /** * Default check-out time for reservations at the space, as HH:mm or HH:mm:ss. */ - public string|null $default_checkout_time, + public string|null $default_checkout_time = null, /** * IANA time zone for the space, e.g. America/Los_Angeles. */ - public string|null $time_zone, + public string|null $time_zone = null, ) {} } diff --git a/src/Resources/ThermostatSchedule.php b/src/Resources/ThermostatSchedule.php index 9d719a87..e2bc5991 100644 --- a/src/Resources/ThermostatSchedule.php +++ b/src/Resources/ThermostatSchedule.php @@ -54,11 +54,11 @@ public function __construct( /** * Indicates whether a person at the thermostat can change the thermostat's settings after the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules) starts. */ - public bool|null $is_override_allowed, + public bool|null $is_override_allowed = null, /** * Number of minutes for which a person at the thermostat can change the thermostat's settings after the activation of the scheduled [climate preset](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-climate-presets). See also [Specifying Manual Override Permissions](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules#specifying-manual-override-permissions). */ - public int|null $max_override_period_minutes, + public int|null $max_override_period_minutes = null, /** * User-friendly name to identify the [thermostat schedule](https://docs.seam.co/capability-guides/thermostats/creating-and-managing-thermostat-schedules). */ diff --git a/src/Resources/UnmanagedAccessCode.php b/src/Resources/UnmanagedAccessCode.php index 5855b3ac..87d2566b 100644 --- a/src/Resources/UnmanagedAccessCode.php +++ b/src/Resources/UnmanagedAccessCode.php @@ -62,11 +62,11 @@ public function __construct( /** * Indicates that Seam cannot convert this unmanaged access code to a managed access code. Some providers do not support management of unmanaged access codes through API integrations. */ - public bool|null $cannot_be_managed, + public bool|null $cannot_be_managed = null, /** * Indicates that Seam cannot delete this unmanaged access code through the provider. If this access code needs to be deleted, it will only be possible from the manufacturer app. */ - public bool|null $cannot_delete_unmanaged_access_code, + public bool|null $cannot_delete_unmanaged_access_code = null, /** * Code used for access. Typically, a numeric or alphanumeric string. */ @@ -82,11 +82,11 @@ public function __construct( /** * Metadata for a dormakaba Oracode unmanaged access code. Only present for unmanaged access codes from dormakaba Oracode devices. */ - public UnmanagedAccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata, + public UnmanagedAccessCode\DormakabaOracodeMetadata|null $dormakaba_oracode_metadata = null, /** * Date and time after which the time-bound access code becomes inactive. */ - public string|null $ends_at, + public string|null $ends_at = null, /** * Errors associated with the [access code](https://docs.seam.co/low-level-apis/smart-locks/access-codes). */ @@ -102,7 +102,7 @@ public function __construct( /** * Date and time at which the time-bound access code becomes active. */ - public string|null $starts_at, + public string|null $starts_at = null, /** * Current status of the access code within the operational lifecycle. `set` indicates that the code is active and operational. `unset` indicates that the code exists on the provider but is not usable on the device. */ @@ -151,35 +151,35 @@ public function __construct( /** * Indicates whether the stay can be cancelled via the Dormakaba Oracode API. */ - public bool|null $is_cancellable, + public bool|null $is_cancellable = null, /** * Indicates whether early check-in is available for this stay. */ - public bool|null $is_early_checkin_able, + public bool|null $is_early_checkin_able = null, /** * Indicates whether the stay can be extended via the Dormakaba Oracode API. */ - public bool|null $is_extendable, + public bool|null $is_extendable = null, /** * Indicates whether the access code can be overridden. When false, the maximum number of overrides has been reached. */ - public bool|null $is_overridable, + public bool|null $is_overridable = null, /** * Dormakaba Oracode site name associated with this access code. */ - public string|null $site_name, + public string|null $site_name = null, /** * Dormakaba Oracode stay ID associated with this access code. */ - public float|null $stay_id, + public float|null $stay_id = null, /** * Dormakaba Oracode user level ID associated with this access code. */ - public string|null $user_level_id, + public string|null $user_level_id = null, /** * Dormakaba Oracode user level name associated with this access code. */ - public string|null $user_level_name, + public string|null $user_level_name = null, ) {} } @@ -217,11 +217,11 @@ public function __construct( /** * Indicates the type of external modification. `modified` means the code's PIN or schedule was changed. `removed` means the code was deleted from the device. */ - public string|null $change_type, + public string|null $change_type = null, /** * Date and time at which Seam created the error. */ - public string|null $created_at, + public string|null $created_at = null, /** * Unique identifier of the type of error. Enables quick recognition and categorization of the issue. */ @@ -233,13 +233,13 @@ public function __construct( /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ - public bool|null $is_bridge_error, + public bool|null $is_bridge_error = null, public bool|null $is_connected_account_error, public bool|null $is_device_error, /** * ID of the managed access code that conflicts with this managed access code, when Seam can identify it. */ - public string|null $managed_access_code_id, + public string|null $managed_access_code_id = null, /** * Detailed description of the error. Provides insights into the issue and potentially how to rectify it. */ @@ -247,11 +247,11 @@ public function __construct( /** * List of fields that were changed externally, with their previous and new values. */ - public array $modified_fields, + public array|null $modified_fields = null, /** * ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. */ - public string|null $unmanaged_access_code_id, + public string|null $unmanaged_access_code_id = null, ) {} } @@ -281,11 +281,11 @@ public function __construct( /** * Indicates the type of external modification. `modified` means the code's PIN or schedule was changed. `removed` means the code was deleted from the device. */ - public string|null $change_type, + public string|null $change_type = null, /** * Date and time at which Seam created the warning. */ - public string|null $created_at, + public string|null $created_at = null, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ @@ -293,7 +293,7 @@ public function __construct( /** * List of fields that were changed externally, with their previous and new values. */ - public array $modified_fields, + public array|null $modified_fields = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/UnmanagedAccessGrant.php b/src/Resources/UnmanagedAccessGrant.php index cc079819..7a898a81 100644 --- a/src/Resources/UnmanagedAccessGrant.php +++ b/src/Resources/UnmanagedAccessGrant.php @@ -93,7 +93,7 @@ public function __construct( /** * Reservation key for the access grant. */ - public string|null $reservation_key, + public string|null $reservation_key = null, /** * IDs of the spaces to which the Access Grant gives access. */ @@ -105,7 +105,7 @@ public function __construct( /** * ID of user identity to which the Access Grant gives access. */ - public string|null $user_identity_id, + public string|null $user_identity_id = null, /** * Warnings associated with the [access grant](https://docs.seam.co/use-cases/granting-access). */ @@ -153,7 +153,7 @@ public function __construct( /** * IDs of the devices that did not receive an access code at grant creation. Use these to identify which specific devices failed when the message reports a partial failure. */ - public array|null $missing_device_ids, + public array|null $missing_device_ids = null, ) {} } @@ -227,7 +227,7 @@ public function __construct( /** * Specific PIN code to use for this access method. Only applicable when mode is 'code'. */ - public string|null $code, + public string|null $code = null, /** * IDs of the access methods created for the requested access method. */ @@ -243,7 +243,7 @@ public function __construct( /** * Maximum number of times the instant key can be used. Only applicable when mode is 'mobile_key'. Defaults to 1 if not specified. */ - public int|null $instant_key_max_use_count, + public int|null $instant_key_max_use_count = null, /** * Access method mode. Supported values: `code`, `card`, `mobile_key`, `cloud_key`. */ @@ -290,7 +290,7 @@ public function __construct( /** * Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). */ - public array $failed_devices, + public array|null $failed_devices = null, /** * Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. */ @@ -365,7 +365,7 @@ public function __construct( /** * Common code key to ensure PIN code reuse across devices. */ - public string|null $common_code_key, + public string|null $common_code_key = null, /** * New device IDs where access codes should be created. */ diff --git a/src/Resources/UnmanagedAccessMethod.php b/src/Resources/UnmanagedAccessMethod.php index ab715685..acfc5177 100644 --- a/src/Resources/UnmanagedAccessMethod.php +++ b/src/Resources/UnmanagedAccessMethod.php @@ -50,7 +50,7 @@ public function __construct( /** * The actual PIN code for code access methods. */ - public string|null $code, + public string|null $code = null, /** * Date and time at which the access method was created. */ @@ -66,11 +66,11 @@ public function __construct( /** * Indicates whether an existing card credential must be assigned to this access method before it can be issued. Only applies to card-mode access methods on systems that support credential assignment. */ - public bool|null $is_assignment_required, + public bool|null $is_assignment_required = null, /** * Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. */ - public bool|null $is_encoding_required, + public bool|null $is_encoding_required = null, /** * Indicates whether the access method has been issued. */ @@ -78,11 +78,11 @@ public function __construct( /** * Indicates whether the access method is ready for card assignment. This is true when the access method is in card mode, has not yet been issued, and the system supports credential assignment. */ - public bool|null $is_ready_for_assignment, + public bool|null $is_ready_for_assignment = null, /** * Indicates whether the access method is ready to be encoded. This is true when the credential has been created and the card has not yet been issued. */ - public bool|null $is_ready_for_encoding, + public bool|null $is_ready_for_encoding = null, /** * Date and time at which the access method was issued. */ @@ -210,7 +210,7 @@ public function __construct( /** * ID of the original access method from which this backup access method was split, if applicable. */ - public string|null $original_access_method_id, + public string|null $original_access_method_id = null, /** * Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. */ diff --git a/src/Resources/UnmanagedDevice.php b/src/Resources/UnmanagedDevice.php index 87649de0..be566c4a 100644 --- a/src/Resources/UnmanagedDevice.php +++ b/src/Resources/UnmanagedDevice.php @@ -71,83 +71,83 @@ public function __construct( /** * Indicates whether the lock supports configuring automatic locking. */ - public bool|null $can_configure_auto_lock, + public bool|null $can_configure_auto_lock = null, /** * Indicates whether the thermostat supports cooling. */ - public bool|null $can_hvac_cool, + public bool|null $can_hvac_cool = null, /** * Indicates whether the thermostat supports heating. */ - public bool|null $can_hvac_heat, + public bool|null $can_hvac_heat = null, /** * Indicates whether the thermostat supports simultaneous heating and cooling. */ - public bool|null $can_hvac_heat_cool, + public bool|null $can_hvac_heat_cool = null, /** * Indicates whether the device supports programming offline access codes. */ - public bool|null $can_program_offline_access_codes, + public bool|null $can_program_offline_access_codes = null, /** * Indicates whether the device supports programming online access codes. */ - public bool|null $can_program_online_access_codes, + public bool|null $can_program_online_access_codes = null, /** * Indicates whether the thermostat supports different climate programs for each day of the week. */ - public bool|null $can_program_thermostat_programs_as_different_each_day, + public bool|null $can_program_thermostat_programs_as_different_each_day = null, /** * Indicates whether the thermostat supports a single climate program applied to every day. */ - public bool|null $can_program_thermostat_programs_as_same_each_day, + public bool|null $can_program_thermostat_programs_as_same_each_day = null, /** * Indicates whether the thermostat supports weekday/weekend climate programs. */ - public bool|null $can_program_thermostat_programs_as_weekday_weekend, + public bool|null $can_program_thermostat_programs_as_weekday_weekend = null, /** * Indicates whether the device supports remote locking. */ - public bool|null $can_remotely_lock, + public bool|null $can_remotely_lock = null, /** * Indicates whether the device supports remote unlocking. */ - public bool|null $can_remotely_unlock, + public bool|null $can_remotely_unlock = null, /** * Indicates whether the thermostat supports running climate programs. */ - public bool|null $can_run_thermostat_programs, + public bool|null $can_run_thermostat_programs = null, /** * Indicates whether the device supports simulating connection in a sandbox. */ - public bool|null $can_simulate_connection, + public bool|null $can_simulate_connection = null, /** * Indicates whether the device supports simulating disconnection in a sandbox. */ - public bool|null $can_simulate_disconnection, + public bool|null $can_simulate_disconnection = null, /** * Indicates whether the hub supports simulating connection in a sandbox. */ - public bool|null $can_simulate_hub_connection, + public bool|null $can_simulate_hub_connection = null, /** * Indicates whether the hub supports simulating disconnection in a sandbox. */ - public bool|null $can_simulate_hub_disconnection, + public bool|null $can_simulate_hub_disconnection = null, /** * Indicates whether the device supports simulating a paid subscription in a sandbox. */ - public bool|null $can_simulate_paid_subscription, + public bool|null $can_simulate_paid_subscription = null, /** * Indicates whether the device supports simulating removal in a sandbox. */ - public bool|null $can_simulate_removal, + public bool|null $can_simulate_removal = null, /** * Indicates whether the thermostat can be turned off. */ - public bool|null $can_turn_off_hvac, + public bool|null $can_turn_off_hvac = null, /** * Indicates whether the lock supports unlocking with an access code. */ - public bool|null $can_unlock_with_code, + public bool|null $can_unlock_with_code = null, /** * Collection of capabilities that the device supports when connected to Seam. Values are `access_code`, which indicates that the device can manage and utilize digital PIN codes for secure access; `lock`, which indicates that the device controls a door locking mechanism, enabling the remote opening and closing of doors and other entry points; `noise_detection`, which indicates that the device supports monitoring and responding to ambient noise levels; `thermostat`, which indicates that the device can regulate and adjust indoor temperatures; `battery`, which indicates that the device can manage battery life and health; and `phone`, which indicates that the device is a mobile device, such as a smartphone. **Important:** Superseded by [capability flags](https://docs.seam.co/capability-guides/device-and-system-capabilities#capability-flags). */ @@ -183,7 +183,7 @@ public function __construct( /** * Location information for the device. */ - public UnmanagedDevice\Location|null $location, + public UnmanagedDevice\Location|null $location = null, /** * properties of the device. */ @@ -234,7 +234,7 @@ public function __construct( /** * Indicates whether the error is related to [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge). */ - public bool|null $is_bridge_error, + public bool|null $is_bridge_error = null, public bool|null $is_connected_account_error, public bool|null $is_device_error, /** @@ -266,21 +266,21 @@ public function __construct( /** * Name of the device location. */ - public string|null $location_name, + public string|null $location_name = null, /** * Name of the room within the device location, when the provider reports one. */ - public string|null $room_name, + public string|null $room_name = null, /** * Time zone of the device location. */ - public string|null $time_zone, + public string|null $time_zone = null, /** * Time zone of the device location. * * @deprecated Use `time_zone` instead. */ - public string|null $timezone, + public string|null $timezone = null, ) {} } @@ -323,27 +323,27 @@ public function __construct( /** * Accessory keypad properties and state. */ - public Properties\AccessoryKeypad|null $accessory_keypad, + public Properties\AccessoryKeypad|null $accessory_keypad = null, /** * Represents the current status of the battery charge level. */ - public Properties\Battery|null $battery, + public Properties\Battery|null $battery = null, /** * Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. */ - public float|null $battery_level, + public float|null $battery_level = null, /** * Alt text for the device image. */ - public string|null $image_alt_text, + public string|null $image_alt_text = null, /** * Image URL for the device. */ - public string|null $image_url, + public string|null $image_url = null, /** * Manufacturer of the device. When a device, such as a smart lock, is connected through a smart hub, the manufacturer of the device might be different from that of the smart hub. */ - public string|null $manufacturer, + public string|null $manufacturer = null, /** * Device model-related properties. */ @@ -359,7 +359,7 @@ public function __construct( * * @deprecated use device.can_program_offline_access_codes */ - public bool|null $offline_access_codes_enabled, + public bool|null $offline_access_codes_enabled = null, /** * Indicates whether the device is online. */ @@ -369,7 +369,7 @@ public function __construct( * * @deprecated use device.can_program_online_access_codes */ - public bool|null $online_access_codes_enabled, + public bool|null $online_access_codes_enabled = null, ) {} } @@ -442,7 +442,7 @@ public function __construct( /** * Keypad battery properties. */ - public AccessoryKeypad\Battery|null $battery, + public AccessoryKeypad\Battery|null $battery = null, /** * Indicates if an accessory keypad is connected to the device. */ @@ -508,11 +508,11 @@ public function __construct( /** * @deprecated use device.properties.model.can_connect_accessory_keypad */ - public bool|null $accessory_keypad_supported, + public bool|null $accessory_keypad_supported = null, /** * Indicates whether the device can connect a accessory keypad. */ - public bool|null $can_connect_accessory_keypad, + public bool|null $can_connect_accessory_keypad = null, /** * Display name of the device model. */ @@ -520,7 +520,7 @@ public function __construct( /** * Indicates whether the device has a built in accessory keypad. */ - public bool|null $has_built_in_keypad, + public bool|null $has_built_in_keypad = null, /** * Display name that corresponds to the manufacturer-specific terminology for the device. */ @@ -528,11 +528,11 @@ public function __construct( /** * @deprecated use device.can_program_offline_access_codes. */ - public bool|null $offline_access_codes_supported, + public bool|null $offline_access_codes_supported = null, /** * @deprecated use device.can_program_online_access_codes. */ - public bool|null $online_access_codes_supported, + public bool|null $online_access_codes_supported = null, ) {} } } diff --git a/src/Resources/Webhook.php b/src/Resources/Webhook.php index 37eb5751..7b50da16 100644 --- a/src/Resources/Webhook.php +++ b/src/Resources/Webhook.php @@ -23,11 +23,11 @@ public function __construct( /** * Types of events that the [webhook](https://docs.seam.co/developer-tools/webhooks) should receive. */ - public array|null $event_types, + public array|null $event_types = null, /** * Secret associated with the [webhook](https://docs.seam.co/developer-tools/webhooks). */ - public string|null $secret, + public string|null $secret = null, /** * URL for the [webhook](https://docs.seam.co/developer-tools/webhooks). */ diff --git a/src/Resources/Workspace.php b/src/Resources/Workspace.php index 4901d453..87e16359 100644 --- a/src/Resources/Workspace.php +++ b/src/Resources/Workspace.php @@ -65,7 +65,7 @@ public function __construct( /** * Publishable key for the [workspace](https://docs.seam.co/core-concepts/workspaces). This key is used to identify the workspace in client-side applications. */ - public string|null $publishable_key, + public string|null $publishable_key = null, /** * ID of the workspace. */ @@ -97,23 +97,23 @@ public function __construct( /** * URL of the inviter logo for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). */ - public string|null $inviter_logo_url, + public string|null $inviter_logo_url = null, /** * Logo shape for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). */ - public string|null $logo_shape, + public string|null $logo_shape = null, /** * Primary button color for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). */ - public string|null $primary_button_color, + public string|null $primary_button_color = null, /** * Primary button text color for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). */ - public string|null $primary_button_text_color, + public string|null $primary_button_text_color = null, /** * Success message for [Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews) in the workspace. See also [Customize the Look and Feel of Your Connect Webviews](https://docs.seam.co/core-concepts/connect-webviews/customizing-connect-webviews#customize-the-look-and-feel-of-your-connect-webviews). */ - public string|null $success_message, + public string|null $success_message = null, ) {} } } From 74917ee79d3107555627faadcd7d81db1ffcb7a8 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 12 Aug 2026 23:59:00 -0700 Subject: [PATCH 29/35] Check for missing required params --- codegen/layouts/partials/route-method.hbs | 5 +++++ codegen/lib/layouts/route.ts | 11 ++++++++++- src/Paginator.php | 16 ++++++++++++++-- tests/PaginatorTest.php | 18 ++++++------------ 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 06c547d5..ea65269b 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -1,5 +1,10 @@ {{{methodPhpDoc this}}} public function {{methodName}}({{{signatureParams}}}): {{returnType}} { +{{#if hasRequiredParams}} + if ({{requiredParamsMissingCheck}}) { + throw new \\InvalidArgumentException("At least one parameter is required for {{path}}"); + } +{{/if}} {{#if hasParams}} $request_payload = []; diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index fbe11da4..03148a6f 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -37,6 +37,8 @@ export interface MethodLayoutContext { path: string returnType: string hasParams: boolean + hasRequiredParams: boolean + requiredParamsMissingCheck: string signatureParams: string usesActionAttempt: boolean usesOnResponse: boolean @@ -92,11 +94,16 @@ const getMethodLayoutContext = ( : 'void' const sortedParameters = sortPhpClientMethodParameters(parameters) + const requiredParameters = parameters.filter((p) => !p.isOptional) + const hasRequiredParams = requiredParameters.length > 0 + const requiredParamsMissingCheck = requiredParameters + .map((p) => `$${p.name} === null`) + .join(' && ') const signatureParams = sortedParameters .map( (p) => - `${(p.isNullable || p.isOptional) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${p.isOptional ? ' = null' : ''}`, + `${(p.isNullable || p.isOptional || hasRequiredParams) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${p.isOptional || hasRequiredParams ? ' = null' : ''}`, ) .concat( usesActionAttempt @@ -137,6 +144,8 @@ const getMethodLayoutContext = ( path, returnType, hasParams: parameters.length > 0, + hasRequiredParams, + requiredParamsMissingCheck, signatureParams, usesActionAttempt, usesOnResponse, diff --git a/src/Paginator.php b/src/Paginator.php index 812012d8..d98dfdc8 100644 --- a/src/Paginator.php +++ b/src/Paginator.php @@ -72,13 +72,25 @@ private function fetchPage(string $cursor): array $data = $request($params); - return [$data, $this->pagination_cache[$cursor] ?? new Pagination()]; + if (!array_key_exists($cursor, $this->pagination_cache)) { + throw new \InvalidArgumentException( + "Cannot use a paginator with an unpaginated endpoint", + ); + } + + return [$data, $this->pagination_cache[$cursor]]; } private function cachePagination($response, string $cursor): void { + if (!is_object($response) || !isset($response->pagination)) { + throw new \InvalidArgumentException( + "Cannot use a paginator with an unpaginated endpoint", + ); + } + $this->pagination_cache[$cursor] = Pagination::from_json( - $response->pagination ?? null, + $response->pagination, ); } diff --git a/tests/PaginatorTest.php b/tests/PaginatorTest.php index 4fbd2c39..c81460dd 100644 --- a/tests/PaginatorTest.php +++ b/tests/PaginatorTest.php @@ -124,21 +124,15 @@ public function testFlattenIteratesEveryResource(): void $this->assertSame(array_unique($ids), $ids); } - /** - * Not every list endpoint returns pagination metadata, and asking for a - * page of one that does not should not fail. - */ - public function testEndpointWithoutPaginationYieldsAnEmptyPagination(): void + public function testEndpointWithoutPaginationIsRejected(): void { $seam = $this->seam(); - $pages = $seam->createPaginator(fn($p) => $seam->workspaces->list()); - - [$workspaces, $pagination] = $pages->firstPage(); + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage("unpaginated endpoint"); - $this->assertNotEmpty($workspaces); - $this->assertInstanceOf(Pagination::class, $pagination); - $this->assertFalse($pagination->has_next_page); - $this->assertNull($pagination->next_page_cursor); + $seam + ->createPaginator(fn($p) => $seam->workspaces->list()) + ->firstPage(); } } From 0aa136cd2b40b356c2c98dd942830adfa11e6114 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 07:15:41 +0000 Subject: [PATCH 30/35] fix: emit a single backslash for the global exception class The route method partial wrote \\InvalidArgumentException, so the generated PHP carried a literal double backslash and failed to parse, breaking the Generate code job. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017FnqoQVLzdhRMBvFa6CoFG --- codegen/layouts/partials/route-method.hbs | 2 +- src/Routes/AccessCodesClient.php | 59 ++++++++-- src/Routes/AccessCodesSimulateClient.php | 11 +- src/Routes/AccessCodesUnmanagedClient.php | 30 ++++- src/Routes/AccessGrantsClient.php | 23 +++- src/Routes/AccessGrantsUnmanagedClient.php | 16 ++- src/Routes/AccessMethodsClient.php | 41 +++++-- src/Routes/AccessMethodsUnmanagedClient.php | 14 ++- src/Routes/AcsAccessGroupsClient.php | 42 ++++++- src/Routes/AcsCredentialsClient.php | 52 +++++++-- src/Routes/AcsEncodersClient.php | 28 ++++- src/Routes/AcsEncodersSimulateClient.php | 28 ++++- src/Routes/AcsEntrancesClient.php | 30 ++++- src/Routes/AcsSystemsClient.php | 21 +++- src/Routes/AcsUsersClient.php | 25 ++++- src/Routes/ActionAttemptsClient.php | 7 +- src/Routes/ClientSessionsClient.php | 14 ++- src/Routes/ConnectWebviewsClient.php | 14 ++- src/Routes/ConnectedAccountsClient.php | 21 +++- .../ConnectedAccountsSimulateClient.php | 7 +- src/Routes/CustomersClient.php | 7 +- src/Routes/DevicesClient.php | 14 ++- src/Routes/DevicesSimulateClient.php | 46 ++++++-- src/Routes/DevicesUnmanagedClient.php | 7 +- src/Routes/InstantKeysClient.php | 7 +- src/Routes/LocksClient.php | 23 +++- src/Routes/LocksSimulateClient.php | 16 ++- .../NoiseSensorsNoiseThresholdsClient.php | 49 +++++++-- src/Routes/NoiseSensorsSimulateClient.php | 7 +- src/Routes/PhonesClient.php | 14 ++- src/Routes/PhonesSimulateClient.php | 7 +- src/Routes/SpacesClient.php | 72 +++++++++--- src/Routes/ThermostatsClient.php | 103 ++++++++++++++---- src/Routes/ThermostatsDailyProgramsClient.php | 33 ++++-- src/Routes/ThermostatsSchedulesClient.php | 49 +++++++-- src/Routes/ThermostatsSimulateClient.php | 16 ++- src/Routes/UserIdentitiesClient.php | 89 ++++++++++++--- src/Routes/UserIdentitiesUnmanagedClient.php | 16 ++- src/Routes/WebhooksClient.php | 36 +++++- src/Routes/WorkspacesClient.php | 7 +- 40 files changed, 914 insertions(+), 189 deletions(-) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index ea65269b..26409c56 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -2,7 +2,7 @@ public function {{methodName}}({{{signatureParams}}}): {{returnType}} { {{#if hasRequiredParams}} if ({{requiredParamsMissingCheck}}) { - throw new \\InvalidArgumentException("At least one parameter is required for {{path}}"); + throw new \InvalidArgumentException("At least one parameter is required for {{path}}"); } {{/if}} {{#if hasParams}} diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index a648f967..35a7023e 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -55,7 +55,7 @@ public function __construct(ClientInterface $client, array $defaults) * @return AccessCode OK */ public function create( - string $device_id, + ?string $device_id = null, ?bool $allow_external_modification = null, ?bool $attempt_for_offline_device = null, ?string $code = null, @@ -72,6 +72,11 @@ public function create( ?bool $use_backup_access_code_pool = null, ?bool $use_offline_access_code = null, ): AccessCode { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/create", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -178,7 +183,7 @@ public function create( * @return array OK */ public function create_multiple( - array $device_ids, + ?array $device_ids = null, ?bool $allow_external_modification = null, ?bool $attempt_for_offline_device = null, ?string $behavior_when_code_cannot_be_shared = null, @@ -191,6 +196,11 @@ public function create_multiple( ?string $starts_at = null, ?bool $use_backup_access_code_pool = null, ): array { + if ($device_ids === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/create_multiple", + ); + } $request_payload = []; $request_payload["device_ids"] = $device_ids; @@ -260,9 +270,14 @@ public function create_multiple( * @return void OK */ public function delete( - string $access_code_id, + ?string $access_code_id = null, ?string $device_id = null, ): void { + if ($access_code_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/delete", + ); + } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -281,8 +296,13 @@ public function delete( * @param string $device_id ID of the device for which you want to generate a code. * @return AccessCode OK */ - public function generate_code(string $device_id): AccessCode + public function generate_code(?string $device_id = null): AccessCode { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/generate_code", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -426,8 +446,14 @@ public function list( * @param string $access_code_id ID of the access code for which you want to pull a backup access code. * @return AccessCode OK */ - public function pull_backup_access_code(string $access_code_id): AccessCode - { + public function pull_backup_access_code( + ?string $access_code_id = null, + ): AccessCode { + if ($access_code_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/pull_backup_access_code", + ); + } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -455,11 +481,16 @@ public function pull_backup_access_code(string $access_code_id): AccessCode * @return void OK */ public function report_device_constraints( - string $device_id, + ?string $device_id = null, ?int $max_code_length = null, ?int $min_code_length = null, ?array $supported_code_lengths = null, ): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/report_device_constraints", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -507,7 +538,7 @@ public function report_device_constraints( * @return void OK */ public function update( - string $access_code_id, + ?string $access_code_id = null, ?bool $allow_external_modification = null, ?bool $attempt_for_offline_device = null, ?string $code = null, @@ -519,6 +550,11 @@ public function update( ?string $starts_at = null, ?string $type = null, ): void { + if ($access_code_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/update", + ); + } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -584,11 +620,16 @@ public function update( * @return void OK */ public function update_multiple( - string $common_code_key, + ?string $common_code_key = null, ?string $ends_at = null, ?string $name = null, ?string $starts_at = null, ): void { + if ($common_code_key === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/update_multiple", + ); + } $request_payload = []; $request_payload["common_code_key"] = $common_code_key; diff --git a/src/Routes/AccessCodesSimulateClient.php b/src/Routes/AccessCodesSimulateClient.php index a2897660..6dd40b74 100644 --- a/src/Routes/AccessCodesSimulateClient.php +++ b/src/Routes/AccessCodesSimulateClient.php @@ -33,10 +33,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return UnmanagedAccessCode OK */ public function create_unmanaged_access_code( - string $code, - string $device_id, - string $name, + ?string $code = null, + ?string $device_id = null, + ?string $name = null, ): UnmanagedAccessCode { + if ($code === null && $device_id === null && $name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/simulate/create_unmanaged_access_code", + ); + } $request_payload = []; $request_payload["code"] = $code; diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index 8662ce2f..9de9433c 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -38,11 +38,16 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function convert_to_managed( - string $access_code_id, + ?string $access_code_id = null, ?bool $allow_external_modification = null, ?bool $force = null, ?bool $is_external_modification_allowed = null, ): void { + if ($access_code_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/unmanaged/convert_to_managed", + ); + } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -73,8 +78,13 @@ public function convert_to_managed( * @param string $access_code_id ID of the unmanaged access code that you want to delete. * @return void OK */ - public function delete(string $access_code_id): void + public function delete(?string $access_code_id = null): void { + if ($access_code_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/unmanaged/delete", + ); + } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -132,13 +142,18 @@ public function get( * @return array OK */ public function list( - string $device_id, + ?string $device_id = null, ?float $limit = null, ?string $page_cursor = null, ?string $search = null, ?string $user_identifier_key = null, ?callable $on_response = null, ): array { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/unmanaged/list", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -182,12 +197,17 @@ public function list( * @return void OK */ public function update( - string $access_code_id, - bool $is_managed, + ?string $access_code_id = null, + ?bool $is_managed = null, ?bool $allow_external_modification = null, ?bool $force = null, ?bool $is_external_modification_allowed = null, ): void { + if ($access_code_id === null && $is_managed === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/unmanaged/update", + ); + } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index a8c23f9c..c9c526c5 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -47,7 +47,7 @@ public function __construct(ClientInterface $client, array $defaults) * @return AccessGrant OK */ public function create( - array $requested_access_methods, + ?array $requested_access_methods = null, ?string $user_identity_id = null, mixed $user_identity = null, ?string $access_grant_key = null, @@ -63,6 +63,11 @@ public function create( ?array $space_keys = null, ?string $starts_at = null, ): AccessGrant { + if ($requested_access_methods === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/create", + ); + } $request_payload = []; $request_payload[ @@ -128,8 +133,13 @@ public function create( * @param string $access_grant_id ID of Access Grant to delete. * @return void OK */ - public function delete(string $access_grant_id): void + public function delete(?string $access_grant_id = null): void { + if ($access_grant_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/delete", + ); + } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; @@ -308,9 +318,14 @@ public function list( * @return AccessGrant OK */ public function request_access_methods( - string $access_grant_id, - array $requested_access_methods, + ?string $access_grant_id = null, + ?array $requested_access_methods = null, ): AccessGrant { + if ($access_grant_id === null && $requested_access_methods === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/request_access_methods", + ); + } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index 3b3d6658..850385dd 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -30,8 +30,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $access_grant_id ID of unmanaged Access Grant to get. * @return UnmanagedAccessGrant OK */ - public function get(string $access_grant_id): UnmanagedAccessGrant + public function get(?string $access_grant_id = null): UnmanagedAccessGrant { + if ($access_grant_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/unmanaged/get", + ); + } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; @@ -116,10 +121,15 @@ public function list( * @return void OK */ public function update( - string $access_grant_id, - bool $is_managed, + ?string $access_grant_id = null, + ?bool $is_managed = null, ?string $access_grant_key = null, ): void { + if ($access_grant_id === null && $is_managed === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/unmanaged/update", + ); + } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index c8bb36c9..80476d33 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -37,10 +37,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function assign_card( - string $access_method_id, - string $card_number, + ?string $access_method_id = null, + ?string $card_number = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($access_method_id === null && $card_number === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/assign_card", + ); + } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -99,10 +104,15 @@ public function delete( * @return ActionAttempt OK */ public function encode( - string $access_method_id, - string $acs_encoder_id, + ?string $access_method_id = null, + ?string $acs_encoder_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($access_method_id === null && $acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/encode", + ); + } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -128,8 +138,13 @@ public function encode( * @param string $access_method_id ID of access method to get. * @return AccessMethod OK */ - public function get(string $access_method_id): AccessMethod + public function get(?string $access_method_id = null): AccessMethod { + if ($access_method_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/get", + ); + } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -152,10 +167,15 @@ public function get(string $access_method_id): AccessMethod * @return Batch OK */ public function get_related( - array $access_method_ids, + ?array $access_method_ids = null, ?array $exclude = null, ?array $include = null, ): Batch { + if ($access_method_ids === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/get_related", + ); + } $request_payload = []; $request_payload["access_method_ids"] = $access_method_ids; @@ -252,10 +272,15 @@ public function list( * @return ActionAttempt OK */ public function unlock_door( - string $access_method_id, - string $acs_entrance_id, + ?string $access_method_id = null, + ?string $acs_entrance_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($access_method_id === null && $acs_entrance_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/unlock_door", + ); + } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; diff --git a/src/Routes/AccessMethodsUnmanagedClient.php b/src/Routes/AccessMethodsUnmanagedClient.php index 9bc0a43a..2d6d918e 100644 --- a/src/Routes/AccessMethodsUnmanagedClient.php +++ b/src/Routes/AccessMethodsUnmanagedClient.php @@ -30,8 +30,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $access_method_id ID of unmanaged access method to get. * @return UnmanagedAccessMethod OK */ - public function get(string $access_method_id): UnmanagedAccessMethod + public function get(?string $access_method_id = null): UnmanagedAccessMethod { + if ($access_method_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/unmanaged/get", + ); + } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -55,11 +60,16 @@ public function get(string $access_method_id): UnmanagedAccessMethod * @return array OK */ public function list( - string $access_grant_id, + ?string $access_grant_id = null, ?string $acs_entrance_id = null, ?string $device_id = null, ?string $space_id = null, ): array { + if ($access_grant_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/unmanaged/list", + ); + } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; diff --git a/src/Routes/AcsAccessGroupsClient.php b/src/Routes/AcsAccessGroupsClient.php index 74cb4208..6938fe4f 100644 --- a/src/Routes/AcsAccessGroupsClient.php +++ b/src/Routes/AcsAccessGroupsClient.php @@ -35,10 +35,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_user( - string $acs_access_group_id, + ?string $acs_access_group_id = null, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/access_groups/add_user", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -60,8 +65,13 @@ public function add_user( * @param string $acs_access_group_id ID of the access group that you want to delete. * @return void OK */ - public function delete(string $acs_access_group_id): void + public function delete(?string $acs_access_group_id = null): void { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/access_groups/delete", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -77,8 +87,13 @@ public function delete(string $acs_access_group_id): void * @param string $acs_access_group_id ID of the access group that you want to get. * @return AcsAccessGroup OK */ - public function get(string $acs_access_group_id): AcsAccessGroup + public function get(?string $acs_access_group_id = null): AcsAccessGroup { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/access_groups/get", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -141,8 +156,13 @@ public function list( * @return array OK */ public function list_accessible_entrances( - string $acs_access_group_id, + ?string $acs_access_group_id = null, ): array { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/access_groups/list_accessible_entrances", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -167,8 +187,13 @@ public function list_accessible_entrances( * @param string $acs_access_group_id ID of the access group for which you want to retrieve all access system users. * @return array OK */ - public function list_users(string $acs_access_group_id): array + public function list_users(?string $acs_access_group_id = null): array { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/access_groups/list_users", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -191,10 +216,15 @@ public function list_users(string $acs_access_group_id): array * @return void OK */ public function remove_user( - string $acs_access_group_id, + ?string $acs_access_group_id = null, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/access_groups/remove_user", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index 86a884c8..33e1b1db 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -34,10 +34,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function assign( - string $acs_credential_id, + ?string $acs_credential_id = null, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ($acs_credential_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/assign", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -72,7 +77,7 @@ public function assign( * @return AcsCredential OK */ public function create( - string $access_method, + ?string $access_method = null, ?string $acs_system_id = null, ?string $acs_user_id = null, ?array $allowed_acs_entrance_ids = null, @@ -86,6 +91,11 @@ public function create( ?string $user_identity_id = null, mixed $visionline_metadata = null, ): AcsCredential { + if ($access_method === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/create", + ); + } $request_payload = []; $request_payload["access_method"] = $access_method; @@ -149,8 +159,13 @@ public function create( * @param string $acs_credential_id ID of the credential that you want to delete. * @return void OK */ - public function delete(string $acs_credential_id): void + public function delete(?string $acs_credential_id = null): void { + if ($acs_credential_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/delete", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -166,8 +181,13 @@ public function delete(string $acs_credential_id): void * @param string $acs_credential_id ID of the credential that you want to get. * @return AcsCredential OK */ - public function get(string $acs_credential_id): AcsCredential + public function get(?string $acs_credential_id = null): AcsCredential { + if ($acs_credential_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/get", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -257,8 +277,14 @@ public function list( * @param string $acs_credential_id ID of the credential for which you want to retrieve all entrances to which the credential grants access. * @return array OK */ - public function list_accessible_entrances(string $acs_credential_id): array - { + public function list_accessible_entrances( + ?string $acs_credential_id = null, + ): array { + if ($acs_credential_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/list_accessible_entrances", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -286,10 +312,15 @@ public function list_accessible_entrances(string $acs_credential_id): array * @return void OK */ public function unassign( - string $acs_credential_id, + ?string $acs_credential_id = null, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ($acs_credential_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/unassign", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -314,10 +345,15 @@ public function unassign( * @return void OK */ public function update( - string $acs_credential_id, + ?string $acs_credential_id = null, ?string $code = null, ?string $ends_at = null, ): void { + if ($acs_credential_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/credentials/update", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index b7cd6c15..f1f2acc8 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -37,11 +37,16 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function encode_credential( - string $acs_encoder_id, + ?string $acs_encoder_id = null, ?string $access_method_id = null, ?string $acs_credential_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/encode_credential", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -72,8 +77,13 @@ public function encode_credential( * @param string $acs_encoder_id ID of the encoder that you want to get. * @return AcsEncoder OK */ - public function get(string $acs_encoder_id): AcsEncoder + public function get(?string $acs_encoder_id = null): AcsEncoder { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/get", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -149,10 +159,15 @@ public function list( * @return ActionAttempt OK */ public function scan_credential( - string $acs_encoder_id, + ?string $acs_encoder_id = null, mixed $salto_ks_metadata = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/scan_credential", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -185,12 +200,17 @@ public function scan_credential( * @return ActionAttempt OK */ public function scan_to_assign_credential( - string $acs_encoder_id, + ?string $acs_encoder_id = null, ?string $acs_user_id = null, mixed $salto_ks_metadata = null, ?string $user_identity_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/scan_to_assign_credential", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; diff --git a/src/Routes/AcsEncodersSimulateClient.php b/src/Routes/AcsEncodersSimulateClient.php index f1616fea..b8be31ce 100644 --- a/src/Routes/AcsEncodersSimulateClient.php +++ b/src/Routes/AcsEncodersSimulateClient.php @@ -31,10 +31,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function next_credential_encode_will_fail( - string $acs_encoder_id, + ?string $acs_encoder_id = null, ?string $error_code = null, ?string $acs_credential_id = null, ): void { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_fail", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -60,9 +65,14 @@ public function next_credential_encode_will_fail( * @return void OK */ public function next_credential_encode_will_succeed( - string $acs_encoder_id, + ?string $acs_encoder_id = null, ?string $scenario = null, ): void { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_succeed", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -86,10 +96,15 @@ public function next_credential_encode_will_succeed( * @return void OK */ public function next_credential_scan_will_fail( - string $acs_encoder_id, + ?string $acs_encoder_id = null, ?string $error_code = null, ?string $acs_credential_id_on_seam = null, ): void { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_fail", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -118,10 +133,15 @@ public function next_credential_scan_will_fail( * @return void OK */ public function next_credential_scan_will_succeed( - string $acs_encoder_id, + ?string $acs_encoder_id = null, ?string $acs_credential_id_on_seam = null, ?string $scenario = null, ): void { + if ($acs_encoder_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_succeed", + ); + } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index 1cd2c1c1..43e04dbf 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -33,8 +33,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $acs_entrance_id ID of the entrance that you want to get. * @return AcsEntrance OK */ - public function get(string $acs_entrance_id): AcsEntrance + public function get(?string $acs_entrance_id = null): AcsEntrance { + if ($acs_entrance_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/entrances/get", + ); + } $request_payload = []; $request_payload["acs_entrance_id"] = $acs_entrance_id; @@ -57,10 +62,15 @@ public function get(string $acs_entrance_id): AcsEntrance * @return void OK */ public function grant_access( - string $acs_entrance_id, + ?string $acs_entrance_id = null, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ($acs_entrance_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/entrances/grant_access", + ); + } $request_payload = []; $request_payload["acs_entrance_id"] = $acs_entrance_id; @@ -167,9 +177,14 @@ public function list( * @return array OK */ public function list_credentials_with_access( - string $acs_entrance_id, + ?string $acs_entrance_id = null, ?array $include_if = null, ): array { + if ($acs_entrance_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/entrances/list_credentials_with_access", + ); + } $request_payload = []; $request_payload["acs_entrance_id"] = $acs_entrance_id; @@ -200,10 +215,15 @@ public function list_credentials_with_access( * @return ActionAttempt OK */ public function unlock( - string $acs_credential_id, - string $acs_entrance_id, + ?string $acs_credential_id = null, + ?string $acs_entrance_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($acs_credential_id === null && $acs_entrance_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/entrances/unlock", + ); + } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index af1d681d..4943b172 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -30,8 +30,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $acs_system_id ID of the access system that you want to get. * @return AcsSystem OK */ - public function get(string $acs_system_id): AcsSystem + public function get(?string $acs_system_id = null): AcsSystem { + if ($acs_system_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/systems/get", + ); + } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; @@ -90,8 +95,13 @@ public function list( * @return array OK */ public function list_compatible_credential_manager_acs_systems( - string $acs_system_id, + ?string $acs_system_id = null, ): array { + if ($acs_system_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/systems/list_compatible_credential_manager_acs_systems", + ); + } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; @@ -116,10 +126,15 @@ public function list_compatible_credential_manager_acs_systems( * @return void OK */ public function report_devices( - string $acs_system_id, + ?string $acs_system_id = null, ?array $acs_encoders = null, ?array $acs_entrances = null, ): void { + if ($acs_system_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/systems/report_devices", + ); + } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 5b72d157..5bc7ffe2 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -33,9 +33,14 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_to_access_group( - string $acs_access_group_id, - string $acs_user_id, + ?string $acs_access_group_id = null, + ?string $acs_user_id = null, ): void { + if ($acs_access_group_id === null && $acs_user_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/add_to_access_group", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -60,8 +65,8 @@ public function add_to_access_group( * @return AcsUser OK */ public function create( - string $acs_system_id, - string $full_name, + ?string $acs_system_id = null, + ?string $full_name = null, mixed $access_schedule = null, ?array $acs_access_group_ids = null, ?string $email = null, @@ -69,6 +74,11 @@ public function create( ?string $phone_number = null, ?string $user_identity_id = null, ): AcsUser { + if ($acs_system_id === null && $full_name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/create", + ); + } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; @@ -282,10 +292,15 @@ public function list_accessible_entrances( * @return void OK */ public function remove_from_access_group( - string $acs_access_group_id, + ?string $acs_access_group_id = null, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ($acs_access_group_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/remove_from_access_group", + ); + } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index 11454f3d..adacf1df 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -33,9 +33,14 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function get( - string $action_attempt_id, + ?string $action_attempt_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($action_attempt_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /action_attempts/get", + ); + } $request_payload = []; $request_payload["action_attempt_id"] = $action_attempt_id; diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index 313d2dd6..8c54248a 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -89,8 +89,13 @@ public function create( * @param string $client_session_id ID of the client session that you want to delete. * @return void OK */ - public function delete(string $client_session_id): void + public function delete(?string $client_session_id = null): void { + if ($client_session_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /client_sessions/delete", + ); + } $request_payload = []; $request_payload["client_session_id"] = $client_session_id; @@ -280,8 +285,13 @@ public function list( * @param string $client_session_id ID of the client session that you want to revoke. * @return void OK */ - public function revoke(string $client_session_id): void + public function revoke(?string $client_session_id = null): void { + if ($client_session_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /client_sessions/revoke", + ); + } $request_payload = []; $request_payload["client_session_id"] = $client_session_id; diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index b5f171b2..d311626a 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -113,8 +113,13 @@ public function create( * @param string $connect_webview_id ID of the Connect Webview that you want to delete. * @return void OK */ - public function delete(string $connect_webview_id): void + public function delete(?string $connect_webview_id = null): void { + if ($connect_webview_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connect_webviews/delete", + ); + } $request_payload = []; $request_payload["connect_webview_id"] = $connect_webview_id; @@ -132,8 +137,13 @@ public function delete(string $connect_webview_id): void * @param string $connect_webview_id ID of the Connect Webview that you want to get. * @return ConnectWebview OK */ - public function get(string $connect_webview_id): ConnectWebview + public function get(?string $connect_webview_id = null): ConnectWebview { + if ($connect_webview_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connect_webviews/get", + ); + } $request_payload = []; $request_payload["connect_webview_id"] = $connect_webview_id; diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index abbb5914..766123a5 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -38,8 +38,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $connected_account_id ID of the connected account that you want to delete. * @return void OK */ - public function delete(string $connected_account_id): void + public function delete(?string $connected_account_id = null): void { + if ($connected_account_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connected_accounts/delete", + ); + } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -147,8 +152,13 @@ public function list( * @param string $connected_account_id ID of the connected account that you want to sync. * @return void OK */ - public function sync(string $connected_account_id): void + public function sync(?string $connected_account_id = null): void { + if ($connected_account_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connected_accounts/sync", + ); + } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -170,13 +180,18 @@ public function sync(string $connected_account_id): void * @return void OK */ public function update( - string $connected_account_id, + ?string $connected_account_id = null, ?array $accepted_capabilities = null, ?bool $automatically_manage_new_devices = null, mixed $custom_metadata = null, ?string $customer_key = null, ?string $display_name = null, ): void { + if ($connected_account_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connected_accounts/update", + ); + } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; diff --git a/src/Routes/ConnectedAccountsSimulateClient.php b/src/Routes/ConnectedAccountsSimulateClient.php index 85e0612e..cd4676ab 100644 --- a/src/Routes/ConnectedAccountsSimulateClient.php +++ b/src/Routes/ConnectedAccountsSimulateClient.php @@ -28,8 +28,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $connected_account_id ID of the connected account you want to simulate as disconnected. * @return void OK */ - public function disconnect(string $connected_account_id): void + public function disconnect(?string $connected_account_id = null): void { + if ($connected_account_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connected_accounts/simulate/disconnect", + ); + } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; diff --git a/src/Routes/CustomersClient.php b/src/Routes/CustomersClient.php index 3f26137a..ab86ff02 100644 --- a/src/Routes/CustomersClient.php +++ b/src/Routes/CustomersClient.php @@ -239,7 +239,7 @@ public function delete_data( * @return void OK */ public function push_data( - string $customer_key, + ?string $customer_key = null, ?array $access_grants = null, ?array $bookings = null, ?array $buildings = null, @@ -260,6 +260,11 @@ public function push_data( ?array $user_identities = null, ?array $users = null, ): void { + if ($customer_key === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /customers/push_data", + ); + } $request_payload = []; $request_payload["customer_key"] = $customer_key; diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index f7807686..73ae542c 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -199,8 +199,13 @@ public function list_device_providers( * @param array $devices Array of devices with provider metadata to update * @return void OK */ - public function report_provider_metadata(array $devices): void + public function report_provider_metadata(?array $devices = null): void { + if ($devices === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/report_provider_metadata", + ); + } $request_payload = []; $request_payload["devices"] = $devices; @@ -224,13 +229,18 @@ public function report_provider_metadata(array $devices): void * @return void OK */ public function update( - string $device_id, + ?string $device_id = null, ?bool $backup_access_code_pool_enabled = null, mixed $custom_metadata = null, ?bool $is_managed = null, ?string $name = null, mixed $properties = null, ): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/update", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/DevicesSimulateClient.php b/src/Routes/DevicesSimulateClient.php index b33bceac..2f8a12fa 100644 --- a/src/Routes/DevicesSimulateClient.php +++ b/src/Routes/DevicesSimulateClient.php @@ -28,8 +28,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $device_id ID of the device that you want to simulate connecting to Seam. * @return void OK */ - public function connect(string $device_id): void + public function connect(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/simulate/connect", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -48,8 +53,13 @@ public function connect(string $device_id): void * @param string $device_id ID of the device whose hub you want to reconnect. * @return void OK */ - public function connect_to_hub(string $device_id): void + public function connect_to_hub(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/simulate/connect_to_hub", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -65,8 +75,13 @@ public function connect_to_hub(string $device_id): void * @param string $device_id ID of the device that you want to simulate disconnecting from Seam. * @return void OK */ - public function disconnect(string $device_id): void + public function disconnect(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/simulate/disconnect", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -86,8 +101,13 @@ public function disconnect(string $device_id): void * @param string $device_id ID of the device whose hub you want to disconnect. * @return void OK */ - public function disconnect_from_hub(string $device_id): void + public function disconnect_from_hub(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/simulate/disconnect_from_hub", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -108,8 +128,15 @@ public function disconnect_from_hub(string $device_id): void * @param bool $is_expired * @return void OK */ - public function paid_subscription(string $device_id, bool $is_expired): void - { + public function paid_subscription( + ?string $device_id = null, + ?bool $is_expired = null, + ): void { + if ($device_id === null && $is_expired === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/simulate/paid_subscription", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -126,8 +153,13 @@ public function paid_subscription(string $device_id, bool $is_expired): void * @param string $device_id ID of the device that you want to simulate removing from Seam. * @return void OK */ - public function remove(string $device_id): void + public function remove(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/simulate/remove", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index e6c222ea..09aa1db4 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -158,10 +158,15 @@ public function list( * @return void OK */ public function update( - string $device_id, + ?string $device_id = null, mixed $custom_metadata = null, ?bool $is_managed = null, ): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/unmanaged/update", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index b04a987c..588bf0ad 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -30,8 +30,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $instant_key_id ID of the Instant Key that you want to delete. * @return void OK */ - public function delete(string $instant_key_id): void + public function delete(?string $instant_key_id = null): void { + if ($instant_key_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /instant_keys/delete", + ); + } $request_payload = []; $request_payload["instant_key_id"] = $instant_key_id; diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index 313a499f..c3f4d21b 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -37,11 +37,16 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function configure_auto_lock( - bool $auto_lock_enabled, - string $device_id, + ?bool $auto_lock_enabled = null, + ?string $device_id = null, ?float $auto_lock_delay_seconds = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($auto_lock_enabled === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /locks/configure_auto_lock", + ); + } $request_payload = []; $request_payload["auto_lock_enabled"] = $auto_lock_enabled; @@ -151,9 +156,14 @@ public function list( * @return ActionAttempt OK */ public function lock_door( - string $device_id, + ?string $device_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /locks/lock_door", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -180,9 +190,14 @@ public function lock_door( * @return ActionAttempt OK */ public function unlock_door( - string $device_id, + ?string $device_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /locks/unlock_door", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/LocksSimulateClient.php b/src/Routes/LocksSimulateClient.php index 70a5f08d..f2fd8dbd 100644 --- a/src/Routes/LocksSimulateClient.php +++ b/src/Routes/LocksSimulateClient.php @@ -34,10 +34,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function keypad_code_entry( - string $code, - string $device_id, + ?string $code = null, + ?string $device_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($code === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /locks/simulate/keypad_code_entry", + ); + } $request_payload = []; $request_payload["code"] = $code; @@ -67,9 +72,14 @@ public function keypad_code_entry( * @return ActionAttempt OK */ public function manual_lock_via_keypad( - string $device_id, + ?string $device_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /locks/simulate/manual_lock_via_keypad", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/NoiseSensorsNoiseThresholdsClient.php b/src/Routes/NoiseSensorsNoiseThresholdsClient.php index 88558830..657c0afa 100644 --- a/src/Routes/NoiseSensorsNoiseThresholdsClient.php +++ b/src/Routes/NoiseSensorsNoiseThresholdsClient.php @@ -36,13 +36,22 @@ public function __construct(ClientInterface $client, array $defaults) * @return NoiseThreshold OK */ public function create( - string $device_id, - string $ends_daily_at, - string $starts_daily_at, + ?string $device_id = null, + ?string $ends_daily_at = null, + ?string $starts_daily_at = null, ?string $name = null, ?float $noise_threshold_decibels = null, ?float $noise_threshold_nrs = null, ): NoiseThreshold { + if ( + $device_id === null && + $ends_daily_at === null && + $starts_daily_at === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /noise_sensors/noise_thresholds/create", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -78,8 +87,15 @@ public function create( * @param string $noise_threshold_id ID of the noise threshold that you want to delete. * @return void OK */ - public function delete(string $device_id, string $noise_threshold_id): void - { + public function delete( + ?string $device_id = null, + ?string $noise_threshold_id = null, + ): void { + if ($device_id === null && $noise_threshold_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /noise_sensors/noise_thresholds/delete", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -98,8 +114,13 @@ public function delete(string $device_id, string $noise_threshold_id): void * @param string $noise_threshold_id ID of the noise threshold that you want to get. * @return NoiseThreshold OK */ - public function get(string $noise_threshold_id): NoiseThreshold + public function get(?string $noise_threshold_id = null): NoiseThreshold { + if ($noise_threshold_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /noise_sensors/noise_thresholds/get", + ); + } $request_payload = []; $request_payload["noise_threshold_id"] = $noise_threshold_id; @@ -121,8 +142,13 @@ public function get(string $noise_threshold_id): NoiseThreshold * @param string $device_id ID of the device for which you want to list noise thresholds. * @return array OK */ - public function list(string $device_id): array + public function list(?string $device_id = null): array { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /noise_sensors/noise_thresholds/list", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -154,14 +180,19 @@ public function list(string $device_id): array * @return void OK */ public function update( - string $device_id, - string $noise_threshold_id, + ?string $device_id = null, + ?string $noise_threshold_id = null, ?string $ends_daily_at = null, ?string $name = null, ?float $noise_threshold_decibels = null, ?float $noise_threshold_nrs = null, ?string $starts_daily_at = null, ): void { + if ($device_id === null && $noise_threshold_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /noise_sensors/noise_thresholds/update", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/NoiseSensorsSimulateClient.php b/src/Routes/NoiseSensorsSimulateClient.php index 7d387ece..5cfd5050 100644 --- a/src/Routes/NoiseSensorsSimulateClient.php +++ b/src/Routes/NoiseSensorsSimulateClient.php @@ -28,8 +28,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $device_id ID of the device for which you want to simulate the triggering of a noise threshold. * @return void OK */ - public function trigger_noise_threshold(string $device_id): void + public function trigger_noise_threshold(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /noise_sensors/simulate/trigger_noise_threshold", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/PhonesClient.php b/src/Routes/PhonesClient.php index f56953e8..be618c20 100644 --- a/src/Routes/PhonesClient.php +++ b/src/Routes/PhonesClient.php @@ -31,8 +31,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $device_id Device ID of the phone that you want to deactivate. * @return void OK */ - public function deactivate(string $device_id): void + public function deactivate(?string $device_id = null): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /phones/deactivate", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -48,8 +53,13 @@ public function deactivate(string $device_id): void * @param string $device_id Device ID of the phone that you want to get. * @return Phone OK */ - public function get(string $device_id): Phone + public function get(?string $device_id = null): Phone { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /phones/get", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/PhonesSimulateClient.php b/src/Routes/PhonesSimulateClient.php index ba99c52e..90d94afb 100644 --- a/src/Routes/PhonesSimulateClient.php +++ b/src/Routes/PhonesSimulateClient.php @@ -34,11 +34,16 @@ public function __construct(ClientInterface $client, array $defaults) * @return Phone OK */ public function create_sandbox_phone( - string $user_identity_id, + ?string $user_identity_id = null, mixed $assa_abloy_metadata = null, ?string $custom_sdk_installation_id = null, mixed $phone_metadata = null, ): Phone { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /phones/simulate/create_sandbox_phone", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 88d64988..6e0ba858 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -33,9 +33,14 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_acs_entrances( - array $acs_entrance_ids, - string $space_id, + ?array $acs_entrance_ids = null, + ?string $space_id = null, ): void { + if ($acs_entrance_ids === null && $space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/add_acs_entrances", + ); + } $request_payload = []; $request_payload["acs_entrance_ids"] = $acs_entrance_ids; @@ -54,9 +59,14 @@ public function add_acs_entrances( * @return void OK */ public function add_connected_account( - string $connected_account_id, - string $space_id, + ?string $connected_account_id = null, + ?string $space_id = null, ): void { + if ($connected_account_id === null && $space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/add_connected_account", + ); + } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -74,8 +84,15 @@ public function add_connected_account( * @param string $space_id ID of the space to which you want to add devices. * @return void OK */ - public function add_devices(array $device_ids, string $space_id): void - { + public function add_devices( + ?array $device_ids = null, + ?string $space_id = null, + ): void { + if ($device_ids === null && $space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/add_devices", + ); + } $request_payload = []; $request_payload["device_ids"] = $device_ids; @@ -99,7 +116,7 @@ public function add_devices(array $device_ids, string $space_id): void * @return Space OK */ public function create( - string $name, + ?string $name = null, ?array $acs_entrance_ids = null, ?array $connected_account_ids = null, mixed $customer_data = null, @@ -107,6 +124,11 @@ public function create( ?array $device_ids = null, ?string $space_key = null, ): Space { + if ($name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/create", + ); + } $request_payload = []; $request_payload["name"] = $name; @@ -144,8 +166,13 @@ public function create( * @param string $space_id ID of the space that you want to delete. * @return void OK */ - public function delete(string $space_id): void + public function delete(?string $space_id = null): void { + if ($space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/delete", + ); + } $request_payload = []; $request_payload["space_id"] = $space_id; @@ -281,9 +308,14 @@ public function list( * @return void OK */ public function remove_acs_entrances( - array $acs_entrance_ids, - string $space_id, + ?array $acs_entrance_ids = null, + ?string $space_id = null, ): void { + if ($acs_entrance_ids === null && $space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/remove_acs_entrances", + ); + } $request_payload = []; $request_payload["acs_entrance_ids"] = $acs_entrance_ids; @@ -302,9 +334,14 @@ public function remove_acs_entrances( * @return void OK */ public function remove_connected_account( - string $connected_account_id, - string $space_id, + ?string $connected_account_id = null, + ?string $space_id = null, ): void { + if ($connected_account_id === null && $space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/remove_connected_account", + ); + } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -322,8 +359,15 @@ public function remove_connected_account( * @param string $space_id ID of the space from which you want to remove devices. * @return void OK */ - public function remove_devices(array $device_ids, string $space_id): void - { + public function remove_devices( + ?array $device_ids = null, + ?string $space_id = null, + ): void { + if ($device_ids === null && $space_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/remove_devices", + ); + } $request_payload = []; $request_payload["device_ids"] = $device_ids; diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index af0f5e09..7e7dd4cf 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -43,10 +43,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function activate_climate_preset( - string $climate_preset_key, - string $device_id, + ?string $climate_preset_key = null, + ?string $device_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($climate_preset_key === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/activate_climate_preset", + ); + } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -78,11 +83,16 @@ public function activate_climate_preset( * @return ActionAttempt OK */ public function cool( - string $device_id, + ?string $device_id = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/cool", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -129,8 +139,8 @@ public function cool( * @return void OK */ public function create_climate_preset( - string $climate_preset_key, - string $device_id, + ?string $climate_preset_key = null, + ?string $device_id = null, ?string $climate_preset_mode = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, @@ -142,6 +152,11 @@ public function create_climate_preset( ?bool $manual_override_allowed = null, ?string $name = null, ): void { + if ($climate_preset_key === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/create_climate_preset", + ); + } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -200,9 +215,14 @@ public function create_climate_preset( * @return void OK */ public function delete_climate_preset( - string $climate_preset_key, - string $device_id, + ?string $climate_preset_key = null, + ?string $device_id = null, ): void { + if ($climate_preset_key === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/delete_climate_preset", + ); + } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -223,11 +243,16 @@ public function delete_climate_preset( * @return ActionAttempt OK */ public function heat( - string $device_id, + ?string $device_id = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/heat", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -268,13 +293,18 @@ public function heat( * @return ActionAttempt OK */ public function heat_cool( - string $device_id, + ?string $device_id = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/heat_cool", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -370,9 +400,14 @@ public function list( * @return ActionAttempt OK */ public function off( - string $device_id, + ?string $device_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/off", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -399,9 +434,14 @@ public function off( * @return void OK */ public function set_fallback_climate_preset( - string $climate_preset_key, - string $device_id, + ?string $climate_preset_key = null, + ?string $device_id = null, ): void { + if ($climate_preset_key === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/set_fallback_climate_preset", + ); + } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -424,11 +464,16 @@ public function set_fallback_climate_preset( * @return ActionAttempt OK */ public function set_fan_mode( - string $device_id, + ?string $device_id = null, ?string $fan_mode = null, ?string $fan_mode_setting = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/set_fan_mode", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -466,14 +511,19 @@ public function set_fan_mode( * @return ActionAttempt OK */ public function set_hvac_mode( - string $device_id, - string $hvac_mode_setting, + ?string $device_id = null, + ?string $hvac_mode_setting = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null && $hvac_mode_setting === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/set_hvac_mode", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -524,12 +574,17 @@ public function set_hvac_mode( * @return void OK */ public function set_temperature_threshold( - string $device_id, + ?string $device_id = null, ?float $lower_limit_celsius = null, ?float $lower_limit_fahrenheit = null, ?float $upper_limit_celsius = null, ?float $upper_limit_fahrenheit = null, ): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/set_temperature_threshold", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -575,8 +630,8 @@ public function set_temperature_threshold( * @return void OK */ public function update_climate_preset( - string $climate_preset_key, - string $device_id, + ?string $climate_preset_key = null, + ?string $device_id = null, ?string $climate_preset_mode = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, @@ -588,6 +643,11 @@ public function update_climate_preset( ?bool $manual_override_allowed = null, ?string $name = null, ): void { + if ($climate_preset_key === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/update_climate_preset", + ); + } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -653,7 +713,7 @@ public function update_climate_preset( * @return ActionAttempt OK */ public function update_weekly_program( - string $device_id, + ?string $device_id = null, ?string $friday_program_id = null, ?string $monday_program_id = null, ?string $saturday_program_id = null, @@ -663,6 +723,11 @@ public function update_weekly_program( ?string $wednesday_program_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/update_weekly_program", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index 3d64dcbc..015fdf1b 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -35,10 +35,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return ThermostatDailyProgram OK */ public function create( - string $device_id, - string $name, - array $periods, + ?string $device_id = null, + ?string $name = null, + ?array $periods = null, ): ThermostatDailyProgram { + if ($device_id === null && $name === null && $periods === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/daily_programs/create", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -64,8 +69,13 @@ public function create( * @param string $thermostat_daily_program_id ID of the thermostat daily program that you want to delete. * @return void OK */ - public function delete(string $thermostat_daily_program_id): void + public function delete(?string $thermostat_daily_program_id = null): void { + if ($thermostat_daily_program_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/daily_programs/delete", + ); + } $request_payload = []; $request_payload[ @@ -87,11 +97,20 @@ public function delete(string $thermostat_daily_program_id): void * @return ActionAttempt OK */ public function update( - string $name, - array $periods, - string $thermostat_daily_program_id, + ?string $name = null, + ?array $periods = null, + ?string $thermostat_daily_program_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { + if ( + $name === null && + $periods === null && + $thermostat_daily_program_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/daily_programs/update", + ); + } $request_payload = []; $request_payload["name"] = $name; diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index 38ad4f07..b2b12fb7 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -37,14 +37,24 @@ public function __construct(ClientInterface $client, array $defaults) * @return ThermostatSchedule OK */ public function create( - string $climate_preset_key, - string $device_id, - string $ends_at, - string $starts_at, + ?string $climate_preset_key = null, + ?string $device_id = null, + ?string $ends_at = null, + ?string $starts_at = null, ?bool $is_override_allowed = null, ?int $max_override_period_minutes = null, ?string $name = null, ): ThermostatSchedule { + if ( + $climate_preset_key === null && + $device_id === null && + $ends_at === null && + $starts_at === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/schedules/create", + ); + } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -78,8 +88,13 @@ public function create( * @param string $thermostat_schedule_id ID of the thermostat schedule that you want to delete. * @return void OK */ - public function delete(string $thermostat_schedule_id): void + public function delete(?string $thermostat_schedule_id = null): void { + if ($thermostat_schedule_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/schedules/delete", + ); + } $request_payload = []; $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; @@ -95,8 +110,14 @@ public function delete(string $thermostat_schedule_id): void * @param string $thermostat_schedule_id ID of the thermostat schedule that you want to get. * @return ThermostatSchedule OK */ - public function get(string $thermostat_schedule_id): ThermostatSchedule - { + public function get( + ?string $thermostat_schedule_id = null, + ): ThermostatSchedule { + if ($thermostat_schedule_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/schedules/get", + ); + } $request_payload = []; $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; @@ -118,9 +139,14 @@ public function get(string $thermostat_schedule_id): ThermostatSchedule * @return array OK */ public function list( - string $device_id, + ?string $device_id = null, ?string $user_identifier_key = null, ): array { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/schedules/list", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -153,7 +179,7 @@ public function list( * @return void OK */ public function update( - string $thermostat_schedule_id, + ?string $thermostat_schedule_id = null, ?string $climate_preset_key = null, ?string $ends_at = null, ?bool $is_override_allowed = null, @@ -161,6 +187,11 @@ public function update( ?string $name = null, ?string $starts_at = null, ): void { + if ($thermostat_schedule_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/schedules/update", + ); + } $request_payload = []; $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; diff --git a/src/Routes/ThermostatsSimulateClient.php b/src/Routes/ThermostatsSimulateClient.php index 244539cd..221eedc8 100644 --- a/src/Routes/ThermostatsSimulateClient.php +++ b/src/Routes/ThermostatsSimulateClient.php @@ -34,13 +34,18 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function hvac_mode_adjusted( - string $device_id, - string $hvac_mode, + ?string $device_id = null, + ?string $hvac_mode = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, ): void { + if ($device_id === null && $hvac_mode === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/simulate/hvac_mode_adjusted", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -82,10 +87,15 @@ public function hvac_mode_adjusted( * @return void OK */ public function temperature_reached( - string $device_id, + ?string $device_id = null, ?float $temperature_celsius = null, ?float $temperature_fahrenheit = null, ): void { + if ($device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /thermostats/simulate/temperature_reached", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index c30cf586..deaf9f2d 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -46,10 +46,15 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_acs_user( - string $acs_user_id, + ?string $acs_user_id = null, ?string $user_identity_id = null, ?string $user_identity_key = null, ): void { + if ($acs_user_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/add_acs_user", + ); + } $request_payload = []; $request_payload["acs_user_id"] = $acs_user_id; @@ -115,8 +120,13 @@ public function create( * @param string $user_identity_id ID of the user identity that you want to delete. * @return void OK */ - public function delete(string $user_identity_id): void + public function delete(?string $user_identity_id = null): void { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/delete", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -135,10 +145,15 @@ public function delete(string $user_identity_id): void * @return InstantKey OK */ public function generate_instant_key( - string $user_identity_id, + ?string $user_identity_id = null, ?string $customization_profile_id = null, ?float $max_use_count = null, ): InstantKey { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/generate_instant_key", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -199,9 +214,14 @@ public function get( * @return void OK */ public function grant_access_to_device( - string $device_id, - string $user_identity_id, + ?string $device_id = null, + ?string $user_identity_id = null, ): void { + if ($device_id === null && $user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/grant_access_to_device", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -280,8 +300,14 @@ public function list( * @param string $user_identity_id ID of the user identity for which you want to retrieve all accessible devices. * @return array OK */ - public function list_accessible_devices(string $user_identity_id): array - { + public function list_accessible_devices( + ?string $user_identity_id = null, + ): array { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/list_accessible_devices", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -303,8 +329,14 @@ public function list_accessible_devices(string $user_identity_id): array * @param string $user_identity_id ID of the user identity for which you want to retrieve all accessible entrances. * @return array OK */ - public function list_accessible_entrances(string $user_identity_id): array - { + public function list_accessible_entrances( + ?string $user_identity_id = null, + ): array { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/list_accessible_entrances", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -329,8 +361,13 @@ public function list_accessible_entrances(string $user_identity_id): array * @param string $user_identity_id ID of the user identity for which you want to retrieve all access systems. * @return array OK */ - public function list_acs_systems(string $user_identity_id): array + public function list_acs_systems(?string $user_identity_id = null): array { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/list_acs_systems", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -350,8 +387,13 @@ public function list_acs_systems(string $user_identity_id): array * @param string $user_identity_id ID of the user identity for which you want to retrieve all access system users. * @return array OK */ - public function list_acs_users(string $user_identity_id): array + public function list_acs_users(?string $user_identity_id = null): array { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/list_acs_users", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -373,9 +415,14 @@ public function list_acs_users(string $user_identity_id): array * @return void OK */ public function remove_acs_user( - string $acs_user_id, - string $user_identity_id, + ?string $acs_user_id = null, + ?string $user_identity_id = null, ): void { + if ($acs_user_id === null && $user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/remove_acs_user", + ); + } $request_payload = []; $request_payload["acs_user_id"] = $acs_user_id; @@ -394,9 +441,14 @@ public function remove_acs_user( * @return void OK */ public function revoke_access_to_device( - string $device_id, - string $user_identity_id, + ?string $device_id = null, + ?string $user_identity_id = null, ): void { + if ($device_id === null && $user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/revoke_access_to_device", + ); + } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -420,12 +472,17 @@ public function revoke_access_to_device( * @return void OK */ public function update( - string $user_identity_id, + ?string $user_identity_id = null, ?string $email_address = null, ?string $full_name = null, ?string $phone_number = null, ?string $user_identity_key = null, ): void { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/update", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index 92a77c06..ec53b23c 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -30,8 +30,13 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $user_identity_id ID of the unmanaged user identity that you want to get. * @return UnmanagedUserIdentity OK */ - public function get(string $user_identity_id): UnmanagedUserIdentity + public function get(?string $user_identity_id = null): UnmanagedUserIdentity { + if ($user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/unmanaged/get", + ); + } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -104,10 +109,15 @@ public function list( * @return void OK */ public function update( - bool $is_managed, - string $user_identity_id, + ?bool $is_managed = null, + ?string $user_identity_id = null, ?string $user_identity_key = null, ): void { + if ($is_managed === null && $user_identity_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/unmanaged/update", + ); + } $request_payload = []; $request_payload["is_managed"] = $is_managed; diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index 29cbb648..6a6dba94 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -31,8 +31,15 @@ public function __construct(ClientInterface $client, array $defaults) * @param array $event_types Types of events that you want the new webhook to receive. * @return Webhook OK */ - public function create(string $url, ?array $event_types = null): Webhook - { + public function create( + ?string $url = null, + ?array $event_types = null, + ): Webhook { + if ($url === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /webhooks/create", + ); + } $request_payload = []; $request_payload["url"] = $url; @@ -55,8 +62,13 @@ public function create(string $url, ?array $event_types = null): Webhook * @param string $webhook_id ID of the webhook that you want to delete. * @return void OK */ - public function delete(string $webhook_id): void + public function delete(?string $webhook_id = null): void { + if ($webhook_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /webhooks/delete", + ); + } $request_payload = []; $request_payload["webhook_id"] = $webhook_id; @@ -72,8 +84,13 @@ public function delete(string $webhook_id): void * @param string $webhook_id ID of the webhook that you want to get. * @return Webhook OK */ - public function get(string $webhook_id): Webhook + public function get(?string $webhook_id = null): Webhook { + if ($webhook_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /webhooks/get", + ); + } $request_payload = []; $request_payload["webhook_id"] = $webhook_id; @@ -106,8 +123,15 @@ public function list(): array * @param string $webhook_id ID of the webhook that you want to update. * @return void OK */ - public function update(array $event_types, string $webhook_id): void - { + public function update( + ?array $event_types = null, + ?string $webhook_id = null, + ): void { + if ($event_types === null && $webhook_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /webhooks/update", + ); + } $request_payload = []; $request_payload["event_types"] = $event_types; diff --git a/src/Routes/WorkspacesClient.php b/src/Routes/WorkspacesClient.php index 752b182b..d378acef 100644 --- a/src/Routes/WorkspacesClient.php +++ b/src/Routes/WorkspacesClient.php @@ -42,7 +42,7 @@ public function __construct(ClientInterface $client, array $defaults) * @return Workspace OK */ public function create( - string $name, + ?string $name = null, ?string $company_name = null, ?string $connect_partner_name = null, mixed $connect_webview_customization = null, @@ -53,6 +53,11 @@ public function create( ?string $webview_primary_button_text_color = null, ?string $webview_success_message = null, ): Workspace { + if ($name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /workspaces/create", + ); + } $request_payload = []; $request_payload["name"] = $name; From cb2650209d82c5130e2502efb620e3f9fd551ed0 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 07:18:46 +0000 Subject: [PATCH 31/35] 4.0.0-beta.2 --- package-lock.json | 4 ++-- package.json | 2 +- src/Utils/PackageVersion.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index da2a2607..acb24669 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.1", + "version": "4.0.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.1", + "version": "4.0.0-beta.2", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index 2bce7fc9..a384b169 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.1", + "version": "4.0.0-beta.2", "type": "module", "private": true, "license": "MIT", diff --git a/src/Utils/PackageVersion.php b/src/Utils/PackageVersion.php index fecc898a..a194602d 100644 --- a/src/Utils/PackageVersion.php +++ b/src/Utils/PackageVersion.php @@ -10,7 +10,7 @@ class PackageVersion * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.1"; + public const VERSION = "4.0.0-beta.2"; public static function get(): string { From 1652d01bcb1fb6694234d057ac498e2ce6da54bf Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 07:27:39 +0000 Subject: [PATCH 32/35] refactor: let PHP enforce the required endpoint parameters Giving every parameter a null default disabled PHP's own arity check, so the generated methods hand rolled one. The check joined the required parameters with && and only fired when every one of them was missing, letting a call that omits some of them through to the API. PHP already rejects a missing argument, and no endpoint method declares an optional parameter ahead of a required one, so the defaults are dropped and the check goes away with them. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017FnqoQVLzdhRMBvFa6CoFG --- codegen/layouts/partials/route-method.hbs | 5 - codegen/lib/layouts/route.ts | 12 +- src/Routes/AccessCodesClient.php | 59 ++-------- src/Routes/AccessCodesSimulateClient.php | 11 +- src/Routes/AccessCodesUnmanagedClient.php | 30 +---- src/Routes/AccessGrantsClient.php | 23 +--- src/Routes/AccessGrantsUnmanagedClient.php | 16 +-- src/Routes/AccessMethodsClient.php | 41 ++----- src/Routes/AccessMethodsUnmanagedClient.php | 14 +-- src/Routes/AcsAccessGroupsClient.php | 42 +------ src/Routes/AcsCredentialsClient.php | 52 ++------- src/Routes/AcsEncodersClient.php | 28 +---- src/Routes/AcsEncodersSimulateClient.php | 28 +---- src/Routes/AcsEntrancesClient.php | 30 +---- src/Routes/AcsSystemsClient.php | 21 +--- src/Routes/AcsUsersClient.php | 25 +---- src/Routes/ActionAttemptsClient.php | 7 +- src/Routes/ClientSessionsClient.php | 14 +-- src/Routes/ConnectWebviewsClient.php | 14 +-- src/Routes/ConnectedAccountsClient.php | 21 +--- .../ConnectedAccountsSimulateClient.php | 7 +- src/Routes/CustomersClient.php | 7 +- src/Routes/DevicesClient.php | 14 +-- src/Routes/DevicesSimulateClient.php | 46 ++------ src/Routes/DevicesUnmanagedClient.php | 7 +- src/Routes/InstantKeysClient.php | 7 +- src/Routes/LocksClient.php | 23 +--- src/Routes/LocksSimulateClient.php | 16 +-- .../NoiseSensorsNoiseThresholdsClient.php | 49 ++------- src/Routes/NoiseSensorsSimulateClient.php | 7 +- src/Routes/PhonesClient.php | 14 +-- src/Routes/PhonesSimulateClient.php | 7 +- src/Routes/SpacesClient.php | 72 +++--------- src/Routes/ThermostatsClient.php | 103 ++++-------------- src/Routes/ThermostatsDailyProgramsClient.php | 33 ++---- src/Routes/ThermostatsSchedulesClient.php | 49 ++------- src/Routes/ThermostatsSimulateClient.php | 16 +-- src/Routes/UserIdentitiesClient.php | 89 +++------------ src/Routes/UserIdentitiesUnmanagedClient.php | 16 +-- src/Routes/WebhooksClient.php | 36 +----- src/Routes/WorkspacesClient.php | 7 +- 41 files changed, 189 insertions(+), 929 deletions(-) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 26409c56..06c547d5 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -1,10 +1,5 @@ {{{methodPhpDoc this}}} public function {{methodName}}({{{signatureParams}}}): {{returnType}} { -{{#if hasRequiredParams}} - if ({{requiredParamsMissingCheck}}) { - throw new \InvalidArgumentException("At least one parameter is required for {{path}}"); - } -{{/if}} {{#if hasParams}} $request_payload = []; diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 03148a6f..1f6d5d6e 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -37,8 +37,6 @@ export interface MethodLayoutContext { path: string returnType: string hasParams: boolean - hasRequiredParams: boolean - requiredParamsMissingCheck: string signatureParams: string usesActionAttempt: boolean usesOnResponse: boolean @@ -94,16 +92,10 @@ const getMethodLayoutContext = ( : 'void' const sortedParameters = sortPhpClientMethodParameters(parameters) - const requiredParameters = parameters.filter((p) => !p.isOptional) - const hasRequiredParams = requiredParameters.length > 0 - const requiredParamsMissingCheck = requiredParameters - .map((p) => `$${p.name} === null`) - .join(' && ') - const signatureParams = sortedParameters .map( (p) => - `${(p.isNullable || p.isOptional || hasRequiredParams) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${p.isOptional || hasRequiredParams ? ' = null' : ''}`, + `${(p.isNullable || p.isOptional) && p.type !== 'mixed' ? '?' : ''}${p.type} $${p.name}${p.isOptional ? ' = null' : ''}`, ) .concat( usesActionAttempt @@ -144,8 +136,6 @@ const getMethodLayoutContext = ( path, returnType, hasParams: parameters.length > 0, - hasRequiredParams, - requiredParamsMissingCheck, signatureParams, usesActionAttempt, usesOnResponse, diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index 35a7023e..a648f967 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -55,7 +55,7 @@ public function __construct(ClientInterface $client, array $defaults) * @return AccessCode OK */ public function create( - ?string $device_id = null, + string $device_id, ?bool $allow_external_modification = null, ?bool $attempt_for_offline_device = null, ?string $code = null, @@ -72,11 +72,6 @@ public function create( ?bool $use_backup_access_code_pool = null, ?bool $use_offline_access_code = null, ): AccessCode { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/create", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -183,7 +178,7 @@ public function create( * @return array OK */ public function create_multiple( - ?array $device_ids = null, + array $device_ids, ?bool $allow_external_modification = null, ?bool $attempt_for_offline_device = null, ?string $behavior_when_code_cannot_be_shared = null, @@ -196,11 +191,6 @@ public function create_multiple( ?string $starts_at = null, ?bool $use_backup_access_code_pool = null, ): array { - if ($device_ids === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/create_multiple", - ); - } $request_payload = []; $request_payload["device_ids"] = $device_ids; @@ -270,14 +260,9 @@ public function create_multiple( * @return void OK */ public function delete( - ?string $access_code_id = null, + string $access_code_id, ?string $device_id = null, ): void { - if ($access_code_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/delete", - ); - } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -296,13 +281,8 @@ public function delete( * @param string $device_id ID of the device for which you want to generate a code. * @return AccessCode OK */ - public function generate_code(?string $device_id = null): AccessCode + public function generate_code(string $device_id): AccessCode { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/generate_code", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -446,14 +426,8 @@ public function list( * @param string $access_code_id ID of the access code for which you want to pull a backup access code. * @return AccessCode OK */ - public function pull_backup_access_code( - ?string $access_code_id = null, - ): AccessCode { - if ($access_code_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/pull_backup_access_code", - ); - } + public function pull_backup_access_code(string $access_code_id): AccessCode + { $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -481,16 +455,11 @@ public function pull_backup_access_code( * @return void OK */ public function report_device_constraints( - ?string $device_id = null, + string $device_id, ?int $max_code_length = null, ?int $min_code_length = null, ?array $supported_code_lengths = null, ): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/report_device_constraints", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -538,7 +507,7 @@ public function report_device_constraints( * @return void OK */ public function update( - ?string $access_code_id = null, + string $access_code_id, ?bool $allow_external_modification = null, ?bool $attempt_for_offline_device = null, ?string $code = null, @@ -550,11 +519,6 @@ public function update( ?string $starts_at = null, ?string $type = null, ): void { - if ($access_code_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/update", - ); - } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -620,16 +584,11 @@ public function update( * @return void OK */ public function update_multiple( - ?string $common_code_key = null, + string $common_code_key, ?string $ends_at = null, ?string $name = null, ?string $starts_at = null, ): void { - if ($common_code_key === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/update_multiple", - ); - } $request_payload = []; $request_payload["common_code_key"] = $common_code_key; diff --git a/src/Routes/AccessCodesSimulateClient.php b/src/Routes/AccessCodesSimulateClient.php index 6dd40b74..a2897660 100644 --- a/src/Routes/AccessCodesSimulateClient.php +++ b/src/Routes/AccessCodesSimulateClient.php @@ -33,15 +33,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return UnmanagedAccessCode OK */ public function create_unmanaged_access_code( - ?string $code = null, - ?string $device_id = null, - ?string $name = null, + string $code, + string $device_id, + string $name, ): UnmanagedAccessCode { - if ($code === null && $device_id === null && $name === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/simulate/create_unmanaged_access_code", - ); - } $request_payload = []; $request_payload["code"] = $code; diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index 9de9433c..8662ce2f 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -38,16 +38,11 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function convert_to_managed( - ?string $access_code_id = null, + string $access_code_id, ?bool $allow_external_modification = null, ?bool $force = null, ?bool $is_external_modification_allowed = null, ): void { - if ($access_code_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/unmanaged/convert_to_managed", - ); - } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -78,13 +73,8 @@ public function convert_to_managed( * @param string $access_code_id ID of the unmanaged access code that you want to delete. * @return void OK */ - public function delete(?string $access_code_id = null): void + public function delete(string $access_code_id): void { - if ($access_code_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/unmanaged/delete", - ); - } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; @@ -142,18 +132,13 @@ public function get( * @return array OK */ public function list( - ?string $device_id = null, + string $device_id, ?float $limit = null, ?string $page_cursor = null, ?string $search = null, ?string $user_identifier_key = null, ?callable $on_response = null, ): array { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/unmanaged/list", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -197,17 +182,12 @@ public function list( * @return void OK */ public function update( - ?string $access_code_id = null, - ?bool $is_managed = null, + string $access_code_id, + bool $is_managed, ?bool $allow_external_modification = null, ?bool $force = null, ?bool $is_external_modification_allowed = null, ): void { - if ($access_code_id === null && $is_managed === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_codes/unmanaged/update", - ); - } $request_payload = []; $request_payload["access_code_id"] = $access_code_id; diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index c9c526c5..a8c23f9c 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -47,7 +47,7 @@ public function __construct(ClientInterface $client, array $defaults) * @return AccessGrant OK */ public function create( - ?array $requested_access_methods = null, + array $requested_access_methods, ?string $user_identity_id = null, mixed $user_identity = null, ?string $access_grant_key = null, @@ -63,11 +63,6 @@ public function create( ?array $space_keys = null, ?string $starts_at = null, ): AccessGrant { - if ($requested_access_methods === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_grants/create", - ); - } $request_payload = []; $request_payload[ @@ -133,13 +128,8 @@ public function create( * @param string $access_grant_id ID of Access Grant to delete. * @return void OK */ - public function delete(?string $access_grant_id = null): void + public function delete(string $access_grant_id): void { - if ($access_grant_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_grants/delete", - ); - } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; @@ -318,14 +308,9 @@ public function list( * @return AccessGrant OK */ public function request_access_methods( - ?string $access_grant_id = null, - ?array $requested_access_methods = null, + string $access_grant_id, + array $requested_access_methods, ): AccessGrant { - if ($access_grant_id === null && $requested_access_methods === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_grants/request_access_methods", - ); - } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; diff --git a/src/Routes/AccessGrantsUnmanagedClient.php b/src/Routes/AccessGrantsUnmanagedClient.php index 850385dd..3b3d6658 100644 --- a/src/Routes/AccessGrantsUnmanagedClient.php +++ b/src/Routes/AccessGrantsUnmanagedClient.php @@ -30,13 +30,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $access_grant_id ID of unmanaged Access Grant to get. * @return UnmanagedAccessGrant OK */ - public function get(?string $access_grant_id = null): UnmanagedAccessGrant + public function get(string $access_grant_id): UnmanagedAccessGrant { - if ($access_grant_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_grants/unmanaged/get", - ); - } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; @@ -121,15 +116,10 @@ public function list( * @return void OK */ public function update( - ?string $access_grant_id = null, - ?bool $is_managed = null, + string $access_grant_id, + bool $is_managed, ?string $access_grant_key = null, ): void { - if ($access_grant_id === null && $is_managed === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_grants/unmanaged/update", - ); - } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index 80476d33..c8bb36c9 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -37,15 +37,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function assign_card( - ?string $access_method_id = null, - ?string $card_number = null, + string $access_method_id, + string $card_number, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($access_method_id === null && $card_number === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/assign_card", - ); - } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -104,15 +99,10 @@ public function delete( * @return ActionAttempt OK */ public function encode( - ?string $access_method_id = null, - ?string $acs_encoder_id = null, + string $access_method_id, + string $acs_encoder_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($access_method_id === null && $acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/encode", - ); - } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -138,13 +128,8 @@ public function encode( * @param string $access_method_id ID of access method to get. * @return AccessMethod OK */ - public function get(?string $access_method_id = null): AccessMethod + public function get(string $access_method_id): AccessMethod { - if ($access_method_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/get", - ); - } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -167,15 +152,10 @@ public function get(?string $access_method_id = null): AccessMethod * @return Batch OK */ public function get_related( - ?array $access_method_ids = null, + array $access_method_ids, ?array $exclude = null, ?array $include = null, ): Batch { - if ($access_method_ids === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/get_related", - ); - } $request_payload = []; $request_payload["access_method_ids"] = $access_method_ids; @@ -272,15 +252,10 @@ public function list( * @return ActionAttempt OK */ public function unlock_door( - ?string $access_method_id = null, - ?string $acs_entrance_id = null, + string $access_method_id, + string $acs_entrance_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($access_method_id === null && $acs_entrance_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/unlock_door", - ); - } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; diff --git a/src/Routes/AccessMethodsUnmanagedClient.php b/src/Routes/AccessMethodsUnmanagedClient.php index 2d6d918e..9bc0a43a 100644 --- a/src/Routes/AccessMethodsUnmanagedClient.php +++ b/src/Routes/AccessMethodsUnmanagedClient.php @@ -30,13 +30,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $access_method_id ID of unmanaged access method to get. * @return UnmanagedAccessMethod OK */ - public function get(?string $access_method_id = null): UnmanagedAccessMethod + public function get(string $access_method_id): UnmanagedAccessMethod { - if ($access_method_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/unmanaged/get", - ); - } $request_payload = []; $request_payload["access_method_id"] = $access_method_id; @@ -60,16 +55,11 @@ public function get(?string $access_method_id = null): UnmanagedAccessMethod * @return array OK */ public function list( - ?string $access_grant_id = null, + string $access_grant_id, ?string $acs_entrance_id = null, ?string $device_id = null, ?string $space_id = null, ): array { - if ($access_grant_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /access_methods/unmanaged/list", - ); - } $request_payload = []; $request_payload["access_grant_id"] = $access_grant_id; diff --git a/src/Routes/AcsAccessGroupsClient.php b/src/Routes/AcsAccessGroupsClient.php index 6938fe4f..74cb4208 100644 --- a/src/Routes/AcsAccessGroupsClient.php +++ b/src/Routes/AcsAccessGroupsClient.php @@ -35,15 +35,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_user( - ?string $acs_access_group_id = null, + string $acs_access_group_id, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/access_groups/add_user", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -65,13 +60,8 @@ public function add_user( * @param string $acs_access_group_id ID of the access group that you want to delete. * @return void OK */ - public function delete(?string $acs_access_group_id = null): void + public function delete(string $acs_access_group_id): void { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/access_groups/delete", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -87,13 +77,8 @@ public function delete(?string $acs_access_group_id = null): void * @param string $acs_access_group_id ID of the access group that you want to get. * @return AcsAccessGroup OK */ - public function get(?string $acs_access_group_id = null): AcsAccessGroup + public function get(string $acs_access_group_id): AcsAccessGroup { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/access_groups/get", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -156,13 +141,8 @@ public function list( * @return array OK */ public function list_accessible_entrances( - ?string $acs_access_group_id = null, + string $acs_access_group_id, ): array { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/access_groups/list_accessible_entrances", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -187,13 +167,8 @@ public function list_accessible_entrances( * @param string $acs_access_group_id ID of the access group for which you want to retrieve all access system users. * @return array OK */ - public function list_users(?string $acs_access_group_id = null): array + public function list_users(string $acs_access_group_id): array { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/access_groups/list_users", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -216,15 +191,10 @@ public function list_users(?string $acs_access_group_id = null): array * @return void OK */ public function remove_user( - ?string $acs_access_group_id = null, + string $acs_access_group_id, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/access_groups/remove_user", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; diff --git a/src/Routes/AcsCredentialsClient.php b/src/Routes/AcsCredentialsClient.php index 33e1b1db..86a884c8 100644 --- a/src/Routes/AcsCredentialsClient.php +++ b/src/Routes/AcsCredentialsClient.php @@ -34,15 +34,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function assign( - ?string $acs_credential_id = null, + string $acs_credential_id, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { - if ($acs_credential_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/assign", - ); - } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -77,7 +72,7 @@ public function assign( * @return AcsCredential OK */ public function create( - ?string $access_method = null, + string $access_method, ?string $acs_system_id = null, ?string $acs_user_id = null, ?array $allowed_acs_entrance_ids = null, @@ -91,11 +86,6 @@ public function create( ?string $user_identity_id = null, mixed $visionline_metadata = null, ): AcsCredential { - if ($access_method === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/create", - ); - } $request_payload = []; $request_payload["access_method"] = $access_method; @@ -159,13 +149,8 @@ public function create( * @param string $acs_credential_id ID of the credential that you want to delete. * @return void OK */ - public function delete(?string $acs_credential_id = null): void + public function delete(string $acs_credential_id): void { - if ($acs_credential_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/delete", - ); - } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -181,13 +166,8 @@ public function delete(?string $acs_credential_id = null): void * @param string $acs_credential_id ID of the credential that you want to get. * @return AcsCredential OK */ - public function get(?string $acs_credential_id = null): AcsCredential + public function get(string $acs_credential_id): AcsCredential { - if ($acs_credential_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/get", - ); - } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -277,14 +257,8 @@ public function list( * @param string $acs_credential_id ID of the credential for which you want to retrieve all entrances to which the credential grants access. * @return array OK */ - public function list_accessible_entrances( - ?string $acs_credential_id = null, - ): array { - if ($acs_credential_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/list_accessible_entrances", - ); - } + public function list_accessible_entrances(string $acs_credential_id): array + { $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -312,15 +286,10 @@ public function list_accessible_entrances( * @return void OK */ public function unassign( - ?string $acs_credential_id = null, + string $acs_credential_id, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { - if ($acs_credential_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/unassign", - ); - } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; @@ -345,15 +314,10 @@ public function unassign( * @return void OK */ public function update( - ?string $acs_credential_id = null, + string $acs_credential_id, ?string $code = null, ?string $ends_at = null, ): void { - if ($acs_credential_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/credentials/update", - ); - } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index f1f2acc8..b7cd6c15 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -37,16 +37,11 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function encode_credential( - ?string $acs_encoder_id = null, + string $acs_encoder_id, ?string $access_method_id = null, ?string $acs_credential_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/encode_credential", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -77,13 +72,8 @@ public function encode_credential( * @param string $acs_encoder_id ID of the encoder that you want to get. * @return AcsEncoder OK */ - public function get(?string $acs_encoder_id = null): AcsEncoder + public function get(string $acs_encoder_id): AcsEncoder { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/get", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -159,15 +149,10 @@ public function list( * @return ActionAttempt OK */ public function scan_credential( - ?string $acs_encoder_id = null, + string $acs_encoder_id, mixed $salto_ks_metadata = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/scan_credential", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -200,17 +185,12 @@ public function scan_credential( * @return ActionAttempt OK */ public function scan_to_assign_credential( - ?string $acs_encoder_id = null, + string $acs_encoder_id, ?string $acs_user_id = null, mixed $salto_ks_metadata = null, ?string $user_identity_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/scan_to_assign_credential", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; diff --git a/src/Routes/AcsEncodersSimulateClient.php b/src/Routes/AcsEncodersSimulateClient.php index b8be31ce..f1616fea 100644 --- a/src/Routes/AcsEncodersSimulateClient.php +++ b/src/Routes/AcsEncodersSimulateClient.php @@ -31,15 +31,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function next_credential_encode_will_fail( - ?string $acs_encoder_id = null, + string $acs_encoder_id, ?string $error_code = null, ?string $acs_credential_id = null, ): void { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_fail", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -65,14 +60,9 @@ public function next_credential_encode_will_fail( * @return void OK */ public function next_credential_encode_will_succeed( - ?string $acs_encoder_id = null, + string $acs_encoder_id, ?string $scenario = null, ): void { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/simulate/next_credential_encode_will_succeed", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -96,15 +86,10 @@ public function next_credential_encode_will_succeed( * @return void OK */ public function next_credential_scan_will_fail( - ?string $acs_encoder_id = null, + string $acs_encoder_id, ?string $error_code = null, ?string $acs_credential_id_on_seam = null, ): void { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_fail", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; @@ -133,15 +118,10 @@ public function next_credential_scan_will_fail( * @return void OK */ public function next_credential_scan_will_succeed( - ?string $acs_encoder_id = null, + string $acs_encoder_id, ?string $acs_credential_id_on_seam = null, ?string $scenario = null, ): void { - if ($acs_encoder_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/encoders/simulate/next_credential_scan_will_succeed", - ); - } $request_payload = []; $request_payload["acs_encoder_id"] = $acs_encoder_id; diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index 43e04dbf..1cd2c1c1 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -33,13 +33,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $acs_entrance_id ID of the entrance that you want to get. * @return AcsEntrance OK */ - public function get(?string $acs_entrance_id = null): AcsEntrance + public function get(string $acs_entrance_id): AcsEntrance { - if ($acs_entrance_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/entrances/get", - ); - } $request_payload = []; $request_payload["acs_entrance_id"] = $acs_entrance_id; @@ -62,15 +57,10 @@ public function get(?string $acs_entrance_id = null): AcsEntrance * @return void OK */ public function grant_access( - ?string $acs_entrance_id = null, + string $acs_entrance_id, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { - if ($acs_entrance_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/entrances/grant_access", - ); - } $request_payload = []; $request_payload["acs_entrance_id"] = $acs_entrance_id; @@ -177,14 +167,9 @@ public function list( * @return array OK */ public function list_credentials_with_access( - ?string $acs_entrance_id = null, + string $acs_entrance_id, ?array $include_if = null, ): array { - if ($acs_entrance_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/entrances/list_credentials_with_access", - ); - } $request_payload = []; $request_payload["acs_entrance_id"] = $acs_entrance_id; @@ -215,15 +200,10 @@ public function list_credentials_with_access( * @return ActionAttempt OK */ public function unlock( - ?string $acs_credential_id = null, - ?string $acs_entrance_id = null, + string $acs_credential_id, + string $acs_entrance_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($acs_credential_id === null && $acs_entrance_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/entrances/unlock", - ); - } $request_payload = []; $request_payload["acs_credential_id"] = $acs_credential_id; diff --git a/src/Routes/AcsSystemsClient.php b/src/Routes/AcsSystemsClient.php index 4943b172..af1d681d 100644 --- a/src/Routes/AcsSystemsClient.php +++ b/src/Routes/AcsSystemsClient.php @@ -30,13 +30,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $acs_system_id ID of the access system that you want to get. * @return AcsSystem OK */ - public function get(?string $acs_system_id = null): AcsSystem + public function get(string $acs_system_id): AcsSystem { - if ($acs_system_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/systems/get", - ); - } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; @@ -95,13 +90,8 @@ public function list( * @return array OK */ public function list_compatible_credential_manager_acs_systems( - ?string $acs_system_id = null, + string $acs_system_id, ): array { - if ($acs_system_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/systems/list_compatible_credential_manager_acs_systems", - ); - } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; @@ -126,15 +116,10 @@ public function list_compatible_credential_manager_acs_systems( * @return void OK */ public function report_devices( - ?string $acs_system_id = null, + string $acs_system_id, ?array $acs_encoders = null, ?array $acs_entrances = null, ): void { - if ($acs_system_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/systems/report_devices", - ); - } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 5bc7ffe2..5b72d157 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -33,14 +33,9 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_to_access_group( - ?string $acs_access_group_id = null, - ?string $acs_user_id = null, + string $acs_access_group_id, + string $acs_user_id, ): void { - if ($acs_access_group_id === null && $acs_user_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/users/add_to_access_group", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; @@ -65,8 +60,8 @@ public function add_to_access_group( * @return AcsUser OK */ public function create( - ?string $acs_system_id = null, - ?string $full_name = null, + string $acs_system_id, + string $full_name, mixed $access_schedule = null, ?array $acs_access_group_ids = null, ?string $email = null, @@ -74,11 +69,6 @@ public function create( ?string $phone_number = null, ?string $user_identity_id = null, ): AcsUser { - if ($acs_system_id === null && $full_name === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/users/create", - ); - } $request_payload = []; $request_payload["acs_system_id"] = $acs_system_id; @@ -292,15 +282,10 @@ public function list_accessible_entrances( * @return void OK */ public function remove_from_access_group( - ?string $acs_access_group_id = null, + string $acs_access_group_id, ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { - if ($acs_access_group_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /acs/users/remove_from_access_group", - ); - } $request_payload = []; $request_payload["acs_access_group_id"] = $acs_access_group_id; diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index adacf1df..11454f3d 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -33,14 +33,9 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function get( - ?string $action_attempt_id = null, + string $action_attempt_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($action_attempt_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /action_attempts/get", - ); - } $request_payload = []; $request_payload["action_attempt_id"] = $action_attempt_id; diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index 8c54248a..313d2dd6 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -89,13 +89,8 @@ public function create( * @param string $client_session_id ID of the client session that you want to delete. * @return void OK */ - public function delete(?string $client_session_id = null): void + public function delete(string $client_session_id): void { - if ($client_session_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /client_sessions/delete", - ); - } $request_payload = []; $request_payload["client_session_id"] = $client_session_id; @@ -285,13 +280,8 @@ public function list( * @param string $client_session_id ID of the client session that you want to revoke. * @return void OK */ - public function revoke(?string $client_session_id = null): void + public function revoke(string $client_session_id): void { - if ($client_session_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /client_sessions/revoke", - ); - } $request_payload = []; $request_payload["client_session_id"] = $client_session_id; diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index d311626a..b5f171b2 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -113,13 +113,8 @@ public function create( * @param string $connect_webview_id ID of the Connect Webview that you want to delete. * @return void OK */ - public function delete(?string $connect_webview_id = null): void + public function delete(string $connect_webview_id): void { - if ($connect_webview_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /connect_webviews/delete", - ); - } $request_payload = []; $request_payload["connect_webview_id"] = $connect_webview_id; @@ -137,13 +132,8 @@ public function delete(?string $connect_webview_id = null): void * @param string $connect_webview_id ID of the Connect Webview that you want to get. * @return ConnectWebview OK */ - public function get(?string $connect_webview_id = null): ConnectWebview + public function get(string $connect_webview_id): ConnectWebview { - if ($connect_webview_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /connect_webviews/get", - ); - } $request_payload = []; $request_payload["connect_webview_id"] = $connect_webview_id; diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index 766123a5..abbb5914 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -38,13 +38,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $connected_account_id ID of the connected account that you want to delete. * @return void OK */ - public function delete(?string $connected_account_id = null): void + public function delete(string $connected_account_id): void { - if ($connected_account_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /connected_accounts/delete", - ); - } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -152,13 +147,8 @@ public function list( * @param string $connected_account_id ID of the connected account that you want to sync. * @return void OK */ - public function sync(?string $connected_account_id = null): void + public function sync(string $connected_account_id): void { - if ($connected_account_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /connected_accounts/sync", - ); - } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -180,18 +170,13 @@ public function sync(?string $connected_account_id = null): void * @return void OK */ public function update( - ?string $connected_account_id = null, + string $connected_account_id, ?array $accepted_capabilities = null, ?bool $automatically_manage_new_devices = null, mixed $custom_metadata = null, ?string $customer_key = null, ?string $display_name = null, ): void { - if ($connected_account_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /connected_accounts/update", - ); - } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; diff --git a/src/Routes/ConnectedAccountsSimulateClient.php b/src/Routes/ConnectedAccountsSimulateClient.php index cd4676ab..85e0612e 100644 --- a/src/Routes/ConnectedAccountsSimulateClient.php +++ b/src/Routes/ConnectedAccountsSimulateClient.php @@ -28,13 +28,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $connected_account_id ID of the connected account you want to simulate as disconnected. * @return void OK */ - public function disconnect(?string $connected_account_id = null): void + public function disconnect(string $connected_account_id): void { - if ($connected_account_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /connected_accounts/simulate/disconnect", - ); - } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; diff --git a/src/Routes/CustomersClient.php b/src/Routes/CustomersClient.php index ab86ff02..3f26137a 100644 --- a/src/Routes/CustomersClient.php +++ b/src/Routes/CustomersClient.php @@ -239,7 +239,7 @@ public function delete_data( * @return void OK */ public function push_data( - ?string $customer_key = null, + string $customer_key, ?array $access_grants = null, ?array $bookings = null, ?array $buildings = null, @@ -260,11 +260,6 @@ public function push_data( ?array $user_identities = null, ?array $users = null, ): void { - if ($customer_key === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /customers/push_data", - ); - } $request_payload = []; $request_payload["customer_key"] = $customer_key; diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 73ae542c..f7807686 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -199,13 +199,8 @@ public function list_device_providers( * @param array $devices Array of devices with provider metadata to update * @return void OK */ - public function report_provider_metadata(?array $devices = null): void + public function report_provider_metadata(array $devices): void { - if ($devices === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/report_provider_metadata", - ); - } $request_payload = []; $request_payload["devices"] = $devices; @@ -229,18 +224,13 @@ public function report_provider_metadata(?array $devices = null): void * @return void OK */ public function update( - ?string $device_id = null, + string $device_id, ?bool $backup_access_code_pool_enabled = null, mixed $custom_metadata = null, ?bool $is_managed = null, ?string $name = null, mixed $properties = null, ): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/update", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/DevicesSimulateClient.php b/src/Routes/DevicesSimulateClient.php index 2f8a12fa..b33bceac 100644 --- a/src/Routes/DevicesSimulateClient.php +++ b/src/Routes/DevicesSimulateClient.php @@ -28,13 +28,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $device_id ID of the device that you want to simulate connecting to Seam. * @return void OK */ - public function connect(?string $device_id = null): void + public function connect(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/simulate/connect", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -53,13 +48,8 @@ public function connect(?string $device_id = null): void * @param string $device_id ID of the device whose hub you want to reconnect. * @return void OK */ - public function connect_to_hub(?string $device_id = null): void + public function connect_to_hub(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/simulate/connect_to_hub", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -75,13 +65,8 @@ public function connect_to_hub(?string $device_id = null): void * @param string $device_id ID of the device that you want to simulate disconnecting from Seam. * @return void OK */ - public function disconnect(?string $device_id = null): void + public function disconnect(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/simulate/disconnect", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -101,13 +86,8 @@ public function disconnect(?string $device_id = null): void * @param string $device_id ID of the device whose hub you want to disconnect. * @return void OK */ - public function disconnect_from_hub(?string $device_id = null): void + public function disconnect_from_hub(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/simulate/disconnect_from_hub", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -128,15 +108,8 @@ public function disconnect_from_hub(?string $device_id = null): void * @param bool $is_expired * @return void OK */ - public function paid_subscription( - ?string $device_id = null, - ?bool $is_expired = null, - ): void { - if ($device_id === null && $is_expired === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/simulate/paid_subscription", - ); - } + public function paid_subscription(string $device_id, bool $is_expired): void + { $request_payload = []; $request_payload["device_id"] = $device_id; @@ -153,13 +126,8 @@ public function paid_subscription( * @param string $device_id ID of the device that you want to simulate removing from Seam. * @return void OK */ - public function remove(?string $device_id = null): void + public function remove(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/simulate/remove", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index 09aa1db4..e6c222ea 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -158,15 +158,10 @@ public function list( * @return void OK */ public function update( - ?string $device_id = null, + string $device_id, mixed $custom_metadata = null, ?bool $is_managed = null, ): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /devices/unmanaged/update", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index 588bf0ad..b04a987c 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -30,13 +30,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $instant_key_id ID of the Instant Key that you want to delete. * @return void OK */ - public function delete(?string $instant_key_id = null): void + public function delete(string $instant_key_id): void { - if ($instant_key_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /instant_keys/delete", - ); - } $request_payload = []; $request_payload["instant_key_id"] = $instant_key_id; diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index c3f4d21b..313a499f 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -37,16 +37,11 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function configure_auto_lock( - ?bool $auto_lock_enabled = null, - ?string $device_id = null, + bool $auto_lock_enabled, + string $device_id, ?float $auto_lock_delay_seconds = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($auto_lock_enabled === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /locks/configure_auto_lock", - ); - } $request_payload = []; $request_payload["auto_lock_enabled"] = $auto_lock_enabled; @@ -156,14 +151,9 @@ public function list( * @return ActionAttempt OK */ public function lock_door( - ?string $device_id = null, + string $device_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /locks/lock_door", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -190,14 +180,9 @@ public function lock_door( * @return ActionAttempt OK */ public function unlock_door( - ?string $device_id = null, + string $device_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /locks/unlock_door", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/LocksSimulateClient.php b/src/Routes/LocksSimulateClient.php index f2fd8dbd..70a5f08d 100644 --- a/src/Routes/LocksSimulateClient.php +++ b/src/Routes/LocksSimulateClient.php @@ -34,15 +34,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function keypad_code_entry( - ?string $code = null, - ?string $device_id = null, + string $code, + string $device_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($code === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /locks/simulate/keypad_code_entry", - ); - } $request_payload = []; $request_payload["code"] = $code; @@ -72,14 +67,9 @@ public function keypad_code_entry( * @return ActionAttempt OK */ public function manual_lock_via_keypad( - ?string $device_id = null, + string $device_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /locks/simulate/manual_lock_via_keypad", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/NoiseSensorsNoiseThresholdsClient.php b/src/Routes/NoiseSensorsNoiseThresholdsClient.php index 657c0afa..88558830 100644 --- a/src/Routes/NoiseSensorsNoiseThresholdsClient.php +++ b/src/Routes/NoiseSensorsNoiseThresholdsClient.php @@ -36,22 +36,13 @@ public function __construct(ClientInterface $client, array $defaults) * @return NoiseThreshold OK */ public function create( - ?string $device_id = null, - ?string $ends_daily_at = null, - ?string $starts_daily_at = null, + string $device_id, + string $ends_daily_at, + string $starts_daily_at, ?string $name = null, ?float $noise_threshold_decibels = null, ?float $noise_threshold_nrs = null, ): NoiseThreshold { - if ( - $device_id === null && - $ends_daily_at === null && - $starts_daily_at === null - ) { - throw new \InvalidArgumentException( - "At least one parameter is required for /noise_sensors/noise_thresholds/create", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -87,15 +78,8 @@ public function create( * @param string $noise_threshold_id ID of the noise threshold that you want to delete. * @return void OK */ - public function delete( - ?string $device_id = null, - ?string $noise_threshold_id = null, - ): void { - if ($device_id === null && $noise_threshold_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /noise_sensors/noise_thresholds/delete", - ); - } + public function delete(string $device_id, string $noise_threshold_id): void + { $request_payload = []; $request_payload["device_id"] = $device_id; @@ -114,13 +98,8 @@ public function delete( * @param string $noise_threshold_id ID of the noise threshold that you want to get. * @return NoiseThreshold OK */ - public function get(?string $noise_threshold_id = null): NoiseThreshold + public function get(string $noise_threshold_id): NoiseThreshold { - if ($noise_threshold_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /noise_sensors/noise_thresholds/get", - ); - } $request_payload = []; $request_payload["noise_threshold_id"] = $noise_threshold_id; @@ -142,13 +121,8 @@ public function get(?string $noise_threshold_id = null): NoiseThreshold * @param string $device_id ID of the device for which you want to list noise thresholds. * @return array OK */ - public function list(?string $device_id = null): array + public function list(string $device_id): array { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /noise_sensors/noise_thresholds/list", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -180,19 +154,14 @@ public function list(?string $device_id = null): array * @return void OK */ public function update( - ?string $device_id = null, - ?string $noise_threshold_id = null, + string $device_id, + string $noise_threshold_id, ?string $ends_daily_at = null, ?string $name = null, ?float $noise_threshold_decibels = null, ?float $noise_threshold_nrs = null, ?string $starts_daily_at = null, ): void { - if ($device_id === null && $noise_threshold_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /noise_sensors/noise_thresholds/update", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/NoiseSensorsSimulateClient.php b/src/Routes/NoiseSensorsSimulateClient.php index 5cfd5050..7d387ece 100644 --- a/src/Routes/NoiseSensorsSimulateClient.php +++ b/src/Routes/NoiseSensorsSimulateClient.php @@ -28,13 +28,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $device_id ID of the device for which you want to simulate the triggering of a noise threshold. * @return void OK */ - public function trigger_noise_threshold(?string $device_id = null): void + public function trigger_noise_threshold(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /noise_sensors/simulate/trigger_noise_threshold", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/PhonesClient.php b/src/Routes/PhonesClient.php index be618c20..f56953e8 100644 --- a/src/Routes/PhonesClient.php +++ b/src/Routes/PhonesClient.php @@ -31,13 +31,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $device_id Device ID of the phone that you want to deactivate. * @return void OK */ - public function deactivate(?string $device_id = null): void + public function deactivate(string $device_id): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /phones/deactivate", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -53,13 +48,8 @@ public function deactivate(?string $device_id = null): void * @param string $device_id Device ID of the phone that you want to get. * @return Phone OK */ - public function get(?string $device_id = null): Phone + public function get(string $device_id): Phone { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /phones/get", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/PhonesSimulateClient.php b/src/Routes/PhonesSimulateClient.php index 90d94afb..ba99c52e 100644 --- a/src/Routes/PhonesSimulateClient.php +++ b/src/Routes/PhonesSimulateClient.php @@ -34,16 +34,11 @@ public function __construct(ClientInterface $client, array $defaults) * @return Phone OK */ public function create_sandbox_phone( - ?string $user_identity_id = null, + string $user_identity_id, mixed $assa_abloy_metadata = null, ?string $custom_sdk_installation_id = null, mixed $phone_metadata = null, ): Phone { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /phones/simulate/create_sandbox_phone", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 6e0ba858..88d64988 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -33,14 +33,9 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_acs_entrances( - ?array $acs_entrance_ids = null, - ?string $space_id = null, + array $acs_entrance_ids, + string $space_id, ): void { - if ($acs_entrance_ids === null && $space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/add_acs_entrances", - ); - } $request_payload = []; $request_payload["acs_entrance_ids"] = $acs_entrance_ids; @@ -59,14 +54,9 @@ public function add_acs_entrances( * @return void OK */ public function add_connected_account( - ?string $connected_account_id = null, - ?string $space_id = null, + string $connected_account_id, + string $space_id, ): void { - if ($connected_account_id === null && $space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/add_connected_account", - ); - } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -84,15 +74,8 @@ public function add_connected_account( * @param string $space_id ID of the space to which you want to add devices. * @return void OK */ - public function add_devices( - ?array $device_ids = null, - ?string $space_id = null, - ): void { - if ($device_ids === null && $space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/add_devices", - ); - } + public function add_devices(array $device_ids, string $space_id): void + { $request_payload = []; $request_payload["device_ids"] = $device_ids; @@ -116,7 +99,7 @@ public function add_devices( * @return Space OK */ public function create( - ?string $name = null, + string $name, ?array $acs_entrance_ids = null, ?array $connected_account_ids = null, mixed $customer_data = null, @@ -124,11 +107,6 @@ public function create( ?array $device_ids = null, ?string $space_key = null, ): Space { - if ($name === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/create", - ); - } $request_payload = []; $request_payload["name"] = $name; @@ -166,13 +144,8 @@ public function create( * @param string $space_id ID of the space that you want to delete. * @return void OK */ - public function delete(?string $space_id = null): void + public function delete(string $space_id): void { - if ($space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/delete", - ); - } $request_payload = []; $request_payload["space_id"] = $space_id; @@ -308,14 +281,9 @@ public function list( * @return void OK */ public function remove_acs_entrances( - ?array $acs_entrance_ids = null, - ?string $space_id = null, + array $acs_entrance_ids, + string $space_id, ): void { - if ($acs_entrance_ids === null && $space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/remove_acs_entrances", - ); - } $request_payload = []; $request_payload["acs_entrance_ids"] = $acs_entrance_ids; @@ -334,14 +302,9 @@ public function remove_acs_entrances( * @return void OK */ public function remove_connected_account( - ?string $connected_account_id = null, - ?string $space_id = null, + string $connected_account_id, + string $space_id, ): void { - if ($connected_account_id === null && $space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/remove_connected_account", - ); - } $request_payload = []; $request_payload["connected_account_id"] = $connected_account_id; @@ -359,15 +322,8 @@ public function remove_connected_account( * @param string $space_id ID of the space from which you want to remove devices. * @return void OK */ - public function remove_devices( - ?array $device_ids = null, - ?string $space_id = null, - ): void { - if ($device_ids === null && $space_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /spaces/remove_devices", - ); - } + public function remove_devices(array $device_ids, string $space_id): void + { $request_payload = []; $request_payload["device_ids"] = $device_ids; diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index 7e7dd4cf..af0f5e09 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -43,15 +43,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return ActionAttempt OK */ public function activate_climate_preset( - ?string $climate_preset_key = null, - ?string $device_id = null, + string $climate_preset_key, + string $device_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($climate_preset_key === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/activate_climate_preset", - ); - } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -83,16 +78,11 @@ public function activate_climate_preset( * @return ActionAttempt OK */ public function cool( - ?string $device_id = null, + string $device_id, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/cool", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -139,8 +129,8 @@ public function cool( * @return void OK */ public function create_climate_preset( - ?string $climate_preset_key = null, - ?string $device_id = null, + string $climate_preset_key, + string $device_id, ?string $climate_preset_mode = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, @@ -152,11 +142,6 @@ public function create_climate_preset( ?bool $manual_override_allowed = null, ?string $name = null, ): void { - if ($climate_preset_key === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/create_climate_preset", - ); - } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -215,14 +200,9 @@ public function create_climate_preset( * @return void OK */ public function delete_climate_preset( - ?string $climate_preset_key = null, - ?string $device_id = null, + string $climate_preset_key, + string $device_id, ): void { - if ($climate_preset_key === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/delete_climate_preset", - ); - } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -243,16 +223,11 @@ public function delete_climate_preset( * @return ActionAttempt OK */ public function heat( - ?string $device_id = null, + string $device_id, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/heat", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -293,18 +268,13 @@ public function heat( * @return ActionAttempt OK */ public function heat_cool( - ?string $device_id = null, + string $device_id, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/heat_cool", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -400,14 +370,9 @@ public function list( * @return ActionAttempt OK */ public function off( - ?string $device_id = null, + string $device_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/off", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -434,14 +399,9 @@ public function off( * @return void OK */ public function set_fallback_climate_preset( - ?string $climate_preset_key = null, - ?string $device_id = null, + string $climate_preset_key, + string $device_id, ): void { - if ($climate_preset_key === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/set_fallback_climate_preset", - ); - } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -464,16 +424,11 @@ public function set_fallback_climate_preset( * @return ActionAttempt OK */ public function set_fan_mode( - ?string $device_id = null, + string $device_id, ?string $fan_mode = null, ?string $fan_mode_setting = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/set_fan_mode", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -511,19 +466,14 @@ public function set_fan_mode( * @return ActionAttempt OK */ public function set_hvac_mode( - ?string $device_id = null, - ?string $hvac_mode_setting = null, + string $device_id, + string $hvac_mode_setting, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null && $hvac_mode_setting === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/set_hvac_mode", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -574,17 +524,12 @@ public function set_hvac_mode( * @return void OK */ public function set_temperature_threshold( - ?string $device_id = null, + string $device_id, ?float $lower_limit_celsius = null, ?float $lower_limit_fahrenheit = null, ?float $upper_limit_celsius = null, ?float $upper_limit_fahrenheit = null, ): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/set_temperature_threshold", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -630,8 +575,8 @@ public function set_temperature_threshold( * @return void OK */ public function update_climate_preset( - ?string $climate_preset_key = null, - ?string $device_id = null, + string $climate_preset_key, + string $device_id, ?string $climate_preset_mode = null, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, @@ -643,11 +588,6 @@ public function update_climate_preset( ?bool $manual_override_allowed = null, ?string $name = null, ): void { - if ($climate_preset_key === null && $device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/update_climate_preset", - ); - } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -713,7 +653,7 @@ public function update_climate_preset( * @return ActionAttempt OK */ public function update_weekly_program( - ?string $device_id = null, + string $device_id, ?string $friday_program_id = null, ?string $monday_program_id = null, ?string $saturday_program_id = null, @@ -723,11 +663,6 @@ public function update_weekly_program( ?string $wednesday_program_id = null, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/update_weekly_program", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/ThermostatsDailyProgramsClient.php b/src/Routes/ThermostatsDailyProgramsClient.php index 015fdf1b..3d64dcbc 100644 --- a/src/Routes/ThermostatsDailyProgramsClient.php +++ b/src/Routes/ThermostatsDailyProgramsClient.php @@ -35,15 +35,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return ThermostatDailyProgram OK */ public function create( - ?string $device_id = null, - ?string $name = null, - ?array $periods = null, + string $device_id, + string $name, + array $periods, ): ThermostatDailyProgram { - if ($device_id === null && $name === null && $periods === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/daily_programs/create", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -69,13 +64,8 @@ public function create( * @param string $thermostat_daily_program_id ID of the thermostat daily program that you want to delete. * @return void OK */ - public function delete(?string $thermostat_daily_program_id = null): void + public function delete(string $thermostat_daily_program_id): void { - if ($thermostat_daily_program_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/daily_programs/delete", - ); - } $request_payload = []; $request_payload[ @@ -97,20 +87,11 @@ public function delete(?string $thermostat_daily_program_id = null): void * @return ActionAttempt OK */ public function update( - ?string $name = null, - ?array $periods = null, - ?string $thermostat_daily_program_id = null, + string $name, + array $periods, + string $thermostat_daily_program_id, bool|array|null $wait_for_action_attempt = null, ): ActionAttempt { - if ( - $name === null && - $periods === null && - $thermostat_daily_program_id === null - ) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/daily_programs/update", - ); - } $request_payload = []; $request_payload["name"] = $name; diff --git a/src/Routes/ThermostatsSchedulesClient.php b/src/Routes/ThermostatsSchedulesClient.php index b2b12fb7..38ad4f07 100644 --- a/src/Routes/ThermostatsSchedulesClient.php +++ b/src/Routes/ThermostatsSchedulesClient.php @@ -37,24 +37,14 @@ public function __construct(ClientInterface $client, array $defaults) * @return ThermostatSchedule OK */ public function create( - ?string $climate_preset_key = null, - ?string $device_id = null, - ?string $ends_at = null, - ?string $starts_at = null, + string $climate_preset_key, + string $device_id, + string $ends_at, + string $starts_at, ?bool $is_override_allowed = null, ?int $max_override_period_minutes = null, ?string $name = null, ): ThermostatSchedule { - if ( - $climate_preset_key === null && - $device_id === null && - $ends_at === null && - $starts_at === null - ) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/schedules/create", - ); - } $request_payload = []; $request_payload["climate_preset_key"] = $climate_preset_key; @@ -88,13 +78,8 @@ public function create( * @param string $thermostat_schedule_id ID of the thermostat schedule that you want to delete. * @return void OK */ - public function delete(?string $thermostat_schedule_id = null): void + public function delete(string $thermostat_schedule_id): void { - if ($thermostat_schedule_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/schedules/delete", - ); - } $request_payload = []; $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; @@ -110,14 +95,8 @@ public function delete(?string $thermostat_schedule_id = null): void * @param string $thermostat_schedule_id ID of the thermostat schedule that you want to get. * @return ThermostatSchedule OK */ - public function get( - ?string $thermostat_schedule_id = null, - ): ThermostatSchedule { - if ($thermostat_schedule_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/schedules/get", - ); - } + public function get(string $thermostat_schedule_id): ThermostatSchedule + { $request_payload = []; $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; @@ -139,14 +118,9 @@ public function get( * @return array OK */ public function list( - ?string $device_id = null, + string $device_id, ?string $user_identifier_key = null, ): array { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/schedules/list", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -179,7 +153,7 @@ public function list( * @return void OK */ public function update( - ?string $thermostat_schedule_id = null, + string $thermostat_schedule_id, ?string $climate_preset_key = null, ?string $ends_at = null, ?bool $is_override_allowed = null, @@ -187,11 +161,6 @@ public function update( ?string $name = null, ?string $starts_at = null, ): void { - if ($thermostat_schedule_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/schedules/update", - ); - } $request_payload = []; $request_payload["thermostat_schedule_id"] = $thermostat_schedule_id; diff --git a/src/Routes/ThermostatsSimulateClient.php b/src/Routes/ThermostatsSimulateClient.php index 221eedc8..244539cd 100644 --- a/src/Routes/ThermostatsSimulateClient.php +++ b/src/Routes/ThermostatsSimulateClient.php @@ -34,18 +34,13 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function hvac_mode_adjusted( - ?string $device_id = null, - ?string $hvac_mode = null, + string $device_id, + string $hvac_mode, ?float $cooling_set_point_celsius = null, ?float $cooling_set_point_fahrenheit = null, ?float $heating_set_point_celsius = null, ?float $heating_set_point_fahrenheit = null, ): void { - if ($device_id === null && $hvac_mode === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/simulate/hvac_mode_adjusted", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -87,15 +82,10 @@ public function hvac_mode_adjusted( * @return void OK */ public function temperature_reached( - ?string $device_id = null, + string $device_id, ?float $temperature_celsius = null, ?float $temperature_fahrenheit = null, ): void { - if ($device_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /thermostats/simulate/temperature_reached", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index deaf9f2d..c30cf586 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -46,15 +46,10 @@ public function __construct(ClientInterface $client, array $defaults) * @return void OK */ public function add_acs_user( - ?string $acs_user_id = null, + string $acs_user_id, ?string $user_identity_id = null, ?string $user_identity_key = null, ): void { - if ($acs_user_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/add_acs_user", - ); - } $request_payload = []; $request_payload["acs_user_id"] = $acs_user_id; @@ -120,13 +115,8 @@ public function create( * @param string $user_identity_id ID of the user identity that you want to delete. * @return void OK */ - public function delete(?string $user_identity_id = null): void + public function delete(string $user_identity_id): void { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/delete", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -145,15 +135,10 @@ public function delete(?string $user_identity_id = null): void * @return InstantKey OK */ public function generate_instant_key( - ?string $user_identity_id = null, + string $user_identity_id, ?string $customization_profile_id = null, ?float $max_use_count = null, ): InstantKey { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/generate_instant_key", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -214,14 +199,9 @@ public function get( * @return void OK */ public function grant_access_to_device( - ?string $device_id = null, - ?string $user_identity_id = null, + string $device_id, + string $user_identity_id, ): void { - if ($device_id === null && $user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/grant_access_to_device", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -300,14 +280,8 @@ public function list( * @param string $user_identity_id ID of the user identity for which you want to retrieve all accessible devices. * @return array OK */ - public function list_accessible_devices( - ?string $user_identity_id = null, - ): array { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/list_accessible_devices", - ); - } + public function list_accessible_devices(string $user_identity_id): array + { $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -329,14 +303,8 @@ public function list_accessible_devices( * @param string $user_identity_id ID of the user identity for which you want to retrieve all accessible entrances. * @return array OK */ - public function list_accessible_entrances( - ?string $user_identity_id = null, - ): array { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/list_accessible_entrances", - ); - } + public function list_accessible_entrances(string $user_identity_id): array + { $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -361,13 +329,8 @@ public function list_accessible_entrances( * @param string $user_identity_id ID of the user identity for which you want to retrieve all access systems. * @return array OK */ - public function list_acs_systems(?string $user_identity_id = null): array + public function list_acs_systems(string $user_identity_id): array { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/list_acs_systems", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -387,13 +350,8 @@ public function list_acs_systems(?string $user_identity_id = null): array * @param string $user_identity_id ID of the user identity for which you want to retrieve all access system users. * @return array OK */ - public function list_acs_users(?string $user_identity_id = null): array + public function list_acs_users(string $user_identity_id): array { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/list_acs_users", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -415,14 +373,9 @@ public function list_acs_users(?string $user_identity_id = null): array * @return void OK */ public function remove_acs_user( - ?string $acs_user_id = null, - ?string $user_identity_id = null, + string $acs_user_id, + string $user_identity_id, ): void { - if ($acs_user_id === null && $user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/remove_acs_user", - ); - } $request_payload = []; $request_payload["acs_user_id"] = $acs_user_id; @@ -441,14 +394,9 @@ public function remove_acs_user( * @return void OK */ public function revoke_access_to_device( - ?string $device_id = null, - ?string $user_identity_id = null, + string $device_id, + string $user_identity_id, ): void { - if ($device_id === null && $user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/revoke_access_to_device", - ); - } $request_payload = []; $request_payload["device_id"] = $device_id; @@ -472,17 +420,12 @@ public function revoke_access_to_device( * @return void OK */ public function update( - ?string $user_identity_id = null, + string $user_identity_id, ?string $email_address = null, ?string $full_name = null, ?string $phone_number = null, ?string $user_identity_key = null, ): void { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/update", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; diff --git a/src/Routes/UserIdentitiesUnmanagedClient.php b/src/Routes/UserIdentitiesUnmanagedClient.php index ec53b23c..92a77c06 100644 --- a/src/Routes/UserIdentitiesUnmanagedClient.php +++ b/src/Routes/UserIdentitiesUnmanagedClient.php @@ -30,13 +30,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param string $user_identity_id ID of the unmanaged user identity that you want to get. * @return UnmanagedUserIdentity OK */ - public function get(?string $user_identity_id = null): UnmanagedUserIdentity + public function get(string $user_identity_id): UnmanagedUserIdentity { - if ($user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/unmanaged/get", - ); - } $request_payload = []; $request_payload["user_identity_id"] = $user_identity_id; @@ -109,15 +104,10 @@ public function list( * @return void OK */ public function update( - ?bool $is_managed = null, - ?string $user_identity_id = null, + bool $is_managed, + string $user_identity_id, ?string $user_identity_key = null, ): void { - if ($is_managed === null && $user_identity_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /user_identities/unmanaged/update", - ); - } $request_payload = []; $request_payload["is_managed"] = $is_managed; diff --git a/src/Routes/WebhooksClient.php b/src/Routes/WebhooksClient.php index 6a6dba94..29cbb648 100644 --- a/src/Routes/WebhooksClient.php +++ b/src/Routes/WebhooksClient.php @@ -31,15 +31,8 @@ public function __construct(ClientInterface $client, array $defaults) * @param array $event_types Types of events that you want the new webhook to receive. * @return Webhook OK */ - public function create( - ?string $url = null, - ?array $event_types = null, - ): Webhook { - if ($url === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /webhooks/create", - ); - } + public function create(string $url, ?array $event_types = null): Webhook + { $request_payload = []; $request_payload["url"] = $url; @@ -62,13 +55,8 @@ public function create( * @param string $webhook_id ID of the webhook that you want to delete. * @return void OK */ - public function delete(?string $webhook_id = null): void + public function delete(string $webhook_id): void { - if ($webhook_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /webhooks/delete", - ); - } $request_payload = []; $request_payload["webhook_id"] = $webhook_id; @@ -84,13 +72,8 @@ public function delete(?string $webhook_id = null): void * @param string $webhook_id ID of the webhook that you want to get. * @return Webhook OK */ - public function get(?string $webhook_id = null): Webhook + public function get(string $webhook_id): Webhook { - if ($webhook_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /webhooks/get", - ); - } $request_payload = []; $request_payload["webhook_id"] = $webhook_id; @@ -123,15 +106,8 @@ public function list(): array * @param string $webhook_id ID of the webhook that you want to update. * @return void OK */ - public function update( - ?array $event_types = null, - ?string $webhook_id = null, - ): void { - if ($event_types === null && $webhook_id === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /webhooks/update", - ); - } + public function update(array $event_types, string $webhook_id): void + { $request_payload = []; $request_payload["event_types"] = $event_types; diff --git a/src/Routes/WorkspacesClient.php b/src/Routes/WorkspacesClient.php index d378acef..752b182b 100644 --- a/src/Routes/WorkspacesClient.php +++ b/src/Routes/WorkspacesClient.php @@ -42,7 +42,7 @@ public function __construct(ClientInterface $client, array $defaults) * @return Workspace OK */ public function create( - ?string $name = null, + string $name, ?string $company_name = null, ?string $connect_partner_name = null, mixed $connect_webview_customization = null, @@ -53,11 +53,6 @@ public function create( ?string $webview_primary_button_text_color = null, ?string $webview_success_message = null, ): Workspace { - if ($name === null) { - throw new \InvalidArgumentException( - "At least one parameter is required for /workspaces/create", - ); - } $request_payload = []; $request_payload["name"] = $name; From 02294e448e96b2f5901231316c12e054b33969df Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 07:33:19 +0000 Subject: [PATCH 33/35] fix: reject a call that omits every one of the required parameters An endpoint whose schema requires one of its parameters marks none of them individually required, so PHP cannot enforce it through the method signature. Blueprint reports this as hasRequiredParameters, which the generated method now guards on. The condition is rendered from the parameter list in the layout rather than assembled in the context builder. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017FnqoQVLzdhRMBvFa6CoFG --- codegen/layouts/partials/route-method.hbs | 5 ++ codegen/lib/class-model.ts | 1 + codegen/lib/layouts/route.ts | 2 + codegen/lib/routes.ts | 5 ++ src/Routes/AccessCodesClient.php | 21 +++++++ src/Routes/AccessCodesUnmanagedClient.php | 5 ++ src/Routes/AccessGrantsClient.php | 26 +++++++++ src/Routes/AccessMethodsClient.php | 23 ++++++++ src/Routes/AcsUsersClient.php | 69 +++++++++++++++++++++++ src/Routes/ClientSessionsClient.php | 12 ++++ src/Routes/ConnectedAccountsClient.php | 5 ++ src/Routes/DevicesClient.php | 5 ++ src/Routes/DevicesUnmanagedClient.php | 5 ++ src/Routes/EventsClient.php | 39 +++++++++++++ src/Routes/InstantKeysClient.php | 5 ++ src/Routes/LocksClient.php | 5 ++ src/Routes/SpacesClient.php | 15 +++++ src/Routes/UserIdentitiesClient.php | 5 ++ 18 files changed, 253 insertions(+) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index 06c547d5..7ce8dfdc 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -1,5 +1,10 @@ {{{methodPhpDoc this}}} public function {{methodName}}({{{signatureParams}}}): {{returnType}} { +{{#if requiresAtLeastOneParameter}} + if ({{#each parameters}}{{#unless @first}} && {{/unless}}${{name}} === null{{/each}}) { + throw new \InvalidArgumentException("At least one parameter is required for {{path}}"); + } +{{/if}} {{#if hasParams}} $request_payload = []; diff --git a/codegen/lib/class-model.ts b/codegen/lib/class-model.ts index c335114e..ebb8e5aa 100644 --- a/codegen/lib/class-model.ts +++ b/codegen/lib/class-model.ts @@ -18,6 +18,7 @@ export interface PhpClientMethod { responseDescription: string isDeprecated: boolean deprecationMessage: string + requiresAtLeastOneParameter: boolean parameters: PhpClientMethodParameter[] returnResource: string returnPath: string diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 1f6d5d6e..a1e0b018 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -37,6 +37,7 @@ export interface MethodLayoutContext { path: string returnType: string hasParams: boolean + requiresAtLeastOneParameter: boolean signatureParams: string usesActionAttempt: boolean usesOnResponse: boolean @@ -136,6 +137,7 @@ const getMethodLayoutContext = ( path, returnType, hasParams: parameters.length > 0, + requiresAtLeastOneParameter: method.requiresAtLeastOneParameter, signatureParams, usesActionAttempt, usesOnResponse, diff --git a/codegen/lib/routes.ts b/codegen/lib/routes.ts index d81cb9f7..b75aa155 100644 --- a/codegen/lib/routes.ts +++ b/codegen/lib/routes.ts @@ -127,6 +127,11 @@ const createClientMethod = (endpoint: Endpoint): PhpClientMethod => { responseDescription: response.description, isDeprecated: endpoint.isDeprecated, deprecationMessage: endpoint.deprecationMessage, + // An endpoint that takes no individually required parameter may still + // require one of them, so PHP cannot enforce it through the signature. + requiresAtLeastOneParameter: + endpoint.request.hasRequiredParameters && + endpoint.request.parameters.every(({ isRequired }) => !isRequired), parameters: endpoint.request.parameters.map((parameter) => ({ name: parameter.name, type: getPhpType(parameter), diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index a648f967..aabfff1e 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -311,6 +311,11 @@ public function get( ?string $code = null, ?string $device_id = null, ): AccessCode { + if ($access_code_id === null && $code === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/get", + ); + } $request_payload = []; if ($access_code_id !== null) { @@ -363,6 +368,22 @@ public function list( ?string $user_identifier_key = null, ?callable $on_response = null, ): array { + if ( + $access_code_ids === null && + $access_grant_id === null && + $access_grant_key === null && + $access_method_id === null && + $customer_key === null && + $device_id === null && + $limit === null && + $page_cursor === null && + $search === null && + $user_identifier_key === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/list", + ); + } $request_payload = []; if ($access_code_ids !== null) { diff --git a/src/Routes/AccessCodesUnmanagedClient.php b/src/Routes/AccessCodesUnmanagedClient.php index 8662ce2f..98b1973f 100644 --- a/src/Routes/AccessCodesUnmanagedClient.php +++ b/src/Routes/AccessCodesUnmanagedClient.php @@ -99,6 +99,11 @@ public function get( ?string $code = null, ?string $device_id = null, ): UnmanagedAccessCode { + if ($access_code_id === null && $code === null && $device_id === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_codes/unmanaged/get", + ); + } $request_payload = []; if ($access_code_id !== null) { diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index a8c23f9c..1a17a5ff 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -150,6 +150,11 @@ public function get( ?string $access_grant_id = null, ?string $access_grant_key = null, ): AccessGrant { + if ($access_grant_id === null && $access_grant_key === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/get", + ); + } $request_payload = []; if ($access_grant_id !== null) { @@ -183,6 +188,16 @@ public function get_related( ?array $exclude = null, ?array $include = null, ): Batch { + if ( + $access_grant_ids === null && + $access_grant_keys === null && + $exclude === null && + $include === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/get_related", + ); + } $request_payload = []; if ($access_grant_ids !== null) { @@ -346,6 +361,17 @@ public function update( ?string $name = null, ?string $starts_at = null, ): void { + if ( + $access_grant_id === null && + $access_grant_key === null && + $ends_at === null && + $name === null && + $starts_at === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_grants/update", + ); + } $request_payload = []; if ($access_grant_id !== null) { diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index c8bb36c9..9f22dddc 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -73,6 +73,15 @@ public function delete( ?string $access_grant_id = null, ?string $reservation_key = null, ): void { + if ( + $access_method_id === null && + $access_grant_id === null && + $reservation_key === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/delete", + ); + } $request_payload = []; if ($access_method_id !== null) { @@ -200,6 +209,20 @@ public function list( ?string $space_id = null, ?callable $on_response = null, ): array { + if ( + $access_code_id === null && + $access_grant_id === null && + $access_grant_key === null && + $acs_entrance_id === null && + $device_id === null && + $limit === null && + $page_cursor === null && + $space_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /access_methods/list", + ); + } $request_payload = []; if ($access_code_id !== null) { diff --git a/src/Routes/AcsUsersClient.php b/src/Routes/AcsUsersClient.php index 5b72d157..5a1b3c11 100644 --- a/src/Routes/AcsUsersClient.php +++ b/src/Routes/AcsUsersClient.php @@ -114,6 +114,15 @@ public function delete( ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ( + $acs_system_id === null && + $acs_user_id === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/delete", + ); + } $request_payload = []; if ($acs_system_id !== null) { @@ -144,6 +153,15 @@ public function get( ?string $acs_system_id = null, ?string $user_identity_id = null, ): AcsUser { + if ( + $acs_user_id === null && + $acs_system_id === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/get", + ); + } $request_payload = []; if ($acs_user_id !== null) { @@ -247,6 +265,15 @@ public function list_accessible_entrances( ?string $acs_user_id = null, ?string $user_identity_id = null, ): array { + if ( + $acs_system_id === null && + $acs_user_id === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/list_accessible_entrances", + ); + } $request_payload = []; if ($acs_system_id !== null) { @@ -316,6 +343,15 @@ public function revoke_access_to_all_entrances( ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ( + $acs_system_id === null && + $acs_user_id === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/revoke_access_to_all_entrances", + ); + } $request_payload = []; if ($acs_system_id !== null) { @@ -348,6 +384,15 @@ public function suspend( ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ( + $acs_system_id === null && + $acs_user_id === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/suspend", + ); + } $request_payload = []; if ($acs_system_id !== null) { @@ -378,6 +423,15 @@ public function unsuspend( ?string $acs_user_id = null, ?string $user_identity_id = null, ): void { + if ( + $acs_system_id === null && + $acs_user_id === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/unsuspend", + ); + } $request_payload = []; if ($acs_system_id !== null) { @@ -420,6 +474,21 @@ public function update( ?string $phone_number = null, ?string $user_identity_id = null, ): void { + if ( + $access_schedule === null && + $acs_system_id === null && + $acs_user_id === null && + $email === null && + $email_address === null && + $full_name === null && + $hid_acs_system_id === null && + $phone_number === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /acs/users/update", + ); + } $request_payload = []; if ($access_schedule !== null) { diff --git a/src/Routes/ClientSessionsClient.php b/src/Routes/ClientSessionsClient.php index 313d2dd6..dbd36c2b 100644 --- a/src/Routes/ClientSessionsClient.php +++ b/src/Routes/ClientSessionsClient.php @@ -197,6 +197,18 @@ public function grant_access( ?string $user_identity_id = null, ?array $user_identity_ids = null, ): void { + if ( + $client_session_id === null && + $connect_webview_ids === null && + $connected_account_ids === null && + $user_identifier_key === null && + $user_identity_id === null && + $user_identity_ids === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /client_sessions/grant_access", + ); + } $request_payload = []; if ($client_session_id !== null) { diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index abbb5914..175a34e7 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -60,6 +60,11 @@ public function get( ?string $connected_account_id = null, ?string $email = null, ): ConnectedAccount { + if ($connected_account_id === null && $email === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /connected_accounts/get", + ); + } $request_payload = []; if ($connected_account_id !== null) { diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index f7807686..0c8ee8b8 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -39,6 +39,11 @@ public function __construct(ClientInterface $client, array $defaults) */ public function get(?string $device_id = null, ?string $name = null): Device { + if ($device_id === null && $name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/get", + ); + } $request_payload = []; if ($device_id !== null) { diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index e6c222ea..a2412032 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -39,6 +39,11 @@ public function get( ?string $device_id = null, ?string $name = null, ): UnmanagedDevice { + if ($device_id === null && $name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /devices/unmanaged/get", + ); + } $request_payload = []; if ($device_id !== null) { diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index f787e91b..62951f44 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -37,6 +37,11 @@ public function get( ?string $device_id = null, ?string $event_type = null, ): Event { + if ($event_id === null && $device_id === null && $event_type === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /events/get", + ); + } $request_payload = []; if ($event_id !== null) { @@ -121,6 +126,40 @@ public function list( ?float $unstable_offset = null, ?string $user_identity_id = null, ): array { + if ( + $access_code_id === null && + $access_code_ids === null && + $access_grant_id === null && + $access_grant_ids === null && + $access_method_id === null && + $access_method_ids === null && + $acs_access_group_id === null && + $acs_credential_id === null && + $acs_encoder_id === null && + $acs_entrance_id === null && + $acs_system_id === null && + $acs_system_ids === null && + $acs_user_id === null && + $between === null && + $connect_webview_id === null && + $connected_account_id === null && + $customer_key === null && + $device_id === null && + $device_ids === null && + $event_ids === null && + $event_type === null && + $event_types === null && + $limit === null && + $since === null && + $space_id === null && + $space_ids === null && + $unstable_offset === null && + $user_identity_id === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /events/list", + ); + } $request_payload = []; if ($access_code_id !== null) { diff --git a/src/Routes/InstantKeysClient.php b/src/Routes/InstantKeysClient.php index b04a987c..ce21eff7 100644 --- a/src/Routes/InstantKeysClient.php +++ b/src/Routes/InstantKeysClient.php @@ -52,6 +52,11 @@ public function get( ?string $instant_key_id = null, ?string $instant_key_url = null, ): InstantKey { + if ($instant_key_id === null && $instant_key_url === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /instant_keys/get", + ); + } $request_payload = []; if ($instant_key_id !== null) { diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index 313a499f..6d13235d 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -76,6 +76,11 @@ public function configure_auto_lock( */ public function get(?string $device_id = null, ?string $name = null): Device { + if ($device_id === null && $name === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /locks/get", + ); + } $request_payload = []; if ($device_id !== null) { diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 88d64988..9b68f45c 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -166,6 +166,11 @@ public function get( ?string $space_id = null, ?string $space_key = null, ): Space { + if ($space_id === null && $space_key === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/get", + ); + } $request_payload = []; if ($space_id !== null) { @@ -199,6 +204,16 @@ public function get_related( ?array $space_ids = null, ?array $space_keys = null, ): Batch { + if ( + $exclude === null && + $include === null && + $space_ids === null && + $space_keys === null + ) { + throw new \InvalidArgumentException( + "At least one parameter is required for /spaces/get_related", + ); + } $request_payload = []; if ($exclude !== null) { diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index c30cf586..7acc9569 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -173,6 +173,11 @@ public function get( ?string $user_identity_id = null, ?string $user_identity_key = null, ): UserIdentity { + if ($user_identity_id === null && $user_identity_key === null) { + throw new \InvalidArgumentException( + "At least one parameter is required for /user_identities/get", + ); + } $request_payload = []; if ($user_identity_id !== null) { From e9f996cb45583911327bfddcbdd909a0884dde19 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Thu, 13 Aug 2026 07:34:31 +0000 Subject: [PATCH 34/35] 4.0.0-beta.3 --- package-lock.json | 4 ++-- package.json | 2 +- src/Utils/PackageVersion.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index acb24669..915b4902 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.2", + "version": "4.0.0-beta.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@seamapi/php", - "version": "4.0.0-beta.2", + "version": "4.0.0-beta.3", "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", diff --git a/package.json b/package.json index a384b169..1d40393a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@seamapi/php", - "version": "4.0.0-beta.2", + "version": "4.0.0-beta.3", "type": "module", "private": true, "license": "MIT", diff --git a/src/Utils/PackageVersion.php b/src/Utils/PackageVersion.php index a194602d..20bdf3b5 100644 --- a/src/Utils/PackageVersion.php +++ b/src/Utils/PackageVersion.php @@ -10,7 +10,7 @@ class PackageVersion * Injected from package.json when a version is cut, by the version * lifecycle script in package.json. Do not edit by hand. */ - public const VERSION = "4.0.0-beta.2"; + public const VERSION = "4.0.0-beta.3"; public static function get(): string { From d9b54ea0adabc61751f881c030c9f332718837bc Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Thu, 13 Aug 2026 11:36:25 -0700 Subject: [PATCH 35/35] Per version semantic release refs --- .github/workflows/version.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index fe504ad7..f463d055 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -41,8 +41,7 @@ jobs: *-*) channel="${VERSION#*-}"; channel="${channel%%.*}" ;; *) echo "Stable release, no channel note required."; exit 0 ;; esac - git fetch origin "+refs/notes/semantic-release:refs/notes/semantic-release" || true - git notes --ref semantic-release add --force \ + git notes --ref "semantic-release-v$VERSION" add --force \ --message "{\"channels\":[\"$channel\"]}" "v$VERSION^{commit}" - git push origin refs/notes/semantic-release + git push origin "refs/notes/semantic-release-v$VERSION" echo "Recorded v$VERSION on the '$channel' channel."