Skip to content

Commit be98f4e

Browse files
soyukamauriau
and
mauriau
authored
feat(graphql): allow to configure max query depth and max query complexity (#6880)
Co-authored-by: mauriau <[email protected]>
1 parent d0a4427 commit be98f4e

File tree

9 files changed

+45
-6
lines changed

9 files changed

+45
-6
lines changed

src/GraphQl/Executor.php

+9-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
use GraphQL\Type\Schema;
1919
use GraphQL\Validator\DocumentValidator;
2020
use GraphQL\Validator\Rules\DisableIntrospection;
21+
use GraphQL\Validator\Rules\QueryComplexity;
22+
use GraphQL\Validator\Rules\QueryDepth;
2123

2224
/**
2325
* Wrapper for the GraphQL facade.
@@ -26,13 +28,19 @@
2628
*/
2729
final class Executor implements ExecutorInterface
2830
{
29-
public function __construct(private readonly bool $graphQlIntrospectionEnabled = true)
31+
public function __construct(private readonly bool $graphQlIntrospectionEnabled = true, private readonly int $maxQueryComplexity = 500, private readonly int $maxQueryDepth = 20)
3032
{
3133
DocumentValidator::addRule(
3234
new DisableIntrospection(
3335
$this->graphQlIntrospectionEnabled ? DisableIntrospection::DISABLED : DisableIntrospection::ENABLED
3436
)
3537
);
38+
39+
$queryComplexity = new QueryComplexity($this->maxQueryComplexity);
40+
DocumentValidator::addRule($queryComplexity);
41+
42+
$queryDepth = new QueryDepth($this->maxQueryDepth);
43+
DocumentValidator::addRule($queryDepth);
3644
}
3745

3846
/**

src/GraphQl/Tests/ExecutorTest.php

+18
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use ApiPlatform\GraphQl\Executor;
1717
use GraphQL\Validator\DocumentValidator;
1818
use GraphQL\Validator\Rules\DisableIntrospection;
19+
use GraphQL\Validator\Rules\QueryComplexity;
20+
use GraphQL\Validator\Rules\QueryDepth;
1921
use PHPUnit\Framework\TestCase;
2022

2123
/**
@@ -38,4 +40,20 @@ public function testDisableIntrospectionQuery(): void
3840
$expected = new DisableIntrospection(DisableIntrospection::ENABLED);
3941
$this->assertEquals($expected, DocumentValidator::getRule(DisableIntrospection::class));
4042
}
43+
44+
public function testChangeValueOfMaxQueryDepth(): void
45+
{
46+
$executor = new Executor(true, 20);
47+
48+
$expected = new QueryComplexity(20);
49+
$this->assertEquals($expected, DocumentValidator::getRule(QueryComplexity::class));
50+
}
51+
52+
public function testChangeValueOfMaxQueryComplexity(): void
53+
{
54+
$executor = new Executor(true, maxQueryDepth: 20);
55+
56+
$expected = new QueryDepth(20);
57+
$this->assertEquals($expected, DocumentValidator::getRule(QueryDepth::class));
58+
}
4159
}

src/Laravel/ApiPlatformProvider.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1294,7 +1294,7 @@ private function registerGraphQl(Application $app): void
12941294
/** @var ConfigRepository */
12951295
$config = $app['config'];
12961296

1297-
return new Executor($config->get('api-platform.graphql.introspection.enabled') ?? false);
1297+
return new Executor($config->get('api-platform.graphql.introspection.enabled') ?? false, $config->get('api-platform.graphql.max_query_complexity'), $config->get('api-platform.graphql.max_query_depth'));
12981298
});
12991299

