registerIntrospectionTypes()   F
last analyzed

Complexity

Conditions 18
Paths 1

Size

Total Lines 390
Code Lines 270

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 18
eloc 270
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 390
rs 3.8933

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\Type;
4
5
use Digia\GraphQL\Error\InvalidTypeException;
6
use Digia\GraphQL\Execution\ResolveInfo;
7
use Digia\GraphQL\GraphQL;
8
use Digia\GraphQL\Language\DirectiveLocationEnum;
9
use Digia\GraphQL\Schema\Schema;
10
use Digia\GraphQL\Type\Definition\AbstractTypeInterface;
11
use Digia\GraphQL\Type\Definition\ArgumentsAwareInterface;
12
use Digia\GraphQL\Type\Definition\Directive;
13
use Digia\GraphQL\Type\Definition\EnumType;
14
use Digia\GraphQL\Type\Definition\Field;
15
use Digia\GraphQL\Type\Definition\InputObjectType;
16
use Digia\GraphQL\Type\Definition\InterfaceType;
17
use Digia\GraphQL\Type\Definition\ListType;
18
use Digia\GraphQL\Type\Definition\NonNullType;
19
use Digia\GraphQL\Type\Definition\ObjectType;
20
use Digia\GraphQL\Type\Definition\ScalarType;
21
use Digia\GraphQL\Type\Definition\TypeInterface;
22
use Digia\GraphQL\Type\Definition\UnionType;
23
use League\Container\ServiceProvider\AbstractServiceProvider;
24
25
class IntrospectionProvider extends AbstractServiceProvider
26
{
27
    /**
28
     * @var array
29
     */
30
    protected $provides = [
31
        // Introspection types
32
        GraphQL::SCHEMA_INTROSPECTION,
33
        GraphQL::DIRECTIVE_INTROSPECTION,
34
        GraphQL::DIRECTIVE_LOCATION_INTROSPECTION,
35
        GraphQL::TYPE_INTROSPECTION,
36
        GraphQL::FIELD_INTROSPECTION,
37
        GraphQL::INPUT_VALUE_INTROSPECTION,
38
        GraphQL::ENUM_VALUE_INTROSPECTION,
39
        GraphQL::TYPE_KIND_INTROSPECTION,
40
        // Meta fields
41
        GraphQL::SCHEMA_META_FIELD_DEFINITION,
42
        GraphQL::TYPE_META_FIELD_DEFINITION,
43
        GraphQL::TYPE_NAME_META_FIELD_DEFINITION,
44
    ];
45
46
    /**
47
     * @inheritdoc
48
     */
49
    public function register()
50
    {
51
        $this->registerIntrospectionTypes();
52
        $this->registerMetaFields();
53
    }
54
55
    /**
56
     * Registers the introspection types with the container.
57
     */
58
    protected function registerIntrospectionTypes()
59
    {
60
        $this->container
61
            ->share(GraphQL::SCHEMA_INTROSPECTION, function () {
0 ignored issues
show
Bug introduced by
The method share() does not exist on Psr\Container\ContainerInterface. It seems like you code against a sub-type of Psr\Container\ContainerInterface such as League\Container\Container. ( Ignorable by Annotation )

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

61
            ->/** @scrutinizer ignore-call */ 
62
              share(GraphQL::SCHEMA_INTROSPECTION, function () {
Loading history...
62
                return newObjectType([
63
                    'name'            => GraphQL::SCHEMA_INTROSPECTION,
64
                    'isIntrospection' => true,
65
                    'description'     =>
66
                        'A GraphQL Schema defines the capabilities of a GraphQL server. It ' .
67
                        'exposes all available types and directives on the server, as well as ' .
68
                        'the entry points for query, mutation, and subscription operations.',
69
                    'fields'          => function () {
70
                        return [
71
                            'types'            => [
72
                                'description' => 'A list of all types supported by this server.',
73
                                'type'        => newNonNull(newList(newNonNull(__Type()))),
74
                                'resolve'     => function (Schema $schema): array {
75
                                    return \array_values($schema->getTypeMap());
76
                                },
77
                            ],
78
                            'queryType'        => [
79
                                'description' => 'The type that query operations will be rooted at.',
80
                                'type'        => newNonNull(__Type()),
81
                                'resolve'     => function (Schema $schema): ?TypeInterface {
82
                                    return $schema->getQueryType();
83
                                },
84
                            ],
85
                            'mutationType'     => [
86
                                'description' =>
87
                                    'If this server supports mutation, the type that ' .
88
                                    'mutation operations will be rooted at.',
89
                                'type'        => __Type(),
90
                                'resolve'     => function (Schema $schema): ?TypeInterface {
91
                                    return $schema->getMutationType();
92
                                },
93
                            ],
94
                            'subscriptionType' => [
95
                                'description' =>
96
                                    'If this server support subscription, the type that ' .
97
                                    'subscription operations will be rooted at.',
98
                                'type'        => __Type(),
99
                                'resolve'     => function (Schema $schema): ?TypeInterface {
100
                                    return $schema->getSubscriptionType();
101
                                },
102
                            ],
103
                            'directives'       => [
104
                                'description' => 'A list of all directives supported by this server.',
105
                                'type'        => newNonNull(newList(newNonNull(__Directive()))),
106
                                'resolve'     => function (Schema $schema): array {
107
                                    return $schema->getDirectives();
108
                                },
109
                            ],
110
                        ];
111
                    }
112
                ]);
113
            });
114
115
        $this->container
116
            ->share(GraphQL::DIRECTIVE_INTROSPECTION, function () {
117
                return newObjectType([
118
                    'name'            => GraphQL::DIRECTIVE_INTROSPECTION,
119
                    'isIntrospection' => true,
120
                    'description'     =>
121
                        'A Directive provides a way to describe alternate runtime execution and ' .
122
                        'type validation behavior in a GraphQL document.' .
123
                        "\n\nIn some cases, you need to provide options to alter GraphQL's " .
124
                        'execution behavior in ways field arguments will not suffice, such as ' .
125
                        'conditionally including or skipping a field. Directives provide this by ' .
126
                        'describing additional information to the executor.',
127
                    'fields'          => function () {
128
                        return [
129
                            'name'        => ['type' => newNonNull(stringType())],
130
                            'description' => ['type' => stringType()],
131
                            'locations'   => [
132
                                'type' => newNonNull(newList(newNonNull(__DirectiveLocation()))),
133
                            ],
134
                            'args'        => [
135
                                'type'    => newNonNull(newList(newNonNull(__InputValue()))),
136
                                'resolve' => function (Directive $directive): array {
137
                                    return $directive->getArguments() ?: [];
138
                                },
139
                            ],
140
                        ];
141
                    }
142
                ]);
143
            });
144
145
        $this->container
146
            ->share(GraphQL::DIRECTIVE_LOCATION_INTROSPECTION, function () {
147
                return newEnumType([
148
                    'name'            => GraphQL::DIRECTIVE_LOCATION_INTROSPECTION,
149
                    'isIntrospection' => true,
150
                    'description'     =>
151
                        'A Directive can be adjacent to many parts of the GraphQL language, a ' .
152
                        '__DirectiveLocation describes one such possible adjacencies.',
153
                    'values'          => [
154
                        DirectiveLocationEnum::QUERY                  => [
155
                            'description' => 'Location adjacent to a query operation.',
156
                        ],
157
                        DirectiveLocationEnum::MUTATION               => [
158
                            'description' => 'Location adjacent to a mutation operation.',
159
                        ],
160
                        DirectiveLocationEnum::SUBSCRIPTION           => [
161
                            'description' => 'Location adjacent to a subscription operation.',
162
                        ],
163
                        DirectiveLocationEnum::FIELD                  => [
164
                            'description' => 'Location adjacent to a field.',
165
                        ],
166
                        DirectiveLocationEnum::FRAGMENT_DEFINITION    => [
167
                            'description' => 'Location adjacent to a fragment definition.',
168
                        ],
169
                        DirectiveLocationEnum::FRAGMENT_SPREAD        => [
170
                            'description' => 'Location adjacent to a fragment spread.',
171
                        ],
172
                        DirectiveLocationEnum::INLINE_FRAGMENT        => [
173
                            'description' => 'Location adjacent to an inline fragment.',
174
                        ],
175
                        DirectiveLocationEnum::VARIABLE_DEFINITION => [
176
                            'description' => 'Location adjacent to a variable definition.',
177
                        ],
178
                        DirectiveLocationEnum::SCHEMA                 => [
179
                            'description' => 'Location adjacent to a schema definition.',
180
                        ],
181
                        DirectiveLocationEnum::SCALAR                 => [
182
                            'description' => 'Location adjacent to a scalar definition.',
183
                        ],
184
                        DirectiveLocationEnum::OBJECT                 => [
185
                            'description' => 'Location adjacent to an object type definition.',
186
                        ],
187
                        DirectiveLocationEnum::FIELD_DEFINITION       => [
188
                            'description' => 'Location adjacent to a field definition.',
189
                        ],
190
                        DirectiveLocationEnum::ARGUMENT_DEFINITION    => [
191
                            'description' => 'Location adjacent to an argument definition.',
192
                        ],
193
                        DirectiveLocationEnum::INTERFACE              => [
194
                            'description' => 'Location adjacent to an interface definition.',
195
                        ],
196
                        DirectiveLocationEnum::UNION                  => [
197
                            'description' => 'Location adjacent to a union definition.',
198
                        ],
199
                        DirectiveLocationEnum::ENUM                   => [
200
                            'description' => 'Location adjacent to an enum definition.',
201
                        ],
202
                        DirectiveLocationEnum::ENUM_VALUE             => [
203
                            'description' => 'Location adjacent to an enum value definition.',
204
                        ],
205
                        DirectiveLocationEnum::INPUT_OBJECT           => [
206
                            'description' => 'Location adjacent to an input object type definition.',
207
                        ],
208
                        DirectiveLocationEnum::INPUT_FIELD_DEFINITION => [
209
                            'description' => 'Location adjacent to an input object field definition.',
210
                        ],
211
                    ],
212
                ]);
213
            });
214
215
        $this->container
216
            ->share(GraphQL::TYPE_INTROSPECTION, function () {
217
                return newObjectType([
218
                    'name'            => GraphQL::TYPE_INTROSPECTION,
219
                    'isIntrospection' => true,
220
                    'description'     =>
221
                        'The fundamental unit of any GraphQL Schema is the type. There are many kinds of ' .
222
                        "types in GraphQL as represented by the `__TypeKind` enum.\n\n" .
223
                        'Depending on the kind of a type, certain fields describe information about that ' .
224
                        'type. Scalar types provide no information beyond a name and description, while ' .
225
                        'Enum types provide their values. Object and Interface types provide the fields ' .
226
                        'they describe. Abstract types, Union and Interface, provide the Object types ' .
227
                        'possible at runtime. List and NonNull types compose other types.',
228
                    'fields'          => function () {
229
                        return [
230
                            'kind'          => [
231
                                'type'    => newNonNull(__TypeKind()),
232
                                'resolve' => function (TypeInterface $type) {
233
                                    if ($type instanceof ScalarType) {
234
                                        return TypeKindEnum::SCALAR;
235
                                    }
236
                                    if ($type instanceof ObjectType) {
237
                                        return TypeKindEnum::OBJECT;
238
                                    }
239
                                    if ($type instanceof InterfaceType) {
240
                                        return TypeKindEnum::INTERFACE;
241
                                    }
242
                                    if ($type instanceof UnionType) {
243
                                        return TypeKindEnum::UNION;
244
                                    }
245
                                    if ($type instanceof EnumType) {
246
                                        return TypeKindEnum::ENUM;
247
                                    }
248
                                    if ($type instanceof InputObjectType) {
249
                                        return TypeKindEnum::INPUT_OBJECT;
250
                                    }
251
                                    if ($type instanceof ListType) {
252
                                        return TypeKindEnum::LIST;
253
                                    }
254
                                    if ($type instanceof NonNullType) {
255
                                        return TypeKindEnum::NON_NULL;
256
                                    }
257
258
                                    throw new InvalidTypeException(\sprintf('Unknown kind of type: %s', (string)$type));
259
                                },
260
                            ],
261
                            'name'          => ['type' => stringType()],
262
                            'description'   => ['type' => stringType()],
263
                            'fields'        => [
264
                                'type'    => newList(newNonNull(__Field())),
265
                                'args'    => [
266
                                    'includeDeprecated' => ['type' => booleanType(), 'defaultValue' => false],
267
                                ],
268
                                'resolve' => function (TypeInterface $type, array $args):
269
                                ?array {
270
                                    ['includeDeprecated' => $includeDeprecated] = $args;
271
272
                                    if ($type instanceof ObjectType || $type instanceof InterfaceType) {
273
                                        $fields = \array_values($type->getFields());
274
275
                                        if (!$includeDeprecated) {
276
                                            $fields = \array_filter($fields, function (Field $field) {
277
                                                return !$field->isDeprecated();
278
                                            });
279
                                        }
280
281
                                        return $fields;
282
                                    }
283
284
                                    return null;
285
                                },
286
                            ],
287
                            'interfaces'    => [
288
                                'type'    => newList(newNonNull(__Type())),
289
                                'resolve' => function (TypeInterface $type): ?array {
290
                                    return $type instanceof ObjectType ? $type->getInterfaces() : null;
291
                                },
292
                            ],
293
                            'possibleTypes' => [
294
                                'type'    => newList(newNonNull(__Type())),
295
                                'resolve' => function (
296
                                    TypeInterface $type,
297
                                    array $args,
298
                                    array $context,
299
                                    ResolveInfo $info
300
                                ):
301
                                ?array {
302
                                    /** @var Schema $schema */
303
                                    $schema = $info->getSchema();
304
                                    /** @noinspection PhpParamsInspection */
305
                                    return $type instanceof AbstractTypeInterface ? $schema->getPossibleTypes($type) : null;
306
                                },
307
                            ],
308
                            'enumValues'    => [
309
                                'type'    => newList(newNonNull(__EnumValue())),
310
                                'args'    => [
311
                                    'includeDeprecated' => ['type' => booleanType(), 'defaultValue' => false],
312
                                ],
313
                                'resolve' => function (TypeInterface $type, array $args): ?array {
314
                                    ['includeDeprecated' => $includeDeprecated] = $args;
315
316
                                    if ($type instanceof EnumType) {
317
                                        $values = \array_values($type->getValues());
318
319
                                        if (!$includeDeprecated) {
320
                                            $values = \array_filter($values, function (Field $field) {
321
                                                return !$field->isDeprecated();
322
                                            });
323
                                        }
324
325
                                        return $values;
326
                                    }
327
328
                                    return null;
329
                                },
330
                            ],
331
                            'inputFields'   => [
332
                                'type'    => newList(newNonNull(__InputValue())),
333
                                'resolve' => function (TypeInterface $type): ?array {
334
                                    return $type instanceof InputObjectType ? $type->getFields() : null;
335
                                },
336
                            ],
337
                            'ofType'        => ['type' => __Type()],
338
                        ];
339
                    }
340
                ]);
341
            });
342
343
        $this->container
344
            ->share(GraphQL::FIELD_INTROSPECTION, function () {
345
                return newObjectType([
346
                    'name'            => GraphQL::FIELD_INTROSPECTION,
347
                    'isIntrospection' => true,
348
                    'description'     =>
349
                        'Object and Interface types are described by a list of Fields, each of ' .
350
                        'which has a name, potentially a list of arguments, and a return type.',
351
                    'fields'          => function () {
352
                        return [
353
                            'name'              => ['type' => newNonNull(stringType())],
354
                            'description'       => ['type' => stringType()],
355
                            'args'              => [
356
                                'type'    => newNonNull(newList(newNonNull(__InputValue()))),
357
                                'resolve' => function (ArgumentsAwareInterface $directive): array {
358
                                    return $directive->getArguments() ?? [];
359
                                },
360
                            ],
361
                            'type'              => ['type' => newNonNull(__Type())],
362
                            'isDeprecated'      => ['type' => newNonNull(booleanType())],
363
                            'deprecationReason' => ['type' => stringType()],
364
                        ];
365
                    }
366
                ]);
367
            });
368
369
        $this->container
370
            ->share(GraphQL::INPUT_VALUE_INTROSPECTION, function () {
371
                return newObjectType([
372
                    'name'            => GraphQL::INPUT_VALUE_INTROSPECTION,
373
                    'isIntrospection' => true,
374
                    'description'     =>
375
                        'Arguments provided to Fields or Directives and the input fields of an ' .
376
                        'InputObject are represented as Input Values which describe their type ' .
377
                        'and optionally a default value.',
378
                    'fields'          => function () {
379
                        return [
380
                            'name'         => ['type' => newNonNull(stringType())],
381
                            'description'  => ['type' => stringType()],
382
                            'type'         => ['type' => newNonNull(__Type())],
383
                            'defaultValue' => [
384
                                'type'        => stringType(),
385
                                'description' =>
386
                                    'A GraphQL-formatted string representing the default value for this ' .
387
                                    'input value.',
388
                                'resolve'     => function (/*$inputValue*/) {
389
                                    // TODO: Implement this when we have support for printing AST.
390
                                    return null;
391
                                }
392
                            ],
393
                        ];
394
                    }
395
                ]);
396
            });
397
398
        $this->container
399
            ->share(GraphQL::ENUM_VALUE_INTROSPECTION, function () {
400
                return newObjectType([
401
                    'name'            => GraphQL::ENUM_VALUE_INTROSPECTION,
402
                    'isIntrospection' => true,
403
                    'description'     =>
404
                        'One possible value for a given Enum. Enum values are unique values, not ' .
405
                        'a placeholder for a string or numeric value. However an Enum value is ' .
406
                        'returned in a JSON response as a string.',
407
                    'fields'          => function () {
408
                        return [
409
                            'name'              => ['type' => newNonNull(stringType())],
410
                            'description'       => ['type' => stringType()],
411
                            'isDeprecated'      => ['type' => newNonNull(booleanType())],
412
                            'deprecationReason' => ['type' => stringType()],
413
                        ];
414
                    }
415
                ]);
416
            });
417
418
        $this->container
419
            ->share(GraphQL::TYPE_KIND_INTROSPECTION, function () {
420
                return newEnumType([
421
                    'name'            => GraphQL::TYPE_KIND_INTROSPECTION,
422
                    'isIntrospection' => true,
423
                    'description'     => 'An enum describing what kind of type a given `__Type` is.',
424
                    'values'          => [
425
                        TypeKindEnum::SCALAR       => [
426
                            'description' => 'Indicates this type is a scalar.',
427
                        ],
428
                        TypeKindEnum::OBJECT       => [
429
                            'description' => 'Indicates this type is an object. `fields` and `interfaces` are valid fields.',
430
                        ],
431
                        TypeKindEnum::INTERFACE    => [
432
                            'description' => 'Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.',
433
                        ],
434
                        TypeKindEnum::UNION        => [
435
                            'description' => 'Indicates this type is a union. `possibleTypes` is a valid field.',
436
                        ],
437
                        TypeKindEnum::ENUM         => [
438
                            'description' => 'Indicates this type is an enum. `enumValues` is a valid field.',
439
                        ],
440
                        TypeKindEnum::INPUT_OBJECT => [
441
                            'description' => 'Indicates this type is an input object. `inputFields` is a valid field.',
442
                        ],
443
                        TypeKindEnum::LIST         => [
444
                            'description' => 'Indicates this type is a list. `ofType` is a valid field.',
445
                        ],
446
                        TypeKindEnum::NON_NULL     => [
447
                            'description' => 'Indicates this type is a non-null. `ofType` is a valid field.',
448
                        ],
449
                    ],
450
                ]);
451
            });
452
    }
453
454
    /**
455
     * Registers the introspection meta fields with the container.
456
     */
457
    protected function registerMetaFields()
458
    {
459
        $this->container
460
            ->share(GraphQL::SCHEMA_META_FIELD_DEFINITION, function ($__Schema) {
461
                return newField([
462
                    'name'        => '__schema',
463
                    'description' => 'Access the current type schema of this server.',
464
                    'type'        => newNonNull($__Schema),
465
                    'resolve'     => function ($source, $args, $context, ResolveInfo $info): Schema {
466
                        return $info->getSchema();
467
                    },
468
                ]);
469
            })
470
            ->addArgument(GraphQL::SCHEMA_INTROSPECTION);
471
472
        $this->container
473
            ->share(GraphQL::TYPE_META_FIELD_DEFINITION, function ($__Type) {
474
                return newField([
475
                    'name'        => '__type',
476
                    'description' => 'Request the type information of a single type.',
477
                    'type'        => $__Type,
478
                    'args'        => ['name' => ['type' => newNonNull(stringType())]],
479
                    'resolve'     => function ($source, $args, $context, ResolveInfo $info): ?TypeInterface {
480
                        ['name' => $name] = $args;
481
                        return $info->getSchema()->getType($name);
482
                    },
483
                ]);
484
            })
485
            ->addArgument(GraphQL::TYPE_INTROSPECTION);
486
487
        $this->container
488
            ->share(GraphQL::TYPE_NAME_META_FIELD_DEFINITION, function () {
489
                return newField([
490
                    'name'        => '__typename',
491
                    'description' => 'The name of the current Object type at runtime.',
492
                    'type'        => newNonNull(stringType()),
493
                    'resolve'     => function ($source, $args, $context, ResolveInfo $info): string {
494
                        return $info->getParentType()->getName();
495
                    },
496
                ]);
497
            });
498
    }
499
}
500