Completed
Push — master ( 4b3d1a...62ffea )
by
unknown
17s
created

ApiPlatformProvider::getOperationHydraDoc()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 7
nc 4
nop 2
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\Bridge\NelmioApiDoc\Extractor\AnnotationsProvider;
15
16
use ApiPlatform\Core\Api\FilterCollection;
17
use ApiPlatform\Core\Api\FilterLocatorTrait;
18
use ApiPlatform\Core\Bridge\NelmioApiDoc\Parser\ApiPlatformParser;
19
use ApiPlatform\Core\Bridge\Symfony\Routing\OperationMethodResolverInterface;
20
use ApiPlatform\Core\Documentation\Documentation;
21
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
22
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
23
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
24
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
25
use Nelmio\ApiDocBundle\Extractor\AnnotationsProviderInterface;
26
use Psr\Container\ContainerInterface;
27
use Symfony\Component\HttpFoundation\Request;
28
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
29
30
/**
31
 * Creates Nelmio ApiDoc annotations for the api platform.
32
 *
33
 * @author Kévin Dunglas <[email protected]>
34
 * @author Teoh Han Hui <[email protected]>
35
 *
36
 * @deprecated since version 2.2, to be removed in 3.0. NelmioApiDocBundle 3 has native support for API Platform.
37
 */
