Completed
Pull Request — master (#45)
by Christoffer
02:13
created

KnownDirectivesRule::enterNode()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 29
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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