Skip to content

Commit ab99fff

Browse files
committed
feat(openapi): manage error resources in global
1 parent 83d005a commit ab99fff

File tree

5 files changed

+56
-54
lines changed

5 files changed

+56
-54
lines changed

src/Metadata/ApiResource.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace ApiPlatform\Metadata;
1515

16+
use ApiPlatform\Metadata\Exception\ProblemExceptionInterface;
1617
use ApiPlatform\Metadata\GraphQl\Operation as GraphQlOperation;
1718
use ApiPlatform\OpenApi\Model\Operation as OpenApiOperation;
1819
use ApiPlatform\State\OptionsInterface;
@@ -45,6 +46,7 @@ class ApiResource extends Metadata
4546
* @param mixed|null $messenger
4647
* @param mixed|null $input
4748
* @param mixed|null $output
49+
* @param array<class-string<ProblemExceptionInterface>>|null $errors
4850
*/
4951
public function __construct(
5052
/**
@@ -966,6 +968,7 @@ public function __construct(
966968
protected ?bool $strictQueryParameterValidation = null,
967969
protected ?bool $hideHydraOperation = null,
968970
protected array $extraProperties = [],
971+
?array $errors = null,
969972
) {
970973
parent::__construct(
971974
shortName: $shortName,
@@ -1005,13 +1008,14 @@ class: $class,
10051008
provider: $provider,
10061009
processor: $processor,
10071010
stateOptions: $stateOptions,
1011+
errors: $errors,
10081012
parameters: $parameters,
10091013
rules: $rules,
10101014
policy: $policy,
10111015
middleware: $middleware,
10121016
strictQueryParameterValidation: $strictQueryParameterValidation,
10131017
hideHydraOperation: $hideHydraOperation,
1014-
extraProperties: $extraProperties
1018+
extraProperties: $extraProperties,
10151019
);
10161020

10171021
$this->operations = null === $operations ? null : new Operations($operations);

src/Metadata/HttpOperation.php

+3-18
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ public function __construct(
154154
protected bool|OpenApiOperation|Webhook|null $openapi = null,
155155
protected ?array $exceptionToStatus = null,
156156
protected ?array $links = null,
157-
protected ?array $errors = null,
158157
protected ?bool $strictQueryParameterValidation = null,
159158
protected ?bool $hideHydraOperation = null,
160159

@@ -202,6 +201,7 @@ public function __construct(
202201
$provider = null,
203202
$processor = null,
204203
?OptionsInterface $stateOptions = null,
204+
?array $errors = null,
205205
array|Parameters|null $parameters = null,
206206
array|string|null $rules = null,
207207
?string $policy = null,
@@ -254,14 +254,15 @@ class: $class,
254254
provider: $provider,
255255
processor: $processor,
256256
stateOptions: $stateOptions,
257+
errors: $errors,
257258
parameters: $parameters,
258259
rules: $rules,
259260
policy: $policy,
260261
middleware: $middleware,
261262
queryParameterValidationEnabled: $queryParameterValidationEnabled,
262263
strictQueryParameterValidation: $strictQueryParameterValidation,
263264
hideHydraOperation: $hideHydraOperation,
264-
extraProperties: $extraProperties
265+
extraProperties: $extraProperties,
265266
);
266267
}
267268

@@ -621,20 +622,4 @@ public function withLinks(array $links): static
621622

622623
return $self;
623624
}
624-
625-
public function getErrors(): ?array
626-
{
627-
return $this->errors;
628-
}
629-
630-
/**
631-
* @param class-string<ProblemExceptionInterface>[] $errors
632-
*/
633-
public function withErrors(array $errors): static
634-
{
635-
$self = clone $this;
636-
$self->errors = $errors;
637-
638-
return $self;
639-
}
640625
}

src/Metadata/Metadata.php

+19
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace ApiPlatform\Metadata;
1515

16+
use ApiPlatform\Metadata\Exception\ProblemExceptionInterface;
1617
use ApiPlatform\State\OptionsInterface;
1718

