20
20
use ApiPlatform \Metadata \ApiResource ;
21
21
use ApiPlatform \Metadata \CollectionOperationInterface ;
22
22
use ApiPlatform \Metadata \Error ;
23
+ use ApiPlatform \Metadata \Exception \OperationNotFoundException ;
23
24
use ApiPlatform \Metadata \Exception \ProblemExceptionInterface ;
24
25
use ApiPlatform \Metadata \Exception \ResourceClassNotFoundException ;
25
26
use ApiPlatform \Metadata \HeaderParameterInterface ;
39
40
use ApiPlatform \OpenApi \Model \MediaType ;
40
41
use ApiPlatform \OpenApi \Model \OAuthFlow ;
41
42
use ApiPlatform \OpenApi \Model \OAuthFlows ;
43
+ use ApiPlatform \OpenApi \Model \Operation ;
42
44
use ApiPlatform \OpenApi \Model \Parameter ;
43
45
use ApiPlatform \OpenApi \Model \PathItem ;
44
46
use ApiPlatform \OpenApi \Model \Paths ;
@@ -172,15 +174,15 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
172
174
173
175
if ($ openapiAttribute instanceof Webhook) {
174
176
$ pathItem = $ openapiAttribute ->getPathItem () ?: new PathItem ();
175
- $ openapiOperation = $ pathItem ->{'get ' .ucfirst (strtolower ($ method ))}() ?: new Model \ Operation ();
177
+ $ openapiOperation = $ pathItem ->{'get ' .ucfirst (strtolower ($ method ))}() ?: new Operation ();
176
178
} elseif (!\is_object ($ openapiAttribute )) {
177
- $ openapiOperation = new Model \ Operation ();
179
+ $ openapiOperation = new Operation ();
178
180
} else {
179
181
$ openapiOperation = $ openapiAttribute ;
180
182
}
181
183
182
184
// Complete with defaults
183
- $ openapiOperation = new Model \ Operation (
185
+ $ openapiOperation = new Operation (
184
186
operationId: null !== $ openapiOperation ->getOperationId () ? $ openapiOperation ->getOperationId () : $ this ->normalizeOperationName ($ operationName ),
185
187
tags: null !== $ openapiOperation ->getTags () ? $ openapiOperation ->getTags () : [$ operation ->getShortName () ?: $ resourceShortName ],
186
188
responses: null !== $ openapiOperation ->getResponses () ? $ openapiOperation ->getResponses () : [],
@@ -273,48 +275,10 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
273
275
$ openapiOperation = $ openapiOperation ->withParameters ($ openapiParameters );
274
276
$ existingResponses = $ openapiOperation ->getResponses () ?: [];
275
277
$ overrideResponses = $ operation ->getExtraProperties ()[self ::OVERRIDE_OPENAPI_RESPONSES ] ?? $ this ->openApiOptions ->getOverrideResponses ();
276
- if ($ operation instanceof HttpOperation && null !== $ operation ->getErrors ()) {
277
- foreach ($ operation ->getErrors () as $ error ) {
278
- $ status = null ;
279
- $ description = null ;
280
- try {
281
- /** @var ProblemExceptionInterface $exception */
282
- $ exception = (new \ReflectionClass ($ error ))->newInstanceWithoutConstructor ();
283
- $ status = $ exception ->getStatus ();
284
- $ description = $ exception ->getTitle ();
285
- } catch (\ReflectionException ) {
286
- }
287
-
288
- try {
289
- $ errorOperation = $ this ->resourceMetadataFactory ->create ($ error )->getOperation ();
290
- if (!is_a ($ errorOperation , Error::class)) {
291
- $ this ->logger ?->warning(\sprintf ('The error class %s is not an ErrorResource ' , $ error ));
292
- continue ;
293
- }
294
- $ status ??= $ errorOperation ->getStatus ();
295
- $ description ??= $ errorOperation ->getDescription ();
296
- } catch (ResourceClassNotFoundException ) {
297
- $ this ->logger ?->warning(\sprintf ('The error class %s is not an ErrorResource ' , $ error ));
298
- continue ;
299
- }
300
-
301
- if (!$ status ) {
302
- $ this ->logger ?->error(\sprintf (
303
- 'The error class %s has no status defined, please either implement ProblemExceptionInterface, or make it an ErrorResource with a status ' ,
304
- $ error ));
305
- continue ;
306
- }
307
-
308
- $ operationErrorSchemas = [];
309
- foreach ($ responseMimeTypes as $ operationFormat ) {
310
- $ operationErrorSchema = $ this ->jsonSchemaFactory ->buildSchema ($ error , $ operationFormat , Schema::TYPE_OUTPUT , null , $ schema , null , $ forceSchemaCollection );
311
- $ operationErrorSchemas [$ operationFormat ] = $ operationErrorSchema ;
312
- $ this ->appendSchemaDefinitions ($ schemas , $ operationErrorSchema ->getDefinitions ());
313
- }
314
-
315
- $ openapiOperation = $ this ->buildOpenApiResponse ($ existingResponses , $ status , $ description ?? '' , $ openapiOperation , $ operation , $ responseMimeTypes , $ operationErrorSchemas , $ resourceMetadataCollection );
316
- }
278
+ if ($ operation instanceof HttpOperation && null !== ($ errors = $ operation ->getErrors ())) {
279
+ $ openapiOperation = $ this ->addOperationErrors ($ openapiOperation , $ errors , $ responseMimeTypes , $ resourceMetadataCollection , $ schema , $ schemas );
317
280
}
281
+
318
282
if ($ overrideResponses || !$ existingResponses ) {
319
283
// Create responses
320
284
switch ($ method ) {
@@ -390,7 +354,7 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
390
354
}
391
355
}
392
356
393
- private function buildOpenApiResponse (array $ existingResponses , int |string $ status , string $ description , ?Model \ Operation $ openapiOperation = null , ?HttpOperation $ operation = null , ?array $ responseMimeTypes = null , ?array $ operationOutputSchemas = null , ?ResourceMetadataCollection $ resourceMetadataCollection = null ): Model \ Operation
357
+ private function buildOpenApiResponse (array $ existingResponses , int |string $ status , string $ description , ?Operation $ openapiOperation = null , ?HttpOperation $ operation = null , ?array $ responseMimeTypes = null , ?array $ operationOutputSchemas = null , ?ResourceMetadataCollection $ resourceMetadataCollection = null ): Operation
394
358
{
395
359
if (isset ($ existingResponses [$ status ])) {
396
360
return $ openapiOperation ;
@@ -421,6 +385,9 @@ private function buildContent(array $responseMimeTypes, array $operationSchemas)
421
385
return $ content ;
422
386
}
423
387
388
+ /**
389
+ * @return array[array<string, string>, array<string, string>]
390
+ */
424
391
private function getMimeTypes (HttpOperation $ operation ): array
425
392
{
426
393
$ requestFormats = $ operation ->getInputFormats () ?: [];
@@ -432,6 +399,11 @@ private function getMimeTypes(HttpOperation $operation): array
432
399
return [$ requestMimeTypes , $ responseMimeTypes ];
433
400
}
434
401
402
+ /**
403
+ * @param array<string, string[]> $responseFormats
404
+ *
405
+ * @return array<string, string>
406
+ */
435
407
private function flattenMimeTypes (array $ responseFormats ): array
436
408
{
437
409
$ responseMimeTypes = [];
@@ -698,7 +670,7 @@ private function appendSchemaDefinitions(\ArrayObject $schemas, \ArrayObject $de
698
670
/**
699
671
* @return array{0: int, 1: Parameter}|null
700
672
*/
701
- private function hasParameter (Model \ Operation $ operation , Parameter $ parameter ): ?array
673
+ private function hasParameter (Operation $ operation , Parameter $ parameter ): ?array
702
674
{
703
675
foreach ($ operation ->getParameters () as $ key => $ existingParameter ) {
704
676
if ($ existingParameter ->getName () === $ parameter ->getName () && $ existingParameter ->getIn () === $ parameter ->getIn ()) {
@@ -738,4 +710,61 @@ private function mergeParameter(Parameter $actual, Parameter $defined): Paramete
738
710
739
711
return $ actual ;
740
712
}
713
+
714
+ /**
715
+ * @param string[] $errors
716
+ * @param array<string, string> $responseMimeTypes
717
+ */
718
+ private function addOperationErrors (Operation $ operation , array $ errors , array $ responseMimeTypes , ResourceMetadataCollection $ resourceMetadataCollection , Schema $ schema , \ArrayObject $ schemas ): Operation
719
+ {
720
+ $ existingResponses = null ;
721
+ foreach ($ errors as $ error ) {
722
+ if (!is_a ($ error , ProblemExceptionInterface::class, true )) {
723
+ $ this ->logger ?->warning(\sprintf ('The error class "%s" does not implement "%s". Did you forget a use statement? ' , $ error , ProblemExceptionInterface::class));
724
+ }
725
+
726
+ $ status = null ;
727
+ $ description = null ;
728
+ try {
729
+ /** @var ProblemExceptionInterface */
730
+ $ exception = (new \ReflectionClass ($ error ))->newInstanceWithoutConstructor ();
731
+ $ status = $ exception ->getStatus ();
732
+ $ description = $ exception ->getTitle ();
733
+ } catch (\ReflectionException ) {
734
+ }
735
+
736
+ try {
737
+ $ errorOperation = $ this ->resourceMetadataFactory ->create ($ error )->getOperation ();
738
+ if (!is_a ($ errorOperation , Error::class)) {
739
+ $ this ->logger ?->warning(\sprintf ('The error class %s is not an ErrorResource ' , $ error ));
740
+ continue ;
741
+ }
742
+ /* @var Error $errorOperation */
743
+ $ status ??= $ errorOperation ->getStatus ();
744
+ $ description ??= $ errorOperation ->getDescription ();
745
+ } catch (ResourceClassNotFoundException |OperationNotFoundException ) {
746
+ $ this ->logger ?->warning(\sprintf ('The error class %s is not an ErrorResource ' , $ error ));
747
+ continue ;
748
+ }
749
+
750
+ if (!$ status ) {
751
+ $ this ->logger ?->error(\sprintf (
752
+ 'The error class %s has no status defined, please either implement ProblemExceptionInterface, or make it an ErrorResource with a status ' ,
753
+ $ error
754
+ ));
755
+ continue ;
756
+ }
757
+
758
+ $ operationErrorSchemas = [];
759
+ foreach ($ responseMimeTypes as $ operationFormat ) {
760
+ $ operationErrorSchema = $ this ->jsonSchemaFactory ->buildSchema ($ error , $ operationFormat , Schema::TYPE_OUTPUT , null , $ schema );
761
+ $ operationErrorSchemas [$ operationFormat ] = $ operationErrorSchema ;
762
+ $ this ->appendSchemaDefinitions ($ schemas , $ operationErrorSchema ->getDefinitions ());
763
+ }
764
+
765
+ $ operation = $ this ->buildOpenApiResponse ($ existingResponses ??= $ operation ->getResponses () ?: [], $ status , $ description ?? '' , $ operation , $ errorOperation , $ responseMimeTypes , $ operationErrorSchemas , $ resourceMetadataCollection );
766
+ }
767
+
768
+ return $ operation ;
769
+ }
741
770
}
0 commit comments