Passed
Pull Request — 2.4 (#2721)
by Han Hui
03:45
created

Configuration::addMercureSection()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 12
nc 2
nop 1
dl 0
loc 14
rs 9.8666
c 0
b 0
f 0
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\DependencyInjection;
15
16
use ApiPlatform\Core\Bridge\Elasticsearch\Metadata\Document\DocumentMetadata;
17
use ApiPlatform\Core\Exception\FilterValidationException;
18
use ApiPlatform\Core\Exception\InvalidArgumentException;
19
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
20
use Doctrine\Bundle\MongoDBBundle\DoctrineMongoDBBundle;
21
use Doctrine\ORM\OptimisticLockException;
22
use Doctrine\ORM\Version as DoctrineOrmVersion;
23
use FOS\UserBundle\FOSUserBundle;
24
use GraphQL\GraphQL;
25
use Symfony\Bundle\FullStack;
0 ignored issues
show
Bug introduced by
The type Symfony\Bundle\FullStack was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
use Symfony\Bundle\MercureBundle\MercureBundle;
27
use Symfony\Bundle\TwigBundle\TwigBundle;
28
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
29
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
30
use Symfony\Component\Config\Definition\ConfigurationInterface;
31
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
32
use Symfony\Component\HttpFoundation\Response;
33
use Symfony\Component\Messenger\MessageBusInterface;
34
use Symfony\Component\Serializer\Exception\ExceptionInterface;
35
36
/**
37
 * The configuration of the bundle.
38
 *
39
 * @author Kévin Dunglas <[email protected]>
40
 * @author Baptiste Meyer <[email protected]>
41
 */
42
final class Configuration implements ConfigurationInterface
43
{
44
    /**
45
     * {@inheritdoc}
46
     */
47
    public function getConfigTreeBuilder()
48
    {
49
        if (method_exists(TreeBuilder::class, 'getRootNode')) {
50
            $treeBuilder = new TreeBuilder('api_platform');
51
            $rootNode = $treeBuilder->getRootNode();
52
        } else {
53
            $treeBuilder = new TreeBuilder();
54
            $rootNode = $treeBuilder->root('api_platform');
55
        }
56
57
        $rootNode
58
            ->children()
59
                ->scalarNode('title')
60
                    ->info('The title of the API.')
61
                    ->cannotBeEmpty()
62
                    ->defaultValue('')
63
                ->end()
64
                ->scalarNode('description')
65
                    ->info('The description of the API.')
66
                    ->cannotBeEmpty()
67
                    ->defaultValue('')
68
                ->end()
69
                ->scalarNode('version')
70
                    ->info('The version of the API.')
71
                    ->cannotBeEmpty()
72
                    ->defaultValue('0.0.0')
73
                ->end()
74
                ->booleanNode('show_webby')->defaultTrue()->info('If true, show Webby on the documentation page')->end()
75
                ->scalarNode('default_operation_path_resolver')
76
                    ->defaultValue('api_platform.operation_path_resolver.underscore')
77
                    ->setDeprecated('The use of the `default_operation_path_resolver` has been deprecated in 2.1 and will be removed in 3.0. Use `path_segment_name_generator` instead.')
78
                    ->info('Specify the default operation path resolver to use for generating resources operations path.')
79
                ->end()
80
                ->scalarNode('name_converter')->defaultNull()->info('Specify a name converter to use.')->end()
81
                ->scalarNode('path_segment_name_generator')->defaultValue('api_platform.path_segment_name_generator.underscore')->info('Specify a path name generator to use.')->end()
82
                ->booleanNode('allow_plain_identifiers')->defaultFalse()->info('Allow plain identifiers, for example "id" instead of "@id" when denormalizing a relation.')->end()
83
                ->arrayNode('validator')
84
                    ->addDefaultsIfNotSet()
85
                    ->children()
86
                        ->variableNode('serialize_payload_fields')->defaultValue([])->info('Enable the serialization of payload fields when a validation error is thrown.')->end()
87
                    ->end()
88
                ->end()
89
                ->arrayNode('eager_loading')
90
                    ->canBeDisabled()
91
                    ->addDefaultsIfNotSet()
92
                    ->children()
93
                        ->booleanNode('fetch_partial')->defaultFalse()->info('Fetch only partial data according to serialization groups. If enabled, Doctrine ORM entities will not work as expected if any of the other fields are used.')->end()
94
                        ->integerNode('max_joins')->defaultValue(30)->info('Max number of joined relations before EagerLoading throws a RuntimeException')->end()
95
                        ->booleanNode('force_eager')->defaultTrue()->info('Force join on every relation. If disabled, it will only join relations having the EAGER fetch mode.')->end()
96
                    ->end()
97
                ->end()
98
                ->booleanNode('enable_fos_user')->defaultValue(class_exists(FOSUserBundle::class))->info('Enable the FOSUserBundle integration.')->end()
99
                ->booleanNode('enable_nelmio_api_doc')
100
                    ->defaultFalse()
101
                    ->setDeprecated('Enabling the NelmioApiDocBundle integration has been deprecated in 2.2 and will be removed in 3.0. NelmioApiDocBundle 3 has native support for API Platform.')
102
                    ->info('Enable the NelmioApiDocBundle integration.')
103
                ->end()
104
                ->booleanNode('enable_swagger')->defaultTrue()->info('Enable the Swagger documentation and export.')->end()
105
                ->booleanNode('enable_swagger_ui')->defaultValue(class_exists(TwigBundle::class))->info('Enable Swagger UI')->end()
106
                ->booleanNode('enable_re_doc')->defaultValue(class_exists(TwigBundle::class))->info('Enable ReDoc')->end()
107
                ->booleanNode('enable_entrypoint')->defaultTrue()->info('Enable the entrypoint')->end()
108
                ->booleanNode('enable_docs')->defaultTrue()->info('Enable the docs')->end()
109
                ->booleanNode('enable_profiler')->defaultTrue()->info('Enable the data collector and the WebProfilerBundle integration.')->end()
110
                ->arrayNode('collection')
111
                    ->addDefaultsIfNotSet()
112
                    ->children()
113
                        ->scalarNode('order')->defaultValue('ASC')->info('The default order of results.')->end() // Default ORDER is required for postgresql and mysql >= 5.7 when using LIMIT/OFFSET request
114
                        ->scalarNode('order_parameter_name')->defaultValue('order')->cannotBeEmpty()->info('The name of the query parameter to order results.')->end()
115
                        ->arrayNode('pagination')
116
                            ->canBeDisabled()
117
                            ->addDefaultsIfNotSet()
118
                            ->children()
119
                                ->booleanNode('enabled')->defaultTrue()->info('To enable or disable pagination for all resource collections by default.')->end()
120
                                ->booleanNode('partial')->defaultFalse()->info('To enable or disable partial pagination for all resource collections by default when pagination is enabled.')->end()
121
                                ->booleanNode('client_enabled')->defaultFalse()->info('To allow the client to enable or disable the pagination.')->end()
122
                                ->booleanNode('client_items_per_page')->defaultFalse()->info('To allow the client to set the number of items per page.')->end()
123
                                ->booleanNode('client_partial')->defaultFalse()->info('To allow the client to enable or disable partial pagination.')->end()
124
                                ->integerNode('items_per_page')->defaultValue(30)->info('The default number of items per page.')->end()
125
                                ->integerNode('maximum_items_per_page')->defaultNull()->info('The maximum number of items per page.')->end()
126
                                ->scalarNode('page_parameter_name')->defaultValue('page')->cannotBeEmpty()->info('The default name of the parameter handling the page number.')->end()
127
                                ->scalarNode('enabled_parameter_name')->defaultValue('pagination')->cannotBeEmpty()->info('The name of the query parameter to enable or disable pagination.')->end()
128
                                ->scalarNode('items_per_page_parameter_name')->defaultValue('itemsPerPage')->cannotBeEmpty()->info('The name of the query parameter to set the number of items per page.')->end()
129
                                ->scalarNode('partial_parameter_name')->defaultValue('partial')->cannotBeEmpty()->info('The name of the query parameter to enable or disable partial pagination.')->end()
130
                            ->end()
131
                        ->end()
132
                    ->end()
133
                ->end()
134
                ->arrayNode('mapping')
135
                    ->addDefaultsIfNotSet()
136
                    ->children()
137
                        ->arrayNode('paths')
138
                            ->prototype('scalar')->end()
139
                        ->end()
140
                    ->end()
141
                ->end()
142
                ->arrayNode('resource_class_directories')
143
                    ->prototype('scalar')->end()
144
                ->end()
145
            ->end();
146
147
        $this->addDoctrineOrmSection($rootNode);
148
        $this->addDoctrineMongoDbOdmSection($rootNode);
149
        $this->addOAuthSection($rootNode);
150
        $this->addGraphQlSection($rootNode);
151
        $this->addSwaggerSection($rootNode);
152
        $this->addHttpCacheSection($rootNode);
153
        $this->addMercureSection($rootNode);
154
        $this->addMessengerSection($rootNode);
155
        $this->addElasticsearchSection($rootNode);
156
157
        $this->addExceptionToStatusSection($rootNode);
158
159
        $this->addFormatSection($rootNode, 'formats', [
160
            'jsonld' => ['mime_types' => ['application/ld+json']],
161
            'json' => ['mime_types' => ['application/json']], // Swagger support
162
            'html' => ['mime_types' => ['text/html']], // Swagger UI support
163
        ]);
164
        $this->addFormatSection($rootNode, 'error_formats', [
165
            'jsonproblem' => ['mime_types' => ['application/problem+json']],
166
            'jsonld' => ['mime_types' => ['application/ld+json']],
167
        ]);
168
169
        return $treeBuilder;
170
    }
171
172
    private function addDoctrineOrmSection(ArrayNodeDefinition $rootNode): void
173
    {
174
        $rootNode
175
            ->children()
176
                ->arrayNode('doctrine')
177
                    ->{class_exists(DoctrineBundle::class) && class_exists(DoctrineOrmVersion::class) ? 'canBeDisabled' : 'canBeEnabled'}()
178
                ->end()
179
            ->end();
180
    }
181
182
    private function addDoctrineMongoDbOdmSection(ArrayNodeDefinition $rootNode): void
183
    {
184
        $rootNode
185
            ->children()
186
                ->arrayNode('doctrine_mongodb_odm')
187
                    ->{class_exists(DoctrineMongoDBBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
188
                ->end()
189
            ->end();
190
    }
191
192
    private function addOAuthSection(ArrayNodeDefinition $rootNode): void
193
    {
194
        $rootNode
195
            ->children()
196
                ->arrayNode('oauth')
197
                    ->canBeEnabled()
198
                    ->addDefaultsIfNotSet()
199
                    ->children()
200
                        ->scalarNode('clientId')->defaultValue('')->info('The oauth client id.')->end()
201
                        ->scalarNode('clientSecret')->defaultValue('')->info('The oauth client secret.')->end()
0 ignored issues
show
Bug introduced by
The method scalarNode() does not exist on Symfony\Component\Config...der\NodeParentInterface. It seems like you code against a sub-type of Symfony\Component\Config...der\NodeParentInterface such as Symfony\Component\Config...ion\Builder\NodeBuilder. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

201
                        ->/** @scrutinizer ignore-call */ scalarNode('clientSecret')->defaultValue('')->info('The oauth client secret.')->end()
Loading history...
202
                        ->scalarNode('type')->defaultValue('oauth2')->info('The oauth client secret.')->end()
203
                        ->scalarNode('flow')->defaultValue('application')->info('The oauth flow grant type.')->end()
204
                        ->scalarNode('tokenUrl')->defaultValue('/oauth/v2/token')->info('The oauth token url.')->end()
205
                        ->scalarNode('authorizationUrl')->defaultValue('/oauth/v2/auth')->info('The oauth authentication url.')->end()
206
                        ->arrayNode('scopes')
207
                            ->prototype('scalar')->end()
208
                        ->end()
209
                    ->end()
210
                ->end()
211
            ->end();
212
    }
213
214
    private function addGraphQlSection(ArrayNodeDefinition $rootNode): void
215
    {
216
        $rootNode
217
            ->children()
218
                ->arrayNode('graphql')
219
                    ->{class_exists(GraphQL::class) ? 'canBeDisabled' : 'canBeEnabled'}()
220
                    ->addDefaultsIfNotSet()
221
                    ->children()
222
                        ->arrayNode('graphiql')
223
                            ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
224
                        ->end()
225
                    ->end()
226
                ->end()
227
            ->end();
228
    }
229
230
    private function addSwaggerSection(ArrayNodeDefinition $rootNode): void
231
    {
232
        $rootNode
233
            ->children()
234
                ->arrayNode('swagger')
235
                    ->addDefaultsIfNotSet()
236
                    ->children()
237
                             ->arrayNode('api_keys')
238
                                 ->prototype('array')
239
                                    ->children()
240
                                    ->scalarNode('name')
241
                                        ->info('The name of the header or query parameter containing the api key.')
242
                                    ->end()
243
                                    ->enumNode('type')
244
                                        ->info('Whether the api key should be a query parameter or a header.')
245
                                        ->values(['query', 'header'])
246
                                    ->end()
247
                                ->end()
248
                             ->end()
249
                        ->end()
250
                    ->end()
251
                ->end()
252
            ->end();
253
    }
254
255
    private function addHttpCacheSection(ArrayNodeDefinition $rootNode): void
256
    {
257
        $rootNode
258
            ->children()
259
                ->arrayNode('http_cache')
260
                    ->addDefaultsIfNotSet()
261
                    ->children()
262
                        ->booleanNode('etag')->defaultTrue()->info('Automatically generate etags for API responses.')->end()
263
                        ->integerNode('max_age')->defaultNull()->info('Default value for the response max age.')->end()
0 ignored issues
show
Bug introduced by
The method integerNode() does not exist on Symfony\Component\Config...der\NodeParentInterface. It seems like you code against a sub-type of Symfony\Component\Config...der\NodeParentInterface such as Symfony\Component\Config...ion\Builder\NodeBuilder. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

263
                        ->/** @scrutinizer ignore-call */ integerNode('max_age')->defaultNull()->info('Default value for the response max age.')->end()
Loading history...
264
                        ->integerNode('shared_max_age')->defaultNull()->info('Default value for the response shared (proxy) max age.')->end()
265
                        ->arrayNode('vary')
266
                            ->defaultValue(['Accept'])
267
                            ->prototype('scalar')->end()
268
                            ->info('Default values of the "Vary" HTTP header.')
269
                        ->end()
270
                        ->booleanNode('public')->defaultNull()->info('To make all responses public by default.')->end()
271
                        ->arrayNode('invalidation')
272
                            ->info('Enable the tags-based cache invalidation system.')
273
                            ->canBeEnabled()
274
                            ->children()
275
                                ->arrayNode('varnish_urls')
276
                                    ->defaultValue([])
277
                                    ->prototype('scalar')->end()
278
                                    ->info('URLs of the Varnish servers to purge using cache tags when a resource is updated.')
279
                                ->end()
280
                                ->variableNode('request_options')
281
                                    ->defaultValue([])
282
                                    ->validate()
283
                                        ->ifTrue(function ($v) { return false === \is_array($v); })
284
                                        ->thenInvalid('The request_options parameter must be an array.')
285
                                    ->end()
286
                                    ->info('To pass options to the client charged with the request.')
287
                                ->end()
288
                            ->end()
289
                        ->end()
290
                    ->end()
291
                ->end()
292
            ->end();
293
    }
294
295
    private function addMercureSection(ArrayNodeDefinition $rootNode): void
296
    {
297
        $rootNode
298
            ->children()
299
                ->arrayNode('mercure')
300
                    ->{class_exists(MercureBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
301
                    ->children()
302
                        ->scalarNode('hub_url')
303
                            ->defaultNull()
304
                            ->info('The URL send in the Link HTTP header. If not set, will default to the URL for the Symfony\'s bundle default hub.')
305
                        ->end()
306
                    ->end()
307
                ->end()
308
            ->end();
309
    }
310
311
    private function addMessengerSection(ArrayNodeDefinition $rootNode): void
312
    {
313
        $rootNode
314
            ->children()
315
                ->arrayNode('messenger')
316
                    ->{!class_exists(FullStack::class) && interface_exists(MessageBusInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
317
                ->end()
318
            ->end();
319
    }
320
321
    private function addElasticsearchSection(ArrayNodeDefinition $rootNode): void
322
    {
323
        $rootNode
324
            ->children()
325
                ->arrayNode('elasticsearch')
326
                    ->canBeEnabled()
327
                    ->addDefaultsIfNotSet()
328
                    ->children()
329
                        ->arrayNode('hosts')
330
                            ->beforeNormalization()->castToArray()->end()
331
                            ->defaultValue([])
332
                            ->prototype('scalar')->end()
0 ignored issues
show
Bug introduced by
The method prototype() does not exist on Symfony\Component\Config...\Builder\NodeDefinition. It seems like you code against a sub-type of Symfony\Component\Config...\Builder\NodeDefinition such as Symfony\Component\Config...der\ArrayNodeDefinition. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

332
                            ->/** @scrutinizer ignore-call */ prototype('scalar')->end()
Loading history...
333
                        ->end()
334
                        ->arrayNode('mapping')
335
                            ->normalizeKeys(false)
336
                            ->useAttributeAsKey('resource_class')
337
                            ->prototype('array')
338
                                ->children()
339
                                    ->scalarNode('index')->defaultNull()->end()
340
                                    ->scalarNode('type')->defaultValue(DocumentMetadata::DEFAULT_TYPE)->end()
341
                                ->end()
342
                            ->end()
343
                        ->end()
344
                    ->end()
345
                ->end()
346
            ->end();
347
    }
348
349
    /**
350
     * @throws InvalidConfigurationException
351
     */
352
    private function addExceptionToStatusSection(ArrayNodeDefinition $rootNode): void
353
    {
354
        $rootNode
355
            ->children()
356
                ->arrayNode('exception_to_status')
357
                    ->defaultValue([
358
                        ExceptionInterface::class => Response::HTTP_BAD_REQUEST,
359
                        InvalidArgumentException::class => Response::HTTP_BAD_REQUEST,
360
                        FilterValidationException::class => Response::HTTP_BAD_REQUEST,
361
                        OptimisticLockException::class => Response::HTTP_CONFLICT,
362
                    ])
363
                    ->info('The list of exceptions mapped to their HTTP status code.')
364
                    ->normalizeKeys(false)
365
                    ->useAttributeAsKey('exception_class')
366
                    ->beforeNormalization()
367
                        ->ifArray()
368
                        ->then(function (array $exceptionToStatus) {
369
                            foreach ($exceptionToStatus as &$httpStatusCode) {
370
                                if (\is_int($httpStatusCode)) {
371
                                    continue;
372
                                }
373
374
                                if (\defined($httpStatusCodeConstant = sprintf('%s::%s', Response::class, $httpStatusCode))) {
375
                                    @trigger_error(sprintf('Using a string "%s" as a constant of the "%s" class is deprecated since API Platform 2.1 and will not be possible anymore in API Platform 3. Use the Symfony\'s custom YAML extension for PHP constants instead (i.e. "!php/const %s").', $httpStatusCode, Response::class, $httpStatusCodeConstant), E_USER_DEPRECATED);
376
377
                                    $httpStatusCode = \constant($httpStatusCodeConstant);
378
                                }
379
                            }
380
381
                            return $exceptionToStatus;
382
                        })
383
                    ->end()
384
                    ->prototype('integer')->end()
385
                    ->validate()
386
                        ->ifArray()
387
                        ->then(function (array $exceptionToStatus) {
388
                            foreach ($exceptionToStatus as $httpStatusCode) {
389
                                if ($httpStatusCode < 100 || $httpStatusCode >= 600) {
390
                                    throw new InvalidConfigurationException(sprintf('The HTTP status code "%s" is not valid.', $httpStatusCode));
391
                                }
392
                            }
393
394
                            return $exceptionToStatus;
395
                        })
396
                    ->end()
397
                ->end()
398
            ->end();
399
    }
400
401
    private function addFormatSection(ArrayNodeDefinition $rootNode, string $key, array $defaultValue): void
402
    {
403
        $rootNode
404
            ->children()
405
                ->arrayNode($key)
406
                    ->defaultValue($defaultValue)
407
                    ->info('The list of enabled formats. The first one will be the default.')
408
                    ->normalizeKeys(false)
409
                    ->useAttributeAsKey('format')
410
                    ->beforeNormalization()
411
                        ->ifArray()
412
                        ->then(function ($v) {
413
                            foreach ($v as $format => $value) {
414
                                if (isset($value['mime_types'])) {
415
                                    continue;
416
                                }
417
418
                                $v[$format] = ['mime_types' => $value];
419
                            }
420
421
                            return $v;
422
                        })
423
                    ->end()
424
                    ->prototype('array')
425
                        ->children()
426
                            ->arrayNode('mime_types')->prototype('scalar')->end()->end()
427
                        ->end()
428
                    ->end()
429
                ->end()
430
            ->end();
431
    }
432
}
433