Completed
Push — master ( a40985...862247 )
by Kévin
14s
created

src/Swagger/Serializer/DocumentationNormalizer.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\Swagger\Serializer;
15
16
use ApiPlatform\Core\Api\FilterCollection;
17
use ApiPlatform\Core\Api\FilterLocatorTrait;
18
use ApiPlatform\Core\Api\OperationMethodResolverInterface;
19
use ApiPlatform\Core\Api\OperationType;
20
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
21
use ApiPlatform\Core\Api\UrlGeneratorInterface;
22
use ApiPlatform\Core\Documentation\Documentation;
23
use ApiPlatform\Core\Exception\RuntimeException;
24
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
25
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
26
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
27
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
28
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
29
use ApiPlatform\Core\PathResolver\OperationPathResolverInterface;
30
use Psr\Container\ContainerInterface;
31
use Symfony\Component\PropertyInfo\Type;
32
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
33
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
34
35
/**
36
 * Creates a machine readable Swagger API documentation.
37
 *
38
 * @author Amrouche Hamza <[email protected]>
39
 * @author Teoh Han Hui <[email protected]>
40
 * @author Kévin Dunglas <[email protected]>
41
 */
42
final class DocumentationNormalizer implements NormalizerInterface
43
{
44
    use FilterLocatorTrait;
45
46
    const SWAGGER_VERSION = '2.0';
47
    const FORMAT = 'json';
48
49
    private $resourceMetadataFactory;
50
    private $propertyNameCollectionFactory;
51
    private $propertyMetadataFactory;
52
    private $resourceClassResolver;
53
    private $operationMethodResolver;
54
    private $operationPathResolver;
55
    private $urlGenerator;
56
    private $nameConverter;
57
    private $oauthEnabled;
58
    private $oauthType;
59
    private $oauthFlow;
60
    private $oauthTokenUrl;
61
    private $oauthAuthorizationUrl;
62
    private $oauthScopes;
63
64
    /**
65
     * @param ContainerInterface|FilterCollection|null $filterLocator The new filter locator or the deprecated filter collection
66
     */
67
    public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, OperationMethodResolverInterface $operationMethodResolver, OperationPathResolverInterface $operationPathResolver, UrlGeneratorInterface $urlGenerator, $filterLocator = null, NameConverterInterface $nameConverter = null, $oauthEnabled = false, $oauthType = '', $oauthFlow = '', $oauthTokenUrl = '', $oauthAuthorizationUrl = '', $oauthScopes = [])
