Passed
Pull Request — master (#118)
by Christoffer
02:09
created

DirectivesRule::validateName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 1
1
<?php
2
3
namespace Digia\GraphQL\SchemaValidator\Rule;
4
5
use Digia\GraphQL\Error\InvariantException;
6
use Digia\GraphQL\Error\ValidationException;
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 Digia\GraphQL\Util\NameValidator;
12
use function Digia\GraphQL\Type\isInputType;
13
14
class DirectivesRule extends AbstractRule
15
{
16
    /**
17
     * @var NameValidator
18
     */
19
    protected $nameValidator;
20
21
    /**
22
     * DirectivesRule constructor.
23
     * @param NameValidator $nameValidator
24
     */
25
    public function __construct(NameValidator $nameValidator)
26
    {
27
        $this->nameValidator = $nameValidator;
28
    }
29
30
    /**
31
     * @inheritdoc
32
     */
33
    public function evaluate(): void
34
    {
35
        $directives = $this->context->getSchema()->getDirectives();
36
37
        foreach ($directives as $directive) {
38
            if (!($directive instanceof DirectiveInterface)) {
39
                $this->context->reportError(
40
                    new ValidationException(
41
                        \sprintf(
42
                            'Expected directive but got: %s.',
43
                            $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

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