diff --git a/package-lock.json b/package-lock.json index 3284f14d..b65fac1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "MIT", "devDependencies": { "@prettier/plugin-php": "^0.25.0", - "@seamapi/blueprint": "^1.7.0", - "@seamapi/fake-seam-connect": "1.86.0", + "@seamapi/blueprint": "^1.8.0", + "@seamapi/fake-seam-connect": "2.0.4", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.1001.0", "change-case": "^5.4.4", @@ -806,9 +806,9 @@ "license": "MIT" }, "node_modules/@seamapi/blueprint": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.7.0.tgz", - "integrity": "sha512-on9pREQKyFgIaS99KLcck5K6AqLEO8Iy5h1heuJ2sEC2A7wXk5s/TL8iDOB3+4eeaAwuHRPgze9/Omo84KjJkw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.8.0.tgz", + "integrity": "sha512-NUghBmYaKreBeBxwPIB2O9hjIFZtEjVj73tAsuKdJR8t4BxlKK1I0XDQXxo3ZsH2QezNlbdo9/0MoX/33VXuhQ==", "dev": true, "license": "MIT", "dependencies": { @@ -820,38 +820,20 @@ "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==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@seamapi/fake-seam-connect/-/fake-seam-connect-2.0.4.tgz", + "integrity": "sha512-pgPUhIMW462B3jIWTuhH/aK2wUxRuOLjhsjB7YxdXw2g+hTVRJ5w4t01spETu1g2bch3Rzffd0whUVLeC4vjwQ==", "dev": true, "license": "MIT", "bin": { "fake-seam-connect": "dist/server.js" }, "engines": { - "node": ">=18.12.0", - "npm": ">= 9.0.0" + "node": ">=22.12.0", + "npm": ">=10.0.0" }, "optionalDependencies": { - "@seamapi/fake-devicedb": ">=1.0.0-rc.0", "zustand": "^4.3.7", "zustand-hoist": "^2.0.0" } diff --git a/package.json b/package.json index 111e78fb..4fc65e1e 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,8 @@ }, "devDependencies": { "@prettier/plugin-php": "^0.25.0", - "@seamapi/blueprint": "^1.7.0", - "@seamapi/fake-seam-connect": "1.86.0", + "@seamapi/blueprint": "^1.8.0", + "@seamapi/fake-seam-connect": "2.0.4", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.1001.0", "change-case": "^5.4.4", diff --git a/src/Routes/AccessCodesClient.php b/src/Routes/AccessCodesClient.php index d5fd5a91..7b6f3638 100644 --- a/src/Routes/AccessCodesClient.php +++ b/src/Routes/AccessCodesClient.php @@ -419,8 +419,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/access_codes/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/access_codes/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessGrantsClient.php b/src/Routes/AccessGrantsClient.php index 9d9074ff..46ae3690 100644 --- a/src/Routes/AccessGrantsClient.php +++ b/src/Routes/AccessGrantsClient.php @@ -215,8 +215,8 @@ public function get_related( } $res = Body::decode( - $this->client->request("POST", "/access_grants/get_related", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/access_grants/get_related", [ + "query" => $request_payload, ]), ); @@ -301,8 +301,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/access_grants/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/access_grants/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/AccessMethodsClient.php b/src/Routes/AccessMethodsClient.php index 561c9cd6..e36c36ca 100644 --- a/src/Routes/AccessMethodsClient.php +++ b/src/Routes/AccessMethodsClient.php @@ -177,8 +177,8 @@ public function get_related( } $res = Body::decode( - $this->client->request("POST", "/access_methods/get_related", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/access_methods/get_related", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/AcsEncodersClient.php b/src/Routes/AcsEncodersClient.php index e21f8c41..0ec0f2b0 100644 --- a/src/Routes/AcsEncodersClient.php +++ b/src/Routes/AcsEncodersClient.php @@ -126,8 +126,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/acs/encoders/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/acs/encoders/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/AcsEntrancesClient.php b/src/Routes/AcsEntrancesClient.php index d3c7502f..d9fcb87e 100644 --- a/src/Routes/AcsEntrancesClient.php +++ b/src/Routes/AcsEntrancesClient.php @@ -145,8 +145,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/acs/entrances/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/acs/entrances/list", [ + "query" => $request_payload, ]), ); @@ -180,9 +180,9 @@ public function list_credentials_with_access( $res = Body::decode( $this->client->request( - "POST", + "GET", "/acs/entrances/list_credentials_with_access", - ["json" => (object) $request_payload], + ["query" => $request_payload], ), ); diff --git a/src/Routes/ActionAttemptsClient.php b/src/Routes/ActionAttemptsClient.php index 79d7eaf6..9e1e4944 100644 --- a/src/Routes/ActionAttemptsClient.php +++ b/src/Routes/ActionAttemptsClient.php @@ -88,8 +88,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/action_attempts/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/action_attempts/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/ConnectWebviewsClient.php b/src/Routes/ConnectWebviewsClient.php index b34ff135..807dd4ae 100644 --- a/src/Routes/ConnectWebviewsClient.php +++ b/src/Routes/ConnectWebviewsClient.php @@ -191,8 +191,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/connect_webviews/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/connect_webviews/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/ConnectedAccountsClient.php b/src/Routes/ConnectedAccountsClient.php index ce997b4f..673d1221 100644 --- a/src/Routes/ConnectedAccountsClient.php +++ b/src/Routes/ConnectedAccountsClient.php @@ -132,8 +132,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/connected_accounts/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/connected_accounts/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/CustomersClient.php b/src/Routes/CustomersClient.php index 3f26137a..0e275dca 100644 --- a/src/Routes/CustomersClient.php +++ b/src/Routes/CustomersClient.php @@ -208,8 +208,8 @@ public function delete_data( $request_payload["user_keys"] = $user_keys; } - $this->client->request("POST", "/customers/delete_data", [ - "json" => (object) $request_payload, + $this->client->request("DELETE", "/customers/delete_data", [ + "query" => $request_payload, ]); } diff --git a/src/Routes/DevicesClient.php b/src/Routes/DevicesClient.php index 680a02c4..e9cf9fae 100644 --- a/src/Routes/DevicesClient.php +++ b/src/Routes/DevicesClient.php @@ -156,8 +156,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/devices/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/devices/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/DevicesUnmanagedClient.php b/src/Routes/DevicesUnmanagedClient.php index ba52cfa0..a4974282 100644 --- a/src/Routes/DevicesUnmanagedClient.php +++ b/src/Routes/DevicesUnmanagedClient.php @@ -138,8 +138,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/devices/unmanaged/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/devices/unmanaged/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/EventsClient.php b/src/Routes/EventsClient.php index 62951f44..e8fd51d5 100644 --- a/src/Routes/EventsClient.php +++ b/src/Routes/EventsClient.php @@ -248,8 +248,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/events/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/events/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/LocksClient.php b/src/Routes/LocksClient.php index 6d13235d..96b6b9f0 100644 --- a/src/Routes/LocksClient.php +++ b/src/Routes/LocksClient.php @@ -140,8 +140,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/locks/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/locks/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/NoiseSensorsClient.php b/src/Routes/NoiseSensorsClient.php index b1450aea..2bc01c76 100644 --- a/src/Routes/NoiseSensorsClient.php +++ b/src/Routes/NoiseSensorsClient.php @@ -71,8 +71,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/noise_sensors/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/noise_sensors/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/SpacesClient.php b/src/Routes/SpacesClient.php index 0b152a08..9d3fca31 100644 --- a/src/Routes/SpacesClient.php +++ b/src/Routes/SpacesClient.php @@ -231,8 +231,8 @@ public function get_related( } $res = Body::decode( - $this->client->request("POST", "/spaces/get_related", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/spaces/get_related", [ + "query" => $request_payload, ]), ); @@ -305,8 +305,8 @@ 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("DELETE", "/spaces/remove_acs_entrances", [ + "query" => $request_payload, ]); } @@ -345,8 +345,8 @@ 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("DELETE", "/spaces/remove_devices", [ + "query" => $request_payload, ]); } diff --git a/src/Routes/ThermostatsClient.php b/src/Routes/ThermostatsClient.php index 1c299708..3af98e17 100644 --- a/src/Routes/ThermostatsClient.php +++ b/src/Routes/ThermostatsClient.php @@ -355,8 +355,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/thermostats/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/thermostats/list", [ + "query" => $request_payload, ]), ); diff --git a/src/Routes/UserIdentitiesClient.php b/src/Routes/UserIdentitiesClient.php index 7138c3f1..863a49b2 100644 --- a/src/Routes/UserIdentitiesClient.php +++ b/src/Routes/UserIdentitiesClient.php @@ -265,8 +265,8 @@ public function list( } $res = Body::decode( - $this->client->request("POST", "/user_identities/list", [ - "json" => (object) $request_payload, + $this->client->request("GET", "/user_identities/list", [ + "query" => $request_payload, ]), ); diff --git a/tests/RetryTest.php b/tests/RetryTest.php index 41f3cec0..967658ba 100644 --- a/tests/RetryTest.php +++ b/tests/RetryTest.php @@ -51,7 +51,7 @@ public function testDoesNotRetryPostOnServiceUnavailable(): void $recorder = RecordingClient::repeating(self::service_unavailable()); try { - $this->seam($recorder)->devices->list(); + $this->seam($recorder)->client->request("POST", "/devices/list"); $this->fail("Expected the 503 to surface"); } catch (\Throwable) { // The error mapping is covered in HttpErrorTest. @@ -74,7 +74,7 @@ public function testDoesNotRetryPostOnConnectionFailure(): void $recorder = RecordingClient::repeating_throwable($connect_error); try { - $this->seam($recorder)->devices->list(); + $this->seam($recorder)->client->request("POST", "/devices/list"); $this->fail("Expected the connection failure to surface"); } catch (ConnectException) { // Expected. @@ -100,7 +100,7 @@ public function testDoesNotRetryPostOnTimeout(): void $recorder = RecordingClient::repeating_throwable($timeout); try { - $this->seam($recorder)->devices->list(); + $this->seam($recorder)->client->request("POST", "/devices/list"); $this->fail("Expected the timeout to surface"); } catch (ConnectException) { // Expected. @@ -123,7 +123,7 @@ public function testDoesNotRetryPostOnTimeoutWithoutAnErrno(): void $recorder = RecordingClient::repeating_throwable($timeout); try { - $this->seam($recorder)->devices->list(); + $this->seam($recorder)->client->request("POST", "/devices/list"); $this->fail("Expected the timeout to surface"); } catch (ConnectException) { // Expected. @@ -135,8 +135,6 @@ 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 { @@ -149,11 +147,9 @@ public function testRetriesIdempotentRequestsOnTimeout(): void $recorder = new RecordingClient([$timeout, $timeout, self::devices()]); - $res = Body::decode( - $this->seam($recorder)->client->request("GET", "/devices/list"), - ); + $devices = $this->seam($recorder)->devices->list(); - $this->assertSame([], $res->devices); + $this->assertSame([], $devices); $this->assertSame(3, $recorder->attempt_count()); } @@ -174,7 +170,7 @@ public function testDoesNotRetryPostOnConnectionReset(): void $recorder = RecordingClient::repeating_throwable($reset); try { - $this->seam($recorder)->devices->list(); + $this->seam($recorder)->client->request("POST", "/devices/list"); $this->fail("Expected the connection reset to surface"); } catch (RequestException) { // Expected. @@ -183,14 +179,11 @@ public function testDoesNotRetryPostOnConnectionReset(): void $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( "Could not resolve host", - new Request("POST", "/devices/list"), + new Request("GET", "/devices/list"), ); $recorder = RecordingClient::repeating_throwable($connect_error); @@ -198,32 +191,23 @@ public function testStopsRetryingOnceRetriesAreExhausted(): void $this->expectException(ConnectException::class); try { - $this->seam($recorder, retries: 1)->client->request( - "GET", - "/devices/list", - ); + $this->seam($recorder, retries: 1)->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( "Could not resolve host", - new Request("POST", "/devices/list"), + new Request("GET", "/devices/list"), ); $recorder = RecordingClient::repeating_throwable($connect_error); try { - $this->seam($recorder, retries: 0)->client->request( - "GET", - "/devices/list", - ); + $this->seam($recorder, retries: 0)->devices->list(); $this->fail("Expected the connection failure to surface"); } catch (ConnectException) { // Expected. @@ -234,12 +218,6 @@ public function testDoesNotRetryWhenRetriesAreDisabled(): void /** * 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 { @@ -249,14 +227,7 @@ public function testSdkReadsAreRetriedOnServiceUnavailable(): void 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.", - ); - } + $devices = $this->seam($recorder)->devices->list(); $this->assertSame([], $devices); $this->assertSame(3, $recorder->attempt_count()); @@ -266,8 +237,6 @@ 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 { @@ -298,10 +267,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. + * A caller reaching for the client directly with an idempotent method + * gets status based retries. */ public function testRetriesIdempotentRequestsOnServiceUnavailable(): void {