Passed
Push — master ( dfc177...74ee7e )
by Kévin
13:45 queued 09:32
created

Bundle/DependencyInjection/Configuration.php (1 issue)

Labels
Severity
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;
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
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
38
39
/**
40
 * The configuration of the bundle.
41
 *
42
 * @author Kévin Dunglas <[email protected]>
43
 * @author Baptiste Meyer <[email protected]>
44
 */
45
final class Configuration implements ConfigurationInterface
46
{
47
    /**
48
     * {@inheritdoc}
49
     */
50
    public function getConfigTreeBuilder()
51
    {
52
        if (method_exists(TreeBuilder::class, 'getRootNode')) {
53
            $treeBuilder = new TreeBuilder('api_platform');
54
            $rootNode = $treeBuilder->getRootNode();
55
        } else {
56
            $treeBuilder = new TreeBuilder();
57
            $rootNode = $treeBuilder->root('api_platform');
58
        }
59
60
        $rootNode
61
            ->beforeNormalization()
62
                ->ifTrue(static function ($v) {
63
                    return false === ($v['enable_swagger'] ?? null);
64
                })
65
                ->then(static function ($v) {
66
                    $v['swagger']['versions'] = [];
67
68
                    return $v;
69
                })
70
            ->end()
71
            ->children()
72
                ->scalarNode('title')
73
                    ->info('The title of the API.')
74
                    ->cannotBeEmpty()
75
                    ->defaultValue('')
76
                ->end()
77
                ->scalarNode('description')
78
                    ->info('The description of the API.')
79
                    ->cannotBeEmpty()
80
                    ->defaultValue('')
81
                ->end()
82
                ->scalarNode('version')
83
                    ->info('The version of the API.')
84
                    ->cannotBeEmpty()
85
                    ->defaultValue('0.0.0')
86
                ->end()
87
                ->booleanNode('show_webby')->defaultTrue()->info('If true, show Webby on the documentation page')->end()
88
                ->scalarNode('default_operation_path_resolver')
89
                    ->defaultValue('api_platform.operation_path_resolver.underscore')
90
                    ->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.')
91
                    ->info('Specify the default operation path resolver to use for generating resources operations path.')
92
                ->end()
93
                ->scalarNode('name_converter')->defaultNull()->info('Specify a name converter to use.')->end()
94
                ->scalarNode('path_segment_name_generator')->defaultValue('api_platform.path_segment_name_generator.underscore')->info('Specify a path name generator to use.')->end()
95
                ->booleanNode('allow_plain_identifiers')->defaultFalse()->info('Allow plain identifiers, for example "id" instead of "@id" when denormalizing a relation.')->end()
96
                ->arrayNode('validator')
97
                    ->addDefaultsIfNotSet()
98
                    ->children()
99
                        ->variableNode('serialize_payload_fields')->defaultValue([])->info('Enable the serialization of payload fields when a validation error is thrown.')->end()
100
                    ->end()
101
                ->end()
102
                ->arrayNode('eager_loading')
103
                    ->canBeDisabled()
104
                    ->addDefaultsIfNotSet()
105
                    ->children()
106
                        ->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()
107
                        ->integerNode('max_joins')->defaultValue(30)->info('Max number of joined relations before EagerLoading throws a RuntimeException')->end()
108
                        ->booleanNode('force_eager')->defaultTrue()->info('Force join on every relation. If disabled, it will only join relations having the EAGER fetch mode.')->end()
109
                    ->end()
110
                ->end()
111
                ->booleanNode('enable_fos_user')->defaultValue(class_exists(FOSUserBundle::class))->info('Enable the FOSUserBundle integration.')->end()
112
                ->booleanNode('enable_nelmio_api_doc')
113
                    ->defaultFalse()
114
                    ->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.')
115
                    ->info('Enable the NelmioApiDocBundle integration.')
116
                ->end()
117
                ->booleanNode('enable_swagger')->defaultTrue()->info('Enable the Swagger documentation and export.')->end()
118
                ->booleanNode('enable_swagger_ui')->defaultValue(class_exists(TwigBundle::class))->info('Enable Swagger UI')->end()
119
                ->booleanNode('enable_re_doc')->defaultValue(class_exists(TwigBundle::class))->info('Enable ReDoc')->end()
120
                ->booleanNode('enable_entrypoint')->defaultTrue()->info('Enable the entrypoint')->end()
121
                ->booleanNode('enable_docs')->defaultTrue()->info('Enable the docs')->end()
122
                ->booleanNode('enable_profiler')->defaultTrue()->info('Enable the data collector and the WebProfilerBundle integration.')->end()
123
                ->arrayNode('collection')
124
                    ->addDefaultsIfNotSet()
125
                    ->children()
126
                        ->scalarNode('exists_parameter_name')->defaultValue('exists')->cannotBeEmpty()->info('The name of the query parameter to filter on nullable field values.')->end()
127
                        ->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
128
                        ->scalarNode('order_parameter_name')->defaultValue('order')->cannotBeEmpty()->info('The name of the query parameter to order results.')->end()
129
                        ->arrayNode('pagination')
130
                            ->canBeDisabled()
131
                            ->addDefaultsIfNotSet()
132
                            ->children()
133
                                ->booleanNode('enabled')
134
                                    ->setDeprecated('The use of the `collection.pagination.enabled` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_enabled` instead.')
135
                                    ->defaultTrue()
136
                                    ->info('To enable or disable pagination for all resource collections by default.')
137
                                ->end()
138
                                ->booleanNode('partial')
139
                                    ->setDeprecated('The use of the `collection.pagination.partial` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_partial` instead.')
140
                                    ->defaultFalse()
141
                                    ->info('To enable or disable partial pagination for all resource collections by default when pagination is enabled.')
142
                                ->end()
143
                                ->booleanNode('client_enabled')
144
                                    ->setDeprecated('The use of the `collection.pagination.client_enabled` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_client_enabled` instead.')
145
                                    ->defaultFalse()
146
                                    ->info('To allow the client to enable or disable the pagination.')
147
                                ->end()
148
                                ->booleanNode('client_items_per_page')
149
                                    ->setDeprecated('The use of the `collection.pagination.client_items_per_page` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_client_items_per_page` instead.')
150
                                    ->defaultFalse()
151
                                    ->info('To allow the client to set the number of items per page.')
152
                                ->end()
153
                                ->booleanNode('client_partial')
154
                                    ->setDeprecated('The use of the `collection.pagination.client_partial` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_client_partial` instead.')
155
                                    ->defaultFalse()
156
                                    ->info('To allow the client to enable or disable partial pagination.')
157
                                ->end()
158
                                ->integerNode('items_per_page')
159
                                    ->setDeprecated('The use of the `collection.pagination.items_per_page` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_items_per_page` instead.')
160
                                    ->defaultValue(30)
161
                                    ->info('The default number of items per page.')
162
                                ->end()
163
                                ->integerNode('maximum_items_per_page')
164
                                    ->setDeprecated('The use of the `collection.pagination.maximum_items_per_page` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_maximum_items_per_page` instead.')
165
                                    ->defaultNull()
166
                                    ->info('The maximum number of items per page.')
167
                                ->end()
168
                                ->scalarNode('page_parameter_name')->defaultValue('page')->cannotBeEmpty()->info('The default name of the parameter handling the page number.')->end()
169
                                ->scalarNode('enabled_parameter_name')->defaultValue('pagination')->cannotBeEmpty()->info('The name of the query parameter to enable or disable pagination.')->end()
170
                                ->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()
171
                                ->scalarNode('partial_parameter_name')->defaultValue('partial')->cannotBeEmpty()->info('The name of the query parameter to enable or disable partial pagination.')->end()
172
                            ->end()
173
                        ->end()
174
                    ->end()
175
                ->end()
176
                ->arrayNode('mapping')
177
                    ->addDefaultsIfNotSet()
178
                    ->children()
179
                        ->arrayNode('paths')
180
                            ->prototype('scalar')->end()
181
                        ->end()
182
                    ->end()
183
                ->end()
184
                ->arrayNode('resource_class_directories')
185
                    ->prototype('scalar')->end()
186
                ->end()
187
            ->end();
188
189
        $this->addDoctrineOrmSection($rootNode);
190
        $this->addDoctrineMongoDbOdmSection($rootNode);
191
        $this->addOAuthSection($rootNode);
192
        $this->addGraphQlSection($rootNode);
193
        $this->addSwaggerSection($rootNode);
194
        $this->addHttpCacheSection($rootNode);
195
        $this->addMercureSection($rootNode);
196
        $this->addMessengerSection($rootNode);
197
        $this->addElasticsearchSection($rootNode);
198
199
        $this->addExceptionToStatusSection($rootNode);
200
201
        $this->addFormatSection($rootNode, 'formats', [
202
            'jsonld' => ['mime_types' => ['application/ld+json']],
203
            'json' => ['mime_types' => ['application/json']], // Swagger support
204
            'html' => ['mime_types' => ['text/html']], // Swagger UI support
205
        ]);
206
        $this->addFormatSection($rootNode, 'patch_formats', []);
207
        $this->addFormatSection($rootNode, 'error_formats', [
208
            'jsonproblem' => ['mime_types' => ['application/problem+json']],
209
            'jsonld' => ['mime_types' => ['application/ld+json']],
210
        ]);
211
212
        $this->addDefaultsSection($rootNode);
213
214
        return $treeBuilder;
215
    }
216
217
    private function addDoctrineOrmSection(ArrayNodeDefinition $rootNode): void
218
    {
219
        $rootNode
220
            ->children()
221
                ->arrayNode('doctrine')
222
                    ->{class_exists(DoctrineBundle::class) && class_exists(DoctrineOrmVersion::class) ? 'canBeDisabled' : 'canBeEnabled'}()
223
                ->end()
224
            ->end();
225
    }
226
227
    private function addDoctrineMongoDbOdmSection(ArrayNodeDefinition $rootNode): void
228
    {
229
        $rootNode
230
            ->children()
231
                ->arrayNode('doctrine_mongodb_odm')
232
                    ->{class_exists(DoctrineMongoDBBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
233
                ->end()
234
            ->end();
235
    }
236
237
    private function addOAuthSection(ArrayNodeDefinition $rootNode): void
238
    {
239
        $rootNode
240
            ->children()
241
                ->arrayNode('oauth')
242
                    ->canBeEnabled()
243
                    ->addDefaultsIfNotSet()
244
                    ->children()
245
                        ->scalarNode('clientId')->defaultValue('')->info('The oauth client id.')->end()
246
                        ->scalarNode('clientSecret')->defaultValue('')->info('The oauth client secret.')->end()
247
                        ->scalarNode('type')->defaultValue('oauth2')->info('The oauth client secret.')->end()
248
                        ->scalarNode('flow')->defaultValue('application')->info('The oauth flow grant type.')->end()
249
                        ->scalarNode('tokenUrl')->defaultValue('/oauth/v2/token')->info('The oauth token url.')->end()
250
                        ->scalarNode('authorizationUrl')->defaultValue('/oauth/v2/auth')->info('The oauth authentication url.')->end()
251
                        ->arrayNode('scopes')
252
                            ->prototype('scalar')->end()
253
                        ->end()
254
                    ->end()
255
                ->end()
256
            ->end();
257
    }
258
259
    private function addGraphQlSection(ArrayNodeDefinition $rootNode): void
260
    {
261
        $rootNode
262
            ->children()
263
                ->arrayNode('graphql')
264
                    ->{class_exists(GraphQL::class) ? 'canBeDisabled' : 'canBeEnabled'}()
265
                    ->addDefaultsIfNotSet()
266
                    ->children()
267
                        ->scalarNode('default_ide')->defaultValue('graphiql')->end()
268
                        ->arrayNode('graphiql')
269
                            ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
270
                        ->end()
271
                        ->arrayNode('graphql_playground')
272
                            ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
273
                        ->end()
274
                        ->scalarNode('nesting_separator')->defaultValue('_')->info('The separator to use to filter nested fields.')->end()
275
                        ->arrayNode('collection')
276
                            ->addDefaultsIfNotSet()
277
                            ->children()
278
                                ->arrayNode('pagination')
279
                                    ->canBeDisabled()
280
                                ->end()
281
                            ->end()
282
                        ->end()
283
                    ->end()
284
                ->end()
285
            ->end();
286
    }
287
288
    private function addSwaggerSection(ArrayNodeDefinition $rootNode): void
289
    {
290
        $defaultVersions = [2, 3];
291
292
        $rootNode
293
            ->children()
294
                ->arrayNode('swagger')
295
                    ->addDefaultsIfNotSet()
296
                    ->children()
297
                        ->arrayNode('versions')
298
                            ->info('The active versions of OpenAPI to be exported or used in the swagger_ui. The first value is the default.')
299
                            ->defaultValue($defaultVersions)
300
                            ->beforeNormalization()
301
                                ->always(static function ($v) {
302
                                    if (!\is_array($v)) {
303
                                        $v = [$v];
304
                                    }
305
306
                                    foreach ($v as &$version) {
307
                                        $version = (int) $version;
308
                                    }
309
310
                                    return $v;
311
                                })
312
                            ->end()
313
                            ->validate()
314
                                ->ifTrue(function ($v) use ($defaultVersions) {
315
                                    return $v !== array_intersect($v, $defaultVersions);
316
                                })
317
                                ->thenInvalid(sprintf('Only the versions %s are supported. Got %s.', implode(' and ', $defaultVersions), '%s'))
318
                            ->end()
319
                            ->prototype('scalar')->end()
320
                        ->end()
321
                        ->arrayNode('api_keys')
322
                            ->prototype('array')
323
                                ->children()
324
                                    ->scalarNode('name')
325
                                        ->info('The name of the header or query parameter containing the api key.')
326
                                    ->end()
327
                                    ->enumNode('type')
328
                                        ->info('Whether the api key should be a query parameter or a header.')
329
                                        ->values(['query', 'header'])
330
                                    ->end()
331
                                ->end()
332
                            ->end()
333
                        ->end()
334
                    ->end()
335
                ->end()
336
            ->end();
337
    }
338
339
    private function addHttpCacheSection(ArrayNodeDefinition $rootNode): void
340
    {
341
        $rootNode
342
            ->children()
343
                ->arrayNode('http_cache')
344
                    ->addDefaultsIfNotSet()
345
                    ->children()
346
                        ->booleanNode('etag')
347
                            ->setDeprecated('The use of the `http_cache.etag` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.cache_headers.etag` instead.')
348
                            ->defaultTrue()
349
                            ->info('Automatically generate etags for API responses.')
350
                        ->end()
351
                        ->integerNode('max_age')
0 ignored issues
show
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

351
                        ->/** @scrutinizer ignore-call */ integerNode('max_age')
Loading history...
352
                            ->setDeprecated('The use of the `http_cache.max_age` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.cache_headers.max_age` instead.')
353
                            ->defaultNull()
354
                            ->info('Default value for the response max age.')
355
                        ->end()
356
                        ->integerNode('shared_max_age')
357
                            ->setDeprecated('The use of the `http_cache.shared_max_age` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.cache_headers.shared_max_age` instead.')
358
                            ->defaultNull()
359
                            ->info('Default value for the response shared (proxy) max age.')
360
                        ->end()
361
                        ->arrayNode('vary')
362
                            ->setDeprecated('The use of the `http_cache.vary` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.cache_headers.vary` instead.')
363
                            ->defaultValue(['Accept'])
364
                            ->prototype('scalar')->end()
365
                            ->info('Default values of the "Vary" HTTP header.')
366
                        ->end()
367
                        ->booleanNode('public')->defaultNull()->info('To make all responses public by default.')->end()
368
                        ->arrayNode('invalidation')
369
                            ->setDeprecated('The use of the `http_cache.invalidation` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.cache_headers.invalidation` instead.')
370
                            ->info('Enable the tags-based cache invalidation system.')
371
                            ->canBeEnabled()
372
                            ->children()
373
                                ->arrayNode('varnish_urls')
374
                                    ->defaultValue([])
375
                                    ->prototype('scalar')->end()
376
                                    ->info('URLs of the Varnish servers to purge using cache tags when a resource is updated.')
377
                                ->end()
378
                                ->variableNode('request_options')
379
                                    ->defaultValue([])
380
                                    ->validate()
381
                                        ->ifTrue(function ($v) { return false === \is_array($v); })
382
                                        ->thenInvalid('The request_options parameter must be an array.')
383
                                    ->end()
384
                                    ->info('To pass options to the client charged with the request.')
385
                                ->end()
386
                            ->end()
387
                        ->end()
388
                    ->end()
389
                ->end()
390
            ->end();
391
    }
392
393
    private function addMercureSection(ArrayNodeDefinition $rootNode): void
394
    {
395
        $rootNode
396
            ->children()
397
                ->arrayNode('mercure')
398
                    ->{class_exists(MercureBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
399
                    ->children()
400
                        ->scalarNode('hub_url')
401
                            ->defaultNull()
402
                            ->info('The URL sent in the Link HTTP header. If not set, will default to the URL for MercureBundle\'s default hub.')
403
                        ->end()
404
                    ->end()
405
                ->end()
406
            ->end();
407
    }
408
409
    private function addMessengerSection(ArrayNodeDefinition $rootNode): void
410
    {
411
        $rootNode
412
            ->children()
413
                ->arrayNode('messenger')
414
                    ->{!class_exists(FullStack::class) && interface_exists(MessageBusInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
415
                ->end()
416
            ->end();
417
    }
418
419
    private function addElasticsearchSection(ArrayNodeDefinition $rootNode): void
420
    {
421
        $rootNode
422
            ->children()
423
                ->arrayNode('elasticsearch')
424
                    ->canBeEnabled()
425
                    ->addDefaultsIfNotSet()
426
                    ->children()
427
                        ->booleanNode('enabled')
428
                            ->defaultFalse()
429
                            ->validate()
430
                                ->ifTrue()
431
                                ->then(static function (bool $v): bool {
432
                                    if (!class_exists(ElasticsearchClient::class)) {
433
                                        throw new InvalidConfigurationException('The elasticsearch/elasticsearch package is required for Elasticsearch support.');
434
                                    }
435
436
                                    return $v;
437
                                })
438
                            ->end()
439
                        ->end()
440
                        ->arrayNode('hosts')
441
                            ->beforeNormalization()->castToArray()->end()
442
                            ->defaultValue([])
443
                            ->prototype('scalar')->end()
444
                        ->end()
445
                        ->arrayNode('mapping')
446
                            ->normalizeKeys(false)
447
                            ->useAttributeAsKey('resource_class')
448
                            ->prototype('array')
449
                                ->children()
450
                                    ->scalarNode('index')->defaultNull()->end()
451
                                    ->scalarNode('type')->defaultValue(DocumentMetadata::DEFAULT_TYPE)->end()
452
                                ->end()
453
                            ->end()
454
                        ->end()
455
                    ->end()
456
                ->end()
457
            ->end();
458
    }
459
460
    /**
461
     * @throws InvalidConfigurationException
462
     */
463
    private function addExceptionToStatusSection(ArrayNodeDefinition $rootNode): void
464
    {
465
        $rootNode
466
            ->children()
467
                ->arrayNode('exception_to_status')
468
                    ->defaultValue([
469
                        SerializerExceptionInterface::class => Response::HTTP_BAD_REQUEST,
470
                        InvalidArgumentException::class => Response::HTTP_BAD_REQUEST,
471
                        FilterValidationException::class => Response::HTTP_BAD_REQUEST,
472
                        OptimisticLockException::class => Response::HTTP_CONFLICT,
473
                    ])
474
                    ->info('The list of exceptions mapped to their HTTP status code.')
475
                    ->normalizeKeys(false)
476
                    ->useAttributeAsKey('exception_class')
477
                    ->beforeNormalization()
478
                        ->ifArray()
479
                        ->then(function (array $exceptionToStatus) {
480
                            foreach ($exceptionToStatus as &$httpStatusCode) {
481
                                if (\is_int($httpStatusCode)) {
482
                                    continue;
483
                                }
484
485
                                if (\defined($httpStatusCodeConstant = sprintf('%s::%s', Response::class, $httpStatusCode))) {
486
                                    @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);
487
488
                                    $httpStatusCode = \constant($httpStatusCodeConstant);
489
                                }
490
                            }
491
492
                            return $exceptionToStatus;
493
                        })
494
                    ->end()
495
                    ->prototype('integer')->end()
496
                    ->validate()
497
                        ->ifArray()
498
                        ->then(function (array $exceptionToStatus) {
499
                            foreach ($exceptionToStatus as $httpStatusCode) {
500
                                if ($httpStatusCode < 100 || $httpStatusCode >= 600) {
501
                                    throw new InvalidConfigurationException(sprintf('The HTTP status code "%s" is not valid.', $httpStatusCode));
502
                                }
503
                            }
504
505
                            return $exceptionToStatus;
506
                        })
507
                    ->end()
508
                ->end()
509
            ->end();
510
    }
511
512
    private function addFormatSection(ArrayNodeDefinition $rootNode, string $key, array $defaultValue): void
513
    {
514
        $rootNode
515
            ->children()
516
                ->arrayNode($key)
517
                    ->defaultValue($defaultValue)
518
                    ->info('The list of enabled formats. The first one will be the default.')
519
                    ->normalizeKeys(false)
520
                    ->useAttributeAsKey('format')
521
                    ->beforeNormalization()
522
                        ->ifArray()
523
                        ->then(function ($v) {
524
                            foreach ($v as $format => $value) {
525
                                if (isset($value['mime_types'])) {
526
                                    continue;
527
                                }
528
529
                                $v[$format] = ['mime_types' => $value];
530
                            }
531
532
                            return $v;
533
                        })
534
                    ->end()
535
                    ->prototype('array')
536
                        ->children()
537
                            ->arrayNode('mime_types')->prototype('scalar')->end()->end()
538
                        ->end()
539
                    ->end()
540
                ->end()
541
            ->end();
542
    }
543
544
    private function addDefaultsSection(ArrayNodeDefinition $rootNode): void
545
    {
546
        $nameConverter = new CamelCaseToSnakeCaseNameConverter();
547
        $defaultsNode = $rootNode->children()->arrayNode('defaults');
548
549
        $defaultsNode
550
            ->ignoreExtraKeys()
551
            ->beforeNormalization()
552
            ->always(function (array $defaults) use ($nameConverter) {
553
                $normalizedDefaults = [];
554
                foreach ($defaults as $option => $value) {
555
                    $option = $nameConverter->normalize($option);
556
                    $normalizedDefaults[$option] = $value;
557
                }
558
559
                return $normalizedDefaults;
560
            });
561
562
        foreach (ApiResource::CONFIGURABLE_DEFAULTS as $attribute) {
563
            $snakeCased = $nameConverter->normalize($attribute);
564
            $defaultsNode->children()->variableNode($snakeCased);
565
        }
566
    }
567
}
568