Skip to content

Commit a7cbb69

Browse files
authored
Merge branch 'main' into claude/action-attempt-status-annotations-f216kn
2 parents dfe533b + 08328ea commit a7cbb69

7 files changed

Lines changed: 96 additions & 13 deletions

File tree

README.md

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -559,24 +559,39 @@ The Seam API parses these params with the corresponding [parser].
559559
[reference implementation]: https://github.com/seamapi/url-search-params-serializer
560560
[parser]: https://github.com/seamapi/url-search-params-parser
561561

562-
#### Errors
562+
### Error Handling
563563

564564
Every exception the SDK raises implements `Seam\SeamException`, so it can be
565565
caught as a group. An API error is a `Seam\HttpApiError` carrying
566566
`getErrorCode()`, `getStatusCode()` and `getRequestId()`, with
567567
`Seam\HttpUnauthorizedError` and `Seam\HttpInvalidInputError` as the two
568568
specific cases worth catching on their own.
569569

570+
#### Validation errors
571+
572+
When the API rejects a request because a parameter is invalid, it throws an
573+
`HttpInvalidInputError`. Look up messages for a parameter you are already
574+
rendering, for example a field in a form:
575+
570576
```php
571-
use Seam\HttpApiError;
572577
use Seam\HttpInvalidInputError;
573578

574579
try {
575-
$seam->devices->get(device_id: $device_id);
580+
$seam->devices->list(device_ids: ["not-a-uuid"]);
576581
} catch (HttpInvalidInputError $error) {
577-
print_r($error->getValidationErrorMessages("device_id"));
578-
} catch (HttpApiError $error) {
579-
print $error->getErrorCode();
582+
print_r($error->getValidationErrorMessages("device_ids"));
583+
}
584+
```
585+
586+
Or read every parameter that failed validation to summarize the request:
587+
588+
```php
589+
foreach ($error->validation_errors as $validation_error) {
590+
printf(
591+
"%s: %s\n",
592+
$validation_error->parameter_name,
593+
implode(", ", $validation_error->error_messages),
594+
);
580595
}
581596
```
582597

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@seamapi/php",
3-
"version": "4.3.0",
3+
"version": "4.4.0",
44
"type": "module",
55
"private": true,
66
"license": "MIT",

src/HttpInvalidInputError.php

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@
77
*/
88
class HttpInvalidInputError extends HttpApiError
99
{
10-
private object $validationErrors;
10+
private object $rawValidationErrors;
11+
12+
/**
13+
* Validation errors, one entry per failed request parameter.
14+
*
15+
* @var list<ValidationError>
16+
*/
17+
public readonly array $validation_errors;
1118

1219
public function __construct(
1320
object $error,
@@ -16,7 +23,21 @@ public function __construct(
1623
) {
1724
parent::__construct($error, $statusCode, $requestId);
1825
$this->errorCode = "invalid_input";
19-
$this->validationErrors = $error->validation_errors ?? (object) [];
26+
$this->rawValidationErrors = $error->validation_errors ?? (object) [];
27+
28+
$validationErrors = [];
29+
foreach (
30+
get_object_vars($this->rawValidationErrors)
31+
as $paramName => $_
32+
) {
33+
if ($paramName !== "_errors") {
34+
$validationErrors[] = new ValidationError(
35+
$paramName,
36+
$this->getValidationErrorMessages($paramName),
37+
);
38+
}
39+
}
40+
$this->validation_errors = $validationErrors;
2041
}
2142

2243
/**
@@ -27,6 +48,6 @@ public function __construct(
2748
*/
2849
public function getValidationErrorMessages(string $paramName): array
2950
{
30-
return $this->validationErrors->{$paramName}->_errors ?? [];
51+
return $this->rawValidationErrors->{$paramName}->_errors ?? [];
3152
}
3253
}

src/ValidationError.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Seam;
4+
5+
/**
6+
* A request parameter that failed validation and its error messages.
7+
*/
8+
final class ValidationError
9+
{
10+
/**
11+
* @param string[] $error_messages
12+
*/
13+
public function __construct(
14+
public readonly string $parameter_name,
15+
public readonly array $error_messages,
16+
) {}
17+
}

src/Version.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Version
1010
* Injected from package.json when a version is cut, by the version
1111
* lifecycle script in package.json. Do not edit by hand.
1212
*/
13-
public const VERSION = "4.3.0";
13+
public const VERSION = "4.4.0";
1414

1515
public static function get(): string
1616
{

tests/HttpErrorTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Seam\HttpInvalidInputError;
99
use Seam\HttpUnauthorizedError;
1010
use Seam\Seam;
11+
use Seam\ValidationError;
1112
use Tests\Support\FakeSeamConnectTestCase;
1213

1314
final class HttpErrorTest extends FakeSeamConnectTestCase
@@ -65,9 +66,38 @@ public function testThrowsInvalidInputErrorWithValidationMessages(): void
6566
["Expected array, received number"],
6667
$error->getValidationErrorMessages("device_ids"),
6768
);
69+
$this->assertEquals(
70+
[
71+
new ValidationError("device_ids", [
72+
"Expected array, received number",
73+
]),
74+
],
75+
$error->validation_errors,
76+
);
6877
}
6978
}
7079

80+
public function testValidationErrorsExcludeRequestWideErrors(): void
81+
{
82+
$error = new HttpInvalidInputError(
83+
(object) [
84+
"validation_errors" => (object) [
85+
"_errors" => ["Request is invalid"],
86+
"device_ids" => (object) [
87+
"_errors" => ["Invalid device IDs"],
88+
],
89+
],
90+
],
91+
400,
92+
null,
93+
);
94+
95+
$this->assertEquals(
96+
[new ValidationError("device_ids", ["Invalid device IDs"])],
97+
$error->validation_errors,
98+
);
99+
}
100+
71101
public function testValidationMessagesAreEmptyForAnUnknownParam(): void
72102
{
73103
try {

0 commit comments

Comments
 (0)