Completed
Push — master ( 93ccd7...3b27ab )
by Vladimir
27s queued 13s
created

ProvidedRequiredArgumentsOnDirectives   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 77
Duplicated Lines 0 %

Test Coverage

Coverage 63.64%

Importance

Changes 0
Metric Value
wmc 15
eloc 44
dl 0
loc 77
ccs 28
cts 44
cp 0.6364
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A missingDirectiveArgMessage() 0 3 1
C getVisitor() 0 70 14
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Validator\Rules;
6
7
use GraphQL\Error\Error;
8
use GraphQL\Language\AST\ArgumentNode;
9
use GraphQL\Language\AST\DirectiveDefinitionNode;
10
use GraphQL\Language\AST\DirectiveNode;
11
use GraphQL\Language\AST\NamedTypeNode;
12
use GraphQL\Language\AST\Node;
13
use GraphQL\Language\AST\NodeKind;
14
use GraphQL\Language\AST\NodeList;
15
use GraphQL\Language\AST\NonNullTypeNode;
16
use GraphQL\Type\Definition\FieldArgument;
17
use GraphQL\Type\Definition\NonNull;
18
use GraphQL\Utils\Utils;
19
use GraphQL\Validator\ValidationContext;
20
use function array_filter;
21
use function is_array;
22
use function iterator_to_array;
23
24
/**
25
 * Provided required arguments on directives
26
 *
27
 * A directive is only valid if all required (non-null without a
28
 * default value) field arguments have been provided.
29
 */
30
class ProvidedRequiredArgumentsOnDirectives extends ValidationRule
31
{
32
    protected static function missingDirectiveArgMessage(string $directiveName, string $argName)
33
    {
34
        return 'Directive "' . $directiveName . '" argument "' . $argName . '" is required but ont provided.';
35
    }
36
37 54
    public function getVisitor(ValidationContext $context)
38
    {
39 54
        $requiredArgsMap   = [];
40 54
        $schema            = $context->getSchema();
41 54
        $definedDirectives = $schema->getDirectives();
42
43 54
        foreach ($definedDirectives as $directive) {
44 54
            $requiredArgsMap[$directive->name] = Utils::keyMap(
45
                array_filter($directive->args, static function (FieldArgument $arg) : bool {
46 54
                    return $arg->getType() instanceof NonNull && ! isset($arg->defaultValue);
47 54
                }),
48
                static function (FieldArgument $arg) : string {
49 53
                    return $arg->name;
50 54
                }
51
            );
52
        }
53
54 54
        $astDefinition = $context->getDocument()->definitions;
55 54
        foreach ($astDefinition as $def) {
56 54
            if (! ($def instanceof DirectiveDefinitionNode)) {
57 48
                continue;
58
            }
59
60 7
            if (is_array($def->arguments)) {
61
                $arguments = $def->arguments;
62 7
            } elseif ($def->arguments instanceof NodeList) {
63 7
                $arguments = iterator_to_array($def->arguments->getIterator());
64
            } else {
65
                $arguments = null;
66
            }
67
68 7
            $requiredArgsMap[$def->name->value] = Utils::keyMap(
69 7
                $arguments
70
                    ? array_filter($arguments, static function (Node $argument) : bool {
71 4
                        return $argument instanceof NonNullTypeNode &&
72
                            (
73
                                ! isset($argument->defaultValue) ||
74 4
                                $argument->defaultValue === null
75
                            );
76 4
                    })
77 7
                    : [],
78
                static function (NamedTypeNode $argument) : string {
79
                    return $argument->name->value;
80 7
                }
81
            );
82
        }
83
84
        return [
85
            NodeKind::DIRECTIVE => static function (DirectiveNode $directiveNode) use ($requiredArgsMap, $context) {
86 10
                $directiveName = $directiveNode->name->value;
87 10
                $requiredArgs  = $requiredArgsMap[$directiveName] ?? null;
88 10
                if (! $requiredArgs) {
89 10
                    return;
90
                }
91
92
                $argNodes   = $directiveNode->arguments ?: [];
93
                $argNodeMap = Utils::keyMap(
94
                    $argNodes,
95
                    static function (ArgumentNode $arg) : string {
96
                        return $arg->name->value;
97
                    }
98
                );
99
100
                foreach ($requiredArgs as $argName => $arg) {
101
                    if (isset($argNodeMap[$argName])) {
102
                        continue;
103
                    }
104
105
                    $context->reportError(
106
                        new Error(static::missingDirectiveArgMessage($directiveName, $argName), [$directiveNode])
107
                    );
108
                }
109 54
            },
110
        ];
111
    }
112
}
113