1819
/**
@@ -34,6 +35,7 @@ abstract class Metadata
3435
* @param mixed|null $processor
3536
* @param Parameters|array<string, Parameter> $parameters
3637
* @param callable|string|array<string, \Illuminate\Contracts\Validation\Rule|array|string> $rules Laravel rules can be a FormRequest class, a callable or an array of rules
38+
* @param array<class-string<ProblemExceptionInterface>>|null $errors
3739
*/
3840
public function __construct(
3941
protected ?string $shortName = null,
@@ -73,6 +75,7 @@ public function __construct(
7375
protected $provider = null,
7476
protected $processor = null,
7577
protected ?OptionsInterface $stateOptions = null,
78+
protected ?array $errors = null,
7679
/*
7780
* @experimental
7881
*/
@@ -694,4 +697,20 @@ public function withHideHydraOperation(bool $hideHydraOperation): static
694697

695698
return $self;
696699
}
700+
701+
public function getErrors(): ?array
702+
{
703+
return $this->errors;
704+
}
705+
706+
/**
707+
* @param class-string<ProblemExceptionInterface>[] $errors
708+
*/
709+
public function withErrors(array $errors): static
710+
{
711+
$self = clone $this;
712+
$self->errors = $errors;
713+
714+
return $self;
715+
}
697716
}

src/Metadata/Operation.php

+17-13
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace ApiPlatform\Metadata;
1515

16+
use ApiPlatform\Metadata\Exception\ProblemExceptionInterface;
1617
use ApiPlatform\State\OptionsInterface;
1718

