Passed
Push — master ( 42be8d...38dc72 )
by Christoffer
02:59
created

IntrospectionTypesProvider   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 396
Duplicated Lines 0 %

Importance

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

1 Method

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

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

356
                            '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...
357
                                // TODO: Implement this when we have support for printing AST.
358
                                return null;
359
                            }
360
                        ],
361
                    ];
362
                }
363
            ]);
364
        }, true/* $shared */);
365
366
        $this->container->add(GraphQL::INTROSPECTION_ENUM_VALUE, function () {
367
            return GraphQLObjectType([
368
                'name'            => GraphQL::INTROSPECTION_ENUM_VALUE,
369
                'isIntrospection' => true,
370
                'description'     =>
371
                    'One possible value for a given Enum. Enum values are unique values, not ' .
372
                    'a placeholder for a string or numeric value. However an Enum value is ' .
373
                    'returned in a JSON response as a string.',
374
                'fields'          => function () {
375
                    return [
376
                        'name'              => ['type' => GraphQLNonNull(GraphQLString())],
377
                        'description'       => ['type' => GraphQLString()],
378
                        'isDeprecated'      => ['type' => GraphQLNonNull(GraphQLBoolean())],
379
                        'deprecationReason' => ['type' => GraphQLString()],
380
                    ];
381
                }
382
            ]);
383
        }, true/* $shared */);
384
385
        $this->container->add(GraphQL::INTROSPECTION_TYPE_KIND, function () {
386
            return GraphQLEnumType([
387
                'name'            => GraphQL::INTROSPECTION_TYPE_KIND,
388
                'isIntrospection' => true,
389
                'description'     => 'An enum describing what kind of type a given `__Type` is.',
390
                'values'          => [
391
                    TypeKindEnum::SCALAR       => [
392
                        'description' => 'Indicates this type is a scalar.',
393
                    ],
394
                    TypeKindEnum::OBJECT       => [
395
                        'description' => 'Indicates this type is an object. `fields` and `interfaces` are valid fields.',
396
                    ],
397
                    TypeKindEnum::INTERFACE    => [
398
                        'description' => 'Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.',
399
                    ],
400
                    TypeKindEnum::UNION        => [
401
                        'description' => 'Indicates this type is a union. `possibleTypes` is a valid field.',
402
                    ],
403
                    TypeKindEnum::ENUM         => [
404
                        'description' => 'Indicates this type is an enum. `enumValues` is a valid field.',
405
                    ],
406
                    TypeKindEnum::INPUT_OBJECT => [
407
                        'description' => 'Indicates this type is an input object. `inputFields` is a valid field.',
408
                    ],
409
                    TypeKindEnum::LIST         => [
410
                        'description' => 'Indicates this type is a list. `ofType` is a valid field.',
411
                    ],
412
                    TypeKindEnum::NON_NULL     => [
413
                        'description' => 'Indicates this type is a non-null. `ofType` is a valid field.',
414
                    ],
415
                ],
416
            ]);
417
        }, true/* $shared */);
418
    }
419
}
420