13001300
$app->singleton(GraphiQlController::class, function (Application $app) {

src/Laravel/config/api-platform.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@
6262
'graphql' => [
6363
'enabled' => false,
6464
'nesting_separator' => '__',
65-
'introspection' => ['enabled' => true]
65+
'introspection' => ['enabled' => true],
66+
'max_query_complexity' => 500,
67+
'max_query_depth' => 200
6668
],
6769

6870
'exception_to_status' => [

src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -565,14 +565,17 @@ private function registerGraphQlConfiguration(ContainerBuilder $container, array
565565
{
566566
$enabled = $this->isConfigEnabled($container, $config['graphql']);
567567
$graphqlIntrospectionEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['introspection']);
568-
569568
$graphiqlEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['graphiql']);
570569
$graphqlPlayGroundEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['graphql_playground']);
570+
$maxQueryDepth = (int) $config['graphql']['max_query_depth'];
571+
$maxQueryComplexity = (int) $config['graphql']['max_query_complexity'];
571572
if ($graphqlPlayGroundEnabled) {
572573
trigger_deprecation('api-platform/core', '3.1', 'GraphQL Playground is deprecated and will be removed in API Platform 4.0. Only GraphiQL will be available in the future. Set api_platform.graphql.graphql_playground to false in the configuration to remove this deprecation.');
573574
}
574575

575576
$container->setParameter('api_platform.graphql.enabled', $enabled);
577+
$container->setParameter('api_platform.graphql.max_query_depth', $maxQueryDepth);
578+
$container->setParameter('api_platform.graphql.max_query_complexity', $maxQueryComplexity);
576579
$container->setParameter('api_platform.graphql.introspection.enabled', $graphqlIntrospectionEnabled);
577580
$container->setParameter('api_platform.graphql.graphiql.enabled', $graphiqlEnabled);
578581
$container->setParameter('api_platform.graphql.graphql_playground.enabled', $graphqlPlayGroundEnabled);

src/Symfony/Bundle/DependencyInjection/Configuration.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
namespace ApiPlatform\Symfony\Bundle\DependencyInjection;
1515

1616
use ApiPlatform\Doctrine\Common\Filter\OrderFilterInterface;
17-
use ApiPlatform\Elasticsearch\State\Options;
1817
use ApiPlatform\Metadata\ApiResource;
1918
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
2019
use ApiPlatform\Metadata\Post;
@@ -165,7 +164,7 @@ public function getConfigTreeBuilder(): TreeBuilder
165164
$this->addExceptionToStatusSection($rootNode);
166165

167166
$this->addFormatSection($rootNode, 'formats', [
168-
'jsonld' => ['mime_types' => ['application/ld+json']]
167+
'jsonld' => ['mime_types' => ['application/ld+json']],
169168
]);
170169
$this->addFormatSection($rootNode, 'patch_formats', [
171170
'json' => ['mime_types' => ['application/merge-patch+json']],
@@ -267,6 +266,10 @@ private function addGraphQlSection(ArrayNodeDefinition $rootNode): void
267266
->arrayNode('introspection')
268267
->canBeDisabled()
269268
->end()
269+
->integerNode('max_query_depth')->defaultValue(20)
270+
->end()
271+
->integerNode('max_query_complexity')->defaultValue(500)
272+
->end()
270273
->scalarNode('nesting_separator')->defaultValue('_')->info('The separator to use to filter nested fields.')->end()
271274
->arrayNode('collection')
272275
->addDefaultsIfNotSet()

src/Symfony/Bundle/Resources/config/graphql.xml

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
<services>
88
<service id="api_platform.graphql.executor" class="ApiPlatform\GraphQl\Executor" public="false">
99
<argument>%api_platform.graphql.introspection.enabled%</argument>
10+
<argument>%api_platform.graphql.max_query_complexity%</argument>
11+
<argument>%api_platform.graphql.max_query_depth%</argument>
1012
</service>
1113

1214
<!-- Resolvers -->

tests/Fixtures/app/AppKernel.php

+1
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ class_exists(NativePasswordHasher::class) ? 'password_hashers' : 'encoders' => [
251251
],
252252
'graphql' => [
253253
'graphql_playground' => false,
254+
'max_query_depth' => 200,
254255
],
255256
'use_symfony_listeners' => $useSymfonyListeners,
256257
'defaults' => [

tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm
123123
'introspection' => [
124124
'enabled' => true,
125125
],
126+
'max_query_depth' => 20,
127+
'max_query_complexity' => 500,
126128
'nesting_separator' => '_',
127129
'collection' => [
128130
'pagination' => [

0 commit comments

Comments
 (0)