68
    {
69
        $this->setFilterLocator($filterLocator, true);
70
71
        $this->resourceMetadataFactory = $resourceMetadataFactory;
72
        $this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
73
        $this->propertyMetadataFactory = $propertyMetadataFactory;
74
        $this->resourceClassResolver = $resourceClassResolver;
75
        $this->operationMethodResolver = $operationMethodResolver;
76
        $this->operationPathResolver = $operationPathResolver;
77
        $this->urlGenerator = $urlGenerator;
78
        $this->nameConverter = $nameConverter;
79
        $this->oauthEnabled = $oauthEnabled;
80
        $this->oauthType = $oauthType;
81
        $this->oauthFlow = $oauthFlow;
82
        $this->oauthTokenUrl = $oauthTokenUrl;
83
        $this->oauthAuthorizationUrl = $oauthAuthorizationUrl;
84
        $this->oauthScopes = $oauthScopes;
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90
    public function normalize($object, $format = null, array $context = [])
91
    {
92
        $mimeTypes = $object->getMimeTypes();
93
        $definitions = new \ArrayObject();
94
        $paths = new \ArrayObject();
95
96
        foreach ($object->getResourceNameCollection() as $resourceClass) {
97
            $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
98
            $resourceShortName = $resourceMetadata->getShortName();
99
100
            $this->addPaths($paths, $definitions, $resourceClass, $resourceShortName, $resourceMetadata, $mimeTypes, OperationType::COLLECTION);
101
            $this->addPaths($paths, $definitions, $resourceClass, $resourceShortName, $resourceMetadata, $mimeTypes, OperationType::ITEM);
102
        }
103
104
        $definitions->ksort();
105
        $paths->ksort();
106
107
        return $this->computeDoc($object, $definitions, $paths);
108
    }
109
110
    /**
111
     * Updates the list of entries in the paths collection.
112
     *
113
     * @param \ArrayObject     $paths
114
     * @param \ArrayObject     $definitions
115
     * @param string           $resourceClass
116
     * @param string           $resourceShortName
117
     * @param ResourceMetadata $resourceMetadata
118
     * @param array            $mimeTypes
119
     * @param string           $operationType
120
     */
121
    private function addPaths(\ArrayObject $paths, \ArrayObject $definitions, string $resourceClass, string $resourceShortName, ResourceMetadata $resourceMetadata, array $mimeTypes, string $operationType)
122
    {
123
        if (null === $operations = $operationType === OperationType::COLLECTION ? $resourceMetadata->getCollectionOperations() : $resourceMetadata->getItemOperations()) {
124
            return;
125
        }
126
127
        foreach ($operations as $operationName => $operation) {
128
            $path = $this->getPath($resourceShortName, $operation, $operationType);
129
            $method = $operationType === OperationType::ITEM ? $this->operationMethodResolver->getItemOperationMethod($resourceClass, $operationName) : $this->operationMethodResolver->getCollectionOperationMethod($resourceClass, $operationName);
130
131
            $paths[$path][strtolower($method)] = $this->getPathOperation($operationName, $operation, $method, $operationType, $resourceClass, $resourceMetadata, $mimeTypes, $definitions);
132
        }
133
    }
134
135
    /**
136
     * Gets the path for an operation.
137
     *
138
     * If the path ends with the optional _format parameter, it is removed
139
     * as optional path parameters are not yet supported.
140
     *
141
     * @see https://github.com/OAI/OpenAPI-Specification/issues/93
142
     *
143
     * @param string $resourceShortName
144
     * @param array  $operation
145
     * @param string $operationType
146
     *
147
     * @return string
148
     */
149
    private function getPath(string $resourceShortName, array $operation, string $operationType): string
150
    {
151
        $path = $this->operationPathResolver->resolveOperationPath($resourceShortName, $operation, $operationType);
152
        if ('.{_format}' === substr($path, -10)) {
153
            $path = substr($path, 0, -10);
154
        }
155
156
        return $path;
157
    }
158
159
    /**
160
     * Gets a path Operation Object.
161
     *
162
     * @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#operation-object
163
     *
164
     * @param string           $operationName
165
     * @param array            $operation
166
     * @param string           $method
167
     * @param string           $operationType
168
     * @param string           $resourceClass
169
     * @param ResourceMetadata $resourceMetadata
170
     * @param string[]         $mimeTypes
171
     * @param \ArrayObject     $definitions
172
     *
173
     * @return \ArrayObject
174
     */
175
    private function getPathOperation(string $operationName, array $operation, string $method, string $operationType, string $resourceClass, ResourceMetadata $resourceMetadata, array $mimeTypes, \ArrayObject $definitions): \ArrayObject
176
    {
177
        $pathOperation = new \ArrayObject($operation['swagger_context'] ?? []);
178
        $resourceShortName = $resourceMetadata->getShortName();
179
        $pathOperation['tags'] ?? $pathOperation['tags'] = [$resourceShortName];
180
        $pathOperation['operationId'] ?? $pathOperation['operationId'] = lcfirst($operationName).ucfirst($resourceShortName).ucfirst($operationType);
181
182
        switch ($method) {
183
            case 'GET':
184
                return $this->updateGetOperation($pathOperation, $mimeTypes, $operationType, $resourceMetadata, $resourceClass, $resourceShortName, $operationName, $definitions);
185
            case 'POST':
186
                return $this->updatePostOperation($pathOperation, $mimeTypes, $operationType, $resourceMetadata, $resourceClass, $resourceShortName, $operationName, $definitions);
187
            case 'PUT':
188
                return $this->updatePutOperation($pathOperation, $mimeTypes, $operationType, $resourceMetadata, $resourceClass, $resourceShortName, $operationName, $definitions);
189
            case 'DELETE':
190
                return $this->updateDeleteOperation($pathOperation, $resourceShortName);
191
            default:
192
                throw new RuntimeException(sprintf('Method "%s" is not supported', $method));
193
        }
194
    }
195
196
    /**
197
     * @param \ArrayObject     $pathOperation
198
     * @param array            $mimeTypes
199
     * @param string           $operationType
200
     * @param ResourceMetadata $resourceMetadata
201
     * @param string           $resourceClass
202
     * @param string           $resourceShortName
203
     * @param string           $operationName
204
     * @param \ArrayObject     $definitions
205
     *
206
     * @return \ArrayObject
207
     */
208
    private function updateGetOperation(\ArrayObject $pathOperation, array $mimeTypes, string $operationType, ResourceMetadata $resourceMetadata, string $resourceClass, string $resourceShortName, string $operationName, \ArrayObject $definitions)
209
    {
210
        $serializerContext = $this->getSerializerContext($operationType, false, $resourceMetadata, $operationName);
211
        $responseDefinitionKey = $this->getDefinition($definitions, $resourceMetadata, $resourceClass, $serializerContext);
212
213
        $pathOperation['produces'] ?? $pathOperation['produces'] = $mimeTypes;
214
215
        if ($operationType === OperationType::COLLECTION || $operationType === OperationType::SUBRESOURCE) {
216
            $pathOperation['summary'] ?? $pathOperation['summary'] = sprintf('Retrieves the collection of %s resources.', $resourceShortName);
217
            $pathOperation['responses'] ?? $pathOperation['responses'] = [
218
                '200' => [
219
                    'description' => sprintf('%s collection response', $resourceShortName),
220
                    'schema' => [
221
                        'type' => 'array',
222
                        'items' => ['$ref' => sprintf('#/definitions/%s', $responseDefinitionKey)],
223
                    ],
224
                ],
225
            ];
226
227 View Code Duplication
            if (!isset($pathOperation['parameters']) && $parameters = $this->getFiltersParameters($resourceClass, $operationName, $resourceMetadata, $definitions, $serializerContext)) {
228
                $pathOperation['parameters'] = $parameters;
229
            }
230
231
            return $pathOperation;
232
        }
233
234
        $pathOperation['summary'] ?? $pathOperation['summary'] = sprintf('Retrieves a %s resource.', $resourceShortName);
235
        $pathOperation['parameters'] ?? $pathOperation['parameters'] = [[
236
            'name' => 'id',
237
            'in' => 'path',
238
            'required' => true,
239
            'type' => 'integer',
240
        ]];
241
        $pathOperation['responses'] ?? $pathOperation['responses'] = [
242
            '200' => [
243
                'description' => sprintf('%s resource response', $resourceShortName),
244
                'schema' => ['$ref' => sprintf('#/definitions/%s', $responseDefinitionKey)],
245
            ],
246
            '404' => ['description' => 'Resource not found'],
247
        ];
248
249
        return $pathOperation;
250
    }
251
252
    /**
253
     * @param \ArrayObject     $pathOperation
254
     * @param array            $mimeTypes
255
     * @param string           $operationType
256
     * @param ResourceMetadata $resourceMetadata
257
     * @param string           $resourceClass
258
     * @param string           $resourceShortName
259
     * @param string           $operationName
260
     * @param \ArrayObject     $definitions
261
     *
262
     * @return \ArrayObject
263
     */
264
    private function updatePostOperation(\ArrayObject $pathOperation, array $mimeTypes, string $operationType, ResourceMetadata $resourceMetadata, string $resourceClass, string $resourceShortName, string $operationName, \ArrayObject $definitions)
265
    {
266
        $pathOperation['consumes'] ?? $pathOperation['consumes'] = $mimeTypes;
267
        $pathOperation['produces'] ?? $pathOperation['produces'] = $mimeTypes;
268
        $pathOperation['summary'] ?? $pathOperation['summary'] = sprintf('Creates a %s resource.', $resourceShortName);
269
        $pathOperation['parameters'] ?? $pathOperation['parameters'] = [[
270
            'name' => lcfirst($resourceShortName),
271
            'in' => 'body',
272
            'description' => sprintf('The new %s resource', $resourceShortName),
273
            'schema' => ['$ref' => sprintf('#/definitions/%s', $this->getDefinition($definitions, $resourceMetadata, $resourceClass,
274
                $this->getSerializerContext($operationType, true, $resourceMetadata, $operationName)
275
            ))],
276
        ]];
277
        $pathOperation['responses'] ?? $pathOperation['responses'] = [
278
            '201' => [
279
                'description' => sprintf('%s resource created', $resourceShortName),
280
                'schema' => ['$ref' => sprintf('#/definitions/%s', $this->getDefinition($definitions, $resourceMetadata, $resourceClass,
281
                    $this->getSerializerContext($operationType, false, $resourceMetadata, $operationName)
282
                ))],
283
            ],
284
            '400' => ['description' => 'Invalid input'],
285
            '404' => ['description' => 'Resource not found'],
286
        ];
287
288
        return $pathOperation;
289
    }
290
291
    /**
292
     * @param \ArrayObject     $pathOperation
293
     * @param array            $mimeTypes
294
     * @param string           $operationType
295
     * @param ResourceMetadata $resourceMetadata
296
     * @param string           $resourceClass
297
     * @param string           $resourceShortName
298
     * @param string           $operationName
299
     * @param \ArrayObject     $definitions
300
     *
301
     * @return \ArrayObject
302
     */
303
    private function updatePutOperation(\ArrayObject $pathOperation, array $mimeTypes, string $operationType, ResourceMetadata $resourceMetadata, string $resourceClass, string $resourceShortName, string $operationName, \ArrayObject $definitions)
304
    {
305
        $pathOperation['consumes'] ?? $pathOperation['consumes'] = $mimeTypes;
306
        $pathOperation['produces'] ?? $pathOperation['produces'] = $mimeTypes;
307
        $pathOperation['summary'] ?? $pathOperation['summary'] = sprintf('Replaces the %s resource.', $resourceShortName);
308
        $pathOperation['parameters'] ?? $pathOperation['parameters'] = [
309
            [
310
                'name' => 'id',
311
                'in' => 'path',
312
                'type' => 'integer',
313
                'required' => true,
314
            ],
315
            [
316
                'name' => lcfirst($resourceShortName),
317
                'in' => 'body',
318
                'description' => sprintf('The updated %s resource', $resourceShortName),
319
                'schema' => ['$ref' => sprintf('#/definitions/%s', $this->getDefinition($definitions, $resourceMetadata, $resourceClass,
320
                    $this->getSerializerContext($operationType, true, $resourceMetadata, $operationName)
321
                ))],
322
            ],
323
        ];
324
        $pathOperation['responses'] ?? $pathOperation['responses'] = [
325
            '200' => [
326
                'description' => sprintf('%s resource updated', $resourceShortName),
327
                'schema' => ['$ref' => sprintf('#/definitions/%s', $this->getDefinition($definitions, $resourceMetadata, $resourceClass,
328
                    $this->getSerializerContext($operationType, false, $resourceMetadata, $operationName)
329
                ))],
330
            ],
331
            '400' => ['description' => 'Invalid input'],
332
            '404' => ['description' => 'Resource not found'],
333
        ];
334
335
        return $pathOperation;
336
    }
337
338
    /**
339
     * @param \ArrayObject $pathOperation
340
     * @param string       $resourceShortName
341
     *
342
     * @return \ArrayObject
343
     */
344
    private function updateDeleteOperation(\ArrayObject $pathOperation, string $resourceShortName): \ArrayObject
345
    {
346
        $pathOperation['summary'] ?? $pathOperation['summary'] = sprintf('Removes the %s resource.', $resourceShortName);
347
        $pathOperation['responses'] ?? $pathOperation['responses'] = [
348
            '204' => ['description' => sprintf('%s resource deleted', $resourceShortName)],
349
            '404' => ['description' => 'Resource not found'],
350
        ];
351
352
        $pathOperation['parameters'] ?? $pathOperation['parameters'] = [[
353
            'name' => 'id',
354
            'in' => 'path',
355
            'type' => 'integer',
356
            'required' => true,
357
        ]];
358
359
        return $pathOperation;
360
    }
361
362
    /**
363
     * @param \ArrayObject     $definitions
364
     * @param ResourceMetadata $resourceMetadata
365
     * @param string           $resourceClass
366
     * @param array|null       $serializerContext
367
     *
368
     * @return string
369
     */
370
    private function getDefinition(\ArrayObject $definitions, ResourceMetadata $resourceMetadata, string $resourceClass, array $serializerContext = null): string
371
    {
372
        if (isset($serializerContext['groups'])) {
373
            $definitionKey = sprintf('%s_%s', $resourceMetadata->getShortName(), md5(serialize($serializerContext['groups'])));
374
        } else {
375
            $definitionKey = $resourceMetadata->getShortName();
376
        }
377
378 View Code Duplication
        if (!isset($definitions[$definitionKey])) {
379
            $definitions[$definitionKey] = [];  // Initialize first to prevent infinite loop
380
            $definitions[$definitionKey] = $this->getDefinitionSchema($resourceClass, $resourceMetadata, $definitions, $serializerContext);
381
        }
382
383
        return $definitionKey;
384
    }
385
386
    /**
387
     * Gets a definition Schema Object.
388
     *
389
     * @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#schemaObject
390
     *
391
     * @param string           $resourceClass
392
     * @param ResourceMetadata $resourceMetadata
393
     * @param \ArrayObject     $definitions
394
     * @param array|null       $serializerContext
395
     *
396
     * @return \ArrayObject
397
     */
398
    private function getDefinitionSchema(string $resourceClass, ResourceMetadata $resourceMetadata, \ArrayObject $definitions, array $serializerContext = null): \ArrayObject
399
    {
400
        $definitionSchema = new \ArrayObject(['type' => 'object']);
401
402
        if (null !== $description = $resourceMetadata->getDescription()) {
403
            $definitionSchema['description'] = $description;
404
        }
405
406
        if (null !== $iri = $resourceMetadata->getIri()) {
407
            $definitionSchema['externalDocs'] = ['url' => $iri];
408
        }
409
410
        $options = isset($serializerContext['groups']) ? ['serializer_groups' => $serializerContext['groups']] : [];
411
        foreach ($this->propertyNameCollectionFactory->create($resourceClass, $options) as $propertyName) {
412
            $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName);
413
            $normalizedPropertyName = $this->nameConverter ? $this->nameConverter->normalize($propertyName) : $propertyName;
414
415
            if ($propertyMetadata->isRequired()) {
416
                $definitionSchema['required'][] = $normalizedPropertyName;
417
            }
418
419
            $definitionSchema['properties'][$normalizedPropertyName] = $this->getPropertySchema($propertyMetadata, $definitions, $serializerContext);
420
        }
421
422
        return $definitionSchema;
423
    }
