vendor/api-platform/core/src/Symfony/Bundle/DependencyInjection/Configuration.php line 495

  1. <?php
  2. /*
  3.  * This file is part of the API Platform project.
  4.  *
  5.  * (c) Kévin Dunglas <dunglas@gmail.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. declare(strict_types=1);
  11. namespace ApiPlatform\Symfony\Bundle\DependencyInjection;
  12. use ApiPlatform\Doctrine\Common\Filter\OrderFilterInterface;
  13. use ApiPlatform\Elasticsearch\Metadata\Document\DocumentMetadata;
  14. use ApiPlatform\Elasticsearch\State\Options;
  15. use ApiPlatform\Exception\FilterValidationException;
  16. use ApiPlatform\Exception\InvalidArgumentException;
  17. use ApiPlatform\Metadata\ApiResource;
  18. use ApiPlatform\Metadata\Post;
  19. use ApiPlatform\Metadata\Put;
  20. use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
  21. use Doctrine\Bundle\MongoDBBundle\DoctrineMongoDBBundle;
  22. use Doctrine\ORM\EntityManagerInterface;
  23. use Doctrine\ORM\OptimisticLockException;
  24. use GraphQL\GraphQL;
  25. use Symfony\Bundle\FullStack;
  26. use Symfony\Bundle\MakerBundle\MakerBundle;
  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. use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
  37. /**
  38.  * The configuration of the bundle.
  39.  *
  40.  * @author Kévin Dunglas <dunglas@gmail.com>
  41.  * @author Baptiste Meyer <baptiste.meyer@gmail.com>
  42.  */
  43. final class Configuration implements ConfigurationInterface
  44. {
  45.     /**
  46.      * {@inheritdoc}
  47.      */
  48.     public function getConfigTreeBuilder(): TreeBuilder
  49.     {
  50.         $treeBuilder = new TreeBuilder('api_platform');
  51.         $rootNode $treeBuilder->getRootNode();
  52.         $rootNode
  53.             ->beforeNormalization()
  54.                 ->ifTrue(static function ($v) {
  55.                     return false === ($v['enable_swagger'] ?? null);
  56.                 })
  57.                 ->then(static function ($v) {
  58.                     $v['swagger']['versions'] = [];
  59.                     return $v;
  60.                 })
  61.             ->end()
  62.             ->children()
  63.                 ->scalarNode('title')
  64.                     ->info('The title of the API.')
  65.                     ->cannotBeEmpty()
  66.                     ->defaultValue('')
  67.                 ->end()
  68.                 ->scalarNode('description')
  69.                     ->info('The description of the API.')
  70.                     ->cannotBeEmpty()
  71.                     ->defaultValue('')
  72.                 ->end()
  73.                 ->scalarNode('version')
  74.                     ->info('The version of the API.')
  75.                     ->cannotBeEmpty()
  76.                     ->defaultValue('0.0.0')
  77.                 ->end()
  78.                 ->booleanNode('show_webby')->defaultTrue()->info('If true, show Webby on the documentation page')->end()
  79.                 ->booleanNode('event_listeners_backward_compatibility_layer')->defaultTrue()->info('If true API Platform uses Symfony event listeners instead of providers and processors.')->end() // TODO: Add link to the documentation
  80.                 ->scalarNode('name_converter')->defaultNull()->info('Specify a name converter to use.')->end()
  81.                 ->scalarNode('asset_package')->defaultNull()->info('Specify an asset package name to use.')->end()
  82.                 ->scalarNode('path_segment_name_generator')->defaultValue('api_platform.metadata.path_segment_name_generator.underscore')->info('Specify a path name generator to use.')->end()
  83.                 ->arrayNode('validator')
  84.                     ->addDefaultsIfNotSet()
  85.                     ->children()
  86.                         ->variableNode('serialize_payload_fields')->defaultValue([])->info('Set to null to serialize all payload fields when a validation error is thrown, or set the fields you want to include explicitly.')->end()
  87.                         ->booleanNode('query_parameter_validation')->defaultValue(true)->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_swagger')->defaultTrue()->info('Enable the Swagger documentation and export.')->end()
  100.                 ->booleanNode('enable_swagger_ui')->defaultValue(class_exists(TwigBundle::class))->info('Enable Swagger UI')->end()
  101.                 ->booleanNode('enable_re_doc')->defaultValue(class_exists(TwigBundle::class))->info('Enable ReDoc')->end()
  102.                 ->booleanNode('enable_entrypoint')->defaultTrue()->info('Enable the entrypoint')->end()
  103.                 ->booleanNode('enable_docs')->defaultTrue()->info('Enable the docs')->end()
  104.                 ->booleanNode('enable_profiler')->defaultTrue()->info('Enable the data collector and the WebProfilerBundle integration.')->end()
  105.                 ->booleanNode('keep_legacy_inflector')->defaultTrue()->info('Keep doctrine/inflector instead of symfony/string to generate plurals for routes.')->end()
  106.                 ->arrayNode('collection')
  107.                     ->addDefaultsIfNotSet()
  108.                     ->children()
  109.                         ->scalarNode('exists_parameter_name')->defaultValue('exists')->cannotBeEmpty()->info('The name of the query parameter to filter on nullable field values.')->end()
  110.                         ->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
  111.                         ->scalarNode('order_parameter_name')->defaultValue('order')->cannotBeEmpty()->info('The name of the query parameter to order results.')->end()
  112.                         ->enumNode('order_nulls_comparison')->defaultNull()->values(array_merge(array_keys(OrderFilterInterface::NULLS_DIRECTION_MAP), [null]))->info('The nulls comparison strategy.')->end()
  113.                         ->arrayNode('pagination')
  114.                             ->canBeDisabled()
  115.                             ->addDefaultsIfNotSet()
  116.                             ->children()
  117.                                 ->scalarNode('page_parameter_name')->defaultValue('page')->cannotBeEmpty()->info('The default name of the parameter handling the page number.')->end()
  118.                                 ->scalarNode('enabled_parameter_name')->defaultValue('pagination')->cannotBeEmpty()->info('The name of the query parameter to enable or disable pagination.')->end()
  119.                                 ->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()
  120.                                 ->scalarNode('partial_parameter_name')->defaultValue('partial')->cannotBeEmpty()->info('The name of the query parameter to enable or disable partial pagination.')->end()
  121.                             ->end()
  122.                         ->end()
  123.                     ->end()
  124.                 ->end()
  125.                 ->arrayNode('mapping')
  126.                     ->addDefaultsIfNotSet()
  127.                     ->children()
  128.                         ->arrayNode('paths')
  129.                             ->prototype('scalar')->end()
  130.                         ->end()
  131.                     ->end()
  132.                 ->end()
  133.                 ->arrayNode('resource_class_directories')
  134.                     ->prototype('scalar')->end()
  135.                 ->end()
  136.             ->end();
  137.         $this->addDoctrineOrmSection($rootNode);
  138.         $this->addDoctrineMongoDbOdmSection($rootNode);
  139.         $this->addOAuthSection($rootNode);
  140.         $this->addGraphQlSection($rootNode);
  141.         $this->addSwaggerSection($rootNode);
  142.         $this->addHttpCacheSection($rootNode);
  143.         $this->addMercureSection($rootNode);
  144.         $this->addMessengerSection($rootNode);
  145.         $this->addElasticsearchSection($rootNode);
  146.         $this->addOpenApiSection($rootNode);
  147.         $this->addMakerSection($rootNode);
  148.         $this->addExceptionToStatusSection($rootNode);
  149.         $this->addFormatSection($rootNode'formats', [
  150.         ]);
  151.         $this->addFormatSection($rootNode'patch_formats', [
  152.             'json' => ['mime_types' => ['application/merge-patch+json']],
  153.         ]);
  154.         $this->addFormatSection($rootNode'docs_formats', [
  155.             'jsonopenapi' => ['mime_types' => ['application/vnd.openapi+json']],
  156.             'yamlopenapi' => ['mime_types' => ['application/vnd.openapi+yaml']],
  157.             'json' => ['mime_types' => ['application/json']], // this is only for legacy reasons, use jsonopenapi instead
  158.             'jsonld' => ['mime_types' => ['application/ld+json']],
  159.             'html' => ['mime_types' => ['text/html']],
  160.         ]);
  161.         $this->addFormatSection($rootNode'error_formats', [
  162.             'jsonld' => ['mime_types' => ['application/ld+json']],
  163.             'jsonproblem' => ['mime_types' => ['application/problem+json']],
  164.             'json' => ['mime_types' => ['application/problem+json''application/json']],
  165.         ]);
  166.         $this->addDefaultsSection($rootNode);
  167.         return $treeBuilder;
  168.     }
  169.     private function addDoctrineOrmSection(ArrayNodeDefinition $rootNode): void
  170.     {
  171.         $rootNode
  172.             ->children()
  173.                 ->arrayNode('doctrine')
  174.                     ->{class_exists(DoctrineBundle::class) && interface_exists(EntityManagerInterface::class) ? 'canBeDisabled' 'canBeEnabled'}()
  175.                 ->end()
  176.             ->end();
  177.     }
  178.     private function addDoctrineMongoDbOdmSection(ArrayNodeDefinition $rootNode): void
  179.     {
  180.         $rootNode
  181.             ->children()
  182.                 ->arrayNode('doctrine_mongodb_odm')
  183.                     ->{class_exists(DoctrineMongoDBBundle::class) ? 'canBeDisabled' 'canBeEnabled'}()
  184.                 ->end()
  185.             ->end();
  186.     }
  187.     private function addOAuthSection(ArrayNodeDefinition $rootNode): void
  188.     {
  189.         $rootNode
  190.             ->children()
  191.                 ->arrayNode('oauth')
  192.                     ->canBeEnabled()
  193.                     ->addDefaultsIfNotSet()
  194.                     ->children()
  195.                         ->scalarNode('clientId')->defaultValue('')->info('The oauth client id.')->end()
  196.                         ->scalarNode('clientSecret')
  197.                             ->defaultValue('')
  198.                             ->info('The OAuth client secret. Never use this parameter in your production environment. It exposes crucial security information. This feature is intended for dev/test environments only. Enable "oauth.pkce" instead')
  199.                         ->end()
  200.                         ->booleanNode('pkce')->defaultFalse()->info('Enable the oauth PKCE.')->end()
  201.                         ->scalarNode('type')->defaultValue('oauth2')->info('The oauth type.')->end()
  202.                         ->scalarNode('flow')->defaultValue('application')->info('The oauth flow grant type.')->end()
  203.                         ->scalarNode('tokenUrl')->defaultValue('')->info('The oauth token url.')->end()
  204.                         ->scalarNode('authorizationUrl')->defaultValue('')->info('The oauth authentication url.')->end()
  205.                         ->scalarNode('refreshUrl')->defaultValue('')->info('The oauth refresh url.')->end()
  206.                         ->arrayNode('scopes')
  207.                             ->prototype('scalar')->end()
  208.                         ->end()
  209.                     ->end()
  210.                 ->end()
  211.             ->end();
  212.     }
  213.     private function addGraphQlSection(ArrayNodeDefinition $rootNode): void
  214.     {
  215.         $rootNode
  216.             ->children()
  217.                 ->arrayNode('graphql')
  218.                     ->{class_exists(GraphQL::class) ? 'canBeDisabled' 'canBeEnabled'}()
  219.                     ->addDefaultsIfNotSet()
  220.                     ->children()
  221.                         ->scalarNode('default_ide')->defaultValue('graphiql')->end()
  222.                         ->arrayNode('graphiql')
  223.                             ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' 'canBeEnabled'}()
  224.                         ->end()
  225.                         ->arrayNode('graphql_playground')
  226.                             ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' 'canBeEnabled'}()
  227.                         ->end()
  228.                         ->arrayNode('introspection')
  229.                             ->canBeDisabled()
  230.                         ->end()
  231.                         ->scalarNode('nesting_separator')->defaultValue('_')->info('The separator to use to filter nested fields.')->end()
  232.                         ->arrayNode('collection')
  233.                             ->addDefaultsIfNotSet()
  234.                             ->children()
  235.                                 ->arrayNode('pagination')
  236.                                     ->canBeDisabled()
  237.                                 ->end()
  238.                             ->end()
  239.                         ->end()
  240.                     ->end()
  241.                 ->end()
  242.             ->end();
  243.     }
  244.     private function addSwaggerSection(ArrayNodeDefinition $rootNode): void
  245.     {
  246.         $supportedVersions = [3];
  247.         $rootNode
  248.             ->children()
  249.                 ->arrayNode('swagger')
  250.                     ->addDefaultsIfNotSet()
  251.                     ->children()
  252.                         ->arrayNode('versions')
  253.                             ->info('The active versions of OpenAPI to be exported or used in Swagger UI. The first value is the default.')
  254.                             ->defaultValue($supportedVersions)
  255.                             ->beforeNormalization()
  256.                                 ->always(static function ($v): array {
  257.                                     if (!\is_array($v)) {
  258.                                         $v = [$v];
  259.                                     }
  260.                                     foreach ($v as &$version) {
  261.                                         $version = (int) $version;
  262.                                     }
  263.                                     return $v;
  264.                                 })
  265.                             ->end()
  266.                             ->validate()
  267.                                 ->ifTrue(static fn($v): bool => $v !== array_intersect($v$supportedVersions))
  268.                                 ->thenInvalid(sprintf('Only the versions %s are supported. Got %s.'implode(' and '$supportedVersions), '%s'))
  269.                             ->end()
  270.                             ->prototype('scalar')->end()
  271.                         ->end()
  272.                         ->arrayNode('api_keys')
  273.                             ->useAttributeAsKey('key')
  274.                             ->validate()
  275.                                 ->ifTrue(static fn($v): bool => (bool) array_filter(array_keys($v), fn($item) => !preg_match('/^[a-zA-Z0-9._-]+$/'$item)))
  276.                                 ->thenInvalid('The api keys "key" is not valid according to the pattern enforced by OpenAPI 3.1 ^[a-zA-Z0-9._-]+$.')
  277.                             ->end()
  278.                             ->prototype('array')
  279.                                 ->children()
  280.                                     ->scalarNode('name')
  281.                                         ->info('The name of the header or query parameter containing the api key.')
  282.                                     ->end()
  283.                                     ->enumNode('type')
  284.                                         ->info('Whether the api key should be a query parameter or a header.')
  285.                                         ->values(['query''header'])
  286.                                     ->end()
  287.                                 ->end()
  288.                             ->end()
  289.                         ->end()
  290.                         ->variableNode('swagger_ui_extra_configuration')
  291.                             ->defaultValue([])
  292.                             ->validate()
  293.                                 ->ifTrue(static fn($v): bool => false === \is_array($v))
  294.                                 ->thenInvalid('The swagger_ui_extra_configuration parameter must be an array.')
  295.                             ->end()
  296.                             ->info('To pass extra configuration to Swagger UI, like docExpansion or filter.')
  297.                         ->end()
  298.                     ->end()
  299.                 ->end()
  300.             ->end();
  301.     }
  302.     private function addHttpCacheSection(ArrayNodeDefinition $rootNode): void
  303.     {
  304.         $rootNode
  305.             ->children()
  306.                 ->arrayNode('http_cache')
  307.                     ->addDefaultsIfNotSet()
  308.                     ->children()
  309.                         ->booleanNode('public')->defaultNull()->info('To make all responses public by default.')->end()
  310.                         ->arrayNode('invalidation')
  311.                             ->info('Enable the tags-based cache invalidation system.')
  312.                             ->canBeEnabled()
  313.                             ->children()
  314.                                 ->arrayNode('varnish_urls')
  315.                                     ->setDeprecated('api-platform/core''3.0''The "varnish_urls" configuration is deprecated, use "urls" or "scoped_clients".')
  316.                                     ->defaultValue([])
  317.                                     ->prototype('scalar')->end()
  318.                                     ->info('URLs of the Varnish servers to purge using cache tags when a resource is updated.')
  319.                                 ->end()
  320.                                 ->arrayNode('urls')
  321.                                     ->defaultValue([])
  322.                                     ->prototype('scalar')->end()
  323.                                     ->info('URLs of the Varnish servers to purge using cache tags when a resource is updated.')
  324.                                 ->end()
  325.                                 ->arrayNode('scoped_clients')
  326.                                     ->defaultValue([])
  327.                                     ->prototype('scalar')->end()
  328.                                     ->info('Service names of scoped client to use by the cache purger.')
  329.                                 ->end()
  330.                                 ->integerNode('max_header_length')
  331.                                     ->defaultValue(7500)
  332.                                     ->info('Max header length supported by the cache server.')
  333.                                 ->end()
  334.                                 ->variableNode('request_options')
  335.                                     ->defaultValue([])
  336.                                     ->validate()
  337.                                         ->ifTrue(static fn($v): bool => false === \is_array($v))
  338.                                         ->thenInvalid('The request_options parameter must be an array.')
  339.                                     ->end()
  340.                                     ->info('To pass options to the client charged with the request.')
  341.                                 ->end()
  342.                                 ->scalarNode('purger')
  343.                                     ->defaultValue('api_platform.http_cache.purger.varnish')
  344.                                     ->info('Specify a purger to use (available values: "api_platform.http_cache.purger.varnish.ban", "api_platform.http_cache.purger.varnish.xkey", "api_platform.http_cache.purger.souin").')
  345.                                 ->end()
  346.                                 ->arrayNode('xkey')
  347.                                     ->setDeprecated('api-platform/core''3.0''The "xkey" configuration is deprecated, use your own purger to customize surrogate keys or the appropriate paramters.')
  348.                                     ->addDefaultsIfNotSet()
  349.                                     ->children()
  350.                                         ->scalarNode('glue')
  351.                                         ->defaultValue(' ')
  352.                                         ->info('xkey glue between keys')
  353.                                         ->end()
  354.                                     ->end()
  355.                                 ->end()
  356.                             ->end()
  357.                         ->end()
  358.                     ->end()
  359.                 ->end()
  360.             ->end();
  361.     }
  362.     private function addMercureSection(ArrayNodeDefinition $rootNode): void
  363.     {
  364.         $rootNode
  365.             ->children()
  366.                 ->arrayNode('mercure')
  367.                     ->{class_exists(MercureBundle::class) ? 'canBeDisabled' 'canBeEnabled'}()
  368.                     ->children()
  369.                         ->scalarNode('hub_url')
  370.                             ->defaultNull()
  371.                             ->info('The URL sent in the Link HTTP header. If not set, will default to the URL for MercureBundle\'s default hub.')
  372.                         ->end()
  373.                         ->booleanNode('include_type')
  374.                             ->defaultFalse()
  375.                             ->info('Always include @type in updates (including delete ones).')
  376.                         ->end()
  377.                     ->end()
  378.                 ->end()
  379.             ->end();
  380.     }
  381.     private function addMessengerSection(ArrayNodeDefinition $rootNode): void
  382.     {
  383.         $rootNode
  384.             ->children()
  385.                 ->arrayNode('messenger')
  386.                     ->{!class_exists(FullStack::class) && interface_exists(MessageBusInterface::class) ? 'canBeDisabled' 'canBeEnabled'}()
  387.                 ->end()
  388.             ->end();
  389.     }
  390.     private function addElasticsearchSection(ArrayNodeDefinition $rootNode): void
  391.     {
  392.         $rootNode
  393.             ->children()
  394.                 ->arrayNode('elasticsearch')
  395.                     ->canBeEnabled()
  396.                     ->addDefaultsIfNotSet()
  397.                     ->children()
  398.                         ->booleanNode('enabled')
  399.                             ->defaultFalse()
  400.                             ->validate()
  401.                                 ->ifTrue()
  402.                                 ->then(static function (bool $v): bool {
  403.                                     if (!(class_exists(\Elasticsearch\Client::class) || class_exists(\Elastic\Elasticsearch\Client::class))) {
  404.                                         throw new InvalidConfigurationException('The elasticsearch/elasticsearch package is required for Elasticsearch support.');
  405.                                     }
  406.                                     return $v;
  407.                                 })
  408.                             ->end()
  409.                         ->end()
  410.                         ->arrayNode('hosts')
  411.                             ->beforeNormalization()->castToArray()->end()
  412.                             ->defaultValue([])
  413.                             ->prototype('scalar')->end()
  414.                         ->end()
  415.                         ->arrayNode('mapping')
  416.                             ->setDeprecated('api-platform/core''3.1'sprintf('The "%%node%%" option is deprecated. Configure an %s as $stateOptions.'Options::class))
  417.                             ->normalizeKeys(false)
  418.                             ->useAttributeAsKey('resource_class')
  419.                             ->prototype('array')
  420.                                 ->children()
  421.                                     ->scalarNode('index')->defaultNull()->end()
  422.                                     ->scalarNode('type')->defaultValue(DocumentMetadata::DEFAULT_TYPE)->end()
  423.                                 ->end()
  424.                             ->end()
  425.                         ->end()
  426.                     ->end()
  427.                 ->end()
  428.             ->end();
  429.     }
  430.     private function addOpenApiSection(ArrayNodeDefinition $rootNode): void
  431.     {
  432.         $rootNode
  433.             ->children()
  434.                 ->arrayNode('openapi')
  435.                     ->addDefaultsIfNotSet()
  436.                         ->children()
  437.                         ->arrayNode('contact')
  438.                         ->addDefaultsIfNotSet()
  439.                             ->children()
  440.                                 ->scalarNode('name')->defaultNull()->info('The identifying name of the contact person/organization.')->end()
  441.                                 ->scalarNode('url')->defaultNull()->info('The URL pointing to the contact information. MUST be in the format of a URL.')->end()
  442.                                 ->scalarNode('email')->defaultNull()->info('The email address of the contact person/organization. MUST be in the format of an email address.')->end()
  443.                             ->end()
  444.                         ->end()
  445.                         ->scalarNode('termsOfService')->defaultNull()->info('A URL to the Terms of Service for the API. MUST be in the format of a URL.')->end()
  446.                         ->arrayNode('license')
  447.                         ->addDefaultsIfNotSet()
  448.                             ->children()
  449.                                 ->scalarNode('name')->defaultNull()->info('The license name used for the API.')->end()
  450.                                 ->scalarNode('url')->defaultNull()->info('URL to the license used for the API. MUST be in the format of a URL.')->end()
  451.                             ->end()
  452.                         ->end()
  453.                         ->variableNode('swagger_ui_extra_configuration')
  454.                             ->defaultValue([])
  455.                             ->validate()
  456.                                 ->ifTrue(static fn($v): bool => false === \is_array($v))
  457.                                 ->thenInvalid('The swagger_ui_extra_configuration parameter must be an array.')
  458.                             ->end()
  459.                             ->info('To pass extra configuration to Swagger UI, like docExpansion or filter.')
  460.                         ->end()
  461.                     ->end()
  462.                 ->end()
  463.             ->end();
  464.     }
  465.     /**
  466.      * @throws InvalidConfigurationException
  467.      */
  468.     private function addExceptionToStatusSection(ArrayNodeDefinition $rootNode): void
  469.     {
  470.         $rootNode
  471.             ->children()
  472.                 ->arrayNode('exception_to_status')
  473.                     ->defaultValue([
  474.                         SerializerExceptionInterface::class => Response::HTTP_BAD_REQUEST,
  475.                         InvalidArgumentException::class => Response::HTTP_BAD_REQUEST,
  476.                         FilterValidationException::class => Response::HTTP_BAD_REQUEST,
  477.                         OptimisticLockException::class => Response::HTTP_CONFLICT,
  478.                     ])
  479.                     ->info('The list of exceptions mapped to their HTTP status code.')
  480.                     ->normalizeKeys(false)
  481.                     ->useAttributeAsKey('exception_class')
  482.                     ->prototype('integer')->end()
  483.                     ->validate()
  484.                         ->ifArray()
  485.                         ->then(static function (array $exceptionToStatus): array {
  486.                             foreach ($exceptionToStatus as $httpStatusCode) {
  487.                                 if ($httpStatusCode 100 || $httpStatusCode >= 600) {
  488.                                     throw new InvalidConfigurationException(sprintf('The HTTP status code "%s" is not valid.'$httpStatusCode));
  489.                                 }
  490.                             }
  491.                             return $exceptionToStatus;
  492.                         })
  493.                     ->end()
  494.                 ->end()
  495.             ->end();
  496.     }
  497.     private function addFormatSection(ArrayNodeDefinition $rootNodestring $key, array $defaultValue): void
  498.     {
  499.         $rootNode
  500.             ->children()
  501.                 ->arrayNode($key)
  502.                     ->defaultValue($defaultValue)
  503.                     ->info('The list of enabled formats. The first one will be the default.')
  504.                     ->normalizeKeys(false)
  505.                     ->useAttributeAsKey('format')
  506.                     ->beforeNormalization()
  507.                         ->ifArray()
  508.                         ->then(static function ($v) {
  509.                             foreach ($v as $format => $value) {
  510.                                 if (isset($value['mime_types'])) {
  511.                                     continue;
  512.                                 }
  513.                                 $v[$format] = ['mime_types' => $value];
  514.                             }
  515.                             return $v;
  516.                         })
  517.                     ->end()
  518.                     ->prototype('array')
  519.                         ->children()
  520.                             ->arrayNode('mime_types')->prototype('scalar')->end()->end()
  521.                         ->end()
  522.                     ->end()
  523.                 ->end()
  524.             ->end();
  525.     }
  526.     private function addDefaultsSection(ArrayNodeDefinition $rootNode): void
  527.     {
  528.         $nameConverter = new CamelCaseToSnakeCaseNameConverter();
  529.         $defaultsNode $rootNode->children()->arrayNode('defaults');
  530.         $defaultsNode
  531.             ->ignoreExtraKeys(false)
  532.             ->beforeNormalization()
  533.             ->always(static function (array $defaults) use ($nameConverter): array {
  534.                 $normalizedDefaults = [];
  535.                 foreach ($defaults as $option => $value) {
  536.                     $option $nameConverter->normalize($option);
  537.                     $normalizedDefaults[$option] = $value;
  538.                 }
  539.                 return $normalizedDefaults;
  540.             });
  541.         $this->defineDefault($defaultsNode, new \ReflectionClass(ApiResource::class), $nameConverter);
  542.         $this->defineDefault($defaultsNode, new \ReflectionClass(Put::class), $nameConverter);
  543.         $this->defineDefault($defaultsNode, new \ReflectionClass(Post::class), $nameConverter);
  544.     }
  545.     private function addMakerSection(ArrayNodeDefinition $rootNode): void
  546.     {
  547.         $rootNode
  548.             ->children()
  549.                 ->arrayNode('maker')
  550.                     ->{class_exists(MakerBundle::class) ? 'canBeDisabled' 'canBeEnabled'}()
  551.                 ->end()
  552.             ->end();
  553.     }
  554.     private function defineDefault(ArrayNodeDefinition $defaultsNode\ReflectionClass $reflectionClassCamelCaseToSnakeCaseNameConverter $nameConverter)
  555.     {
  556.         foreach ($reflectionClass->getConstructor()->getParameters() as $parameter) {
  557.             $defaultsNode->children()->variableNode($nameConverter->normalize($parameter->getName()));
  558.         }
  559.     }
  560. }