1819
/**
@@ -47,19 +48,20 @@ abstract class Operation extends Metadata
4748
* class?: string|null,
4849
* name?: string,
4950
* }|string|false|null $output {@see https://api-platform.com/docs/core/dto/#specifying-an-input-or-an-output-data-representation}
50-
* @param string|array|bool|null $mercure {@see https://api-platform.com/docs/core/mercure}
51-
* @param string|bool|null $messenger {@see https://api-platform.com/docs/core/messenger/#dispatching-a-resource-through-the-message-bus}
52-
* @param bool|null $elasticsearch {@see https://api-platform.com/docs/core/elasticsearch/}
53-
* @param bool|null $read {@see https://api-platform.com/docs/core/events/#the-event-system}
54-
* @param bool|null $deserialize {@see https://api-platform.com/docs/core/events/#the-event-system}
55-
* @param bool|null $validate {@see https://api-platform.com/docs/core/events/#the-event-system}
56-
* @param bool|null $write {@see https://api-platform.com/docs/core/events/#the-event-system}
57-
* @param bool|null $serialize {@see https://api-platform.com/docs/core/events/#the-event-system}
58-
* @param bool|null $fetchPartial {@see https://api-platform.com/docs/core/performance/#fetch-partial}
59-
* @param bool|null $forceEager {@see https://api-platform.com/docs/core/performance/#force-eager}
60-
* @param string|callable|null $provider {@see https://api-platform.com/docs/core/state-providers/#state-providers}
61-
* @param string|callable|null $processor {@see https://api-platform.com/docs/core/state-processors/#state-processors}
62-
* @param array<string, Parameter> $parameters
51+
* @param string|array|bool|null $mercure {@see https://api-platform.com/docs/core/mercure}
52+
* @param string|bool|null $messenger {@see https://api-platform.com/docs/core/messenger/#dispatching-a-resource-through-the-message-bus}
53+
* @param bool|null $elasticsearch {@see https://api-platform.com/docs/core/elasticsearch/}
54+
* @param bool|null $read {@see https://api-platform.com/docs/core/events/#the-event-system}
55+
* @param bool|null $deserialize {@see https://api-platform.com/docs/core/events/#the-event-system}
56+
* @param bool|null $validate {@see https://api-platform.com/docs/core/events/#the-event-system}
57+
* @param bool|null $write {@see https://api-platform.com/docs/core/events/#the-event-system}
58+
* @param bool|null $serialize {@see https://api-platform.com/docs/core/events/#the-event-system}
59+
* @param bool|null $fetchPartial {@see https://api-platform.com/docs/core/performance/#fetch-partial}
60+
* @param bool|null $forceEager {@see https://api-platform.com/docs/core/performance/#force-eager}
61+
* @param string|callable|null $provider {@see https://api-platform.com/docs/core/state-providers/#state-providers}
62+
* @param string|callable|null $processor {@see https://api-platform.com/docs/core/state-processors/#state-processors}
63+
* @param array<string, Parameter> $parameters
64+
* @param array<class-string<ProblemExceptionInterface>>|null $errors
6365
*/
6466
public function __construct(
6567
protected ?string $shortName = null,
@@ -806,6 +808,7 @@ public function __construct(
806808
protected $provider = null,
807809
protected $processor = null,
808810
protected ?OptionsInterface $stateOptions = null,
811+
?array $errors = null,
809812
array|Parameters|null $parameters = null,
810813
array|string|null $rules = null,
811814
?string $policy = null,
@@ -853,6 +856,7 @@ class: $class,
853856
provider: $provider,
854857
processor: $processor,
855858
stateOptions: $stateOptions,
859+
errors: $errors,
856860
parameters: $parameters,
857861
rules: $rules,
858862
policy: $policy,

src/OpenApi/Factory/OpenApiFactory.php

+12-22
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,12 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
364364
$existingResponses = $openapiOperation->getResponses() ?: [];
365365
$overrideResponses = $operation->getExtraProperties()[self::OVERRIDE_OPENAPI_RESPONSES] ?? $this->openApiOptions->getOverrideResponses();
366366
$errors = null;
367+
/** @var array<class-string|string, Error> */
368+
$errorOperations = [];
367369
if ($operation instanceof HttpOperation && null !== ($errors = $operation->getErrors())) {
368-
/** @var array<class-string|string, Error> */
369-
$errorOperations = [];
370370
foreach ($errors as $error) {
371371
$errorOperations[$error] = $this->getErrorResource($error);
372372
}
373-
374-
$openapiOperation = $this->addOperationErrors($openapiOperation, $errorOperations, $resourceMetadataCollection, $schema, $schemas, $operation);
375373
}
376374

377375
if ($overrideResponses || !$existingResponses) {
@@ -385,24 +383,16 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
385383
$successStatus = (string) $operation->getStatus() ?: 201;
386384
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, \sprintf('%s resource created', $resourceShortName), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas, $resourceMetadataCollection);
387385

388-
if (null === $errors) {
389-
$openapiOperation = $this->addOperationErrors($openapiOperation, [
390-
$defaultError->withStatus(400)->withDescription('Invalid input'),
391-
$defaultValidationError,
392-
], $resourceMetadataCollection, $schema, $schemas, $operation);
393-
}
386+
$errorOperations[] = $defaultError->withStatus(400)->withDescription('Invalid input');
387+
$errorOperations[] = $defaultValidationError;
394388
break;
395389
case 'PATCH':
396390
case 'PUT':
397391
$successStatus = (string) $operation->getStatus() ?: 200;
398392
$openapiOperation = $this->buildOpenApiResponse($existingResponses, $successStatus, \sprintf('%s resource updated', $resourceShortName), $openapiOperation, $operation, $responseMimeTypes, $operationOutputSchemas, $resourceMetadataCollection);
399393

400-
if (null === $errors) {
401-
$openapiOperation = $this->addOperationErrors($openapiOperation, [
402-
$defaultError->withStatus(400)->withDescription('Invalid input'),
403-
$defaultValidationError,
404-
], $resourceMetadataCollection, $schema, $schemas, $operation);
405-
}
394+
$errorOperations[] = $defaultError->withStatus(400)->withDescription('Invalid input');
395+
$errorOperations[] = $defaultValidationError;
406396
break;
407397
case 'DELETE':
408398
$successStatus = (string) $operation->getStatus() ?: 204;
@@ -412,15 +402,15 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
412402
}
413403

414404
if ($overrideResponses && !isset($existingResponses[403]) && $operation->getSecurity()) {
415-
$openapiOperation = $this->addOperationErrors($openapiOperation, [
416-
$defaultError->withStatus(403)->withDescription('Forbidden'),
417-
], $resourceMetadataCollection, $schema, $schemas, $operation);
405+
$errorOperations[] = $defaultError->withStatus(403)->withDescription('Forbidden');
418406
}
419407

420408
if ($overrideResponses && !$operation instanceof CollectionOperationInterface && 'POST' !== $operation->getMethod() && !isset($existingResponses[404]) && null === $errors) {
421-
$openapiOperation = $this->addOperationErrors($openapiOperation, [
422-
$defaultError->withStatus(404)->withDescription('Not found'),
423-
], $resourceMetadataCollection, $schema, $schemas, $operation);
409+
$errorOperations[] = $defaultError->withStatus(404)->withDescription('Not found');
410+
}
411+
412+
if ($errorOperations) {
413+
$openapiOperation = $this->addOperationErrors($openapiOperation, $errorOperations, $resourceMetadataCollection, $schema, $schemas, $operation);
424414
}
425415

426416
if (!$openapiOperation->getResponses()) {

0 commit comments

Comments
 (0)