Skip to content

Commit

Permalink
feat(graphql): allow to configure max query depth and max query compl…
Browse files Browse the repository at this point in the history
…exity
  • Loading branch information
mauriau authored and soyuka committed Dec 20, 2024
1 parent d0a4427 commit bac3983
Show file tree
Hide file tree
Showing 9 changed files with 45 additions and 6 deletions.
10 changes: 9 additions & 1 deletion src/GraphQl/Executor.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
use GraphQL\Type\Schema;
use GraphQL\Validator\DocumentValidator;
use GraphQL\Validator\Rules\DisableIntrospection;
use GraphQL\Validator\Rules\QueryComplexity;
use GraphQL\Validator\Rules\QueryDepth;

/**
* Wrapper for the GraphQL facade.
Expand All @@ -26,13 +28,19 @@
*/
final class Executor implements ExecutorInterface
{
public function __construct(private readonly bool $graphQlIntrospectionEnabled = true)
public function __construct(private readonly bool $graphQlIntrospectionEnabled = true, private readonly int $maxQueryComplexity = 500, private readonly int $maxQueryDepth = 20)
{
DocumentValidator::addRule(
new DisableIntrospection(
$this->graphQlIntrospectionEnabled ? DisableIntrospection::DISABLED : DisableIntrospection::ENABLED
)
);

$queryComplexity = new QueryComplexity($this->maxQueryComplexity);
DocumentValidator::addRule($queryComplexity);

$queryDepth = new QueryDepth($this->maxQueryDepth);
DocumentValidator::addRule($queryDepth);
}

/**
Expand Down
18 changes: 18 additions & 0 deletions src/GraphQl/Tests/ExecutorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
use ApiPlatform\GraphQl\Executor;
use GraphQL\Validator\DocumentValidator;
use GraphQL\Validator\Rules\DisableIntrospection;
use GraphQL\Validator\Rules\QueryComplexity;
use GraphQL\Validator\Rules\QueryDepth;
use PHPUnit\Framework\TestCase;

/**
Expand All @@ -38,4 +40,20 @@ public function testDisableIntrospectionQuery(): void
$expected = new DisableIntrospection(DisableIntrospection::ENABLED);
$this->assertEquals($expected, DocumentValidator::getRule(DisableIntrospection::class));
}

public function testChangeValueOfMaxQueryDepth(): void
{
$executor = new Executor(true, 20);

$expected = new QueryComplexity(20);
$this->assertEquals($expected, DocumentValidator::getRule(QueryComplexity::class));
}

public function testChangeValueOfMaxQueryComplexity(): void
{
$executor = new Executor(true, maxQueryDepth: 20);

$expected = new QueryDepth(20);
$this->assertEquals($expected, DocumentValidator::getRule(QueryDepth::class));
}
}
2 changes: 1 addition & 1 deletion src/Laravel/ApiPlatformProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -1294,7 +1294,7 @@ private function registerGraphQl(Application $app): void
/** @var ConfigRepository */
$config = $app['config'];

return new Executor($config->get('api-platform.graphql.introspection.enabled') ?? false);
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'));
});

$app->singleton(GraphiQlController::class, function (Application $app) {
Expand Down
4 changes: 3 additions & 1 deletion src/Laravel/config/api-platform.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@
'graphql' => [
'enabled' => false,
'nesting_separator' => '__',
'introspection' => ['enabled' => true]
'introspection' => ['enabled' => true],
'max_query_complexity' => 500,
'max_query_depth' => 200
],

'exception_to_status' => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -565,14 +565,17 @@ private function registerGraphQlConfiguration(ContainerBuilder $container, array
{
$enabled = $this->isConfigEnabled($container, $config['graphql']);
$graphqlIntrospectionEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['introspection']);

$graphiqlEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['graphiql']);
$graphqlPlayGroundEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['graphql_playground']);
$maxQueryDepth = (int) $config['graphql']['max_query_depth'];
$maxQueryComplexity = (int) $config['graphql']['max_query_complexity'];
if ($graphqlPlayGroundEnabled) {
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.');
}

$container->setParameter('api_platform.graphql.enabled', $enabled);
$container->setParameter('api_platform.graphql.max_query_depth', $maxQueryDepth);
$container->setParameter('api_platform.graphql.max_query_complexity', $maxQueryComplexity);
$container->setParameter('api_platform.graphql.introspection.enabled', $graphqlIntrospectionEnabled);
$container->setParameter('api_platform.graphql.graphiql.enabled', $graphiqlEnabled);
$container->setParameter('api_platform.graphql.graphql_playground.enabled', $graphqlPlayGroundEnabled);
Expand Down
7 changes: 5 additions & 2 deletions src/Symfony/Bundle/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
namespace ApiPlatform\Symfony\Bundle\DependencyInjection;

use ApiPlatform\Doctrine\Common\Filter\OrderFilterInterface;
use ApiPlatform\Elasticsearch\State\Options;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
use ApiPlatform\Metadata\Post;
Expand Down Expand Up @@ -165,7 +164,7 @@ public function getConfigTreeBuilder(): TreeBuilder
$this->addExceptionToStatusSection($rootNode);

$this->addFormatSection($rootNode, 'formats', [
'jsonld' => ['mime_types' => ['application/ld+json']]
'jsonld' => ['mime_types' => ['application/ld+json']],
]);
$this->addFormatSection($rootNode, 'patch_formats', [
'json' => ['mime_types' => ['application/merge-patch+json']],
Expand Down Expand Up @@ -267,6 +266,10 @@ private function addGraphQlSection(ArrayNodeDefinition $rootNode): void
->arrayNode('introspection')
->canBeDisabled()
->end()
->integerNode('max_query_depth')->defaultValue(20)
->end()
->integerNode('max_query_complexity')->defaultValue(500)
->end()
->scalarNode('nesting_separator')->defaultValue('_')->info('The separator to use to filter nested fields.')->end()
->arrayNode('collection')
->addDefaultsIfNotSet()
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/Resources/config/graphql.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
<services>
<service id="api_platform.graphql.executor" class="ApiPlatform\GraphQl\Executor" public="false">
<argument>%api_platform.graphql.introspection.enabled%</argument>
<argument>%api_platform.graphql.max_query_complexity%</argument>
<argument>%api_platform.graphql.max_query_depth%</argument>
</service>

<!-- Resolvers -->
Expand Down
1 change: 1 addition & 0 deletions tests/Fixtures/app/AppKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ class_exists(NativePasswordHasher::class) ? 'password_hashers' : 'encoders' => [
],
'graphql' => [
'graphql_playground' => false,
'max_query_depth' => 200,
],
'use_symfony_listeners' => $useSymfonyListeners,
'defaults' => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm
'introspection' => [
'enabled' => true,
],
'max_query_depth' => 20,
'max_query_complexity' => 500,
'nesting_separator' => '_',
'collection' => [
'pagination' => [
Expand Down

0 comments on commit bac3983

Please sign in to comment.