424
425
    /**
426
     * Gets a property Schema Object.
427
     *
428
     * @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#schemaObject
429
     *
430
     * @param PropertyMetadata $propertyMetadata
431
     * @param \ArrayObject     $definitions
432
     * @param array|null       $serializerContext
433
     *
434
     * @return \ArrayObject
435
     */
436
    private function getPropertySchema(PropertyMetadata $propertyMetadata, \ArrayObject $definitions, array $serializerContext = null): \ArrayObject
437
    {
438
        $propertySchema = new \ArrayObject();
439
440
        if (false === $propertyMetadata->isWritable()) {
441
            $propertySchema['readOnly'] = true;
442
        }
443
444
        if (null !== $description = $propertyMetadata->getDescription()) {
445
            $propertySchema['description'] = $description;
446
        }
447
448
        if (null === $type = $propertyMetadata->getType()) {
449
            return $propertySchema;
450
        }
451
452
        $isCollection = $type->isCollection();
453
        if (null === $valueType = $isCollection ? $type->getCollectionValueType() : $type) {
454
            $builtinType = 'string';
455
            $className = null;
456
        } else {
457
            $builtinType = $valueType->getBuiltinType();
458
            $className = $valueType->getClassName();
459
        }
460
461
        $valueSchema = $this->getType($builtinType, $isCollection, $className, $propertyMetadata->isReadableLink(), $definitions, $serializerContext);
462
463
        return new \ArrayObject((array) $propertySchema + $valueSchema);
464
    }
