Passed
Push — master ( 8bd912...d93388 )
by Alan
06:58 queued 02:20
created

Bridge/Symfony/Bundle/Action/SwaggerUiAction.php (1 issue)

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\Symfony\Bundle\Action;
15
16
use ApiPlatform\Core\Api\FormatsProviderInterface;
17
use ApiPlatform\Core\Documentation\Documentation;
18
use ApiPlatform\Core\Exception\RuntimeException;
19
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
20
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
21
use ApiPlatform\Core\Util\RequestAttributesExtractor;
22
use Symfony\Component\HttpFoundation\Request;
23
use Symfony\Component\HttpFoundation\Response;
24
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
25
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
26
use Twig\Environment as TwigEnvironment;
27
28
/**
29
 * Displays the documentation.
30
 *
31
 * @author Kévin Dunglas <[email protected]>
32
 */
33
final class SwaggerUiAction
34
{
35
    private $resourceNameCollectionFactory;
36
    private $resourceMetadataFactory;
37
    private $normalizer;
38
    private $twig;
39
    private $urlGenerator;
40
    private $title;
41
    private $description;
42
    private $version;
43
    private $showWebby;
44
    private $formats;
45
    private $oauthEnabled;
46
    private $oauthClientId;
47
    private $oauthClientSecret;
48
    private $oauthType;
49
    private $oauthFlow;
50
    private $oauthTokenUrl;
51
    private $oauthAuthorizationUrl;
52
    private $oauthScopes;
53
    private $formatsProvider;
54
    private $swaggerUiEnabled;
55
    private $reDocEnabled;
56
    private $graphqlEnabled;
57
    private $graphiQlEnabled;
58
    private $graphQlPlaygroundEnabled;
59
    private $swaggerVersions;
60
61
    /**
62
     * @param int[] $swaggerVersions
63
     */
64
    public function __construct(ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, NormalizerInterface $normalizer, TwigEnvironment $twig, UrlGeneratorInterface $urlGenerator, string $title = '', string $description = '', string $version = '', $formats = [], $oauthEnabled = false, $oauthClientId = '', $oauthClientSecret = '', $oauthType = '', $oauthFlow = '', $oauthTokenUrl = '', $oauthAuthorizationUrl = '', $oauthScopes = [], bool $showWebby = true, bool $swaggerUiEnabled = false, bool $reDocEnabled = false, bool $graphqlEnabled = false, bool $graphiQlEnabled = false, bool $graphQlPlaygroundEnabled = false, array $swaggerVersions = [2, 3])
65
    {
66
        $this->resourceNameCollectionFactory = $resourceNameCollectionFactory;
67
        $this->resourceMetadataFactory = $resourceMetadataFactory;
68
        $this->normalizer = $normalizer;
69
        $this->twig = $twig;
70
        $this->urlGenerator = $urlGenerator;
71
        $this->title = $title;
72
        $this->showWebby = $showWebby;
73
        $this->description = $description;
74
        $this->version = $version;
75
        $this->oauthEnabled = $oauthEnabled;
76
        $this->oauthClientId = $oauthClientId;
77
        $this->oauthClientSecret = $oauthClientSecret;
78
        $this->oauthType = $oauthType;
79
        $this->oauthFlow = $oauthFlow;
80
        $this->oauthTokenUrl = $oauthTokenUrl;
81
        $this->oauthAuthorizationUrl = $oauthAuthorizationUrl;
82
        $this->oauthScopes = $oauthScopes;
83
        $this->swaggerUiEnabled = $swaggerUiEnabled;
84
        $this->reDocEnabled = $reDocEnabled;
85
        $this->graphqlEnabled = $graphqlEnabled;
86
        $this->graphiQlEnabled = $graphiQlEnabled;
87
        $this->graphQlPlaygroundEnabled = $graphQlPlaygroundEnabled;
88
        $this->swaggerVersions = $swaggerVersions;
89
90
        if (\is_array($formats)) {
91
            $this->formats = $formats;
92
93
            return;
94
        }
95
96
        @trigger_error(sprintf('Passing an array or an instance of "%s" as 5th parameter of the constructor of "%s" is deprecated since API Platform 2.5, pass an array instead', FormatsProviderInterface::class, __CLASS__), E_USER_DEPRECATED);
97
        $this->formatsProvider = $formats;
98
    }
99
100
    public function __invoke(Request $request)
101
    {
102
        $attributes = RequestAttributesExtractor::extractAttributes($request);
103
104
        // BC check to be removed in 3.0
105
        if (null === $this->formatsProvider) {
106
            $formats = $attributes ? $this
0 ignored issues
show
$attributes is an empty array, thus is always false.
Loading history...
107
                ->resourceMetadataFactory
108
                ->create($attributes['resource_class'])
109
                ->getOperationAttribute($attributes, 'output_formats', [], true) : $this->formats;
110
        } else {
111
            $formats = $this->formatsProvider->getFormatsFromAttributes($attributes);
112
        }
113
114
        $documentation = new Documentation($this->resourceNameCollectionFactory->create(), $this->title, $this->description, $this->version);
115
116
        return new Response($this->twig->render('@ApiPlatform/SwaggerUi/index.html.twig', $this->getContext($request, $documentation) + ['formats' => $formats]));
117
    }
118
119
    /**
120
     * Gets the base Twig context.
121
     */
122
    private function getContext(Request $request, Documentation $documentation): array
123
    {
124
        $context = [
125
            'title' => $this->title,
126
            'description' => $this->description,
127
            'showWebby' => $this->showWebby,
128
            'swaggerUiEnabled' => $this->swaggerUiEnabled,
129
            'reDocEnabled' => $this->reDocEnabled,
130
            'graphqlEnabled' => $this->graphqlEnabled,
131
            'graphiQlEnabled' => $this->graphiQlEnabled,
132
            'graphQlPlaygroundEnabled' => $this->graphQlPlaygroundEnabled,
133
        ];
134
135
        $swaggerContext = ['spec_version' => $request->query->getInt('spec_version', $this->swaggerVersions[0] ?? 2)];
136
        if ('' !== $baseUrl = $request->getBaseUrl()) {
137
            $swaggerContext['base_url'] = $baseUrl;
138
        }
139
140
        $swaggerData = [
141
            'url' => $this->urlGenerator->generate('api_doc', ['format' => 'json']),
142
            'spec' => $this->normalizer->normalize($documentation, 'json', $swaggerContext),
143
        ];
144
145
        $swaggerData['oauth'] = [
146
            'enabled' => $this->oauthEnabled,
147
            'clientId' => $this->oauthClientId,
148
            'clientSecret' => $this->oauthClientSecret,
149
            'type' => $this->oauthType,
150
            'flow' => $this->oauthFlow,
151
            'tokenUrl' => $this->oauthTokenUrl,
152
            'authorizationUrl' => $this->oauthAuthorizationUrl,
153
            'scopes' => $this->oauthScopes,
154
        ];
155
156
        if ($request->isMethodSafe(false) && null !== $resourceClass = $request->attributes->get('_api_resource_class')) {
157
            $swaggerData['id'] = $request->attributes->get('id');
158
            $swaggerData['queryParameters'] = $request->query->all();
159
160
            $metadata = $this->resourceMetadataFactory->create($resourceClass);
161
            $swaggerData['shortName'] = $metadata->getShortName();
162
163
            if (null !== $collectionOperationName = $request->attributes->get('_api_collection_operation_name')) {
164
                $swaggerData['operationId'] = sprintf('%s%sCollection', $collectionOperationName, ucfirst($swaggerData['shortName']));
165
            } elseif (null !== $itemOperationName = $request->attributes->get('_api_item_operation_name')) {
166
                $swaggerData['operationId'] = sprintf('%s%sItem', $itemOperationName, ucfirst($swaggerData['shortName']));
167
            } elseif (null !== $subresourceOperationContext = $request->attributes->get('_api_subresource_context')) {
168
                $swaggerData['operationId'] = $subresourceOperationContext['operationId'];
169
            }
170
171
            [$swaggerData['path'], $swaggerData['method']] = $this->getPathAndMethod($swaggerData);
172
        }
173
174
        return $context + ['swagger_data' => $swaggerData];
175
    }
176
177
    private function getPathAndMethod(array $swaggerData): array
178
    {
179
        foreach ($swaggerData['spec']['paths'] as $path => $operations) {
180
            foreach ($operations as $method => $operation) {
181
                if ($operation['operationId'] === $swaggerData['operationId']) {
182
                    return [$path, $method];
183
                }
184
            }
185
        }
186
187
        throw new RuntimeException(sprintf('The operation "%s" cannot be found in the Swagger specification.', $swaggerData['operationId']));
188
    }
189
}
190