SchemaExtender::createInfo()   C
last analyzed

Complexity

Conditions 16
Paths 11

Size

Total Lines 101
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 60
nc 11
nop 2
dl 0
loc 101
rs 5.5666
c 0
b 0
f 0

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\Language\Node\DirectiveDefinitionNode;
6
use Digia\GraphQL\Language\Node\DocumentNode;
7
use Digia\GraphQL\Language\Node\EnumTypeExtensionNode;
8
use Digia\GraphQL\Language\Node\InputObjectTypeExtensionNode;
9
use Digia\GraphQL\Language\Node\InterfaceTypeExtensionNode;
10
use Digia\GraphQL\Language\Node\NameAwareInterface;
11
use Digia\GraphQL\Language\Node\NodeInterface;
12
use Digia\GraphQL\Language\Node\ObjectTypeExtensionNode;
13
use Digia\GraphQL\Language\Node\ScalarTypeExtensionNode;
14
use Digia\GraphQL\Language\Node\SchemaDefinitionNode;
15
use Digia\GraphQL\Language\Node\SchemaExtensionNode;
16
use Digia\GraphQL\Language\Node\TypeSystemDefinitionNodeInterface;
17
use Digia\GraphQL\Language\Node\UnionTypeExtensionNode;
18
use Digia\GraphQL\Schema\DefinitionBuilder;
19
use Digia\GraphQL\Schema\Resolver\ResolverRegistryInterface;
20
use Digia\GraphQL\Schema\Schema;
21
use Digia\GraphQL\Type\Definition\InterfaceType;
22
use Digia\GraphQL\Type\Definition\ObjectType;
23
use Digia\GraphQL\Type\Definition\TypeInterface;
24
use function Digia\GraphQL\Type\newSchema;
25
use function Digia\GraphQL\Util\toString;
26
27
class SchemaExtender implements SchemaExtenderInterface
28
{
29
    /**
30
     * @inheritdoc
31
     */
32
    public function extend(
33
        Schema $schema,
34
        DocumentNode $document,
35
        ?ResolverRegistryInterface $resolverRegistry = null,
36
        array $options = []
37
    ): Schema {
38
        $context = $this->createContext($schema, $document, $resolverRegistry, $options);
39
40
        // If this document contains no new types, extensions, or directives then
41
        // return the same unmodified GraphQLSchema instance.
42
        if (!$context->isSchemaExtended()) {
43
            return $schema;
44
        }
45
46
        $operationTypes = $context->getExtendedOperationTypes();
47
48
        /** @noinspection PhpUnhandledExceptionInspection */
49
        return newSchema([
50
            'query'        => $operationTypes['query'] ?? null,
51
            'mutation'     => $operationTypes['mutation'] ?? null,
52
            'subscription' => $operationTypes['subscription'] ?? null,
53
            'types'        => $context->getExtendedTypes(),
54
            'directives'   => $context->getExtendedDirectives(),
55
            'astNode'      => $schema->getAstNode(),
56
        ]);
57
    }
58
59
    /**
60
     * @inheritdoc
61
     */
62
    protected function createContext(
63
        Schema $schema,
64
        DocumentNode $document,
65
        ?ResolverRegistryInterface $resolverRegistry,
66
        array $options
67
    ): ExtensionContextInterface {
68
        /** @noinspection PhpUnhandledExceptionInspection */
69
        $info = $this->createInfo($schema, $document);
70
71
        // Context has to be created in order to create the definition builder,
72
        // because we are using its `resolveType` function to resolve types.
73
        $context = new ExtensionContext($info);
74
75
        /** @noinspection PhpUnhandledExceptionInspection */
76
        $definitionBuilder = new DefinitionBuilder(
77
            $info->getTypeDefinitionMap(),
78
            $resolverRegistry,
79
            $options['types'] ?? [],
80
            $options['directives'] ?? [],
81
            [$context, 'resolveType']
82
        );
83
84
        return $context->setDefinitionBuilder($definitionBuilder);
85
    }
86
87
    /**
88
     * @param Schema       $schema
89
     * @param DocumentNode $document
90
     * @return ExtendInfo
91
     * @throws SchemaExtensionException
92
     */
93
    protected function createInfo(Schema $schema, DocumentNode $document): ExtendInfo
94
    {
95
        $typeDefinitionMap    = [];
96
        $typeExtensionsMap    = [];
97
        $directiveDefinitions = [];
98
        $schemaExtensions     = [];
99
100
        foreach ($document->getDefinitions() as $definition) {
101
            if ($definition instanceof SchemaDefinitionNode) {
102
                // Sanity check that a schema extension is not defining a new schema
103
                throw new SchemaExtensionException('Cannot define a new schema within a schema extension.', [$definition]);
104
            }
105
106
            if ($definition instanceof SchemaExtensionNode) {
107
                $schemaExtensions[] = $definition;
108
109
                continue;
110
            }
111
112
            if ($definition instanceof DirectiveDefinitionNode) {
113
                $directiveName     = $definition->getNameValue();
114
                $existingDirective = $schema->getDirective($directiveName);
0 ignored issues
show
Bug introduced by
It seems like $directiveName can also be of type null; however, parameter $name of Digia\GraphQL\Schema\Schema::getDirective() 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

114
                $existingDirective = $schema->getDirective(/** @scrutinizer ignore-type */ $directiveName);
Loading history...
115
116
                if (null !== $existingDirective) {
117
                    throw new SchemaExtensionException(
118
                        \sprintf(
119
                            'Directive "%s" already exists in the schema. It cannot be redefined.',
120
                            $directiveName
121
                        ),
122
                        [$definition]
123
                    );
124
                }
125
126
                $directiveDefinitions[] = $definition;
127
128
                continue;
129
            }
130
131
            if ($definition instanceof TypeSystemDefinitionNodeInterface && $definition instanceof NameAwareInterface) {
132
                // Sanity check that none of the defined types conflict with the schema's existing types.
133
                $typeName     = $definition->getNameValue();
134
                $existingType = $schema->getType($typeName);
0 ignored issues
show
Bug introduced by
It seems like $typeName can also be of type null; however, parameter $name of Digia\GraphQL\Schema\Schema::getType() 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

134
                $existingType = $schema->getType(/** @scrutinizer ignore-type */ $typeName);
Loading history...
135
136
                if (null !== $existingType) {
137
                    throw new SchemaExtensionException(
138
                        \sprintf(
139
                            'Type "%s" already exists in the schema. It cannot also ' .
140
                            'be defined in this type definition.',
141
                            $typeName
142
                        ),
143
                        [$definition]
144
                    );
145
                }
146
147
                $typeDefinitionMap[$typeName] = $definition;
148
149
                continue;
150
            }
151
152
            if ($definition instanceof ObjectTypeExtensionNode || $definition instanceof InterfaceTypeExtensionNode) {
153
                // Sanity check that this type extension exists within the schema's existing types.
154
                $extendedTypeName = $definition->getNameValue();
155
                $existingType     = $schema->getType($extendedTypeName);
156
157
                if (null === $existingType) {
158
                    throw new SchemaExtensionException(
159
                        \sprintf(
160
                            'Cannot extend type "%s" because it does not exist in the existing schema.',
161
                            $extendedTypeName
162
                        ),
163
                        [$definition]
164
                    );
165
                }
166
167
                $this->checkExtensionNode($existingType, $definition);
168
169
                $typeExtensionsMap[$extendedTypeName] = \array_merge(
170
                    $typeExtensionsMap[$extendedTypeName] ?? [],
171
                    [$definition]
172
                );
173
174
                continue;
175
            }
176
177
            if ($definition instanceof ScalarTypeExtensionNode ||
178
                $definition instanceof UnionTypeExtensionNode ||
179
                $definition instanceof EnumTypeExtensionNode ||
180
                $definition instanceof InputObjectTypeExtensionNode) {
181
                throw new SchemaExtensionException(
182
                    \sprintf('The %s kind is not yet supported by extendSchema().', $definition->getKind())
183
                );
184
            }
185
        }
186
187
        return new ExtendInfo(
188
            $schema,
189
            $document,
190
            $typeDefinitionMap,
191
            $typeExtensionsMap,
192
            $directiveDefinitions,
193
            $schemaExtensions
194
        );
195
    }
196
197
    /**
198
     * @param TypeInterface $type
199
     * @param NodeInterface $node
200
     * @throws SchemaExtensionException
201
     */
202
    protected function checkExtensionNode(TypeInterface $type, NodeInterface $node): void
203
    {
204
        if ($node instanceof ObjectTypeExtensionNode && !($type instanceof ObjectType)) {
205
            throw new SchemaExtensionException(
206
                \sprintf('Cannot extend non-object type "%s".', toString($type)),
207
                [$node]
208
            );
209
        }
210
211
        if ($node instanceof InterfaceTypeExtensionNode && !($type instanceof InterfaceType)) {
212
            throw new SchemaExtensionException(
213
                \sprintf('Cannot extend non-interface type "%s".', toString($type)),
214
                [$node]
215
            );
216
        }
217
    }
218
}
219