465
466
    /**
467
     * Gets the Swagger's type corresponding to the given PHP's type.
468
     *
469
     * @param string       $type
470
     * @param bool         $isCollection
471
     * @param string       $className
472
     * @param bool         $readableLink
473
     * @param \ArrayObject $definitions
474
     * @param array|null   $serializerContext
475
     *
476
     * @return array
477
     */
478
    private function getType(string $type, bool $isCollection, string $className = null, bool $readableLink = null, \ArrayObject $definitions, array $serializerContext = null): array
479
    {
480
        if ($isCollection) {
481
            return ['type' => 'array', 'items' => $this->getType($type, false, $className, $readableLink, $definitions, $serializerContext)];
482
        }
483
484
        if (Type::BUILTIN_TYPE_STRING === $type) {
485
            return ['type' => 'string'];
486
        }
487
488
        if (Type::BUILTIN_TYPE_INT === $type) {
489
            return ['type' => 'integer'];
490
        }
491
492
        if (Type::BUILTIN_TYPE_FLOAT === $type) {
493
            return ['type' => 'number'];
494
        }
495
496
        if (Type::BUILTIN_TYPE_BOOL === $type) {
497
            return ['type' => 'boolean'];
498
        }
499
500
        if (Type::BUILTIN_TYPE_OBJECT === $type) {
501
            if (null === $className) {
502
                return ['type' => 'string'];
503
            }
504
505
            if (is_subclass_of($className, \DateTimeInterface::class)) {
506
                return ['type' => 'string', 'format' => 'date-time'];
507
            }
508
509
            if (!$this->resourceClassResolver->isResourceClass($className)) {
510
                return ['type' => 'string'];
511
            }
512
513
            if (true === $readableLink) {
514
                return ['$ref' => sprintf('#/definitions/%s', $this->getDefinition($definitions,
515
                    $this->resourceMetadataFactory->create($className),
516
                    $className, $serializerContext)
517
                )];
518
            }
519
        }
520
521
        return ['type' => 'string'];
522
    }
