• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 11176269767

04 Oct 2024 08:02AM UTC coverage: 7.725% (+0.3%) from 7.441%
11176269767

push

github

soyuka
chore: remove useless require-dev

12744 of 164973 relevant lines covered (7.72%)

27.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.34
/src/Symfony/Bundle/DependencyInjection/Configuration.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
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\Symfony\Bundle\DependencyInjection;
15

16
use ApiPlatform\Doctrine\Common\Filter\OrderFilterInterface;
17
use ApiPlatform\Elasticsearch\State\Options;
18
use ApiPlatform\Metadata\ApiResource;
19
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
20
use ApiPlatform\Metadata\Post;
21
use ApiPlatform\Metadata\Put;
22
use ApiPlatform\Symfony\Controller\MainController;
23
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
24
use Doctrine\Bundle\MongoDBBundle\DoctrineMongoDBBundle;
25
use Doctrine\ORM\EntityManagerInterface;
26
use Doctrine\ORM\OptimisticLockException;
27
use GraphQL\GraphQL;
28
use Symfony\Bundle\FullStack;
29
use Symfony\Bundle\MakerBundle\MakerBundle;
30
use Symfony\Bundle\MercureBundle\MercureBundle;
31
use Symfony\Bundle\TwigBundle\TwigBundle;
32
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
33
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
34
use Symfony\Component\Config\Definition\ConfigurationInterface;
35
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
36
use Symfony\Component\HttpFoundation\Response;
37
use Symfony\Component\Messenger\MessageBusInterface;
38
use Symfony\Component\Serializer\Exception\ExceptionInterface as SerializerExceptionInterface;
39
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
40

41
/**
42
 * The configuration of the bundle.
43
 *
44
 * @author Kévin Dunglas <dunglas@gmail.com>
45
 * @author Baptiste Meyer <baptiste.meyer@gmail.com>
46
 */
