Passed
Push — improve-filter-message ( b945de...00229f )
by Han Hui
04:37
created

Configuration::addHttpCacheSection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 38
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 36
nc 1
nop 1
dl 0
loc 38
rs 9.344
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 Elasticsearch\Client as ElasticsearchClient;
24
use FOS\UserBundle\FOSUserBundle;
25
use GraphQL\GraphQL;
26
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...
27
use Symfony\Bundle\MercureBundle\MercureBundle;
28
use Symfony\Bundle\TwigBundle\TwigBundle;
29
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
30
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
31
use Symfony\Component\Config\Definition\ConfigurationInterface;
32
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
33
use Symfony\Component\HttpFoundation\Response;
34
use Symfony\Component\Messenger\MessageBusInterface;
35
use Symfony\Component\Serializer\Exception\ExceptionInterface as SerializerExceptionInterface;
36
37
/**
38
 * The configuration of the bundle.
39
 *
40
 * @author Kévin Dunglas <[email protected]>
41
 * @author Baptiste Meyer <[email protected]>
42
 */
43
final class Configuration implements ConfigurationInterface
44
{
45
    /**
46
     * {@inheritdoc}
47
     */
48
    public function getConfigTreeBuilder()
49
    {
50
        if (method_exists(TreeBuilder::class, 'getRootNode')) {
51
            $treeBuilder = new TreeBuilder('api_platform');
52
            $rootNode = $treeBuilder->getRootNode();
53
        } else {
54
            $treeBuilder = new TreeBuilder();
55
            $rootNode = $treeBuilder->root('api_platform');
0 ignored issues
show
Deprecated Code introduced by
The function Symfony\Component\Config...der\TreeBuilder::root() has been deprecated: since Symfony 4.3, pass the root name to the constructor instead ( Ignorable by Annotation )

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

55
            $rootNode = /** @scrutinizer ignore-deprecated */ $treeBuilder->root('api_platform');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
56
        }
57
58
        $rootNode
59
            ->children()
60
                ->scalarNode('title')
61
                    ->info('The title of the API.')
62
                    ->cannotBeEmpty()
63
                    ->defaultValue('')
64
                ->end()
65
                ->scalarNode('description')
66
                    ->info('The description of the API.')
67
                    ->cannotBeEmpty()
68
                    ->defaultValue('')
69
                ->end()
70
                ->scalarNode('version')
71
                    ->info('The version of the API.')
72
                    ->cannotBeEmpty()
73
                    ->defaultValue('0.0.0')
74
                ->end()
75
                ->booleanNode('show_webby')->defaultTrue()->info('If true, show Webby on the documentation page')->end()
76
                ->scalarNode('default_operation_path_resolver')
77
                    ->defaultValue('api_platform.operation_path_resolver.underscore')
78
                    ->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.')
79
                    ->info('Specify the default operation path resolver to use for generating resources operations path.')
80
                ->end()
81
                ->scalarNode('name_converter')->defaultNull()->info('Specify a name converter to use.')->end()
82
                ->scalarNode('path_segment_name_generator')->defaultValue('api_platform.path_segment_name_generator.underscore')->info('Specify a path name generator to use.')->end()
83
                ->booleanNode('allow_plain_identifiers')->defaultFalse()->info('Allow plain identifiers, for example "id" instead of "@id" when denormalizing a relation.')->end()
84
                ->arrayNode('validator')
85
                    ->addDefaultsIfNotSet()
86
                    ->children()
87
                        ->variableNode('serialize_payload_fields')->defaultValue([])->info('Enable the serialization of payload fields when a validation error is thrown.')->end()
88
                    ->end()
89
                ->end()
90
                ->arrayNode('eager_loading')
91
                    ->canBeDisabled()
92
                    ->addDefaultsIfNotSet()
93
                    ->children()
94
                        ->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()
95
                        ->integerNode('max_joins')->defaultValue(30)->info('Max number of joined relations before EagerLoading throws a RuntimeException')->end()
96
                        ->booleanNode('force_eager')->defaultTrue()->info('Force join on every relation. If disabled, it will only join relations having the EAGER fetch mode.')->end()
97
                    ->end()
98
                ->end()
99
                ->booleanNode('enable_fos_user')->defaultValue(class_exists(FOSUserBundle::class))->info('Enable the FOSUserBundle integration.')->end()
100
                ->booleanNode('enable_nelmio_api_doc')
101
                    ->defaultFalse()
102
                    ->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.')
103
                    ->info('Enable the NelmioApiDocBundle integration.')
104
                ->end()
105
                ->booleanNode('enable_swagger')->defaultTrue()->info('Enable the Swagger documentation and export.')->end()
106
                ->booleanNode('enable_swagger_ui')->defaultValue(class_exists(TwigBundle::class))->info('Enable Swagger UI')->end()
107
                ->booleanNode('enable_re_doc')->defaultValue(class_exists(TwigBundle::class))->info('Enable ReDoc')->end()
108
                ->booleanNode('enable_entrypoint')->defaultTrue()->info('Enable the entrypoint')->end()
109
                ->booleanNode('enable_docs')->defaultTrue()->info('Enable the docs')->end()
110
                ->booleanNode('enable_profiler')->defaultTrue()->info('Enable the data collector and the WebProfilerBundle integration.')->end()
111
                ->arrayNode('collection')
112
                    ->addDefaultsIfNotSet()
113
                    ->children()
114
                        ->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
115
                        ->scalarNode('order_parameter_name')->defaultValue('order')->cannotBeEmpty()->info('The name of the query parameter to order results.')->end()
116
                        ->arrayNode('pagination')
117
                            ->canBeDisabled()
118
                            ->addDefaultsIfNotSet()
119
                            ->children()
120
                                ->booleanNode('enabled')->defaultTrue()->info('To enable or disable pagination for all resource collections by default.')->end()
121
                                ->booleanNode('partial')->defaultFalse()->info('To enable or disable partial pagination for all resource collections by default when pagination is enabled.')->end()
122
                                ->booleanNode('client_enabled')->defaultFalse()->info('To allow the client to enable or disable the pagination.')->end()
123
                                ->booleanNode('client_items_per_page')->defaultFalse()->info('To allow the client to set the number of items per page.')->end()
124
                                ->booleanNode('client_partial')->defaultFalse()->info('To allow the client to enable or disable partial pagination.')->end()
125
                                ->integerNode('items_per_page')->defaultValue(30)->info('The default number of items per page.')->end()
126
                                ->integerNode('maximum_items_per_page')->defaultNull()->info('The maximum number of items per page.')->end()
127
                                ->scalarNode('page_parameter_name')->defaultValue('page')->cannotBeEmpty()->info('The default name of the parameter handling the page number.')->end()
128
                                ->scalarNode('enabled_parameter_name')->defaultValue('pagination')->cannotBeEmpty()->info('The name of the query parameter to enable or disable pagination.')->end()
129
                                ->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()
130
                                ->scalarNode('partial_parameter_name')->defaultValue('partial')->cannotBeEmpty()->info('The name of the query parameter to enable or disable partial pagination.')->end()
131
                            ->end()
132
                        ->end()
133
                    ->end()
134
                ->end()
135
                ->arrayNode('mapping')
136
                    ->addDefaultsIfNotSet()
137
                    ->children()
138
                        ->arrayNode('paths')
139
                            ->prototype('scalar')->end()
140
                        ->end()
141
                    ->end()
142
                ->end()
143
                ->arrayNode('resource_class_directories')
144
                    ->prototype('scalar')->end()
145
                ->end()
146
            ->end();
147
148
        $this->addDoctrineOrmSection($rootNode);
149
        $this->addDoctrineMongoDbOdmSection($rootNode);
150
        $this->addOAuthSection($rootNode);
151
        $this->addGraphQlSection($rootNode);
152
        $this->addSwaggerSection($rootNode);
153
        $this->addHttpCacheSection($rootNode);
154
        $this->addMercureSection($rootNode);
155
        $this->addMessengerSection($rootNode);
156
        $this->addElasticsearchSection($rootNode);
157
158
        $this->addExceptionToStatusSection($rootNode);
159
160
        $this->addFormatSection($rootNode, 'formats', [
161
            'jsonld' => ['mime_types' => ['application/ld+json']],
162
            'json' => ['mime_types' => ['application/json']], // Swagger support
163
            'html' => ['mime_types' => ['text/html']], // Swagger UI support
164
        ]);
165
        $this->addFormatSection($rootNode, 'error_formats', [
166
            'jsonproblem' => ['mime_types' => ['application/problem+json']],
167
            'jsonld' => ['mime_types' => ['application/ld+json']],
168
        ]);
169
170
        return $treeBuilder;
171
    }
172
173
    private function addDoctrineOrmSection(ArrayNodeDefinition $rootNode): void
174
    {
175
        $rootNode
176
            ->children()
177
                ->arrayNode('doctrine')
178
                    ->{class_exists(DoctrineBundle::class) && class_exists(DoctrineOrmVersion::class) ? 'canBeDisabled' : 'canBeEnabled'}()
179
                ->end()
180
            ->end();
181
    }
182
183
    private function addDoctrineMongoDbOdmSection(ArrayNodeDefinition $rootNode): void
184
    {
185
        $rootNode
186
            ->children()
187
                ->arrayNode('doctrine_mongodb_odm')
188
                    ->{class_exists(DoctrineMongoDBBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
189
                ->end()
190
            ->end();
191
    }
192
193
    private function addOAuthSection(ArrayNodeDefinition $rootNode): void
194
    {
195
        $rootNode
196
            ->children()
197
                ->arrayNode('oauth')
198
                    ->canBeEnabled()
199
                    ->addDefaultsIfNotSet()
200
                    ->children()
201
                        ->scalarNode('clientId')->defaultValue('')->info('The oauth client id.')->end()
202
                        ->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

202
                        ->/** @scrutinizer ignore-call */ scalarNode('clientSecret')->defaultValue('')->info('The oauth client secret.')->end()
Loading history...
203
                        ->scalarNode('type')->defaultValue('oauth2')->info('The oauth client secret.')->end()
204
                        ->scalarNode('flow')->defaultValue('application')->info('The oauth flow grant type.')->end()
205
                        ->scalarNode('tokenUrl')->defaultValue('/oauth/v2/token')->info('The oauth token url.')->end()
206
                        ->scalarNode('authorizationUrl')->defaultValue('/oauth/v2/auth')->info('The oauth authentication url.')->end()
207
                        ->arrayNode('scopes')
208
                            ->prototype('scalar')->end()
209
                        ->end()
210
                    ->end()
211
                ->end()
212
            ->end();
213
    }
214
215
    private function addGraphQlSection(ArrayNodeDefinition $rootNode): void
216
    {
217
        $rootNode
218
            ->children()
219
                ->arrayNode('graphql')
220
                    ->{class_exists(GraphQL::class) ? 'canBeDisabled' : 'canBeEnabled'}()
221
                    ->addDefaultsIfNotSet()
222
                    ->children()
223
                        ->arrayNode('graphiql')
224
                            ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
225
                        ->end()
226
                    ->end()
227
                ->end()
228
            ->end();
229
    }
230
231
    private function addSwaggerSection(ArrayNodeDefinition $rootNode): void
232
    {
233
        $rootNode
234
            ->children()
235
                ->arrayNode('swagger')
236
                    ->addDefaultsIfNotSet()
237
                    ->children()
238
                             ->arrayNode('api_keys')
239
                                 ->prototype('array')
240
                                    ->children()
241
                                    ->scalarNode('name')
242
                                        ->info('The name of the header or query parameter containing the api key.')
243
                                    ->end()
244
                                    ->enumNode('type')
245
                                        ->info('Whether the api key should be a query parameter or a header.')
246
                                        ->values(['query', 'header'])
247
                                    ->end()
248
                                ->end()
249
                             ->end()
250
                        ->end()
251
                    ->end()
252
                ->end()
253
            ->end();
254
    }
255
256
    private function addHttpCacheSection(ArrayNodeDefinition $rootNode): void
257
    {
258
        $rootNode
259
            ->children()
260
                ->arrayNode('http_cache')
261
                    ->addDefaultsIfNotSet()
262
                    ->children()
263
                        ->booleanNode('etag')->defaultTrue()->info('Automatically generate etags for API responses.')->end()
264
                        ->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

264
                        ->/** @scrutinizer ignore-call */ integerNode('max_age')->defaultNull()->info('Default value for the response max age.')->end()
Loading history...
265
                        ->integerNode('shared_max_age')->defaultNull()->info('Default value for the response shared (proxy) max age.')->end()
266
                        ->arrayNode('vary')
267
                            ->defaultValue(['Accept'])
268
                            ->prototype('scalar')->end()
269
                            ->info('Default values of the "Vary" HTTP header.')
270
                        ->end()
271
                        ->booleanNode('public')->defaultNull()->info('To make all responses public by default.')->end()
272
                        ->arrayNode('invalidation')
273
                            ->info('Enable the tags-based cache invalidation system.')
274
                            ->canBeEnabled()
275
                            ->children()
276
                                ->arrayNode('varnish_urls')
277
                                    ->defaultValue([])
278
                                    ->prototype('scalar')->end()
279
                                    ->info('URLs of the Varnish servers to purge using cache tags when a resource is updated.')
280
                                ->end()
281
                                ->variableNode('request_options')
282
                                    ->defaultValue([])
283
                                    ->validate()
284
                                        ->ifTrue(function ($v) { return false === \is_array($v); })
285
                                        ->thenInvalid('The request_options parameter must be an array.')
286
                                    ->end()
287
                                    ->info('To pass options to the client charged with the request.')
288
                                ->end()
289
                            ->end()
290
                        ->end()
291
                    ->end()
292
                ->end()
293
            ->end();
294
    }
295
296
    private function addMercureSection(ArrayNodeDefinition $rootNode): void
297
    {
298
        $rootNode
299
            ->children()
300
                ->arrayNode('mercure')
301
                    ->{class_exists(MercureBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
302
                    ->children()
303
                        ->scalarNode('hub_url')
304
                            ->defaultNull()
305
                            ->info('The URL sent in the Link HTTP header. If not set, will default to the URL for MercureBundle\'s default hub.')
306
                        ->end()
307
                    ->end()
308
                ->end()
309
            ->end();
310
    }
311
312
    private function addMessengerSection(ArrayNodeDefinition $rootNode): void
313
    {
314
        $rootNode
315
            ->children()
316
                ->arrayNode('messenger')
317
                    ->{!class_exists(FullStack::class) && interface_exists(MessageBusInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
318
                ->end()
319
            ->end();
320
    }
321
322
    private function addElasticsearchSection(ArrayNodeDefinition $rootNode): void
323
    {
324
        $rootNode
325
            ->children()
326
                ->arrayNode('elasticsearch')
327
                    ->canBeEnabled()
328
                    ->addDefaultsIfNotSet()
329
                    ->children()
330
                        ->booleanNode('enabled')
331
                            ->defaultFalse()
332
                            ->validate()
333
                                ->ifTrue()
334
                                ->then(static function (bool $v): bool {
335
                                    if (!class_exists(ElasticsearchClient::class)) {
336
                                        throw new InvalidConfigurationException('The elasticsearch/elasticsearch package is required for Elasticsearch support.');
337
                                    }
338
339
                                    return $v;
340
                                })
341
                            ->end()
342
                        ->end()
343
                        ->arrayNode('hosts')
0 ignored issues
show
Bug introduced by
The method arrayNode() 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

343
                        ->/** @scrutinizer ignore-call */ arrayNode('hosts')
Loading history...
344
                            ->beforeNormalization()->castToArray()->end()
345
                            ->defaultValue([])
346
                            ->prototype('scalar')->end()
347
                        ->end()
348
                        ->arrayNode('mapping')
349
                            ->normalizeKeys(false)
350
                            ->useAttributeAsKey('resource_class')
351
                            ->prototype('array')
352
                                ->children()
353
                                    ->scalarNode('index')->defaultNull()->end()
354
                                    ->scalarNode('type')->defaultValue(DocumentMetadata::DEFAULT_TYPE)->end()
355
                                ->end()
356
                            ->end()
357
                        ->end()
358
                    ->end()
359
                ->end()
360
            ->end();
361
    }
362
363
    /**
364
     * @throws InvalidConfigurationException
365
     */
366
    private function addExceptionToStatusSection(ArrayNodeDefinition $rootNode): void
367
    {
368
        $rootNode
369
            ->children()
370
                ->arrayNode('exception_to_status')
371
                    ->defaultValue([
372
                        SerializerExceptionInterface::class => Response::HTTP_BAD_REQUEST,
373
                        InvalidArgumentException::class => Response::HTTP_BAD_REQUEST,
374
                        FilterValidationException::class => Response::HTTP_BAD_REQUEST,
375
                        OptimisticLockException::class => Response::HTTP_CONFLICT,
376
                    ])
377
                    ->info('The list of exceptions mapped to their HTTP status code.')
378
                    ->normalizeKeys(false)
379
                    ->useAttributeAsKey('exception_class')
380
                    ->beforeNormalization()
381
                        ->ifArray()
382
                        ->then(function (array $exceptionToStatus) {
383
                            foreach ($exceptionToStatus as &$httpStatusCode) {
384
                                if (\is_int($httpStatusCode)) {
385
                                    continue;
386
                                }
387
388
                                if (\defined($httpStatusCodeConstant = sprintf('%s::%s', Response::class, $httpStatusCode))) {
389
                                    @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);
390
391
                                    $httpStatusCode = \constant($httpStatusCodeConstant);
392
                                }
393
                            }
394
395
                            return $exceptionToStatus;
396
                        })
397
                    ->end()
398
                    ->prototype('integer')->end()
399
                    ->validate()
400
                        ->ifArray()
401
                        ->then(function (array $exceptionToStatus) {
402
                            foreach ($exceptionToStatus as $httpStatusCode) {
403
                                if ($httpStatusCode < 100 || $httpStatusCode >= 600) {
404
                                    throw new InvalidConfigurationException(sprintf('The HTTP status code "%s" is not valid.', $httpStatusCode));
405
                                }
406
                            }
407
408
                            return $exceptionToStatus;
409
                        })
410
                    ->end()
411
                ->end()
412
            ->end();
413
    }
414
415
    private function addFormatSection(ArrayNodeDefinition $rootNode, string $key, array $defaultValue): void
416
    {
417
        $rootNode
418
            ->children()
419
                ->arrayNode($key)
420
                    ->defaultValue($defaultValue)
421
                    ->info('The list of enabled formats. The first one will be the default.')
422
                    ->normalizeKeys(false)
423
                    ->useAttributeAsKey('format')
424
                    ->beforeNormalization()
425
                        ->ifArray()
426
                        ->then(function ($v) {
427
                            foreach ($v as $format => $value) {
428
                                if (isset($value['mime_types'])) {
429
                                    continue;
430
                                }
431
432
                                $v[$format] = ['mime_types' => $value];
433
                            }
434
435
                            return $v;
436
                        })
437
                    ->end()
438
                    ->prototype('array')
439
                        ->children()
440
                            ->arrayNode('mime_types')->prototype('scalar')->end()->end()
441
                        ->end()
442
                    ->end()
443
                ->end()
444
            ->end();
445
    }
446
}
447