523
524
    /**
525
     * Computes the Swagger documentation.
526
     *
527
     * @param Documentation $documentation
528
     * @param \ArrayObject  $definitions
529
     * @param \ArrayObject  $paths
530
     *
531
     * @return array
532
     */
533
    private function computeDoc(Documentation $documentation, \ArrayObject $definitions, \ArrayObject $paths): array
534
    {
535
        $doc = [
536
            'swagger' => self::SWAGGER_VERSION,
537
            'basePath' => $this->urlGenerator->generate('api_entrypoint'),
538
            'info' => [
539
                'title' => $documentation->getTitle(),
540
                'version' => $documentation->getVersion(),
541
            ],
542
            'paths' => $paths,
543
        ];
544
545
        if ($this->oauthEnabled) {
546
            $doc['securityDefinitions'] = [
547
                'oauth' => [
548
                    'type' => $this->oauthType,
549
                    'description' => 'OAuth client_credentials Grant',
550
                    'flow' => $this->oauthFlow,
551
                    'tokenUrl' => $this->oauthTokenUrl,
552
                    'authorizationUrl' => $this->oauthAuthorizationUrl,
553
                    'scopes' => $this->oauthScopes,
554
                ],
555
            ];
556
557
            $doc['security'] = [['oauth' => []]];
558
        }
559
560
        if ('' !== $description = $documentation->getDescription()) {
561
            $doc['info']['description'] = $description;
562
        }
563
564
        if (count($definitions) > 0) {
565
            $doc['definitions'] = $definitions;
566
        }
567
568
        return $doc;
569
    }