47
final class Configuration implements ConfigurationInterface
48
{
49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function getConfigTreeBuilder(): TreeBuilder
53
    {
54
        $treeBuilder = new TreeBuilder('api_platform');
57✔
55
        $rootNode = $treeBuilder->getRootNode();
57✔
56

57
        $rootNode
57✔
58
            ->beforeNormalization()
57✔
59
                ->ifTrue(static function ($v) {
57✔
60
                    return false === ($v['enable_swagger'] ?? null);
57✔
61
                })
57✔
62
                ->then(static function ($v) {
57✔
63
                    $v['swagger']['versions'] = [];
3✔
64

65
                    return $v;
3✔
66
                })
57✔
67
            ->end()
57✔
68
            ->children()
57✔
69
                ->scalarNode('title')
57✔
70
                    ->info('The title of the API.')
57✔
71
                    ->cannotBeEmpty()
57✔
72
                    ->defaultValue('')
57✔
73
                ->end()
57✔
74
                ->scalarNode('description')
57✔
75
                    ->info('The description of the API.')
57✔
76
                    ->cannotBeEmpty()
57✔
77
                    ->defaultValue('')
57✔
78
                ->end()
57✔
79
                ->scalarNode('version')
57✔
80
                    ->info('The version of the API.')
57✔
81
                    ->cannotBeEmpty()
57✔
82
                    ->defaultValue('0.0.0')
57✔
83
                ->end()
57✔
84
                ->booleanNode('show_webby')->defaultTrue()->info('If true, show Webby on the documentation page')->end()
57✔
85
                ->booleanNode('use_symfony_listeners')->defaultFalse()->info(sprintf('Uses Symfony event listeners instead of the %s.', MainController::class))->end()
57✔
86
                ->scalarNode('name_converter')->defaultNull()->info('Specify a name converter to use.')->end()
57✔
87
                ->scalarNode('asset_package')->defaultNull()->info('Specify an asset package name to use.')->end()
57✔
88
                ->scalarNode('path_segment_name_generator')->defaultValue('api_platform.metadata.path_segment_name_generator.underscore')->info('Specify a path name generator to use.')->end()
57✔
89
                ->scalarNode('inflector')->defaultValue('api_platform.metadata.inflector')->info('Specify an inflector to use.')->end()
57✔
90
                ->arrayNode('validator')
57✔
91
                    ->addDefaultsIfNotSet()
57✔
92
                    ->children()
57✔
93
                        ->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()
57✔
94
                        ->booleanNode('query_parameter_validation')->defaultValue(true)->end()
57✔
95
                    ->end()
57✔
96
                ->end()
57✔
97
                ->arrayNode('eager_loading')
57✔
98
                    ->canBeDisabled()
57✔
99
                    ->addDefaultsIfNotSet()
57✔
100
                    ->children()
57✔
101
                        ->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()
57✔
102
                        ->integerNode('max_joins')->defaultValue(30)->info('Max number of joined relations before EagerLoading throws a RuntimeException')->end()
57✔
103
                        ->booleanNode('force_eager')->defaultTrue()->info('Force join on every relation. If disabled, it will only join relations having the EAGER fetch mode.')->end()
57✔
104
                    ->end()
57✔
105
                ->end()
57✔
106
                ->booleanNode('handle_symfony_errors')->defaultFalse()->info('Allows to handle symfony exceptions.')->end()
57✔
107
                ->booleanNode('enable_swagger')->defaultTrue()->info('Enable the Swagger documentation and export.')->end()
57✔
108
                ->booleanNode('enable_swagger_ui')->defaultValue(class_exists(TwigBundle::class))->info('Enable Swagger UI')->end()
57✔
109
                ->booleanNode('enable_re_doc')->defaultValue(class_exists(TwigBundle::class))->info('Enable ReDoc')->end()
57✔
110
                ->booleanNode('enable_entrypoint')->defaultTrue()->info('Enable the entrypoint')->end()
57✔
111
                ->booleanNode('enable_docs')->defaultTrue()->info('Enable the docs')->end()
57✔
112
                ->booleanNode('enable_profiler')->defaultTrue()->info('Enable the data collector and the WebProfilerBundle integration.')->end()
57✔
113
                ->booleanNode('enable_link_security')->defaultFalse()->info('Enable security for Links (sub resources)')->end()
57✔
114
                ->arrayNode('collection')
57✔
115
                    ->addDefaultsIfNotSet()
57✔
116
                    ->children()
57✔
117
                        ->scalarNode('exists_parameter_name')->defaultValue('exists')->cannotBeEmpty()->info('The name of the query parameter to filter on nullable field values.')->end()
57✔
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
57✔
119
                        ->scalarNode('order_parameter_name')->defaultValue('order')->cannotBeEmpty()->info('The name of the query parameter to order results.')->end()
57✔
120
                        ->enumNode('order_nulls_comparison')->defaultNull()->values(interface_exists(OrderFilterInterface::class) ? array_merge(array_keys(OrderFilterInterface::NULLS_DIRECTION_MAP), [null]) : [null])->info('The nulls comparison strategy.')->end()
57✔
121
                        ->arrayNode('pagination')
57✔
122
                            ->canBeDisabled()
57✔
123
                            ->addDefaultsIfNotSet()
57✔
124
                            ->children()
57✔
125
                                ->scalarNode('page_parameter_name')->defaultValue('page')->cannotBeEmpty()->info('The default name of the parameter handling the page number.')->end()
57✔
126
                                ->scalarNode('enabled_parameter_name')->defaultValue('pagination')->cannotBeEmpty()->info('The name of the query parameter to enable or disable pagination.')->end()
57✔
127
                                ->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()
57✔
128
                                ->scalarNode('partial_parameter_name')->defaultValue('partial')->cannotBeEmpty()->info('The name of the query parameter to enable or disable partial pagination.')->end()
57✔
129
                            ->end()
57✔
130
                        ->end()
57✔
131
                    ->end()
57✔
132
                ->end()
57✔
133
                ->arrayNode('mapping')
57✔
134
                    ->addDefaultsIfNotSet()
57✔
135
                    ->children()
57✔
136
                        ->arrayNode('paths')
57✔
137
                            ->prototype('scalar')->end()
57✔
138
                        ->end()
57✔
139
                    ->end()
57✔
140
                ->end()
57✔
141
                ->arrayNode('resource_class_directories')
57✔
142
                    ->prototype('scalar')->end()
57✔
143
                ->end()
57✔
144
                ->arrayNode('serializer')
57✔
145
                    ->addDefaultsIfNotSet()
57✔
146
                    ->children()
57✔
147
                        ->booleanNode('hydra_prefix')->defaultFalse()->info('Use the "hydra:" prefix.')->end()
57✔
148
                    ->end()
57✔
149
                ->end()
57✔
150
            ->end();
57✔
151

152
        $this->addDoctrineOrmSection($rootNode);
57✔
153
        $this->addDoctrineMongoDbOdmSection($rootNode);
57✔
154
        $this->addOAuthSection($rootNode);
57✔
155
        $this->addGraphQlSection($rootNode);
57✔
156
        $this->addSwaggerSection($rootNode);
57✔
157
        $this->addHttpCacheSection($rootNode);
57✔
158
        $this->addMercureSection($rootNode);
57✔
159
        $this->addMessengerSection($rootNode);
57✔
160
        $this->addElasticsearchSection($rootNode);
57✔
161
        $this->addOpenApiSection($rootNode);
57✔
162
        $this->addMakerSection($rootNode);
57✔
163

164
        $this->addExceptionToStatusSection($rootNode);
57✔
165

166
        $this->addFormatSection($rootNode, 'formats', [
57✔
167
            'jsonld' => ['mime_types' => ['application/ld+json']]
57✔
168
        ]);
57✔
169
        $this->addFormatSection($rootNode, 'patch_formats', [
57✔
170
            'json' => ['mime_types' => ['application/merge-patch+json']],
57✔
171
        ]);
57✔
172
        $this->addFormatSection($rootNode, 'docs_formats', [
57✔
173
            'jsonopenapi' => ['mime_types' => ['application/vnd.openapi+json']],
57✔
174
            'yamlopenapi' => ['mime_types' => ['application/vnd.openapi+yaml']],
57✔
175
            'jsonld' => ['mime_types' => ['application/ld+json']],
57✔
176
            'html' => ['mime_types' => ['text/html']],
57✔
177
        ]);
57✔
178
        $this->addFormatSection($rootNode, 'error_formats', [
57✔
179
            'jsonld' => ['mime_types' => ['application/ld+json']],
57✔
180
            'jsonproblem' => ['mime_types' => ['application/problem+json']],
57✔
181
            'json' => ['mime_types' => ['application/problem+json', 'application/json']],
57✔
182
        ]);
57✔
183
        $rootNode
57✔
184
            ->children()
57✔
185
                ->arrayNode('jsonschema_formats')
57✔
186
                    ->scalarPrototype()->end()
57✔
187
                    ->defaultValue([])
57✔
188
                    ->info('The JSON formats to compute the JSON Schemas for.')
57✔
189
                ->end()
57✔
190
            ->end();
57✔
191

192
        $this->addDefaultsSection($rootNode);
57✔
193

194
        return $treeBuilder;
57✔
195
    }
196

197
    private function addDoctrineOrmSection(ArrayNodeDefinition $rootNode): void
198
    {
199
        $rootNode
57✔
200
            ->children()
57✔
201
                ->arrayNode('doctrine')
57✔
202
                    ->{class_exists(DoctrineBundle::class) && interface_exists(EntityManagerInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
57✔
203
                ->end()
57✔
204
            ->end();
57✔
205
    }
206

207
    private function addDoctrineMongoDbOdmSection(ArrayNodeDefinition $rootNode): void
208
    {
209
        $rootNode
57✔
210
            ->children()
57✔
211
                ->arrayNode('doctrine_mongodb_odm')
57✔
212
                    ->{class_exists(DoctrineMongoDBBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
57✔
213
                ->end()
57✔
214
            ->end();
57✔
215
    }
216

217
    private function addOAuthSection(ArrayNodeDefinition $rootNode): void
218
    {
219
        $rootNode
57✔
220
            ->children()
57✔
221
                ->arrayNode('oauth')
57✔
222
                    ->canBeEnabled()
57✔
223
                    ->addDefaultsIfNotSet()
57✔
224
                    ->children()
57✔
225
                        ->scalarNode('clientId')->defaultValue('')->info('The oauth client id.')->end()
57✔
226
                        ->scalarNode('clientSecret')
57✔
227
                            ->defaultValue('')
57✔
228
                            ->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')
57✔
229
                        ->end()
57✔
230
                        ->booleanNode('pkce')->defaultFalse()->info('Enable the oauth PKCE.')->end()
57✔
231
                        ->scalarNode('type')->defaultValue('oauth2')->info('The oauth type.')->end()
57✔
232
                        ->scalarNode('flow')->defaultValue('application')->info('The oauth flow grant type.')->end()
57✔
233
                        ->scalarNode('tokenUrl')->defaultValue('')->info('The oauth token url.')->end()
57✔
234
                        ->scalarNode('authorizationUrl')->defaultValue('')->info('The oauth authentication url.')->end()
57✔
235
                        ->scalarNode('refreshUrl')->defaultValue('')->info('The oauth refresh url.')->end()
57✔
236
                        ->arrayNode('scopes')
57✔
237
                            ->prototype('scalar')->end()
57✔
238
                        ->end()
57✔
239
                    ->end()
57✔
240
                ->end()
57✔
241
            ->end();
57✔
242
    }
243

244
    private function addGraphQlSection(ArrayNodeDefinition $rootNode): void
245
    {
246
        $rootNode
57✔
247
            ->children()
57✔
248
                ->arrayNode('graphql')
57✔
249
                    ->{class_exists(GraphQL::class) ? 'canBeDisabled' : 'canBeEnabled'}()
57✔
250
                    ->addDefaultsIfNotSet()
57✔
251
                    ->children()
57✔
252
                        ->scalarNode('default_ide')->defaultValue('graphiql')->end()
57✔
253
                        ->arrayNode('graphiql')
57✔
254
                            ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
57✔
255
                        ->end()
57✔
256
                        ->arrayNode('graphql_playground')
57✔
257
                            ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
57✔
258
                        ->end()
57✔
259
                        ->arrayNode('introspection')
57✔
260
                            ->canBeDisabled()
57✔
261
                        ->end()
57✔
262
                        ->scalarNode('nesting_separator')->defaultValue('_')->info('The separator to use to filter nested fields.')->end()
57✔
263
                        ->arrayNode('collection')
57✔
264
                            ->addDefaultsIfNotSet()
57✔
265
                            ->children()
57✔
266
                                ->arrayNode('pagination')
57✔
267
                                    ->canBeDisabled()
57✔
268
                                ->end()
57✔
269
                            ->end()
57✔
270
                        ->end()
57✔
271
                    ->end()
57✔
272
                ->end()
57✔
273
            ->end();
57✔
274
    }
275

276
    private function addSwaggerSection(ArrayNodeDefinition $rootNode): void
277
    {
278
        $supportedVersions = [3];
57✔
279

280
        $rootNode
57✔
281
            ->children()
57✔
282
                ->arrayNode('swagger')
57✔
283
                    ->addDefaultsIfNotSet()
57✔
284
                    ->children()
57✔
285
                        ->arrayNode('versions')
57✔
286
                            ->info('The active versions of OpenAPI to be exported or used in Swagger UI. The first value is the default.')
57✔
287
                            ->defaultValue($supportedVersions)
57✔
288
                            ->beforeNormalization()
57✔
289
                                ->always(static function ($v): array {
57✔
290
                                    if (!\is_array($v)) {
6✔
291
                                        $v = [$v];
×
292
                                    }
293

294
                                    foreach ($v as &$version) {
6✔
295
                                        $version = (int) $version;
3✔
296
                                    }
297

298
                                    return $v;
6✔
299
                                })
57✔
300
                            ->end()
57✔
301
                            ->validate()
57✔
302
                                ->ifTrue(static fn ($v): bool => $v !== array_intersect($v, $supportedVersions))
57✔
303
                                ->thenInvalid(sprintf('Only the versions %s are supported. Got %s.', implode(' and ', $supportedVersions), '%s'))
57✔
304
                            ->end()
57✔
305
                            ->prototype('scalar')->end()
57✔
306
                        ->end()
57✔
307
                        ->arrayNode('api_keys')
57✔
308
                            ->useAttributeAsKey('key')
57✔
309
                            ->validate()
57✔
310
                                ->ifTrue(static fn ($v): bool => (bool) array_filter(array_keys($v), fn ($item) => !preg_match('/^[a-zA-Z0-9._-]+$/', $item)))
57✔
311
                                ->thenInvalid('The api keys "key" is not valid according to the pattern enforced by OpenAPI 3.1 ^[a-zA-Z0-9._-]+$.')
57✔
312
                            ->end()
57✔
313
                            ->prototype('array')
57✔
314
                                ->children()
57✔
315
                                    ->scalarNode('name')
57✔
316
                                        ->info('The name of the header or query parameter containing the api key.')
57✔
317
                                    ->end()
57✔
318
                                    ->enumNode('type')
57✔
319
                                        ->info('Whether the api key should be a query parameter or a header.')
57✔
320
                                        ->values(['query', 'header'])
57✔
321
                                    ->end()
57✔
322
                                ->end()
57✔
323
                            ->end()
57✔
324
                        ->end()
57✔
325
                        ->variableNode('swagger_ui_extra_configuration')
57✔
326
                            ->defaultValue([])
57✔
327
                            ->validate()
57✔
328
                                ->ifTrue(static fn ($v): bool => false === \is_array($v))
57✔
329
                                ->thenInvalid('The swagger_ui_extra_configuration parameter must be an array.')
57✔
330
                            ->end()
57✔
331
                            ->info('To pass extra configuration to Swagger UI, like docExpansion or filter.')
57✔
332
                        ->end()
57✔
333
                    ->end()
57✔
334
                ->end()
57✔
335
            ->end();
57✔
336
    }
337

338
    private function addHttpCacheSection(ArrayNodeDefinition $rootNode): void
339
    {
340
        $rootNode
57✔
341
            ->children()
57✔
342
                ->arrayNode('http_cache')
57✔
343
                    ->addDefaultsIfNotSet()
57✔
344
                    ->children()
57✔
345
                        ->booleanNode('public')->defaultNull()->info('To make all responses public by default.')->end()
57✔
346
                        ->arrayNode('invalidation')
57✔
347
                            ->info('Enable the tags-based cache invalidation system.')
57✔
348
                            ->canBeEnabled()
57✔
349
                            ->children()
57✔
350
                                ->arrayNode('varnish_urls')
57✔
351
                                    ->setDeprecated('api-platform/core', '3.0', 'The "varnish_urls" configuration is deprecated, use "urls" or "scoped_clients".')
57✔
352
                                    ->defaultValue([])
57✔
353
                                    ->prototype('scalar')->end()
57✔
354
                                    ->info('URLs of the Varnish servers to purge using cache tags when a resource is updated.')
57✔
355
                                ->end()
57✔
356
                                ->arrayNode('urls')
57✔
357
                                    ->defaultValue([])
57✔
358
                                    ->prototype('scalar')->end()
57✔
359
                                    ->info('URLs of the Varnish servers to purge using cache tags when a resource is updated.')
57✔
360
                                ->end()
57✔
361
                                ->arrayNode('scoped_clients')
57✔
362
                                    ->defaultValue([])
57✔
363
                                    ->prototype('scalar')->end()
57✔
364
                                    ->info('Service names of scoped client to use by the cache purger.')
57✔
365
                                ->end()
57✔
366
                                ->integerNode('max_header_length')
57✔
367
                                    ->defaultValue(7500)
57✔
368
                                    ->info('Max header length supported by the cache server.')
57✔
369
                                ->end()
57✔
370
                                ->variableNode('request_options')
57✔
371
                                    ->defaultValue([])
57✔
372
                                    ->validate()
57✔
373
                                        ->ifTrue(static fn ($v): bool => false === \is_array($v))
57✔
374
                                        ->thenInvalid('The request_options parameter must be an array.')
57✔
375
                                    ->end()
57✔
376
                                    ->info('To pass options to the client charged with the request.')
57✔
377
                                ->end()
57✔
378
                                ->scalarNode('purger')
57✔
379
                                    ->defaultValue('api_platform.http_cache.purger.varnish')
57✔
380
                                    ->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").')
57✔
381
                                ->end()
57✔
382
                                ->arrayNode('xkey')
57✔
383
                                    ->setDeprecated('api-platform/core', '3.0', 'The "xkey" configuration is deprecated, use your own purger to customize surrogate keys or the appropriate paramters.')
57✔
384
                                    ->addDefaultsIfNotSet()
57✔
385
                                    ->children()
57✔
386
                                        ->scalarNode('glue')
57✔
387
                                        ->defaultValue(' ')
57✔
388
                                        ->info('xkey glue between keys')
57✔
389
                                        ->end()
57✔
390
                                    ->end()
57✔
391
                                ->end()
57✔
392
                            ->end()
57✔
393
                        ->end()
57✔
394
                    ->end()
57✔
395
                ->end()
57✔
396
            ->end();
57✔
397
    }
398

399
    private function addMercureSection(ArrayNodeDefinition $rootNode): void
400
    {
401
        $rootNode
57✔
402
            ->children()
57✔
403
                ->arrayNode('mercure')
57✔
404
                    ->{class_exists(MercureBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
57✔
405
                    ->children()
57✔
406
                        ->scalarNode('hub_url')
57✔
407
                            ->defaultNull()
57✔
408
                            ->info('The URL sent in the Link HTTP header. If not set, will default to the URL for MercureBundle\'s default hub.')
57✔
409
                        ->end()
57✔
410
                        ->booleanNode('include_type')
57✔
411
                            ->defaultFalse()
57✔
412
                            ->info('Always include @type in updates (including delete ones).')
57✔
413
                        ->end()
57✔
414
                    ->end()
57✔
415
                ->end()
57✔
416
            ->end();
57✔
417
    }
418

419
    private function addMessengerSection(ArrayNodeDefinition $rootNode): void
420
    {
421
        $rootNode
57✔
422
            ->children()
57✔
423
                ->arrayNode('messenger')
57✔
424
                    ->{!class_exists(FullStack::class) && interface_exists(MessageBusInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
57✔
425
                ->end()
57✔
426
            ->end();
57✔
427
    }
428

429
    private function addElasticsearchSection(ArrayNodeDefinition $rootNode): void
430
    {
431
        $rootNode
57✔
432
            ->children()
57✔
433
                ->arrayNode('elasticsearch')
57✔
434
                    ->canBeEnabled()
57✔
435
                    ->addDefaultsIfNotSet()
57✔
436
                    ->children()
57✔
437
                        ->booleanNode('enabled')
57✔
438
                            ->defaultFalse()
57✔
439
                            ->validate()
57✔
440
                                ->ifTrue()
57✔
441
                                ->then(static function (bool $v): bool {
57✔
442
                                    if (!(class_exists(\Elasticsearch\Client::class) || class_exists(\Elastic\Elasticsearch\Client::class))) {
3✔
443
                                        throw new InvalidConfigurationException('The elasticsearch/elasticsearch package is required for Elasticsearch support.');
×
444
                                    }
445

446
                                    return $v;
3✔
447
                                })
57✔
448
                            ->end()
57✔
449
                        ->end()
57✔
450
                        ->arrayNode('hosts')
57✔
451
                            ->beforeNormalization()->castToArray()->end()
57✔
452
                            ->defaultValue([])
57✔
453
                            ->prototype('scalar')->end()
57✔
454
                        ->end()
57✔
455
                    ->end()
57✔
456
                ->end()
57✔
457
            ->end();
57✔
458
    }
459

460
    private function addOpenApiSection(ArrayNodeDefinition $rootNode): void
461
    {
462
        $rootNode
57✔
463
            ->children()
57✔
464
                ->arrayNode('openapi')
57✔
465
                    ->addDefaultsIfNotSet()
57✔
466
                        ->children()
57✔
467
                        ->arrayNode('contact')
57✔
468
                        ->addDefaultsIfNotSet()
57✔
469
                            ->children()
57✔
470
                                ->scalarNode('name')->defaultNull()->info('The identifying name of the contact person/organization.')->end()
57✔
471
                                ->scalarNode('url')->defaultNull()->info('The URL pointing to the contact information. MUST be in the format of a URL.')->end()
57✔
472
                                ->scalarNode('email')->defaultNull()->info('The email address of the contact person/organization. MUST be in the format of an email address.')->end()
57✔
473
                            ->end()
57✔
474
                        ->end()
57✔
475
                        ->scalarNode('termsOfService')->defaultNull()->info('A URL to the Terms of Service for the API. MUST be in the format of a URL.')->end()
57✔
476
                        ->arrayNode('license')
57✔
477
                        ->addDefaultsIfNotSet()
57✔
478
                            ->children()
57✔
479
                                ->scalarNode('name')->defaultNull()->info('The license name used for the API.')->end()
57✔
480
                                ->scalarNode('url')->defaultNull()->info('URL to the license used for the API. MUST be in the format of a URL.')->end()
57✔
481
                            ->end()
57✔
482
                        ->end()
57✔
483
                        ->variableNode('swagger_ui_extra_configuration')
57✔
484
                            ->defaultValue([])
57✔
485
                            ->validate()
57✔
486
                                ->ifTrue(static fn ($v): bool => false === \is_array($v))
57✔
487
                                ->thenInvalid('The swagger_ui_extra_configuration parameter must be an array.')
57✔
488
                            ->end()
57✔
489
                            ->info('To pass extra configuration to Swagger UI, like docExpansion or filter.')
57✔
490
                        ->end()
57✔
491
                        ->booleanNode('overrideResponses')->defaultTrue()->info('Whether API Platform adds automatic responses to the OpenAPI documentation.')
57✔
492
                    ->end()
57✔
493
                ->end()
57✔
494
            ->end();
57✔
495
    }
496

497
    /**
498
     * @throws InvalidConfigurationException
499
     */
500
    private function addExceptionToStatusSection(ArrayNodeDefinition $rootNode): void
501
    {
502
        $rootNode
57✔
503
            ->children()
57✔
504
                ->arrayNode('exception_to_status')
57✔
505
                    ->defaultValue([
57✔
506
                        SerializerExceptionInterface::class => Response::HTTP_BAD_REQUEST,
57✔
507
                        InvalidArgumentException::class => Response::HTTP_BAD_REQUEST,
57✔
508
                        OptimisticLockException::class => Response::HTTP_CONFLICT,
57✔
509
                    ])
57✔
510
                    ->info('The list of exceptions mapped to their HTTP status code.')
57✔
511
                    ->normalizeKeys(false)
57✔
512
                    ->useAttributeAsKey('exception_class')
57✔
513
                    ->prototype('integer')->end()
57✔
514
                    ->validate()
57✔
515
                        ->ifArray()
57✔
516
                        ->then(static function (array $exceptionToStatus): array {
57✔
517
                            foreach ($exceptionToStatus as $httpStatusCode) {
15✔
518
                                if ($httpStatusCode < 100 || $httpStatusCode >= 600) {
15✔
519
                                    throw new InvalidConfigurationException(sprintf('The HTTP status code "%s" is not valid.', $httpStatusCode));
12✔
520
                                }
521
                            }
522

523
                            return $exceptionToStatus;
3✔
524
                        })
57✔
525
                    ->end()
57✔
526
                ->end()
57✔
527
            ->end();
57✔
528
    }
529

530
    private function addFormatSection(ArrayNodeDefinition $rootNode, string $key, array $defaultValue): void
531
    {
532
        $rootNode
57✔
533
            ->children()
57✔
534
                ->arrayNode($key)
57✔
535
                    ->defaultValue($defaultValue)
57✔
536
                    ->info('The list of enabled formats. The first one will be the default.')
57✔
537
                    ->normalizeKeys(false)
57✔
538
                    ->useAttributeAsKey('format')
57✔
539
                    ->beforeNormalization()
57✔
540
                        ->ifArray()
57✔
541
                        ->then(static function ($v) {
57✔
542
                            foreach ($v as $format => $value) {
3✔
543
                                if (isset($value['mime_types'])) {
3✔
544
                                    continue;
×
545
                                }
546

547
                                $v[$format] = ['mime_types' => $value];
3✔
548
                            }
549

550
                            return $v;
3✔
551
                        })
57✔
552
                    ->end()
57✔
553
                    ->prototype('array')
57✔
554
                        ->children()
57✔
555
                            ->arrayNode('mime_types')->prototype('scalar')->end()->end()
57✔
556
                        ->end()
57✔
557
                    ->end()
57✔
558
                ->end()
57✔
559
            ->end();
57✔
560
    }
561

562
    private function addDefaultsSection(ArrayNodeDefinition $rootNode): void
563
    {
564
        $nameConverter = new CamelCaseToSnakeCaseNameConverter();
57✔
565
        $defaultsNode = $rootNode->children()->arrayNode('defaults');
57✔
566

567
        $defaultsNode
57✔
568
            ->ignoreExtraKeys(false)
57✔
569
            ->beforeNormalization()
57✔
570
            ->always(static function (array $defaults) use ($nameConverter): array {
57✔
571
                $normalizedDefaults = [];
3✔
572
                foreach ($defaults as $option => $value) {
3✔
573
                    $option = $nameConverter->normalize($option);
3✔
574
                    $normalizedDefaults[$option] = $value;
3✔
575
                }
576

577
                return $normalizedDefaults;
3✔
578
            });
57✔
579

580
        $this->defineDefault($defaultsNode, new \ReflectionClass(ApiResource::class), $nameConverter);
57✔
581
        $this->defineDefault($defaultsNode, new \ReflectionClass(Put::class), $nameConverter);
57✔
582
        $this->defineDefault($defaultsNode, new \ReflectionClass(Post::class), $nameConverter);
57✔
583
    }
584

585
    private function addMakerSection(ArrayNodeDefinition $rootNode): void
586
    {
587
        $rootNode
57✔
588
            ->children()
57✔
589
                ->arrayNode('maker')
57✔
590
                    ->{class_exists(MakerBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
57✔
591
                ->end()
57✔
592
            ->end();
57✔
593
    }
594

595
    private function defineDefault(ArrayNodeDefinition $defaultsNode, \ReflectionClass $reflectionClass, CamelCaseToSnakeCaseNameConverter $nameConverter): void
596
    {
597
        foreach ($reflectionClass->getConstructor()->getParameters() as $parameter) {
57✔
598
            $defaultsNode->children()->variableNode($nameConverter->normalize($parameter->getName()));
57✔
599
        }
600
    }
601
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc