Passed
Pull Request — master (#3135)
by BENOIT
04:13
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\Annotation\ApiResource;
17
use ApiPlatform\Core\Bridge\Elasticsearch\Metadata\Document\DocumentMetadata;
18
use ApiPlatform\Core\Exception\FilterValidationException;
19
use ApiPlatform\Core\Exception\InvalidArgumentException;
20
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
21
use Doctrine\Bundle\MongoDBBundle\DoctrineMongoDBBundle;
22
use Doctrine\ORM\OptimisticLockException;
23
use Doctrine\ORM\Version as DoctrineOrmVersion;
24
use Elasticsearch\Client as ElasticsearchClient;
25
use FOS\UserBundle\FOSUserBundle;
26
use GraphQL\GraphQL;
27
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...
28
use Symfony\Bundle\MercureBundle\MercureBundle;
29
use Symfony\Bundle\TwigBundle\TwigBundle;
30
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
31
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
32
use Symfony\Component\Config\Definition\ConfigurationInterface;
33
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
34
use Symfony\Component\HttpFoundation\Response;
35
use Symfony\Component\Messenger\MessageBusInterface;
36
use Symfony\Component\Serializer\Exception\ExceptionInterface as SerializerExceptionInterface;
37
38
/**
39
 * The configuration of the bundle.
40
 *
41
 * @author Kévin Dunglas <[email protected]>
42
 * @author Baptiste Meyer <[email protected]>
43
 */
44
final class Configuration implements ConfigurationInterface
45
{
46
    /**
47
     * {@inheritdoc}
48
     */
49
    public function getConfigTreeBuilder()
50
    {
51
        if (method_exists(TreeBuilder::class, 'getRootNode')) {
52
            $treeBuilder = new TreeBuilder('api_platform');
53
            $rootNode = $treeBuilder->getRootNode();
54
        } else {
55
            $treeBuilder = new TreeBuilder();
56
            $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

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

217
                        ->/** @scrutinizer ignore-call */ scalarNode('clientSecret')->defaultValue('')->info('The oauth client secret.')->end()
Loading history...
218
                        ->scalarNode('type')->defaultValue('oauth2')->info('The oauth client secret.')->end()
219
                        ->scalarNode('flow')->defaultValue('application')->info('The oauth flow grant type.')->end()
220
                        ->scalarNode('tokenUrl')->defaultValue('/oauth/v2/token')->info('The oauth token url.')->end()
221
                        ->scalarNode('authorizationUrl')->defaultValue('/oauth/v2/auth')->info('The oauth authentication url.')->end()
222
                        ->arrayNode('scopes')
223
                            ->prototype('scalar')->end()
224
                        ->end()
225
                    ->end()
226
                ->end()
227
            ->end();
228
    }
229
230
    private function addGraphQlSection(ArrayNodeDefinition $rootNode): void
231
    {
232
        $rootNode
233
            ->children()
234
                ->arrayNode('graphql')
235
                    ->{class_exists(GraphQL::class) ? 'canBeDisabled' : 'canBeEnabled'}()
236
                    ->addDefaultsIfNotSet()
237
                    ->children()
238
                        ->scalarNode('default_ide')->defaultValue('graphiql')->end()
239
                        ->arrayNode('graphiql')
240
                            ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
241
                        ->end()
242
                        ->arrayNode('graphql_playground')
243
                            ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
244
                        ->end()
245
                        ->scalarNode('nesting_separator')->defaultValue('_')->info('The separator to use to filter nested fields.')->end()
246
                        ->arrayNode('collection')
247
                            ->addDefaultsIfNotSet()
248
                            ->children()
249
                                ->arrayNode('pagination')
250
                                    ->canBeDisabled()
251
                                ->end()
252
                            ->end()
253
                        ->end()
254
                    ->end()
255
                ->end()
256
            ->end();
257
    }
258
259
    private function addSwaggerSection(ArrayNodeDefinition $rootNode): void
260
    {
261
        $defaultVersions = [2, 3];
262
263
        $rootNode
264
            ->children()
265
                ->arrayNode('swagger')
266
                    ->addDefaultsIfNotSet()
267
                    ->children()
268
                        ->arrayNode('versions')
269
                            ->info('The active versions of OpenAPI to be exported or used in the swagger_ui. The first value is the default.')
270
                            ->defaultValue($defaultVersions)
271
                            ->beforeNormalization()
272
                                ->always(static function ($v) {
273
                                    if (!\is_array($v)) {
274
                                        $v = [$v];
275
                                    }
276
277
                                    foreach ($v as &$version) {
278
                                        $version = (int) $version;
279
                                    }
280
281
                                    return $v;
282
                                })
283
                            ->end()
284
                            ->validate()
285
                                ->ifTrue(function ($v) use ($defaultVersions) {
286
                                    return $v !== array_intersect($v, $defaultVersions);
287
                                })
288
                                ->thenInvalid(sprintf('Only the versions %s are supported. Got %s.', implode(' and ', $defaultVersions), '%s'))
289
                            ->end()
290
                            ->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

290
                            ->/** @scrutinizer ignore-call */ prototype('scalar')->end()
Loading history...
291
                        ->end()
292
                        ->arrayNode('api_keys')
293
                            ->prototype('array')
294
                                ->children()
295
                                    ->scalarNode('name')
296
                                        ->info('The name of the header or query parameter containing the api key.')
297
                                    ->end()
298
                                    ->enumNode('type')
299
                                        ->info('Whether the api key should be a query parameter or a header.')
300
                                        ->values(['query', 'header'])
301
                                    ->end()
302
                                ->end()
303
                            ->end()
304
                        ->end()
305
                    ->end()
306
                ->end()
307
            ->end();
308
    }
309
310
    private function addHttpCacheSection(ArrayNodeDefinition $rootNode): void
311
    {
312
        $rootNode
313
            ->children()
314
                ->arrayNode('http_cache')
315
                    ->addDefaultsIfNotSet()
316
                    ->children()
317
                        ->booleanNode('etag')->defaultTrue()->info('Automatically generate etags for API responses.')->end()
318
                        ->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

318
                        ->/** @scrutinizer ignore-call */ integerNode('max_age')->defaultNull()->info('Default value for the response max age.')->end()
Loading history...
319
                        ->integerNode('shared_max_age')->defaultNull()->info('Default value for the response shared (proxy) max age.')->end()
320
                        ->arrayNode('vary')
321
                            ->defaultValue(['Accept'])
322
                            ->prototype('scalar')->end()
323
                            ->info('Default values of the "Vary" HTTP header.')
324
                        ->end()
325
                        ->booleanNode('public')->defaultNull()->info('To make all responses public by default.')->end()
326
                        ->arrayNode('invalidation')
327
                            ->info('Enable the tags-based cache invalidation system.')
328
                            ->canBeEnabled()
329
                            ->children()
330
                                ->arrayNode('varnish_urls')
331
                                    ->defaultValue([])
332
                                    ->prototype('scalar')->end()
333
                                    ->info('URLs of the Varnish servers to purge using cache tags when a resource is updated.')
334
                                ->end()
335
                                ->variableNode('request_options')
336
                                    ->defaultValue([])
337
                                    ->validate()
338
                                        ->ifTrue(function ($v) { return false === \is_array($v); })
339
                                        ->thenInvalid('The request_options parameter must be an array.')
340
                                    ->end()
341
                                    ->info('To pass options to the client charged with the request.')
342
                                ->end()
343
                            ->end()
344
                        ->end()
345
                    ->end()
346
                ->end()
347
            ->end();
348
    }
349
350
    private function addMercureSection(ArrayNodeDefinition $rootNode): void
351
    {
352
        $rootNode
353
            ->children()
354
                ->arrayNode('mercure')
355
                    ->{class_exists(MercureBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
356
                    ->children()
357
                        ->scalarNode('hub_url')
358
                            ->defaultNull()
359
                            ->info('The URL sent in the Link HTTP header. If not set, will default to the URL for MercureBundle\'s default hub.')
360
                        ->end()
361
                    ->end()
362
                ->end()
363
            ->end();
364
    }
365
366
    private function addMessengerSection(ArrayNodeDefinition $rootNode): void
367
    {
368
        $rootNode
369
            ->children()
370
                ->arrayNode('messenger')
371
                    ->{!class_exists(FullStack::class) && interface_exists(MessageBusInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
372
                ->end()
373
            ->end();
374
    }
375
376
    private function addElasticsearchSection(ArrayNodeDefinition $rootNode): void
377
    {
378
        $rootNode
379
            ->children()
380
                ->arrayNode('elasticsearch')
381
                    ->canBeEnabled()
382
                    ->addDefaultsIfNotSet()
383
                    ->children()
384
                        ->booleanNode('enabled')
385
                            ->defaultFalse()
386
                            ->validate()
387
                                ->ifTrue()
388
                                ->then(static function (bool $v): bool {
389
                                    if (!class_exists(ElasticsearchClient::class)) {
390
                                        throw new InvalidConfigurationException('The elasticsearch/elasticsearch package is required for Elasticsearch support.');
391
                                    }
392
393
                                    return $v;
394
                                })
395
                            ->end()
396
                        ->end()
397
                        ->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

397
                        ->/** @scrutinizer ignore-call */ arrayNode('hosts')
Loading history...
398
                            ->beforeNormalization()->castToArray()->end()
399
                            ->defaultValue([])
400
                            ->prototype('scalar')->end()
401
                        ->end()
402
                        ->arrayNode('mapping')
403
                            ->normalizeKeys(false)
404
                            ->useAttributeAsKey('resource_class')
405
                            ->prototype('array')
406
                                ->children()
407
                                    ->scalarNode('index')->defaultNull()->end()
408
                                    ->scalarNode('type')->defaultValue(DocumentMetadata::DEFAULT_TYPE)->end()
409
                                ->end()
410
                            ->end()
411
                        ->end()
412
                    ->end()
413
                ->end()
414
            ->end();
415
    }
416
417
    /**
418
     * @throws InvalidConfigurationException
419
     */
420
    private function addExceptionToStatusSection(ArrayNodeDefinition $rootNode): void
421
    {
422
        $rootNode
423
            ->children()
424
                ->arrayNode('exception_to_status')
425
                    ->defaultValue([
426
                        SerializerExceptionInterface::class => Response::HTTP_BAD_REQUEST,
427
                        InvalidArgumentException::class => Response::HTTP_BAD_REQUEST,
428
                        FilterValidationException::class => Response::HTTP_BAD_REQUEST,
429
                        OptimisticLockException::class => Response::HTTP_CONFLICT,
430
                    ])
431
                    ->info('The list of exceptions mapped to their HTTP status code.')
432
                    ->normalizeKeys(false)
433
                    ->useAttributeAsKey('exception_class')
434
                    ->beforeNormalization()
435
                        ->ifArray()
436
                        ->then(function (array $exceptionToStatus) {
437
                            foreach ($exceptionToStatus as &$httpStatusCode) {
438
                                if (\is_int($httpStatusCode)) {
439
                                    continue;
440
                                }
441
442
                                if (\defined($httpStatusCodeConstant = sprintf('%s::%s', Response::class, $httpStatusCode))) {
443
                                    @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);
444
445
                                    $httpStatusCode = \constant($httpStatusCodeConstant);
446
                                }
447
                            }
448
449
                            return $exceptionToStatus;
450
                        })
451
                    ->end()
452
                    ->prototype('integer')->end()
453
                    ->validate()
454
                        ->ifArray()
455
                        ->then(function (array $exceptionToStatus) {
456
                            foreach ($exceptionToStatus as $httpStatusCode) {
457
                                if ($httpStatusCode < 100 || $httpStatusCode >= 600) {
458
                                    throw new InvalidConfigurationException(sprintf('The HTTP status code "%s" is not valid.', $httpStatusCode));
459
                                }
460
                            }
461
462
                            return $exceptionToStatus;
463
                        })
464
                    ->end()
465
                ->end()
466
            ->end();
467
    }
468
469
    private function addFormatSection(ArrayNodeDefinition $rootNode, string $key, array $defaultValue): void
470
    {
471
        $rootNode
472
            ->children()
473
                ->arrayNode($key)
474
                    ->defaultValue($defaultValue)
475
                    ->info('The list of enabled formats. The first one will be the default.')
476
                    ->normalizeKeys(false)
477
                    ->useAttributeAsKey('format')
478
                    ->beforeNormalization()
479
                        ->ifArray()
480
                        ->then(function ($v) {
481
                            foreach ($v as $format => $value) {
482
                                if (isset($value['mime_types'])) {
483
                                    continue;
484
                                }
485
486
                                $v[$format] = ['mime_types' => $value];
487
                            }
488
489
                            return $v;
490
                        })
491
                    ->end()
492
                    ->prototype('array')
493
                        ->children()
494
                            ->arrayNode('mime_types')->prototype('scalar')->end()->end()
495
                        ->end()
496
                    ->end()
497
                ->end()
498
            ->end();
499
    }
500
501
    private function addDefaultsSection(ArrayNodeDefinition $rootNode): void
502
    {
503
        $defaultsNode = $rootNode->children()->arrayNode('defaults')->addDefaultsIfNotSet();
504
        $attributesNode = $defaultsNode->children()->arrayNode('attributes')->addDefaultsIfNotSet();
505
        $properties = (new \ReflectionClass(ApiResource::class))->getProperties();
506
507
        foreach ($properties as $property) {
508
            $optionName = $property->getName();
509
            if ('attributes' === $optionName) { // "attributes" is a sub-level and not a config option
510
                continue;
511
            }
512
513
            $node = $property->isPublic() ? $defaultsNode : $attributesNode;
514
            $node->children()->variableNode($optionName);
515
        }
516
    }
517
}
518