570
571
    /**
572
     * Gets Swagger parameters corresponding to enabled filters.
573
     *
574
     * @param string           $resourceClass
575
     * @param string           $operationName
576
     * @param ResourceMetadata $resourceMetadata
577
     * @param \ArrayObject     $definitions
578
     * @param array|null       $serializerContext
579
     *
580
     * @return array
581
     */
582
    private function getFiltersParameters(string $resourceClass, string $operationName, ResourceMetadata $resourceMetadata, \ArrayObject $definitions, array $serializerContext = null): array
583
    {
584
        if (null === $this->filterLocator) {
585
            return [];
586
        }
587
588
        $parameters = [];
589
        $resourceFilters = $resourceMetadata->getCollectionOperationAttribute($operationName, 'filters', [], true);
590
        foreach ($resourceFilters as $filterId) {
591
            if (!$filter = $this->getFilter($filterId)) {
0 ignored issues
show
Are you sure the assignment to $filter is correct as $this->getFilter($filterId) (which targets ApiPlatform\Core\Api\Fil...catorTrait::getFilter()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
592
                continue;
593
            }
594
595
            foreach ($filter->getDescription($resourceClass) as $name => $data) {
596
                $parameter = [
597
                    'name' => $name,
598
                    'in' => 'query',
599
                    'required' => $data['required'],
600
                ];
601
                $parameter += $this->getType($data['type'], false, null, null, $definitions, $serializerContext);
602
603
                if (isset($data['swagger'])) {
604
                    $parameter = $data['swagger'] + $parameter;
605
                }
606
607
                $parameters[] = $parameter;
608
            }
609
        }
610
611
        return $parameters;
612
    }
613
614
    /**
615
     * {@inheritdoc}
616
     */
617
    public function supportsNormalization($data, $format = null)
618
    {
619
        return self::FORMAT === $format && $data instanceof Documentation;
620
    }
621
622
    /**
623
     * @param string           $operationType
624
     * @param bool             $denormalization
625
     * @param ResourceMetadata $resourceMetadata
626
     * @param string           $operationType
627
     *
628
     * @return array|null
629
     */
630
    private function getSerializerContext(string $operationType, bool $denormalization, ResourceMetadata $resourceMetadata, string $operationName)
631
    {
632
        $contextKey = $denormalization ? 'denormalization_context' : 'normalization_context';
633
634
        if (OperationType::COLLECTION === $operationType) {
635
            return $resourceMetadata->getCollectionOperationAttribute($operationName, $contextKey, null, true);
636
        }
637
638
        return $resourceMetadata->getItemOperationAttribute($operationName, $contextKey, null, true);
639
    }
640
}
641