Skip to content

Commit df3203e

Browse files
authored
Merge pull request #6903 from magento-honey-badgers/PWA-1700
[honey] PWA-1700: Limit Query Complexity in GQL implementation
2 parents 8c25bda + 25d95ca commit df3203e

File tree

3 files changed

+65
-12
lines changed

3 files changed

+65
-12
lines changed

dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/QueryComplexityLimiterTest.php

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ public function testQueryComplexityIsLimited()
2828
products {
2929
items {
3030
name
31+
nameAlias: name
32+
...configurableFields
33+
... on BundleProduct {
34+
items {
35+
options {
36+
uid
37+
label
38+
}
39+
}
40+
}
3141
categories {
3242
id
3343
position
@@ -308,18 +318,6 @@ public function testQueryComplexityIsLimited()
308318
percentage_value
309319
website_id
310320
}
311-
tier_prices {
312-
customer_group_id
313-
qty
314-
percentage_value
315-
website_id
316-
}
317-
tier_prices {
318-
customer_group_id
319-
qty
320-
percentage_value
321-
website_id
322-
}
323321
tier_price
324322
manufacturer
325323
sku
@@ -393,6 +391,16 @@ public function testQueryComplexityIsLimited()
393391
}
394392
}
395393
}
394+
395+
fragment configurableFields on ConfigurableProduct {
396+
variants {
397+
attributes {
398+
uid
399+
code
400+
label
401+
}
402+
}
403+
}
396404
QUERY;
397405

398406
self::expectExceptionMessageMatches('/Max query complexity should be 300 but got 302/');

lib/internal/Magento/Framework/GraphQl/Query/QueryComplexityLimiter.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@
77

88
namespace Magento\Framework\GraphQl\Query;
99

10+
use GraphQL\Language\AST\Node;
11+
use GraphQL\Language\AST\NodeKind;
12+
use GraphQL\Language\Parser;
13+
use GraphQL\Language\Source;
14+
use GraphQL\Language\Visitor;
1015
use GraphQL\Validator\DocumentValidator;
1116
use GraphQL\Validator\Rules\DisableIntrospection;
1217
use GraphQL\Validator\Rules\QueryDepth;
1318
use GraphQL\Validator\Rules\QueryComplexity;
19+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
1420

1521
/**
1622
* QueryComplexityLimiter
@@ -57,6 +63,7 @@ public function __construct(
5763
* Sets limits for query complexity
5864
*
5965
* @return void
66+
* @throws GraphQlInputException
6067
*/
6168
public function execute(): void
6269
{
@@ -66,4 +73,39 @@ public function execute(): void
6673
);
6774
DocumentValidator::addRule(new QueryDepth($this->queryDepth));
6875
}
76+
77+
/**
78+
* Performs a preliminary field count check before performing more extensive query validation.
79+
*
80+
* This is necessary for performance optimization, as extremely large queries require a substantial
81+
* amount of time to fully validate and can affect server performance.
82+
*
83+
* @param string $query
84+
* @throws GraphQlInputException
85+
*/
86+
public function validateFieldCount(string $query): void
87+
{
88+
if (!empty($query)) {
89+
$totalFieldCount = 0;
90+
$queryAst = Parser::parse(new Source($query ?: '', 'GraphQL'));
91+
Visitor::visit(
92+
$queryAst,
93+
[
94+
'leave' => [
95+
NodeKind::FIELD => function (Node $node) use (&$totalFieldCount) {
96+
$totalFieldCount++;
97+
}
98+
]
99+
]
100+
);
101+
102+
if ($totalFieldCount > $this->queryComplexity) {
103+
throw new GraphQlInputException(__(
104+
'Max query complexity should be %1 but got %2.',
105+
$this->queryComplexity,
106+
$totalFieldCount
107+
));
108+
}
109+
}
110+
}
69111
}

lib/internal/Magento/Framework/GraphQl/Query/QueryProcessor.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace Magento\Framework\GraphQl\Query;
99

1010
use Magento\Framework\GraphQl\Exception\ExceptionFormatter;
11+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
1112
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
1213
use Magento\Framework\GraphQl\Schema;
1314

@@ -57,6 +58,7 @@ public function __construct(
5758
* @param array|null $variableValues
5859
* @param string|null $operationName
5960
* @return Promise|array
61+
* @throws GraphQlInputException
6062
*/
6163
public function process(
6264
Schema $schema,
@@ -66,6 +68,7 @@ public function process(
6668
string $operationName = null
6769
) : array {
6870
if (!$this->exceptionFormatter->shouldShowDetail()) {
71+
$this->queryComplexityLimiter->validateFieldCount($source);
6972
$this->queryComplexityLimiter->execute();
7073
}
7174

0 commit comments

Comments
 (0)