Completed
Push — master ( f36e2b...cea2fe )
by Christoffer
02:19
created

IntrospectionTypesProvider   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 397
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 397
rs 10
c 0
b 0
f 0
wmc 19

1 Method

Rating   Name   Duplication   Size   Complexity  
D register() 0 377 19
1
<?php
2
3
namespace Digia\GraphQL\Provider;
4
5
use Digia\GraphQL\GraphQL;
6
use Digia\GraphQL\Language\AST\DirectiveLocationEnum;
7
use Digia\GraphQL\Type\Definition\AbstractType;
8
use Digia\GraphQL\Type\Definition\DirectiveInterface;
9
use Digia\GraphQL\Type\Definition\EnumType;
10
use Digia\GraphQL\Type\Definition\Field;
11
use Digia\GraphQL\Type\Definition\InputObjectType;
12
use Digia\GraphQL\Type\Definition\InterfaceType;
13
use Digia\GraphQL\Type\Definition\ListType;
14
use Digia\GraphQL\Type\Definition\NonNullType;
15
use Digia\GraphQL\Type\Definition\ObjectType;
16
use Digia\GraphQL\Type\Definition\ScalarType;
17
use Digia\GraphQL\Type\Definition\TypeInterface;
18
use Digia\GraphQL\Type\Definition\UnionType;
19
use Digia\GraphQL\Type\SchemaInterface;
20
use Digia\GraphQL\Type\TypeKindEnum;
21
use League\Container\ServiceProvider\AbstractServiceProvider;
22
use function Digia\GraphQL\Type\__Directive;
23
use function Digia\GraphQL\Type\__DirectiveLocation;
24
use function Digia\GraphQL\Type\__EnumValue;
25
use function Digia\GraphQL\Type\__Field;
26
use function Digia\GraphQL\Type\__InputValue;
27
use function Digia\GraphQL\Type\__Type;
28
use function Digia\GraphQL\Type\__TypeKind;
29
use function Digia\GraphQL\Type\GraphQLBoolean;
30
use function Digia\GraphQL\Type\GraphQLEnumType;
31
use function Digia\GraphQL\Type\GraphQLList;
32
use function Digia\GraphQL\Type\GraphQLNonNull;
33
use function Digia\GraphQL\Type\GraphQLObjectType;
34
use function Digia\GraphQL\Type\GraphQLString;
35
36
class IntrospectionTypesProvider extends AbstractServiceProvider
37
{
38
39
    /**
40
     * @var array
41
     */
42
    protected $provides = [
43
        GraphQL::INTROSPECTION_SCHEMA,
44
        GraphQL::INTROSPECTION_DIRECTIVE,
45
        GraphQL::INTROSPECTION_DIRECTIVE_LOCATION,
46
        GraphQL::INTROSPECTION_TYPE,
47
        GraphQL::INTROSPECTION_FIELD,
48
        GraphQL::INTROSPECTION_INPUT_VALUE,
49
        GraphQL::INTROSPECTION_ENUM_VALUE,
50
        GraphQL::INTROSPECTION_TYPE_KIND,
51
    ];
52
53
    /**
54
     * @inheritdoc
55
     */
56
    public function register()
57
    {
58
        $this->container->add(GraphQL::INTROSPECTION_SCHEMA, function () {
59
            return GraphQLObjectType([
60
                'name'            => GraphQL::INTROSPECTION_SCHEMA,
61
                'isIntrospection' => true,
62
                'description'     =>
63
                    'A GraphQL Schema defines the capabilities of a GraphQL server. It ' .
64
                    'exposes all available types and directives on the server, as well as ' .
65
                    'the entry points for query, mutation, and subscription operations.',
66
                'fields'          => function () {
67
                    return [
68
                        'types'            => [
69
                            'description' => 'A list of all types supported by this server.',
70
                            'type'        => GraphQLNonNull(GraphQLList(GraphQLNonNull(__Type()))),
71
                            'resolve'     => function (SchemaInterface $schema): array {
72
                                return array_values($schema->getTypeMap());
73
                            },
74
                        ],
75
                        'queryType'        => [
76
                            'description' => 'The type that query operations will be rooted at.',
77
                            'type'        => GraphQLNonNull(__Type()),
78
                            'resolve'     => function (SchemaInterface $schema): ObjectType {
79
                                return $schema->getQuery();
80
                            },
81
                        ],
82
                        'mutationType'     => [
83
                            'description' =>
84
                                'If this server supports mutation, the type that ' .
85
                                'mutation operations will be rooted at.',
86
                            'type'        => __Type(),
87
                            'resolve'     => function (SchemaInterface $schema): ObjectType {
88
                                return $schema->getMutation();
89
                            },
90
                        ],
91
                        'subscriptionType' => [
92
                            'description' =>
93
                                'If this server support subscription, the type that ' .
94
                                'subscription operations will be rooted at.',
95
                            'type'        => __Type(),
96
                            'resolve'     => function (SchemaInterface $schema): ObjectType {
97
                                return $schema->getSubscription();
98
                            },
99
                        ],
100
                        'directives'       => [
101
                            'description' => 'A list of all directives supported by this server.',
102
                            'type'        => GraphQLNonNull(GraphQLList(GraphQLNonNull(__Directive()))),
103
                            'resolve'     => function (SchemaInterface $schema): array {
104
                                return $schema->getDirectives();
105
                            },
106
                        ],
107
                    ];
108
                }
109
            ]);
110
        }, true/* $shared */);
111
112
        $this->container->add(GraphQL::INTROSPECTION_DIRECTIVE, function () {
113
            return GraphQLObjectType([
114
                'name'            => GraphQL::INTROSPECTION_DIRECTIVE,
115
                'isIntrospection' => true,
116
                'description'     =>
117
                    'A Directive provides a way to describe alternate runtime execution and ' .
118
                    'type validation behavior in a GraphQL document.' .
119
                    "\n\nIn some cases, you need to provide options to alter GraphQL's " .
120
                    'execution behavior in ways field arguments will not suffice, such as ' .
121
                    'conditionally including or skipping a field. Directives provide this by ' .
122
                    'describing additional information to the executor.',
123
                'fields'          => function () {
124
                    return [
125
                        'name'        => ['type' => GraphQLNonNull(GraphQLString())],
126
                        'description' => ['type' => GraphQLString()],
127
                        'locations'   => [
128
                            'type' => GraphQLNonNull(GraphQLList(GraphQLNonNull(__DirectiveLocation()))),
129
                        ],
130
                        'args'        => [
131
                            'type'    => GraphQLNonNull(GraphQLList(GraphQLNonNull(__InputValue()))),
132
                            'resolve' => function (DirectiveInterface $directive): array {
133
                                return $directive->getArguments() ?: [];
134
                            },
135
                        ],
136
                    ];
137
                }
138
            ]);
139
        }, true/* $shared */);
140
141
        $this->container->add(GraphQL::INTROSPECTION_DIRECTIVE_LOCATION, function () {
142
            return GraphQLEnumType([
143
                'name'            => GraphQL::INTROSPECTION_DIRECTIVE_LOCATION,
144
                'isIntrospection' => true,
145
                'description'     =>
146
                    'A Directive can be adjacent to many parts of the GraphQL language, a ' .
147
                    '__DirectiveLocation describes one such possible adjacencies.',
148
                'values'          => [
149
                    DirectiveLocationEnum::QUERY                  => [
150
                        'description' => 'Location adjacent to a query operation.',
151
                    ],
152
                    DirectiveLocationEnum::MUTATION               => [
153
                        'description' => 'Location adjacent to a mutation operation.',
154
                    ],
155
                    DirectiveLocationEnum::SUBSCRIPTION           => [
156
                        'description' => 'Location adjacent to a subscription operation.',
157
                    ],
158
                    DirectiveLocationEnum::FIELD                  => [
159
                        'description' => 'Location adjacent to a field.',
160
                    ],
161
                    DirectiveLocationEnum::FRAGMENT_DEFINITION    => [
162
                        'description' => 'Location adjacent to a fragment definition.',
163
                    ],
164
                    DirectiveLocationEnum::FRAGMENT_SPREAD        => [
165
                        'description' => 'Location adjacent to a fragment spread.',
166
                    ],
167
                    DirectiveLocationEnum::INLINE_FRAGMENT        => [
168
                        'description' => 'Location adjacent to an inline fragment.',
169
                    ],
170
                    DirectiveLocationEnum::SCHEMA                 => [
171
                        'description' => 'Location adjacent to a schema definition.',
172
                    ],
173
                    DirectiveLocationEnum::SCALAR                 => [
174
                        'description' => 'Location adjacent to a scalar definition.',
175
                    ],
176
                    DirectiveLocationEnum::OBJECT                 => [
177
                        'description' => 'Location adjacent to an object type definition.',
178
                    ],
179
                    DirectiveLocationEnum::FIELD_DEFINITION       => [
180
                        'description' => 'Location adjacent to a field definition.',
181
                    ],
182
                    DirectiveLocationEnum::ARGUMENT_DEFINITION    => [
183
                        'description' => 'Location adjacent to an argument definition.',
184
                    ],
185
                    DirectiveLocationEnum::INTERFACE              => [
186
                        'description' => 'Location adjacent to an interface definition.',
187
                    ],
188
                    DirectiveLocationEnum::UNION                  => [
189
                        'description' => 'Location adjacent to a union definition.',
190
                    ],
191
                    DirectiveLocationEnum::ENUM                   => [
192
                        'description' => 'Location adjacent to an enum definition.',
193
                    ],
194
                    DirectiveLocationEnum::ENUM_VALUE             => [
195
                        'description' => 'Location adjacent to an enum value definition.',
196
                    ],
197
                    DirectiveLocationEnum::INPUT_OBJECT           => [
198
                        'description' => 'Location adjacent to an input object type definition.',
199
                    ],
200
                    DirectiveLocationEnum::INPUT_FIELD_DEFINITION => [
201
                        'description' => 'Location adjacent to an input object field definition.',
202
                    ],
203
                ],
204
            ]);
205
        }, true/* $shared */);
206
207
        $this->container->add(GraphQL::INTROSPECTION_TYPE, function () {
208
            return GraphQLObjectType([
209
                'name'            => GraphQL::INTROSPECTION_TYPE,
210
                'isIntrospection' => true,
211
                'description'     =>
212
                    'The fundamental unit of any GraphQL Schema is the type. There are ' .
213
                    'many kinds of types in GraphQL as represented by the `__TypeKind` enum.' .
214
                    '\n\nDepending on the kind of a type, certain fields describe ' .
215
                    'information about that type. Scalar types provide no information ' .
216
                    'beyond a name and description, while Enum types provide their values. ' .
217
                    'Object and Interface types provide the fields they describe. Abstract ' .
218
                    'types, Union and Interface, provide the Object types possible ' .
219
                    'at runtime. List and NonNull types compose other types.',
220
                'fields'          => function () {
221
                    return [
222
                        'kind'          => [
223
                            'type'    => GraphQLNonNull(__TypeKind()),
224
                            'resolve' => function (TypeInterface $type) {
225
                                if ($type instanceof ScalarType) {
226
                                    return TypeKindEnum::SCALAR;
227
                                }
228
                                if ($type instanceof ObjectType) {
229
                                    return TypeKindEnum::OBJECT;
230
                                }
231
                                if ($type instanceof InterfaceType) {
232
                                    return TypeKindEnum::INTERFACE;
233
                                }
234
                                if ($type instanceof UnionType) {
235
                                    return TypeKindEnum::UNION;
236
                                }
237
                                if ($type instanceof EnumType) {
238
                                    return TypeKindEnum::ENUM;
239
                                }
240
                                if ($type instanceof InputObjectType) {
241
                                    return TypeKindEnum::INPUT_OBJECT;
242
                                }
243
                                if ($type instanceof ListType) {
244
                                    return TypeKindEnum::LIST;
245
                                }
246
                                if ($type instanceof NonNullType) {
247
                                    return TypeKindEnum::NON_NULL;
248
                                }
249
250
                                throw new \Exception(sprintf('Unknown kind of type: %s', $type));
0 ignored issues
show
Bug introduced by
$type of type Digia\GraphQL\Type\Definition\TypeInterface is incompatible with the type string expected by parameter $args of sprintf(). ( Ignorable by Annotation )

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

250
                                throw new \Exception(sprintf('Unknown kind of type: %s', /** @scrutinizer ignore-type */ $type));
Loading history...
251
                            },
252
                        ],
253
                        'name'          => ['type' => GraphQLString()],
254
                        'description'   => ['type' => GraphQLString()],
255
                        'fields'        => [
256
                            'type'    => GraphQLList(GraphQLNonNull(__Field())),
257
                            'args'    => [
258
                                'includeDeprecated' => ['type' => GraphQLBoolean(), 'defaultValue' => false],
259
                            ],
260
                            'resolve' => function (TypeInterface $type, array $args): ?array {
261
                                [$includeDeprecated] = $args;
262
263
                                if ($type instanceof ObjectType || $type instanceof InterfaceType) {
264
                                    $fields = array_values($type->getFields());
265
266
                                    if (!$includeDeprecated) {
267
                                        $fields = array_filter($fields, function (Field $field) {
268
                                            return !$field->isDeprecated();
269
                                        });
270
                                    }
271
272
                                    return $fields;
273
                                }
274
275
                                return null;
276
                            },
277
                        ],
278
                        'interfaces'    => [
279
                            'type'    => GraphQLList(GraphQLNonNull(__Type())),
280
                            'resolve' => function (TypeInterface $type): ?array {
281
                                return $type instanceof ObjectType ? $type->getInterfaces() : null;
282
                            },
283
                        ],
284
                        'possibleTypes' => [
285
                            'type'    => GraphQLList(GraphQLNonNull(__Type())),
286
                            'resolve' => function (TypeInterface $type, $args, $context, $info): ?array {
287
                                /** @var SchemaInterface $schema */
288
                                [$schema] = $info;
289
                                /** @noinspection PhpParamsInspection */
290
                                return $type instanceof AbstractType ? $schema->getPossibleTypes($type) : null;
291
                            },
292
                        ],
293
                        'enumValues'    => [
294
                            'type'    => GraphQLList(GraphQLNonNull(__EnumValue())),
295
                            'args'    => [
296
                                'includeDeprecated' => ['type' => GraphQLBoolean(), 'defaultValue' => false],
297
                            ],
298
                            'resolve' => function (TypeInterface $type, array $args): ?array {
299
                                [$includeDeprecated] = $args;
300
301
                                if ($type instanceof EnumType) {
302
                                    $values = array_values($type->getValues());
303
304
                                    if (!$includeDeprecated) {
305
                                        $values = array_filter($values, function (Field $field) {
306
                                            return !$field->isDeprecated();
307
                                        });
308
                                    }
309
310
                                    return $values;
311
                                }
312
313
                                return null;
314
                            },
315
                        ],
316
                        'inputFields'   => [
317
                            'type'    => GraphQLList(GraphQLNonNull(__InputValue())),
318
                            'resolve' => function (TypeInterface $type): ?array {
319
                                return $type instanceof InputObjectType ? $type->getFields() : null;
320
                            },
321
                        ],
322
                        'ofType'        => ['type' => __Type()],
323
                    ];
324
                }
325
            ]);
326
        }, true/* $shared */);
327
328
        $this->container->add(GraphQL::INTROSPECTION_FIELD, function () {
329
            return GraphQLObjectType([
330
                'name'            => GraphQL::INTROSPECTION_FIELD,
331
                'isIntrospection' => true,
332
                'description'     =>
333
                    'Object and Interface types are described by a list of Fields, each of ' .
334
                    'which has a name, potentially a list of arguments, and a return type.',
335
                'fields'          => function () {
336
                    return [
337
                        'name'              => ['type' => GraphQLNonNull(GraphQLString())],
338
                        'description'       => ['type' => GraphQLString()],
339
                        'args'              => [
340
                            'type'    => GraphQLNonNull(GraphQLList(GraphQLNonNull(__InputValue()))),
341
                            'resolve' => function (DirectiveInterface $directive): array {
342
                                return $directive->getArguments() ?: [];
343
                            },
344
                        ],
345
                        'type'              => ['type' => GraphQLNonNull(__Type())],
346
                        'isDeprecated'      => ['type' => GraphQLNonNull(GraphQLBoolean())],
347
                        'deprecationReason' => ['type' => GraphQLString()],
348
                    ];
349
                }
350
            ]);
351
        }, true/* $shared */);
352
353
        $this->container->add(GraphQL::INTROSPECTION_INPUT_VALUE, function () {
354
            return GraphQLObjectType([
355
                'name'            => GraphQL::INTROSPECTION_INPUT_VALUE,
356
                'isIntrospection' => true,
357
                'description'     =>
358
                    'Arguments provided to Fields or Directives and the input fields of an ' .
359
                    'InputObject are represented as Input Values which describe their type ' .
360
                    'and optionally a default value.',
361
                'fields'          => function () {
362
                    return [
363
                        'name'         => ['type' => GraphQLNonNull(GraphQLString())],
364
                        'description'  => ['type' => GraphQLString()],
365
                        'type'         => ['type' => GraphQLNonNull(__Type())],
366
                        'defaultValue' => [
367
                            'type'        => GraphQLString(),
368
                            'description' =>
369
                                'A GraphQL-formatted string representing the default value for this ' .
370
                                'input value.',
371
                            'resolve'     => function ($inputValue) {
0 ignored issues
show
Unused Code introduced by
The parameter $inputValue is not used and could be removed. ( Ignorable by Annotation )

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

371
                            'resolve'     => function (/** @scrutinizer ignore-unused */ $inputValue) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
372
                                // TODO: Implement this when we have support for printing AST.
373
                                return null;
374
                            }
375
                        ],
376
                    ];
377
                }
378
            ]);
379
        }, true/* $shared */);
380
381
        $this->container->add(GraphQL::INTROSPECTION_ENUM_VALUE, function () {
382
            return GraphQLObjectType([
383
                'name'            => GraphQL::INTROSPECTION_ENUM_VALUE,
384
                'isIntrospection' => true,
385
                'description'     =>
386
                    'One possible value for a given Enum. Enum values are unique values, not ' .
387
                    'a placeholder for a string or numeric value. However an Enum value is ' .
388
                    'returned in a JSON response as a string.',
389
                'fields'          => function () {
390
                    return [
391
                        'name'              => ['type' => GraphQLNonNull(GraphQLString())],
392
                        'description'       => ['type' => GraphQLString()],
393
                        'isDeprecated'      => ['type' => GraphQLNonNull(GraphQLBoolean())],
394
                        'deprecationReason' => ['type' => GraphQLString()],
395
                    ];
396
                }
397
            ]);
398
        }, true/* $shared */);
399
400
        $this->container->add(GraphQL::INTROSPECTION_TYPE_KIND, function () {
401
            return GraphQLEnumType([
402
                'name'            => GraphQL::INTROSPECTION_TYPE_KIND,
403
                'isIntrospection' => true,
404
                'description'     => 'An enum describing what kind of type a given `__Type` is.',
405
                'values'          => [
406
                    TypeKindEnum::SCALAR       => [
407
                        'description' => 'Indicates this type is a scalar.',
408
                    ],
409
                    TypeKindEnum::OBJECT       => [
410
                        'description' => 'Indicates this type is an object. `fields` and `interfaces` are valid fields.',
411
                    ],
412
                    TypeKindEnum::INTERFACE    => [
413
                        'description' => 'Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.',
414
                    ],
415
                    TypeKindEnum::UNION        => [
416
                        'description' => 'Indicates this type is a union. `possibleTypes` is a valid field.',
417
                    ],
418
                    TypeKindEnum::ENUM         => [
419
                        'description' => 'Indicates this type is an enum. `enumValues` is a valid field.',
420
                    ],
421
                    TypeKindEnum::INPUT_OBJECT => [
422
                        'description' => 'Indicates this type is an input object. `inputFields` is a valid field.',
423
                    ],
424
                    TypeKindEnum::LIST         => [
425
                        'description' => 'Indicates this type is a list. `ofType` is a valid field.',
426
                    ],
427
                    TypeKindEnum::NON_NULL     => [
428
                        'description' => 'Indicates this type is a non-null. `ofType` is a valid field.',
429
                    ],
430
                ],
431
            ]);
432
        }, true/* $shared */);
433
    }
434
}
435