Passed
Pull Request — master (#173)
by Christoffer
03:13
created

SchemaExtender::createInfo()   D

Complexity

Conditions 13
Paths 9

Size

Total Lines 88
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 88
c 0
b 0
f 0
rs 4.9922
cc 13
eloc 53
nc 9
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Digia\GraphQL\Schema\Extension;
4
5
use Digia\GraphQL\Error\ExtensionException;
6
use Digia\GraphQL\Error\InvariantException;
7
use Digia\GraphQL\Language\Node\DirectiveDefinitionNode;
8
use Digia\GraphQL\Language\Node\DocumentNode;
9
use Digia\GraphQL\Language\Node\EnumTypeExtensionNode;
10
use Digia\GraphQL\Language\Node\InputObjectTypeExtensionNode;
11
use Digia\GraphQL\Language\Node\InterfaceTypeExtensionNode;
12
use Digia\GraphQL\Language\Node\NodeInterface;
13
use Digia\GraphQL\Language\Node\ObjectTypeExtensionNode;
14
use Digia\GraphQL\Language\Node\ScalarTypeExtensionNode;
15
use Digia\GraphQL\Language\Node\TypeDefinitionNodeInterface;
16
use Digia\GraphQL\Language\Node\UnionTypeExtensionNode;
17
use Digia\GraphQL\Schema\DefinitionBuilder;
18
use Digia\GraphQL\Schema\SchemaInterface;
19
use Digia\GraphQL\Type\Definition\InterfaceType;
20
use Digia\GraphQL\Type\Definition\ObjectType;
21
use Digia\GraphQL\Type\Definition\TypeInterface;
22
use Psr\SimpleCache\CacheInterface;
23
use Psr\SimpleCache\InvalidArgumentException;
24
use function Digia\GraphQL\Type\newSchema;
25
use function Digia\GraphQL\Util\toString;
26
27
class SchemaExtender implements SchemaExtenderInterface
28
{
29
    /**
30
     * @var CacheInterface
31
     */
32
    protected $cache;
33
34
    /**
35
     * BuilderContextCreator constructor.
36
     * @param CacheInterface $cache
37
     */
38
    public function __construct(CacheInterface $cache)
39
    {
40
        $this->cache = $cache;
41
    }
42
43
    /**
44
     * @param SchemaInterface $schema
45
     * @param DocumentNode    $document
46
     * @return SchemaInterface
47
     * @throws InvariantException
48
     * @throws ExtensionException
49
     * @throws InvalidArgumentException
50
     */
51
    public function extend(SchemaInterface $schema, DocumentNode $document): SchemaInterface
52
    {
53
        $context = $this->createContext($schema, $document);
54
55
        // If this document contains no new types, extensions, or directives then
56
        // return the same unmodified GraphQLSchema instance.
57
        if (!$context->isSchemaExtended()) {
58
            return $schema;
59
        }
60
61
        return newSchema([
62
            'query'        => $context->getExtendedQueryType(),
63
            'mutation'     => $context->getExtendedMutationType(),
64
            'subscription' => $context->getExtendedSubscriptionType(),
65
            'types'        => $context->getExtendedTypes(),
66
            'directives'   => $context->getExtendedDirectives(),
67
            'astNode'      => $schema->getAstNode(),
68
        ]);
69
    }
70
71
    /**
72
     * @inheritdoc
73
     */
74
    public function createContext(SchemaInterface $schema, DocumentNode $document): ExtensionContextInterface
75
    {
76
        $info = $this->createInfo($schema, $document);
77
78
        // Context has to be created in order to create the definition builder,
79
        // because we are using its `resolveType` function to resolve types.
80
        $context = new ExtensionContext($info);
81
82
        $definitionBuilder = new DefinitionBuilder(
83
            $info->getTypeDefinitionMap(),
84
            null,
85
            [$context, 'resolveType'],
86
            $this->cache
87
        );
88
89
        return $context->setDefinitionBuilder($definitionBuilder);
90
    }
91
92
    /**
93
     * @param SchemaInterface $schema
94
     * @param DocumentNode    $document
95
     * @return ExtendInfo
96
     * @throws ExtensionException
97
     */
98
    protected function createInfo(SchemaInterface $schema, DocumentNode $document): ExtendInfo
99
    {
100
        $typeDefinitionMap    = [];
101
        $typeExtensionsMap    = [];
102
        $directiveDefinitions = [];
103
104
        foreach ($document->getDefinitions() as $definition) {
105
            if ($definition instanceof TypeDefinitionNodeInterface) {
106
                // Sanity check that none of the defined types conflict with the schema's existing types.
107
                $typeName     = $definition->getNameValue();
108
                $existingType = $schema->getType($typeName);
109
110
                if (null !== $existingType) {
111
                    throw new ExtensionException(
112
                        \sprintf(
113
                            'Type "%s" already exists in the schema. It cannot also ' .
114
                            'be defined in this type definition.',
115
                            $typeName
116
                        ),
117
                        [$definition]
118
                    );
119
                }
120
121
                $typeDefinitionMap[$typeName] = $definition;
122
123
                continue;
124
            }
125
126
            if ($definition instanceof ObjectTypeExtensionNode || $definition instanceof InterfaceTypeExtensionNode) {
127
                // Sanity check that this type extension exists within the schema's existing types.
128
                $extendedTypeName = $definition->getNameValue();
129
                $existingType     = $schema->getType($extendedTypeName);
130
131
                if (null === $existingType) {
132
                    throw new ExtensionException(
133
                        \sprintf(
134
                            'Cannot extend type "%s" because it does not exist in the existing schema.',
135
                            $extendedTypeName
136
                        ),
137
                        [$definition]
138
                    );
139
                }
140
141
                $this->checkExtensionNode($existingType, $definition);
142
143
                $typeExtensionsMap[$extendedTypeName] = \array_merge(
144
                    $typeExtensionsMap[$extendedTypeName] ?? [],
145
                    [$definition]
146
                );
147
148
                continue;
149
            }
150
151
            if ($definition instanceof DirectiveDefinitionNode) {
152
                $directiveName     = $definition->getNameValue();
153
                $existingDirective = $schema->getDirective($directiveName);
154
155
                if (null !== $existingDirective) {
156
                    throw new ExtensionException(
157
                        \sprintf(
158
                            'Directive "%s" already exists in the schema. It cannot be redefined.',
159
                            $directiveName
160
                        ),
161
                        [$definition]
162
                    );
163
                }
164
165
                $directiveDefinitions[] = $definition;
166
167
                continue;
168
            }
169
170
            if ($definition instanceof ScalarTypeExtensionNode ||
171
                $definition instanceof UnionTypeExtensionNode ||
172
                $definition instanceof EnumTypeExtensionNode ||
173
                $definition instanceof InputObjectTypeExtensionNode) {
174
                throw new ExtensionException(
175
                    \sprintf('The %s kind is not yet supported by extendSchema().', $definition->getKind())
176
                );
177
            }
178
        }
179
180
        return new ExtendInfo(
181
            $schema,
182
            $document,
183
            $typeDefinitionMap,
184
            $typeExtensionsMap,
185
            $directiveDefinitions
186
        );
187
    }
188
189
    /**
190
     * @param TypeInterface $type
191
     * @param NodeInterface $node
192
     * @throws ExtensionException
193
     */
194
    protected function checkExtensionNode(TypeInterface $type, NodeInterface $node): void
195
    {
196
        if ($node instanceof ObjectTypeExtensionNode && !($type instanceof ObjectType)) {
197
            throw new ExtensionException(
198
                \sprintf('Cannot extend non-object type "%s".', toString($type)),
199
                [$node]
200
            );
201
        }
202
203
        if ($node instanceof InterfaceTypeExtensionNode && !($type instanceof InterfaceType)) {
204
            throw new ExtensionException(
205
                \sprintf('Cannot extend non-interface type "%s".', toString($type)),
206
                [$node]
207
            );
208
        }
209
    }
210
}
211