Completed
Pull Request — master (#110)
by Christoffer
03:33
created

KnownDirectivesRule::enterDirective()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 27
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 13
nc 3
nop 1
1
<?php
2
3
namespace Digia\GraphQL\Validation\Rule;
4
5
use Digia\GraphQL\Error\ValidationException;
6
use Digia\GraphQL\Language\DirectiveLocationEnum;
7
use Digia\GraphQL\Language\Node\DirectiveNode;
8
use Digia\GraphQL\Language\Node\EnumTypeDefinitionNode;
9
use Digia\GraphQL\Language\Node\EnumTypeExtensionNode;
10
use Digia\GraphQL\Language\Node\EnumValueDefinitionNode;
11
use Digia\GraphQL\Language\Node\FieldDefinitionNode;
12
use Digia\GraphQL\Language\Node\FieldNode;
13
use Digia\GraphQL\Language\Node\FragmentDefinitionNode;
14
use Digia\GraphQL\Language\Node\FragmentSpreadNode;
15
use Digia\GraphQL\Language\Node\InlineFragmentNode;
16
use Digia\GraphQL\Language\Node\InputObjectTypeDefinitionNode;
17
use Digia\GraphQL\Language\Node\InputObjectTypeExtensionNode;
18
use Digia\GraphQL\Language\Node\InputValueDefinitionNode;
19
use Digia\GraphQL\Language\Node\InterfaceTypeDefinitionNode;
20
use Digia\GraphQL\Language\Node\InterfaceTypeExtensionNode;
21
use Digia\GraphQL\Language\Node\NodeInterface;
22
use Digia\GraphQL\Language\Node\ObjectTypeDefinitionNode;
23
use Digia\GraphQL\Language\Node\ObjectTypeExtensionNode;
24
use Digia\GraphQL\Language\Node\OperationDefinitionNode;
25
use Digia\GraphQL\Language\Node\ScalarTypeDefinitionNode;
26
use Digia\GraphQL\Language\Node\ScalarTypeExtensionNode;
27
use Digia\GraphQL\Language\Node\SchemaDefinitionNode;
28
use Digia\GraphQL\Language\Node\UnionTypeDefinitionNode;
29
use Digia\GraphQL\Language\Node\UnionTypeExtensionNode;
30
use Digia\GraphQL\Language\Visitor\AcceptsVisitorsInterface;
31
use Digia\GraphQL\Type\Definition\Directive;
32
use function Digia\GraphQL\Util\find;
33
use function Digia\GraphQL\Validation\misplacedDirectiveMessage;
34
use function Digia\GraphQL\Validation\unknownDirectiveMessage;
35
36
/**
37
 * Known directives
38
 *
39
 * A GraphQL document is only valid if all `@directives` are known by the
40
 * schema and legally positioned.
41
 */
42
class KnownDirectivesRule extends AbstractRule
43
{
44
    /**
45
     * @inheritdoc
46
     */
47
    protected function enterDirective(DirectiveNode $node): ?NodeInterface
48
    {
49
        /** @var Directive $directiveDefinition */
50
        $directiveDefinition = find(
51
            $this->context->getSchema()->getDirectives(),
52
            function (Directive $definition) use ($node) {
53
                return $definition->getName() === $node->getNameValue();
54
            }
55
        );
56
57
        if (null == $directiveDefinition) {
58
            $this->context->reportError(
59
                new ValidationException(unknownDirectiveMessage((string)$node), [$node])
60
            );
61
62
            return $node;
63
        }
64
65
        $location = $this->getDirectiveLocationFromASTPath($node);
66
67
        if (null !== $location && !\in_array($location, $directiveDefinition->getLocations())) {
68
            $this->context->reportError(
69
                new ValidationException(misplacedDirectiveMessage((string)$node, $location), [$node])
70
            );
71
        }
72
73
        return $node;
74
    }
75
76
    /**
77
     * @param NodeInterface|AcceptsVisitorsInterface $node
78
     * @return string|null
79
     */
80
    protected function getDirectiveLocationFromASTPath(NodeInterface $node): ?string
81
    {
82
        /** @var NodeInterface $appliedTo */
83
        $appliedTo = $node->getAncestor();
0 ignored issues
show
Bug introduced by
The method getAncestor() does not exist on Digia\GraphQL\Language\Node\NodeInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Digia\GraphQL\Language\Node\SelectionNodeInterface or Digia\GraphQL\Language\N...DefinitionNodeInterface or Digia\GraphQL\Language\Node\ValueNodeInterface or Digia\GraphQL\Language\Node\TypeNodeInterface or Digia\GraphQL\Language\N...eExtensionNodeInterface or Digia\GraphQL\Language\N...DefinitionNodeInterface or Digia\GraphQL\Language\N...DefinitionNodeInterface or Digia\GraphQL\Language\N...DefinitionNodeInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

83
        /** @scrutinizer ignore-call */ 
84
        $appliedTo = $node->getAncestor();
Loading history...
84
85
        if ($appliedTo instanceof OperationDefinitionNode) {
86
            switch ($appliedTo->getOperation()) {
87
                case 'query':
88
                    return DirectiveLocationEnum::QUERY;
89
                case 'mutation':
90
                    return DirectiveLocationEnum::MUTATION;
91
                case 'subscription':
92
                    return DirectiveLocationEnum::SUBSCRIPTION;
93
                default:
94
                    return null;
95
            }
96
        }
97
        if ($appliedTo instanceof FieldNode) {
98
            return DirectiveLocationEnum::FIELD;
99
        }
100
        if ($appliedTo instanceof FragmentSpreadNode) {
101
            return DirectiveLocationEnum::FRAGMENT_SPREAD;
102
        }
103
        if ($appliedTo instanceof InlineFragmentNode) {
104
            return DirectiveLocationEnum::INLINE_FRAGMENT;
105
        }
106
        if ($appliedTo instanceof FragmentDefinitionNode) {
107
            return DirectiveLocationEnum::FRAGMENT_DEFINITION;
108
        }
109
        if ($appliedTo instanceof SchemaDefinitionNode) {
110
            return DirectiveLocationEnum::SCHEMA;
111
        }
112
        if ($appliedTo instanceof ScalarTypeDefinitionNode || $appliedTo instanceof ScalarTypeExtensionNode) {
113
            return DirectiveLocationEnum::SCALAR;
114
        }
115
        if ($appliedTo instanceof ObjectTypeDefinitionNode || $appliedTo instanceof ObjectTypeExtensionNode) {
116
            return DirectiveLocationEnum::OBJECT;
117
        }
118
        if ($appliedTo instanceof FieldDefinitionNode) {
119
            return DirectiveLocationEnum::FIELD_DEFINITION;
120
        }
121
        if ($appliedTo instanceof InterfaceTypeDefinitionNode || $appliedTo instanceof InterfaceTypeExtensionNode) {
122
            return DirectiveLocationEnum::INTERFACE;
123
        }
124
        if ($appliedTo instanceof UnionTypeDefinitionNode || $appliedTo instanceof UnionTypeExtensionNode) {
125
            return DirectiveLocationEnum::UNION;
126
        }
127
        if ($appliedTo instanceof EnumTypeDefinitionNode || $appliedTo instanceof EnumTypeExtensionNode) {
128
            return DirectiveLocationEnum::ENUM;
129
        }
130
        if ($appliedTo instanceof EnumValueDefinitionNode) {
131
            return DirectiveLocationEnum::ENUM_VALUE;
132
        }
133
        if ($appliedTo instanceof InputObjectTypeDefinitionNode || $appliedTo instanceof InputObjectTypeExtensionNode) {
134
            return DirectiveLocationEnum::INPUT_OBJECT;
135
        }
136
        if ($appliedTo instanceof InputValueDefinitionNode) {
137
            $parentNode = $node->getAncestor(2);
138
            return $parentNode instanceof InputObjectTypeDefinitionNode
139
                ? DirectiveLocationEnum::INPUT_FIELD_DEFINITION
140
                : DirectiveLocationEnum::ARGUMENT_DEFINITION;
141
        }
142
143
        return null;
144
    }
145
}
146