Passed
Pull Request — master (#159)
by Christoffer
03:39
created

DirectivesRule::getAllDirectiveArgumentNodes()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.2
c 0
b 0
f 0
cc 4
eloc 7
nc 2
nop 2
1
<?php
2
3
namespace Digia\GraphQL\SchemaValidation\Rule;
4
5
use Digia\GraphQL\Error\InvariantException;
6
use Digia\GraphQL\Error\SchemaValidationException;
7
use Digia\GraphQL\Language\Node\DirectiveNode;
8
use Digia\GraphQL\Language\Node\NodeAwareInterface;
9
use Digia\GraphQL\Type\Definition\Directive;
10
use Digia\GraphQL\Type\Definition\DirectiveInterface;
11
use function Digia\GraphQL\Type\isInputType;
12
use function Digia\GraphQL\Util\isValidNameError;
13
14
class DirectivesRule extends AbstractRule
15
{
16
    /**
17
     * @inheritdoc
18
     */
19
    public function evaluate(): void
20
    {
21
        $directives = $this->context->getSchema()->getDirectives();
22
23
        foreach ($directives as $directive) {
24
            if (!($directive instanceof DirectiveInterface)) {
25
                $this->context->reportError(
26
                    new SchemaValidationException(
27
                        \sprintf(
28
                            'Expected directive but got: %s.',
29
                            $directive instanceof NodeAwareInterface ? $directive->getAstNode() : $directive
0 ignored issues
show
Bug introduced by
It seems like $directive instanceof Di...tAstNode() : $directive can also be of type Digia\GraphQL\Language\Node\NodeInterface; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

29
                            /** @scrutinizer ignore-type */ $directive instanceof NodeAwareInterface ? $directive->getAstNode() : $directive
Loading history...
30
                        )
31
                    )
32
                );
33
34
                return;
35
            }
36
37
            // Ensure they are named correctly.
38
            $this->validateName($directive);
39
40
            // TODO: Ensure proper locations.
41
42
            // Ensure the arguments are valid.
43
            $argumentNames = [];
44
45
            foreach ($directive->getArguments() as $argument) {
46
                $argumentName = $argument->getName();
47
48
                // Ensure they are named correctly.
49
                $this->validateName($argument);
50
51
                // Ensure they are unique per directive.
52
                if (isset($argumentNames[$argumentName])) {
53
                    $this->context->reportError(
54
                        new SchemaValidationException(
55
                            \sprintf(
56
                                'Argument @%s(%s:) can only be defined once.',
57
                                $directive->getName(),
58
                                $argumentName
59
                            ),
60
                            $this->getAllDirectiveArgumentNodes($directive, $argumentName)
61
                        )
62
                    );
63
64
                    continue;
65
                }
66
67
                $argumentNames[$argumentName] = true;
68
69
                // Ensure the type is an input type.
70
                if (!isInputType($argument->getType())) {
71
                    $this->context->reportError(
72
                        new SchemaValidationException(
73
                            \sprintf(
74
                                'The type of @%s(%s:) must be Input Type but got: %s.',
75
                                $directive->getName(),
76
                                $argumentName,
77
                                (string)$argument->getType()
78
                            ),
79
                            $this->getAllDirectiveArgumentNodes($directive, $argumentName)
80
                        )
81
                    );
82
                }
83
            }
84
        }
85
    }
86
87
    /**
88
     * @param Directive $directive
89
     * @param string    $argumentName
90
     * @return array
91
     */
92
    protected function getAllDirectiveArgumentNodes(Directive $directive, string $argumentName)
93
    {
94
        $nodes = [];
95
96
        /** @var DirectiveNode $directiveNode */
97
        $directiveNode = $directive->getAstNode();
98
99
        if (null !== $directiveNode) {
100
            foreach ($directiveNode->getArguments() as $node) {
101
                if ($node->getNameValue() === $argumentName) {
102
                    $nodes[] = $node;
103
                }
104
            }
105
        }
106
107
        return $nodes;
108
    }
109
110
    /**
111
     * @param mixed $node
112
     * @throws InvariantException
113
     */
114
    protected function validateName($node): void
115
    {
116
        // Ensure names are valid, however introspection types opt out.
117
        $error = isValidNameError($node->getName(), $node);
118
119
        if (null !== $error) {
120
            $this->context->reportError($error);
121
        }
122
    }
123
}
124