Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -539,8 +539,10 @@ try {
}
```

A response that is not shaped like a Seam error, such as a gateway returning
HTML, raises the underlying Guzzle exception instead.
An error response that is not shaped like a Seam error, such as a gateway
returning HTML, raises the underlying Guzzle exception instead. A successful
response that does not carry the resource the endpoint returns raises
`Seam\InvalidResponseError`, with `getPath()` and `getKey()`.

## Development and Testing

Expand Down
6 changes: 3 additions & 3 deletions codegen/layouts/partials/route-method.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
{{#if usesActionAttempt}}

return ResolveActionAttempt::resolve_action_attempt(
{{returnResource}}::from_json($res->{{returnPath}}),
{{returnResource}}::from_json(Body::read($res, "{{returnPath}}", "{{path}}")),
$this->client,
$wait_for_action_attempt ?? $this->defaults["wait_for_action_attempt"]
);
Expand All @@ -43,9 +43,9 @@
{{#unless returnsVoid}}

{{#if isArrayResponse}}
return array_map(fn ($r) => {{returnResource}}::from_json($r), $res->{{returnPath}});
return array_map(fn ($r) => {{returnResource}}::from_json($r), Body::read_list($res, "{{returnPath}}", "{{path}}"));
{{else}}
return {{returnResource}}::from_json($res->{{returnPath}});
return {{returnResource}}::from_json(Body::read($res, "{{returnPath}}", "{{path}}"));
{{/if}}
{{/unless}}
{{/if}}
Expand Down
52 changes: 52 additions & 0 deletions src/Http/Body.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use GuzzleHttp\Utils;
use Psr\Http\Message\ResponseInterface;
use Seam\InvalidResponseError;

/**
* Reads the JSON body of a response.
Expand Down Expand Up @@ -38,4 +39,55 @@ public static function decode(ResponseInterface $response): mixed
return null;
}
}

/**
* Reads the resource an endpoint returns out of its response envelope.
*
* @throws InvalidResponseError If the envelope does not carry the key
*/
public static function read(mixed $res, string $key, string $path): mixed
{
if (!is_object($res)) {
throw new InvalidResponseError(
$path,
$key,
"got " . get_debug_type($res) . " instead of a response object",
);
}

if (!property_exists($res, $key)) {
throw new InvalidResponseError(
$path,
$key,
"which the response does not contain",
);
}

return $res->$key;
}

/**
* Reads a list of resources out of a response envelope.
*
* @return array<array-key, mixed>
*
* @throws InvalidResponseError If the envelope does not carry a list
*/
public static function read_list(
mixed $res,
string $key,
string $path,
): array {
$value = self::read($res, $key, $path);

if (!is_array($value)) {
throw new InvalidResponseError(
$path,
$key,
"got " . get_debug_type($value) . " instead of a list",
);
}

return $value;
}
}
9 changes: 6 additions & 3 deletions src/Http/ResolveActionAttempt.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Seam\ActionAttemptFailedError;
use Seam\ActionAttemptTimeoutError;
use Seam\InvalidOptionsError;
use Seam\InvalidResponseError;
use Seam\Resources\ActionAttempt;

/**
Expand Down Expand Up @@ -105,12 +106,14 @@ private static function get_action_attempt(
);

$action_attempt = ActionAttempt::from_json(
$res->action_attempt ?? null,
Body::read($res, "action_attempt", "/action_attempts/get"),
);

if ($action_attempt === null) {
throw new \UnexpectedValueException(
"Seam returned no action attempt for {$action_attempt_id}",
throw new InvalidResponseError(
"/action_attempts/get",
"action_attempt",
"which was empty for {$action_attempt_id}",
);
}

Expand Down
37 changes: 37 additions & 0 deletions src/InvalidResponseError.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Seam;

/**
* Error thrown when a successful response does not carry the resource the
* endpoint is defined to return.
*/
class InvalidResponseError extends \UnexpectedValueException implements
SeamException
{
public function __construct(
private string $path,
private string $key,
string $reason,
) {
parent::__construct(
"Seam returned an invalid response for {$path}: expected \"{$key}\", {$reason}",
);
}

/**
* The endpoint path the response came from, e.g. `/devices/get`.
*/
public function getPath(): string
{
return $this->path;
}

/**
* The response key that should have carried the resource.
*/
public function getKey(): string
{
return $this->key;
}
}
28 changes: 22 additions & 6 deletions src/Routes/AccessCodesClient.php

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion src/Routes/AccessCodesSimulateClient.php

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions src/Routes/AccessCodesUnmanagedClient.php

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 17 additions & 5 deletions src/Routes/AccessGrantsClient.php

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions src/Routes/AccessGrantsUnmanagedClient.php

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading