Failed Conditions
Push — master ( e31947...cc39b3 )
by Šimon
11s
created

KnownDirectives::getVisitor()   B

Complexity

Conditions 6
Paths 1

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6.1215

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 31
ccs 17
cts 20
cp 0.85
rs 8.9617
c 0
b 0
f 0
cc 6
nc 1
nop 1
crap 6.1215
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\DirectiveNode;
9
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
10
use GraphQL\Language\AST\NodeKind;
11
use GraphQL\Language\DirectiveLocation;
12
use GraphQL\Validator\ValidationContext;
13
use function count;
14
use function in_array;
15
use function sprintf;
16
17
class KnownDirectives extends ValidationRule
18
{
19 110
    public function getVisitor(ValidationContext $context)
20
    {
21
        return [
22
            NodeKind::DIRECTIVE => function (DirectiveNode $node, $key, $parent, $path, $ancestors) use ($context) {
23 7
                $directiveDef = null;
24 7
                foreach ($context->getSchema()->getDirectives() as $def) {
25 7
                    if ($def->name === $node->name->value) {
26 5
                        $directiveDef = $def;
27 7
                        break;
28
                    }
29
                }
30
31 7
                if (! $directiveDef) {
32 2
                    $context->reportError(new Error(
33 2
                        self::unknownDirectiveMessage($node->name->value),
34 2
                        [$node]
35
                    ));
36
37 2
                    return;
38
                }
39 5
                $candidateLocation = $this->getDirectiveLocationForASTPath($ancestors);
40
41 5
                if (! $candidateLocation) {
42
                    $context->reportError(new Error(
43
                        self::misplacedDirectiveMessage($node->name->value, $node->type),
0 ignored issues
show
Bug introduced by
The property type does not seem to exist on GraphQL\Language\AST\DirectiveNode.
Loading history...
44
                        [$node]
45
                    ));
46 5
                } elseif (! in_array($candidateLocation, $directiveDef->locations)) {
47 2
                    $context->reportError(new Error(
48 2
                        self::misplacedDirectiveMessage($node->name->value, $candidateLocation),
49 2
                        [$node]
50
                    ));
51
                }
52 110
            },
53
        ];
54
    }
55
56 2
    public static function unknownDirectiveMessage($directiveName)
57
    {
58 2
        return sprintf('Unknown directive "%s".', $directiveName);
59
    }
60
61
    /**
62
     * @param (Node|NodeList)[] $ancestors
63
     */
64 5
    private function getDirectiveLocationForASTPath(array $ancestors)
65
    {
66 5
        $appliedTo = $ancestors[count($ancestors) - 1];
67 5
        switch ($appliedTo->kind) {
68 5
            case NodeKind::OPERATION_DEFINITION:
69 2
                switch ($appliedTo->operation) {
70 2
                    case 'query':
71 2
                        return DirectiveLocation::QUERY;
72 2
                    case 'mutation':
73 2
                        return DirectiveLocation::MUTATION;
74
                    case 'subscription':
75
                        return DirectiveLocation::SUBSCRIPTION;
76
                }
77
                break;
78 5
            case NodeKind::FIELD:
79 3
                return DirectiveLocation::FIELD;
80 4
            case NodeKind::FRAGMENT_SPREAD:
81 2
                return DirectiveLocation::FRAGMENT_SPREAD;
82 2
            case NodeKind::INLINE_FRAGMENT:
83
                return DirectiveLocation::INLINE_FRAGMENT;
84 2
            case NodeKind::FRAGMENT_DEFINITION:
85
                return DirectiveLocation::FRAGMENT_DEFINITION;
86 2
            case NodeKind::SCHEMA_DEFINITION:
87 2
                return DirectiveLocation::SCHEMA;
88 2
            case NodeKind::SCALAR_TYPE_DEFINITION:
89 2
            case NodeKind::SCALAR_TYPE_EXTENSION:
90 2
                return DirectiveLocation::SCALAR;
91 2
            case NodeKind::OBJECT_TYPE_DEFINITION:
92 2
            case NodeKind::OBJECT_TYPE_EXTENSION:
93 2
                return DirectiveLocation::OBJECT;
94 2
            case NodeKind::FIELD_DEFINITION:
95 2
                return DirectiveLocation::FIELD_DEFINITION;
96 2
            case NodeKind::INTERFACE_TYPE_DEFINITION:
97 2
            case NodeKind::INTERFACE_TYPE_EXTENSION:
98 2
                return DirectiveLocation::IFACE;
99 2
            case NodeKind::UNION_TYPE_DEFINITION:
100 2
            case NodeKind::UNION_TYPE_EXTENSION:
101 2
                return DirectiveLocation::UNION;
102 2
            case NodeKind::ENUM_TYPE_DEFINITION:
103 2
            case NodeKind::ENUM_TYPE_EXTENSION:
104 2
                return DirectiveLocation::ENUM;
105 2
            case NodeKind::ENUM_VALUE_DEFINITION:
106 2
                return DirectiveLocation::ENUM_VALUE;
107 2
            case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
108 2
            case NodeKind::INPUT_OBJECT_TYPE_EXTENSION:
109 2
                return DirectiveLocation::INPUT_OBJECT;
110 2
            case NodeKind::INPUT_VALUE_DEFINITION:
111 2
                $parentNode = $ancestors[count($ancestors) - 3];
112
113 2
                return $parentNode instanceof InputObjectTypeDefinitionNode
114 2
                    ? DirectiveLocation::INPUT_FIELD_DEFINITION
115 2
                    : DirectiveLocation::ARGUMENT_DEFINITION;
116
        }
117
    }
118
119 2
    public static function misplacedDirectiveMessage($directiveName, $location)
120
    {
121 2
        return sprintf('Directive "%s" may not be used on "%s".', $directiveName, $location);
122
    }
123
}
124