38
final class ApiPlatformProvider implements AnnotationsProviderInterface
39
{
40
    use FilterLocatorTrait;
41
42
    private $resourceNameCollectionFactory;
43
    private $documentationNormalizer;
44
    private $resourceMetadataFactory;
45
    private $operationMethodResolver;
46
47
    /**
48
     * @param ContainerInterface|FilterCollection $filterLocator The new filter locator or the deprecated filter collection
49
     */
50
    public function __construct(ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, NormalizerInterface $documentationNormalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, $filterLocator, OperationMethodResolverInterface $operationMethodResolver)
51
    {
52
        @trigger_error('The '.__CLASS__.' class is deprecated since version 2.2 and will be removed in 3.0. NelmioApiDocBundle 3 has native support for API Platform.', E_USER_DEPRECATED);
53
54
        $this->setFilterLocator($filterLocator);
55
56
        $this->resourceNameCollectionFactory = $resourceNameCollectionFactory;
57
        $this->documentationNormalizer = $documentationNormalizer;
58
        $this->resourceMetadataFactory = $resourceMetadataFactory;
59
        $this->operationMethodResolver = $operationMethodResolver;
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65
    public function getAnnotations(): array
66
    {
67
        $resourceNameCollection = $this->resourceNameCollectionFactory->create();
68
        $hydraDoc = $this->documentationNormalizer->normalize(new Documentation($resourceNameCollection));
69
        if (empty($hydraDoc)) {
70
            return [];
71
        }
72
73
        $entrypointHydraDoc = $this->getResourceHydraDoc($hydraDoc, '#Entrypoint');
74
        if (null === $entrypointHydraDoc) {
75
            return [];
76
        }
77
78
        $annotations = [];
79
        foreach ($resourceNameCollection as $resourceClass) {
80
            $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
81
82
            $prefixedShortName = ($iri = $resourceMetadata->getIri()) ? $iri : '#'.$resourceMetadata->getShortName();
83
            $resourceHydraDoc = $this->getResourceHydraDoc($hydraDoc, $prefixedShortName);
84
            if (null === $resourceHydraDoc) {
85
                continue;
86
            }
87
88 View Code Duplication
            if (null !== $collectionOperations = $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...
89
                foreach ($collectionOperations as $operationName => $operation) {
90
                    $annotations[] = $this->getApiDoc(true, $resourceClass, $resourceMetadata, $operationName, $resourceHydraDoc, $entrypointHydraDoc);
91
                }
92
            }
93
94
            if (null !== $itemOperations = $resourceMetadata->getItemOperations()) {
95
                foreach ($itemOperations as $operationName => $operation) {
96
                    $annotations[] = $this->getApiDoc(false, $resourceClass, $resourceMetadata, $operationName, $resourceHydraDoc);
97
                }
98
            }
99
        }
100
101
        return $annotations;
102
    }
103
104
    /**
105
     * Builds ApiDoc annotation from ApiPlatform data.
106
     *
107
     * @param bool             $collection
108
     * @param string           $resourceClass
109
     * @param ResourceMetadata $resourceMetadata
110
     * @param string           $operationName
111
     * @param array            $resourceHydraDoc
112
     * @param array            $entrypointHydraDoc
113
     *
114
     * @return ApiDoc
115
     */
116
    private function getApiDoc(bool $collection, string $resourceClass, ResourceMetadata $resourceMetadata, string $operationName, array $resourceHydraDoc, array $entrypointHydraDoc = []): ApiDoc
117
    {
118
        if ($collection) {
119
            $method = $this->operationMethodResolver->getCollectionOperationMethod($resourceClass, $operationName);
120
            $route = $this->operationMethodResolver->getCollectionOperationRoute($resourceClass, $operationName);
121
            $operationHydraDoc = $this->getCollectionOperationHydraDoc($resourceMetadata->getShortName(), $method, $entrypointHydraDoc);
122
        } else {
123
            $method = $this->operationMethodResolver->getItemOperationMethod($resourceClass, $operationName);
124
            $route = $this->operationMethodResolver->getItemOperationRoute($resourceClass, $operationName);
125
            $operationHydraDoc = $this->getOperationHydraDoc($method, $resourceHydraDoc);
126
        }
127
128
        $data = [
129
            'resource' => $route->getPath(),
130
            'description' => $operationHydraDoc['hydra:title'] ?? '',
131
            'resourceDescription' => $resourceHydraDoc['hydra:title'] ?? '',
132
            'section' => $resourceHydraDoc['hydra:title'] ?? '',
133
        ];
134
135 View Code Duplication
        if (isset($operationHydraDoc['expects']) && 'owl:Nothing' !== $operationHydraDoc['expects']) {
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...
136
            $data['input'] = sprintf('%s:%s:%s', ApiPlatformParser::IN_PREFIX, $resourceClass, $operationName);
137
        }
138
139 View Code Duplication
        if (isset($operationHydraDoc['returns']) && 'owl:Nothing' !== $operationHydraDoc['returns']) {
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...
140
            $data['output'] = sprintf('%s:%s:%s', ApiPlatformParser::OUT_PREFIX, $resourceClass, $operationName);
141
        }
142
143
        if ($collection && Request::METHOD_GET === $method) {
144
            $resourceFilters = $resourceMetadata->getCollectionOperationAttribute($operationName, 'filters', [], true);
145
146
            $data['filters'] = [];
147
            foreach ($resourceFilters as $filterId) {
148
                if ($filter = $this->getFilter($filterId)) {
0 ignored issues
show
Bug introduced by
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...
149
                    foreach ($filter->getDescription($resourceClass) as $name => $definition) {
150
                        $data['filters'][] = ['name' => $name] + $definition;
151
                    }
152
                }
153
            }
154
        }
155
156
        $apiDoc = new ApiDoc($data);
157
        $apiDoc->setRoute($route);
158
159
        return $apiDoc;
160
    }
161
162
    /**
163
     * Gets Hydra documentation for the given resource.
164
     *
165
     * @param array  $hydraApiDoc
166
     * @param string $prefixedShortName
167
     *
168
     * @return array|null
169
     */
170
    private function getResourceHydraDoc(array $hydraApiDoc, string $prefixedShortName)
171
    {
172
        if (!isset($hydraApiDoc['hydra:supportedClass']) || !is_array($hydraApiDoc['hydra:supportedClass'])) {
173
            return null;
174
        }
175
176
        foreach ($hydraApiDoc['hydra:supportedClass'] as $supportedClass) {
177
            if (isset($supportedClass['@id']) && $supportedClass['@id'] === $prefixedShortName) {
178
                return $supportedClass;
179
            }
180
        }
181
182
        return null;
183
    }
184
185
    /**
186
     * Gets the Hydra documentation of a given operation.
187
     *
188
     * @param string $method
189
     * @param array  $hydraDoc
190
     *
191
     * @return array
192
     */
193
    private function getOperationHydraDoc(string $method, array $hydraDoc): array
194
    {
195
        if (!isset($hydraDoc['hydra:supportedOperation']) || !is_array($hydraDoc['hydra:supportedOperation'])) {
196
            return [];
197
        }
198
199
        foreach ($hydraDoc['hydra:supportedOperation'] as $supportedOperation) {
200
            if ($supportedOperation['hydra:method'] === $method) {
201
                return $supportedOperation;
202
            }
203
        }
204
205
        return [];
206
    }
207
208
    /**
209
     * Gets the Hydra documentation for the collection operation.
210
     *
211
     * @param string $shortName
212
     * @param string $method
213
     * @param array  $hydraEntrypointDoc
214
     *
215
     * @return array
216
     */
217
    private function getCollectionOperationHydraDoc(string $shortName, string $method, array $hydraEntrypointDoc): array
218
    {
219
        if (!isset($hydraEntrypointDoc['hydra:supportedProperty']) || !is_array($hydraEntrypointDoc['hydra:supportedProperty'])) {
220
            return [];
221
        }
222
223
        $propertyName = '#Entrypoint/'.lcfirst($shortName);
224
225
        foreach ($hydraEntrypointDoc['hydra:supportedProperty'] as $supportedProperty) {
226
            if (isset($supportedProperty['hydra:property']['@id'])
227
                && $supportedProperty['hydra:property']['@id'] === $propertyName) {
228
                return $this->getOperationHydraDoc($method, $supportedProperty['hydra:property']);
229
            }
230
        }
231
232
        return [];
233
    }
234
}
235