Use directives to calculate query complexity

This commit is contained in:
Pascal de Vink 2017-04-24 14:00:29 +02:00
parent 5948d5198e
commit 11a1b13b72
No known key found for this signature in database
GPG Key ID: 22469329EFE074B1
2 changed files with 79 additions and 0 deletions

View File

@ -12,6 +12,7 @@ use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\AST\OperationDefinitionNode; use GraphQL\Language\AST\OperationDefinitionNode;
use GraphQL\Language\AST\SelectionSetNode; use GraphQL\Language\AST\SelectionSetNode;
use GraphQL\Language\Visitor; use GraphQL\Language\Visitor;
use GraphQL\Type\Definition\Directive;
use GraphQL\Type\Definition\FieldDefinition; use GraphQL\Type\Definition\FieldDefinition;
use GraphQL\Validator\ValidationContext; use GraphQL\Validator\ValidationContext;
@ -138,6 +139,10 @@ class QueryComplexity extends AbstractQuerySecurity
$fieldDef = $astFieldInfo[1]; $fieldDef = $astFieldInfo[1];
if ($fieldDef instanceof FieldDefinition) { if ($fieldDef instanceof FieldDefinition) {
if ($this->directiveExcludesField($node)) {
break;
}
$args = $this->buildFieldArguments($node); $args = $this->buildFieldArguments($node);
//get complexity fn using fieldDef complexity //get complexity fn using fieldDef complexity
if (method_exists($fieldDef, 'getComplexityFn')) { if (method_exists($fieldDef, 'getComplexityFn')) {
@ -205,6 +210,32 @@ class QueryComplexity extends AbstractQuerySecurity
return $args; return $args;
} }
private function directiveExcludesField(FieldNode $node) {
foreach ($node->directives as $directiveNode) {
if ($directiveNode->name->value === 'deprecated') {
return false;
}
$variableValues = Values::getVariableValues(
$this->context->getSchema(),
$this->variableDefs,
$this->getRawVariableValues()
);
if ($directiveNode->name->value === 'include') {
$directive = Directive::includeDirective();
$directiveArgs = Values::getArgumentValues($directive, $directiveNode, $variableValues);
return !$directiveArgs['if'];
} else {
$directive = Directive::skipDirective();
$directiveArgs = Values::getArgumentValues($directive, $directiveNode, $variableValues);
return $directiveArgs['if'];
}
}
}
protected function isEnabled() protected function isEnabled()
{ {
return $this->getMaxQueryComplexity() !== static::DISABLED; return $this->getMaxQueryComplexity() !== static::DISABLED;

View File

@ -86,6 +86,54 @@ class QueryComplexityTest extends AbstractQuerySecurityTest
$this->assertDocumentValidators($query, 3, 4); $this->assertDocumentValidators($query, 3, 4);
} }
public function testQueryWithEnabledIncludeDirectives()
{
$query = 'query MyQuery($withDogs: Boolean!) { human { dogs(name: "Root") @include(if:$withDogs) { name } } }';
$this->getRule()->setRawVariableValues(['withDogs' => true]);
$this->assertDocumentValidators($query, 3, 4);
}
public function testQueryWithDisabledIncludeDirectives()
{
$query = 'query MyQuery($withDogs: Boolean!) { human { dogs(name: "Root") @include(if:$withDogs) { name } } }';
$this->getRule()->setRawVariableValues(['withDogs' => false]);
$this->assertDocumentValidators($query, 1, 2);
}
public function testQueryWithEnabledSkipDirectives()
{
$query = 'query MyQuery($withoutDogs: Boolean!) { human { dogs(name: "Root") @skip(if:$withoutDogs) { name } } }';
$this->getRule()->setRawVariableValues(['withoutDogs' => true]);
$this->assertDocumentValidators($query, 1, 2);
}
public function testQueryWithDisabledSkipDirectives()
{
$query = 'query MyQuery($withoutDogs: Boolean!) { human { dogs(name: "Root") @skip(if:$withoutDogs) { name } } }';
$this->getRule()->setRawVariableValues(['withoutDogs' => false]);
$this->assertDocumentValidators($query, 3, 4);
}
public function testQueryWithMultipleDirectives()
{
$query = 'query MyQuery($withDogs: Boolean!, $withoutDogName: Boolean!) { human { dogs(name: "Root") @include(if:$withDogs) { name @skip(if:$withoutDogName) } } }';
$this->getRule()->setRawVariableValues([
'withDogs' => true,
'withoutDogName' => true
]);
$this->assertDocumentValidators($query, 2, 3);
}
public function testComplexityIntrospectionQuery() public function testComplexityIntrospectionQuery()
{ {
$this->assertIntrospectionQuery(181); $this->assertIntrospectionQuery(181);