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

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the API Platform project.
  4.  *
  5.  * (c) Kévin Dunglas <[email protected]>
  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\Core\Bridge\Symfony\Bundle\DependencyInjection;
  12. use ApiPlatform\Core\Bridge\Elasticsearch\Metadata\Document\DocumentMetadata;
  13. use ApiPlatform\Core\Exception\FilterValidationException;
  14. use ApiPlatform\Core\Exception\InvalidArgumentException;
  15. use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
  16. use Doctrine\Bundle\MongoDBBundle\DoctrineMongoDBBundle;
  17. use Doctrine\ORM\OptimisticLockException;
  18. use Doctrine\ORM\Version as DoctrineOrmVersion;
  19. use Elasticsearch\Client as ElasticsearchClient;
  20. use FOS\UserBundle\FOSUserBundle;
  21. use GraphQL\GraphQL;
  22. use Symfony\Bundle\FullStack;
  23. use Symfony\Bundle\MercureBundle\MercureBundle;
  24. use Symfony\Bundle\TwigBundle\TwigBundle;
  25. use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
  26. use Symfony\Component\Config\Definition\Builder\TreeBuilder;
  27. use Symfony\Component\Config\Definition\ConfigurationInterface;
  28. use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
  29. use Symfony\Component\HttpFoundation\Response;
  30. use Symfony\Component\Messenger\MessageBusInterface;
  31. use Symfony\Component\Serializer\Exception\ExceptionInterface as SerializerExceptionInterface;
  32. /**
  33.  * The configuration of the bundle.
  34.  *
  35.  * @author Kévin Dunglas <[email protected]>
  36.  * @author Baptiste Meyer <[email protected]>
  37.  */
  38. final class Configuration implements ConfigurationInterface
  39. {
  40.     /**
  41.      * {@inheritdoc}
  42.      */
  43.     public function getConfigTreeBuilder()
  44.     {
  45.         if (method_exists(TreeBuilder::class, 'getRootNode')) {
  46.             $treeBuilder = new TreeBuilder('api_platform');
  47.             $rootNode $treeBuilder->getRootNode();
  48.         } else {
  49.             $treeBuilder = new TreeBuilder();
  50.             $rootNode $treeBuilder->root('api_platform');
  51.         }
  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.                 ->scalarNode('default_operation_path_resolver')
  80.                     ->defaultValue('api_platform.operation_path_resolver.underscore')
  81.                     ->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.')
  82.                     ->info('Specify the default operation path resolver to use for generating resources operations path.')
  83.                 ->end()
  84.                 ->scalarNode('name_converter')->defaultNull()->info('Specify a name converter to use.')->end()
  85.                 ->scalarNode('path_segment_name_generator')->defaultValue('api_platform.path_segment_name_generator.underscore')->info('Specify a path name generator to use.')->end()
  86.                 ->booleanNode('allow_plain_identifiers')->defaultFalse()->info('Allow plain identifiers, for example "id" instead of "@id" when denormalizing a relation.')->end()
  87.                 ->arrayNode('validator')
  88.                     ->addDefaultsIfNotSet()
  89.                     ->children()
  90.                         ->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()
  91.                     ->end()
  92.                 ->end()
  93.                 ->arrayNode('eager_loading')
  94.                     ->canBeDisabled()
  95.                     ->addDefaultsIfNotSet()
  96.                     ->children()
  97.                         ->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()
  98.                         ->integerNode('max_joins')->defaultValue(30)->info('Max number of joined relations before EagerLoading throws a RuntimeException')->end()
  99.                         ->booleanNode('force_eager')->defaultTrue()->info('Force join on every relation. If disabled, it will only join relations having the EAGER fetch mode.')->end()
  100.                     ->end()
  101.                 ->end()
  102.                 ->booleanNode('enable_fos_user')->defaultValue(class_exists(FOSUserBundle::class))->info('Enable the FOSUserBundle integration.')->end()
  103.                 ->booleanNode('enable_nelmio_api_doc')
  104.                     ->defaultFalse()
  105.                     ->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.')
  106.                     ->info('Enable the NelmioApiDocBundle integration.')
  107.                 ->end()
  108.                 ->booleanNode('enable_swagger')->defaultTrue()->info('Enable the Swagger documentation and export.')->end()
  109.                 ->booleanNode('enable_swagger_ui')->defaultValue(class_exists(TwigBundle::class))->info('Enable Swagger UI')->end()
  110.                 ->booleanNode('enable_re_doc')->defaultValue(class_exists(TwigBundle::class))->info('Enable ReDoc')->end()
  111.                 ->booleanNode('enable_entrypoint')->defaultTrue()->info('Enable the entrypoint')->end()
  112.                 ->booleanNode('enable_docs')->defaultTrue()->info('Enable the docs')->end()
  113.                 ->booleanNode('enable_profiler')->defaultTrue()->info('Enable the data collector and the WebProfilerBundle integration.')->end()
  114.                 ->arrayNode('collection')
  115.                     ->addDefaultsIfNotSet()
  116.                     ->children()
  117.                         ->scalarNode('exists_parameter_name')->defaultValue('exists')->cannotBeEmpty()->info('The name of the query parameter to filter on nullable field values.')->end()
  118.                         ->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
  119.                         ->scalarNode('order_parameter_name')->defaultValue('order')->cannotBeEmpty()->info('The name of the query parameter to order results.')->end()
  120.                         ->arrayNode('pagination')
  121.                             ->canBeDisabled()
  122.                             ->addDefaultsIfNotSet()
  123.                             ->children()
  124.                                 ->booleanNode('enabled')->defaultTrue()->info('To enable or disable pagination for all resource collections by default.')->end()
  125.                                 ->booleanNode('partial')->defaultFalse()->info('To enable or disable partial pagination for all resource collections by default when pagination is enabled.')->end()
  126.                                 ->booleanNode('client_enabled')->defaultFalse()->info('To allow the client to enable or disable the pagination.')->end()
  127.                                 ->booleanNode('client_items_per_page')->defaultFalse()->info('To allow the client to set the number of items per page.')->end()
  128.                                 ->booleanNode('client_partial')->defaultFalse()->info('To allow the client to enable or disable partial pagination.')->end()
  129.                                 ->integerNode('items_per_page')->defaultValue(30)->info('The default number of items per page.')->end()
  130.                                 ->integerNode('maximum_items_per_page')->defaultNull()->info('The maximum number of items per page.')->end()
  131.                                 ->scalarNode('page_parameter_name')->defaultValue('page')->cannotBeEmpty()->info('The default name of the parameter handling the page number.')->end()
  132.                                 ->scalarNode('enabled_parameter_name')->defaultValue('pagination')->cannotBeEmpty()->info('The name of the query parameter to enable or disable pagination.')->end()
  133.                                 ->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()
  134.                                 ->scalarNode('partial_parameter_name')->defaultValue('partial')->cannotBeEmpty()->info('The name of the query parameter to enable or disable partial pagination.')->end()
  135.                             ->end()
  136.                         ->end()
  137.                     ->end()
  138.                 ->end()
  139.                 ->arrayNode('mapping')
  140.                     ->addDefaultsIfNotSet()
  141.                     ->children()
  142.                         ->arrayNode('paths')
  143.                             ->prototype('scalar')->end()
  144.                         ->end()
  145.                     ->end()
  146.                 ->end()
  147.                 ->arrayNode('resource_class_directories')
  148.                     ->prototype('scalar')->end()
  149.                 ->end()
  150.             ->end();
  151.         $this->addDoctrineOrmSection($rootNode);
  152.         $this->addDoctrineMongoDbOdmSection($rootNode);
  153.         $this->addOAuthSection($rootNode);
  154.         $this->addGraphQlSection($rootNode);
  155.         $this->addSwaggerSection($rootNode);
  156.         $this->addHttpCacheSection($rootNode);
  157.         $this->addMercureSection($rootNode);
  158.         $this->addMessengerSection($rootNode);
  159.         $this->addElasticsearchSection($rootNode);
  160.         $this->addExceptionToStatusSection($rootNode);
  161.         $this->addFormatSection($rootNode'formats', [
  162.             'jsonld' => ['mime_types' => ['application/ld+json']],
  163.             'json' => ['mime_types' => ['application/json']], // Swagger support
  164.             'html' => ['mime_types' => ['text/html']], // Swagger UI support
  165.         ]);
  166.         $this->addFormatSection($rootNode'patch_formats', []);
  167.         $this->addFormatSection($rootNode'error_formats', [
  168.             'jsonproblem' => ['mime_types' => ['application/problem+json']],
  169.             'jsonld' => ['mime_types' => ['application/ld+json']],
  170.         ]);
  171.         return $treeBuilder;
  172.     }
  173.     private function addDoctrineOrmSection(ArrayNodeDefinition $rootNode): void
  174.     {
  175.         $rootNode
  176.             ->children()
  177.                 ->arrayNode('doctrine')
  178.                     ->{class_exists(DoctrineBundle::class) && class_exists(DoctrineOrmVersion::class) ? 'canBeDisabled' 'canBeEnabled'}()
  179.                 ->end()
  180.             ->end();
  181.     }
  182.     private function addDoctrineMongoDbOdmSection(ArrayNodeDefinition $rootNode): void
  183.     {
  184.         $rootNode
  185.             ->children()
  186.                 ->arrayNode('doctrine_mongodb_odm')
  187.                     ->{class_exists(DoctrineMongoDBBundle::class) ? 'canBeDisabled' 'canBeEnabled'}()
  188.                 ->end()
  189.             ->end();
  190.     }
  191.     private function addOAuthSection(ArrayNodeDefinition $rootNode): void
  192.     {
  193.         $rootNode
  194.             ->children()
  195.                 ->arrayNode('oauth')
  196.                     ->canBeEnabled()
  197.                     ->addDefaultsIfNotSet()
  198.                     ->children()
  199.                         ->scalarNode('clientId')->defaultValue('')->info('The oauth client id.')->end()
  200.                         ->scalarNode('clientSecret')->defaultValue('')->info('The oauth client secret.')->end()
  201.                         ->scalarNode('type')->defaultValue('oauth2')->info('The oauth client secret.')->end()
  202.                         ->scalarNode('flow')->defaultValue('application')->info('The oauth flow grant type.')->end()
  203.                         ->scalarNode('tokenUrl')->defaultValue('/oauth/v2/token')->info('The oauth token url.')->end()
  204.                         ->scalarNode('authorizationUrl')->defaultValue('/oauth/v2/auth')->info('The oauth authentication url.')->end()
  205.                         ->arrayNode('scopes')
  206.                             ->prototype('scalar')->end()
  207.                         ->end()
  208.                     ->end()
  209.                 ->end()
  210.             ->end();
  211.     }
  212.     private function addGraphQlSection(ArrayNodeDefinition $rootNode): void
  213.     {
  214.         $rootNode
  215.             ->children()
  216.                 ->arrayNode('graphql')
  217.                     ->{class_exists(GraphQL::class) ? 'canBeDisabled' 'canBeEnabled'}()
  218.                     ->addDefaultsIfNotSet()
  219.                     ->children()
  220.                         ->scalarNode('default_ide')->defaultValue('graphiql')->end()
  221.                         ->arrayNode('graphiql')
  222.                             ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' 'canBeEnabled'}()
  223.                         ->end()
  224.                         ->arrayNode('graphql_playground')
  225.                             ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' 'canBeEnabled'}()
  226.                         ->end()
  227.                         ->scalarNode('nesting_separator')->defaultValue('_')->info('The separator to use to filter nested fields.')->end()
  228.                         ->arrayNode('collection')
  229.                             ->addDefaultsIfNotSet()
  230.                             ->children()
  231.                                 ->arrayNode('pagination')
  232.                                     ->canBeDisabled()
  233.                                 ->end()
  234.                             ->end()
  235.                         ->end()
  236.                     ->end()
  237.                 ->end()
  238.             ->end();
  239.     }
  240.     private function addSwaggerSection(ArrayNodeDefinition $rootNode): void
  241.     {
  242.         $defaultVersions = [23];
  243.         $rootNode
  244.             ->children()
  245.                 ->arrayNode('swagger')
  246.                     ->addDefaultsIfNotSet()
  247.                     ->children()
  248.                         ->arrayNode('versions')
  249.                             ->info('The active versions of OpenAPI to be exported or used in the swagger_ui. The first value is the default.')
  250.                             ->defaultValue($defaultVersions)
  251.                             ->beforeNormalization()
  252.                                 ->always(static function ($v) {
  253.                                     if (!\is_array($v)) {
  254.                                         $v = [$v];
  255.                                     }
  256.                                     foreach ($v as &$version) {
  257.                                         $version = (int) $version;
  258.                                     }
  259.                                     return $v;
  260.                                 })
  261.                             ->end()
  262.                             ->validate()
  263.                                 ->ifTrue(function ($v) use ($defaultVersions) {
  264.                                     return $v !== array_intersect($v$defaultVersions);
  265.                                 })
  266.                                 ->thenInvalid(sprintf('Only the versions %s are supported. Got %s.'implode(' and '$defaultVersions), '%s'))
  267.                             ->end()
  268.                             ->prototype('scalar')->end()
  269.                         ->end()
  270.                         ->arrayNode('api_keys')
  271.                             ->prototype('array')
  272.                                 ->children()
  273.                                     ->scalarNode('name')
  274.                                         ->info('The name of the header or query parameter containing the api key.')
  275.                                     ->end()
  276.                                     ->enumNode('type')
  277.                                         ->info('Whether the api key should be a query parameter or a header.')
  278.                                         ->values(['query''header'])
  279.                                     ->end()
  280.                                 ->end()
  281.                             ->end()
  282.                         ->end()
  283.                     ->end()
  284.                 ->end()
  285.             ->end();
  286.     }
  287.     private function addHttpCacheSection(ArrayNodeDefinition $rootNode): void
  288.     {
  289.         $rootNode
  290.             ->children()
  291.                 ->arrayNode('http_cache')
  292.                     ->addDefaultsIfNotSet()
  293.                     ->children()
  294.                         ->booleanNode('etag')->defaultTrue()->info('Automatically generate etags for API responses.')->end()
  295.                         ->integerNode('max_age')->defaultNull()->info('Default value for the response max age.')->end()
  296.                         ->integerNode('shared_max_age')->defaultNull()->info('Default value for the response shared (proxy) max age.')->end()
  297.                         ->arrayNode('vary')
  298.                             ->defaultValue(['Accept'])
  299.                             ->prototype('scalar')->end()
  300.                             ->info('Default values of the "Vary" HTTP header.')
  301.                         ->end()
  302.                         ->booleanNode('public')->defaultNull()->info('To make all responses public by default.')->end()
  303.                         ->arrayNode('invalidation')
  304.                             ->info('Enable the tags-based cache invalidation system.')
  305.                             ->canBeEnabled()
  306.                             ->children()
  307.                                 ->arrayNode('varnish_urls')
  308.                                     ->defaultValue([])
  309.                                     ->prototype('scalar')->end()
  310.                                     ->info('URLs of the Varnish servers to purge using cache tags when a resource is updated.')
  311.                                 ->end()
  312.                                 ->variableNode('request_options')
  313.                                     ->defaultValue([])
  314.                                     ->validate()
  315.                                         ->ifTrue(function ($v) { return false === \is_array($v); })
  316.                                         ->thenInvalid('The request_options parameter must be an array.')
  317.                                     ->end()
  318.                                     ->info('To pass options to the client charged with the request.')
  319.                                 ->end()
  320.                             ->end()
  321.                         ->end()
  322.                     ->end()
  323.                 ->end()
  324.             ->end();
  325.     }
  326.     private function addMercureSection(ArrayNodeDefinition $rootNode): void
  327.     {
  328.         $rootNode
  329.             ->children()
  330.                 ->arrayNode('mercure')
  331.                     ->{class_exists(MercureBundle::class) ? 'canBeDisabled' 'canBeEnabled'}()
  332.                     ->children()
  333.                         ->scalarNode('hub_url')
  334.                             ->defaultNull()
  335.                             ->info('The URL sent in the Link HTTP header. If not set, will default to the URL for MercureBundle\'s default hub.')
  336.                         ->end()
  337.                     ->end()
  338.                 ->end()
  339.             ->end();
  340.     }
  341.     private function addMessengerSection(ArrayNodeDefinition $rootNode): void
  342.     {
  343.         $rootNode
  344.             ->children()
  345.                 ->arrayNode('messenger')
  346.                     ->{!class_exists(FullStack::class) && interface_exists(MessageBusInterface::class) ? 'canBeDisabled' 'canBeEnabled'}()
  347.                 ->end()
  348.             ->end();
  349.     }
  350.     private function addElasticsearchSection(ArrayNodeDefinition $rootNode): void
  351.     {
  352.         $rootNode
  353.             ->children()
  354.                 ->arrayNode('elasticsearch')
  355.                     ->canBeEnabled()
  356.                     ->addDefaultsIfNotSet()
  357.                     ->children()
  358.                         ->booleanNode('enabled')
  359.                             ->defaultFalse()
  360.                             ->validate()
  361.                                 ->ifTrue()
  362.                                 ->then(static function (bool $v): bool {
  363.                                     if (!class_exists(ElasticsearchClient::class)) {
  364.                                         throw new InvalidConfigurationException('The elasticsearch/elasticsearch package is required for Elasticsearch support.');
  365.                                     }
  366.                                     return $v;
  367.                                 })
  368.                             ->end()
  369.                         ->end()
  370.                         ->arrayNode('hosts')
  371.                             ->beforeNormalization()->castToArray()->end()
  372.                             ->defaultValue([])
  373.                             ->prototype('scalar')->end()
  374.                         ->end()
  375.                         ->arrayNode('mapping')
  376.                             ->normalizeKeys(false)
  377.                             ->useAttributeAsKey('resource_class')
  378.                             ->prototype('array')
  379.                                 ->children()
  380.                                     ->scalarNode('index')->defaultNull()->end()
  381.                                     ->scalarNode('type')->defaultValue(DocumentMetadata::DEFAULT_TYPE)->end()
  382.                                 ->end()
  383.                             ->end()
  384.                         ->end()
  385.                     ->end()
  386.                 ->end()
  387.             ->end();
  388.     }
  389.     /**
  390.      * @throws InvalidConfigurationException
  391.      */
  392.     private function addExceptionToStatusSection(ArrayNodeDefinition $rootNode): void
  393.     {
  394.         $rootNode
  395.             ->children()
  396.                 ->arrayNode('exception_to_status')
  397.                     ->defaultValue([
  398.                         SerializerExceptionInterface::class => Response::HTTP_BAD_REQUEST,
  399.                         InvalidArgumentException::class => Response::HTTP_BAD_REQUEST,
  400.                         FilterValidationException::class => Response::HTTP_BAD_REQUEST,
  401.                         OptimisticLockException::class => Response::HTTP_CONFLICT,
  402.                     ])
  403.                     ->info('The list of exceptions mapped to their HTTP status code.')
  404.                     ->normalizeKeys(false)
  405.                     ->useAttributeAsKey('exception_class')
  406.                     ->beforeNormalization()
  407.                         ->ifArray()
  408.                         ->then(function (array $exceptionToStatus) {
  409.                             foreach ($exceptionToStatus as &$httpStatusCode) {
  410.                                 if (\is_int($httpStatusCode)) {
  411.                                     continue;
  412.                                 }
  413.                                 if (\defined($httpStatusCodeConstant sprintf('%s::%s'Response::class, $httpStatusCode))) {
  414.                                     @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").'$httpStatusCodeResponse::class, $httpStatusCodeConstant), E_USER_DEPRECATED);
  415.                                     $httpStatusCode = \constant($httpStatusCodeConstant);
  416.                                 }
  417.                             }
  418.                             return $exceptionToStatus;
  419.                         })
  420.                     ->end()
  421.                     ->prototype('integer')->end()
  422.                     ->validate()
  423.                         ->ifArray()
  424.                         ->then(function (array $exceptionToStatus) {
  425.                             foreach ($exceptionToStatus as $httpStatusCode) {
  426.                                 if ($httpStatusCode 100 || $httpStatusCode >= 600) {
  427.                                     throw new InvalidConfigurationException(sprintf('The HTTP status code "%s" is not valid.'$httpStatusCode));
  428.                                 }
  429.                             }
  430.                             return $exceptionToStatus;
  431.                         })
  432.                     ->end()
  433.                 ->end()
  434.             ->end();
  435.     }
  436.     private function addFormatSection(ArrayNodeDefinition $rootNodestring $key, array $defaultValue): void
  437.     {
  438.         $rootNode
  439.             ->children()
  440.                 ->arrayNode($key)
  441.                     ->defaultValue($defaultValue)
  442.                     ->info('The list of enabled formats. The first one will be the default.')
  443.                     ->normalizeKeys(false)
  444.                     ->useAttributeAsKey('format')
  445.                     ->beforeNormalization()
  446.                         ->ifArray()
  447.                         ->then(function ($v) {
  448.                             foreach ($v as $format => $value) {
  449.                                 if (isset($value['mime_types'])) {
  450.                                     continue;
  451.                                 }
  452.                                 $v[$format] = ['mime_types' => $value];
  453.                             }
  454.                             return $v;
  455.                         })
  456.                     ->end()
  457.                     ->prototype('array')
  458.                         ->children()
  459.                             ->arrayNode('mime_types')->prototype('scalar')->end()->end()
  460.                         ->end()
  461.                     ->end()
  462.                 ->end()
  463.             ->end();
  464.     }
  465. }