Completed
Pull Request — master (#617)
by Amrouche
03:15
created

ApiDocumentationBuilder   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 388
Duplicated Lines 14.69 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
wmc 56
lcom 1
cbo 11
dl 57
loc 388
rs 6.5957
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 21 3
F getApiDocumentation() 45 153 28
C getSwaggerOperation() 12 131 12
C getRange() 0 48 13

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ApiDocumentationBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ApiDocumentationBuilder, and based on these observations, apply Extract Interface, too.

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
namespace ApiPlatform\Core\Swagger;
13
14
use ApiPlatform\Core\Api\IriConverterInterface;
15
use ApiPlatform\Core\Api\OperationMethodResolverInterface;
16
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
17
use ApiPlatform\Core\Api\UrlGeneratorInterface;
18
use ApiPlatform\Core\Documentation\ApiDocumentationBuilderInterface;
19
use ApiPlatform\Core\Exception\InvalidArgumentException;
20
use ApiPlatform\Core\JsonLd\ContextBuilderInterface;
21
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
22
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
23
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
24
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
25
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
26
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
27
use Symfony\Component\PropertyInfo\Type;
28
29
/**
30
 * Creates a machine readable Swagger API documentation.
31
 *
32
 * @author Amrouche Hamza <[email protected]>
33
 * @author Kévin Dunglas <[email protected]>
34
 */
35
final class ApiDocumentationBuilder implements ApiDocumentationBuilderInterface
36
{
37
    const SWAGGER_VERSION = '2.0';
38
39
    private $resourceNameCollectionFactory;
40
    private $resourceMetadataFactory;
41
    private $propertyNameCollectionFactory;
42
    private $propertyMetadataFactory;
43
    private $contextBuilder;
44
    private $resourceClassResolver;
45
    private $operationMethodResolver;
46
    private $urlGenerator;
47
    private $title;
48
    private $description;
49
    private $iriConverter;
50
    private $version;
51
    private $mimeTypes = [];
52
53
    public function __construct(ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ContextBuilderInterface $contextBuilder, ResourceClassResolverInterface $resourceClassResolver, OperationMethodResolverInterface $operationMethodResolver, UrlGeneratorInterface $urlGenerator, IriConverterInterface $iriConverter, array $formats, string $title, string $description, string $version = null)
54
    {
55
        $this->resourceNameCollectionFactory = $resourceNameCollectionFactory;
56
        $this->resourceMetadataFactory = $resourceMetadataFactory;
57
        $this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
58
        $this->propertyMetadataFactory = $propertyMetadataFactory;
59
        $this->contextBuilder = $contextBuilder;
60
        $this->resourceClassResolver = $resourceClassResolver;
61
        $this->operationMethodResolver = $operationMethodResolver;
62
        $this->urlGenerator = $urlGenerator;
63
        $this->title = $title;
64
        $this->description = $description;
65
        $this->iriConverter = $iriConverter;
66
        $this->version = $version;
67
68
        foreach ($formats as $format => $mimeTypes) {
69
            foreach ($mimeTypes as $mimeType) {
70
                $this->mimeTypes[] = $mimeType;
71
            }
72
        }
73
    }
74
75
    /**
76
     * {@inheritdoc}
77
     */
78
    public function getApiDocumentation() : array
79
    {
80
        $classes = [];
81
        $operation = [];
82
        $customOperation = [];
83
84
85
        $itemOperationsDocs = [];
86
        $definitions = [];
87
88
        foreach ($this->resourceNameCollectionFactory->create() as $resourceClass) {
89
            $operation['item'] = [];
90
            $operation['collection'] = [];
91
            $customOperations['item'] = [];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$customOperations was never initialized. Although not strictly required by PHP, it is generally a good practice to add $customOperations = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
92
            $customOperations['collection'] = [];
0 ignored issues
show
Bug introduced by
The variable $customOperations does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
93
            $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
94
95
            $shortName = $resourceMetadata->getShortName();
96
            $prefixedShortName = ($iri = $resourceMetadata->getIri()) ? $iri : '#'.$shortName;
97
98
            $class = [
99
                'name' => $shortName,
100
                'externalDocs' => ['url' => $prefixedShortName],
101
            ];
102
103
            if ($description = $resourceMetadata->getDescription()) {
104
                $class = [
105
                    'name' => $shortName,
106
                    'description' => $description,
107
                    'externalDocs' => ['url' => $prefixedShortName],
108
                ];
109
            }
110
111
            $attributes = $resourceMetadata->getAttributes();
112
            $context = [];
113
114
            if (isset($attributes['normalization_context']['groups'])) {
115
                $context['serializer_groups'] = $attributes['normalization_context']['groups'];
116
            }
117
118 View Code Duplication
            if (isset($attributes['denormalization_context']['groups'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
119
                $context['serializer_groups'] = isset($context['serializer_groups']) ? array_merge($context['serializer_groups'], $attributes['denormalization_context']['groups']) : $context['serializer_groups'];
120
            }
121
122
            $definitions[$shortName] = [
123
                'type' => 'object',
124
                'xml' => ['name' => 'response'],
125
            ];
126
127
            foreach ($this->propertyNameCollectionFactory->create($resourceClass, $context) as $propertyName) {
128
                $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName);
129
130
                if ($propertyMetadata->isRequired()) {
131
                    $definitions[$shortName]['required'][] = $propertyName;
132
                }
133
134
                $range = $this->getRange($propertyMetadata);
135
                if (null === $range) {
136
                    continue;
137
                }
138
139
                if ($propertyMetadata->getDescription()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $propertyMetadata->getDescription() of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
140
                    $definitions[$shortName]['properties'][$propertyName]['description'] = $propertyMetadata->getDescription();
141
                }
142
143
                if ($range['complex']) {
144
                    $definitions[$shortName]['properties'][$propertyName] = ['$ref' => $range['value']];
145
                } else {
146
                    $definitions[$shortName]['properties'][$propertyName] = ['type' => $range['value']];
147
148
                    if (isset($range['example'])) {
149
                        $definitions[$shortName]['properties'][$propertyName]['example'] = $range['example'];
150
                    }
151
                }
152
            }
153
154 View Code Duplication
            if ($operations = $resourceMetadata->getItemOperations()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
155
                foreach ($operations as $operationName => $itemOperation) {
156
                    $method = $this->operationMethodResolver->getItemOperationMethod($resourceClass, $operationName);
157
                    $swaggerOperation = $this->getSwaggerOperation($resourceClass, $resourceMetadata, $operationName, $itemOperation, $prefixedShortName, false, $definitions, $method);
158
                    $operation['item'] = array_merge($operation['item'], $swaggerOperation);
159
                    if ($operationName !== strtolower($method)) {
160
                        $customOperations['item'][] = $operationName;
161
                    }
162
                }
163
            }
164
165 View Code Duplication
            if ($operations = $resourceMetadata->getCollectionOperations()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
166
                foreach ($operations as $operationName => $collectionOperation) {
167
                    $method = $this->operationMethodResolver->getCollectionOperationMethod($resourceClass, $operationName);
168
                    $swaggerOperation = $this->getSwaggerOperation($resourceClass, $resourceMetadata, $operationName, $collectionOperation, $prefixedShortName, true, $definitions, $method);
169
                    $operation['collection'] = array_merge($operation['collection'], $swaggerOperation);
170
                    if ($operationName !== strtolower($method)) {
171
                        $customOperations['collection'][] = $operationName;
172
                    }
173
                }
174
            }
175
            try {
176
                $resourceClassIri = $this->iriConverter->getIriFromResourceClass($resourceClass);
177
                $itemOperationsDocs[$resourceClassIri] = $operation['collection'];
178
179 View Code Duplication
                if (!empty($customOperations['collection'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
180
                    foreach ($customOperations['collection'] as $customOperation) {
181
                        $path = $resourceMetadata->getCollectionOperationAttribute($customOperation, 'path');
182
                        if (null !== $path) {
183
                            $method = $this->operationMethodResolver->getCollectionOperationMethod($resourceClass, $customOperation);
184
                            $customSwaggerOperation = $this->getSwaggerOperation($resourceClass, $resourceMetadata, $customOperation, [$method], $prefixedShortName, true, $definitions, $method);
185
186
                            $itemOperationsDocs[$path] = $customSwaggerOperation;
187
                        }
188
                 }
189
                }
190
191
192
                $resourceClassIri .= '/{id}';
193
194
                $itemOperationsDocs[$resourceClassIri] = $operation['item'];
195
196 View Code Duplication
                if (!empty($customOperations['item'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197
                    foreach ($customOperations['item'] as $customOperation) {
198
                        $path = $resourceMetadata->getItemOperationAttribute($customOperation, 'path');
199
                        if (null !== $path) {
200
                            $method = $this->operationMethodResolver->getItemOperationMethod($resourceClass, $customOperation);
201
                            $customSwaggerOperation = $this->getSwaggerOperation($resourceClass, $resourceMetadata, $customOperation, [$method], $prefixedShortName, true, $definitions, $method);
202
203
                            $itemOperationsDocs[$path] = $customSwaggerOperation;
204
                        }
205
                    }
206
                }
207
208
            } catch (InvalidArgumentException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
209
            }
210
211
            $classes[] = $class;
212
        }
213
214
        $doc['swagger'] = self::SWAGGER_VERSION;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$doc was never initialized. Although not strictly required by PHP, it is generally a good practice to add $doc = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
215
        if ('' !== $this->title) {
216
            $doc['info']['title'] = $this->title;
217
        }
218
219
        if ('' !== $this->description) {
220
            $doc['info']['description'] = $this->description;
221
        }
222
        $doc['info']['version'] = $this->version ?? '0.0.0';
223
        $doc['basePath'] = $this->urlGenerator->generate('api_hydra_entrypoint');
224
        $doc['definitions'] = $definitions;
225
        $doc['externalDocs'] = ['description' => 'Find more about API Platform', 'url' => 'https://api-platform.com'];
226
        $doc['tags'] = $classes;
227
        $doc['paths'] = $itemOperationsDocs;
228
229
        return $doc;
230
    }
231
232
    /**
233
     * Gets and populates if applicable a Swagger operation.
234
     */
235
    private function getSwaggerOperation(string $resourceClass, ResourceMetadata $resourceMetadata, string $operationName, array $operation, string $prefixedShortName, bool $collection, array $properties, string $method) : array
0 ignored issues
show
Unused Code introduced by
The parameter $operationName is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $prefixedShortName is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $properties is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
236
    {
237
        $methodSwagger = strtolower($method);
238
        $swaggerOperation = $operation['swagger_context'] ?? [];
239
        $shortName = $resourceMetadata->getShortName();
240
        $swaggerOperation[$methodSwagger] = [];
241
        $swaggerOperation[$methodSwagger]['tags'] = [$shortName];
242
243
        switch ($method) {
244
            case 'GET':
0 ignored issues
show
Coding Style introduced by
CASE statements must be defined using a colon

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
245
                $swaggerOperation[$methodSwagger]['produces'] = $this->mimeTypes;
246
247
                if ($collection) {
248 View Code Duplication
                    if (!isset($swaggerOperation[$methodSwagger]['title'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
249
                        $swaggerOperation[$methodSwagger]['summary'] = sprintf('Retrieves the collection of %s resources.', $shortName);
250
                    }
251
252
                    $swaggerOperation[$methodSwagger]['responses'] = [
253
                        '200' => [
254
                            'description' => 'Successful operation',
255
                             'schema' => [
256
                                'type' => 'array',
257
                                'items' => ['$ref' => sprintf('#/definitions/%s', $shortName)],
258
                             ],
259
                        ],
260
                    ];
261
                    break;
262
                }
263
264 View Code Duplication
                if (!isset($swaggerOperation[$methodSwagger]['title'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
265
                    $swaggerOperation[$methodSwagger]['summary'] = sprintf('Retrieves %s resource.', $shortName);
266
                }
267
268
                $swaggerOperation[$methodSwagger]['parameters'][] = [
269
                    'name' => 'id',
270
                    'in' => 'path',
271
                    'required' => true,
272
                    'type' => 'integer',
273
                ];
274
275
                $swaggerOperation[$methodSwagger]['responses'] = [
276
                    '200' => [
277
                        'description' => 'Successful operation',
278
                        'schema' => ['$ref' => sprintf('#/definitions/%s', $shortName)],
279
                    ],
280
                    '404' => ['description' => 'Resource not found'],
281
                ];
282
                break;
283
284
            case 'POST':
0 ignored issues
show
Coding Style introduced by
CASE statements must be defined using a colon

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
285
                $swaggerOperation[$methodSwagger]['consumes'] = $swaggerOperation[$methodSwagger]['produces'] = $this->mimeTypes;
286
287 View Code Duplication
                if (!isset($swaggerOperation[$methodSwagger]['title'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
288
                    $swaggerOperation[$methodSwagger]['summary'] = sprintf('Creates a %s resource.', $shortName);
289
                }
290
291
                if ($this->resourceClassResolver->isResourceClass($resourceClass)) {
292
                    $swaggerOperation[$methodSwagger]['parameters'][] = [
293
                        'in' => 'body',
294
                        'name' => 'body',
295
                        'description' => sprintf('%s resource to be added', $shortName),
296
                        'schema' => [
297
                            '$ref' => sprintf('#/definitions/%s', $shortName),
298
                        ],
299
                    ];
300
                }
301
302
                $swaggerOperation[$methodSwagger]['responses'] = [
303
                        '201' => [
304
                            'description' => 'Successful operation',
305
                            'schema' => ['$ref' => sprintf('#/definitions/%s', $shortName)],
306
                        ],
307
                        '400' => ['description' => 'Invalid input'],
308
                        '404' => ['description' => 'Resource not found'],
309
                ];
310
            break;
311
312
            case 'PUT':
0 ignored issues
show
Coding Style introduced by
CASE statements must be defined using a colon

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
313
                $swaggerOperation[$methodSwagger]['consumes'] = $swaggerOperation[$methodSwagger]['produces'] = $this->mimeTypes;
314
315 View Code Duplication
                if (!isset($swaggerOperation[$methodSwagger]['title'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
316
                    $swaggerOperation[$methodSwagger]['summary'] = sprintf('Replaces the %s resource.', $shortName);
317
                }
318
319
                if ($this->resourceClassResolver->isResourceClass($resourceClass)) {
320
                    $swaggerOperation[$methodSwagger]['parameters'] = [
321
                        [
322
                            'name' => 'id',
323
                            'in' => 'path',
324
                            'required' => true,
325
                            'type' => 'integer',
326
                        ],
327
                        [
328
                            'in' => 'body',
329
                            'name' => 'body',
330
                            'description' => sprintf('%s resource to be added', $shortName),
331
                            'schema' => [
332
                                '$ref' => sprintf('#/definitions/%s', $shortName),
333
                            ],
334
                        ],
335
                    ];
336
                }
337
338
                $swaggerOperation[$methodSwagger]['responses'] = [
339
                    '200' => [
340
                        'description' => 'Successful operation',
341
                        'schema' => ['$ref' => sprintf('#/definitions/%s', $shortName)],
342
                    ],
343
                    '400' => ['description' => 'Invalid input'],
344
                    '404' => ['description' => 'Resource not found'],
345
                ];
346
            break;
347
348
            case 'DELETE':
349
                $swaggerOperation[$methodSwagger]['responses'] = [
350
                    '204' => ['description' => 'Deleted'],
351
                    '404' => ['description' => 'Resource not found'],
352
                ];
353
354
                $swaggerOperation[$methodSwagger]['parameters'] = [[
355
                    'name' => 'id',
356
                    'in' => 'path',
357
                    'required' => true,
358
                    'type' => 'integer',
359
                ]];
360
            break;
361
        }
362
        ksort($swaggerOperation);
363
364
        return $swaggerOperation;
365
    }
366
367
    /**
368
     * Gets the range of the property.
369
     *
370
     * @param PropertyMetadata $propertyMetadata
371
     *
372
     * @return array|null
373
     */
374
    private function getRange(PropertyMetadata $propertyMetadata) : array
375
    {
376
        $type = $propertyMetadata->getType();
377
        if (!$type) {
378
            return;
379
        }
380
381
        if ($type->isCollection() && $collectionType = $type->getCollectionValueType()) {
382
            $type = $collectionType;
383
        }
384
385
        switch ($type->getBuiltinType()) {
386
            case Type::BUILTIN_TYPE_STRING:
387
                return ['complex' => false, 'value' => 'string'];
388
389
            case Type::BUILTIN_TYPE_INT:
390
                return ['complex' => false, 'value' => 'integer'];
391
392
            case Type::BUILTIN_TYPE_FLOAT:
393
                return ['complex' => false, 'value' => 'number'];
394
395
            case Type::BUILTIN_TYPE_BOOL:
396
                return ['complex' => false, 'value' => 'boolean'];
397
398
            case Type::BUILTIN_TYPE_OBJECT:
399
                $className = $type->getClassName();
400
                if (null === $className) {
401
                    return;
402
                }
403
404
                if (is_subclass_of($className, \DateTimeInterface::class)) {
1 ignored issue
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \DateTimeInterface::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
405
                    return ['complex' => false, 'value' => 'string', 'example' => '1988-01-21T00:00:00+00:00'];
406
                }
407
408
                if (!$this->resourceClassResolver->isResourceClass($className)) {
409
                    return;
410
                }
411
412
                if ($propertyMetadata->isReadableLink()) {
413
                    return ['complex' => true, 'value' => sprintf('#/definitions/%s', $this->resourceMetadataFactory->create($className)->getShortName())];
414
                }
415
416
                return ['complex' => false, 'value' => 'string', 'example' => '/my/iri'];
417
418
            default:
419
                return ['complex' => false, 'value' => 'null'];
420
        }
421
    }
422
}
423