FieldsBuilder   F
last analyzed

Complexity

Total Complexity 97

Size/Duplication

Total Lines 420
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 211
dl 0
loc 420
rs 2
c 1
b 0
f 0
wmc 97

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getMutationFields() 0 18 3
A getCollectionQueryFields() 0 15 4
A getItemQueryFields() 0 15 4
D getResourceObjectTypeFields() 0 54 21
A resolveResourceArgs() 0 11 3
A getNodeQueryFields() 0 8 1
A __construct() 0 15 1
F getResourceFieldConfiguration() 0 52 17
A normalizePropertyName() 0 3 2
B mergeFilterArgs() 0 20 8
B convertType() 0 25 11
C getFilterArgs() 0 36 12
B convertFilterArgsToTypes() 0 35 7
A getGraphQlPaginationArgs() 0 42 3

How to fix   Complexity   

Complex Class

Complex classes like FieldsBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FieldsBuilder, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\GraphQl\Type;
15
16
use ApiPlatform\Core\DataProvider\Pagination;
17
use ApiPlatform\Core\Exception\ResourceClassNotFoundException;
18
use ApiPlatform\Core\GraphQl\Resolver\Factory\ResolverFactoryInterface;
19
use ApiPlatform\Core\GraphQl\Type\Definition\TypeInterface;
20
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
21
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
22
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
23
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
24
use Doctrine\Common\Inflector\Inflector;
25
use GraphQL\Type\Definition\InputObjectType;
26
use GraphQL\Type\Definition\NullableType;
27
use GraphQL\Type\Definition\Type as GraphQLType;
28
use GraphQL\Type\Definition\WrappingType;
29
use Psr\Container\ContainerInterface;
30
use Symfony\Component\Config\Definition\Exception\InvalidTypeException;
31
use Symfony\Component\PropertyInfo\Type;
32
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
33
34
/**
35
 * Builds the GraphQL fields.
36
 *
37
 * @experimental
38
 *
39
 * @author Alan Poulain <[email protected]>
40
 */
41
final class FieldsBuilder implements FieldsBuilderInterface
42
{
43
    private $propertyNameCollectionFactory;
44
    private $propertyMetadataFactory;
45
    private $resourceMetadataFactory;
46
    private $typesContainer;
47
    private $typeBuilder;
48
    private $typeConverter;
49
    private $itemResolverFactory;
50
    private $collectionResolverFactory;
51
    private $itemMutationResolverFactory;
52
    private $filterLocator;
53
    private $pagination;
54
    private $nameConverter;
55
    private $nestingSeparator;
56
57
    public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, TypesContainerInterface $typesContainer, TypeBuilderInterface $typeBuilder, TypeConverterInterface $typeConverter, ResolverFactoryInterface $itemResolverFactory, ResolverFactoryInterface $collectionResolverFactory, ResolverFactoryInterface $itemMutationResolverFactory, ContainerInterface $filterLocator, Pagination $pagination, ?NameConverterInterface $nameConverter, string $nestingSeparator)
58
    {
59
        $this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
60
        $this->propertyMetadataFactory = $propertyMetadataFactory;
61
        $this->resourceMetadataFactory = $resourceMetadataFactory;
62
        $this->typesContainer = $typesContainer;
63
        $this->typeBuilder = $typeBuilder;
64
        $this->typeConverter = $typeConverter;
65
        $this->itemResolverFactory = $itemResolverFactory;
66
        $this->collectionResolverFactory = $collectionResolverFactory;
67
        $this->itemMutationResolverFactory = $itemMutationResolverFactory;
68
        $this->filterLocator = $filterLocator;
69
        $this->pagination = $pagination;
70
        $this->nameConverter = $nameConverter;
71
        $this->nestingSeparator = $nestingSeparator;
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77
    public function getNodeQueryFields(): array
78
    {
79
        return [
80
            'type' => $this->typeBuilder->getNodeInterface(),
81
            'args' => [
82
                'id' => ['type' => GraphQLType::nonNull(GraphQLType::id())],
83
            ],
84
            'resolve' => ($this->itemResolverFactory)(),
85
        ];
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91
    public function getItemQueryFields(string $resourceClass, ResourceMetadata $resourceMetadata, string $queryName, array $configuration): array
92
    {
93
        $shortName = $resourceMetadata->getShortName();
94
        $fieldName = lcfirst('item_query' === $queryName ? $shortName : $queryName.$shortName);
95
96
        $deprecationReason = (string) $resourceMetadata->getGraphqlAttribute($queryName, 'deprecation_reason', '', true);
97
98
        if ($fieldConfiguration = $this->getResourceFieldConfiguration(null, null, $deprecationReason, new Type(Type::BUILTIN_TYPE_OBJECT, true, $resourceClass), $resourceClass, false, $queryName, null)) {
99
            $args = $this->resolveResourceArgs($configuration['args'] ?? [], $queryName, $shortName);
0 ignored issues
show
Bug introduced by Alan Poulain
It seems like $shortName can also be of type null; however, parameter $shortName of ApiPlatform\Core\GraphQl...::resolveResourceArgs() 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

99
            $args = $this->resolveResourceArgs($configuration['args'] ?? [], $queryName, /** @scrutinizer ignore-type */ $shortName);
Loading history...
100
            $configuration['args'] = $args ?: $configuration['args'] ?? ['id' => ['type' => GraphQLType::nonNull(GraphQLType::id())]];
101
102
            return [$fieldName => array_merge($fieldConfiguration, $configuration)];
103
        }
104
105
        return [];
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function getCollectionQueryFields(string $resourceClass, ResourceMetadata $resourceMetadata, string $queryName, array $configuration): array
112
    {
113
        $shortName = $resourceMetadata->getShortName();
114
        $fieldName = lcfirst('collection_query' === $queryName ? $shortName : $queryName.$shortName);
115
116
        $deprecationReason = (string) $resourceMetadata->getGraphqlAttribute($queryName, 'deprecation_reason', '', true);
117
118
        if ($fieldConfiguration = $this->getResourceFieldConfiguration(null, null, $deprecationReason, new Type(Type::BUILTIN_TYPE_OBJECT, false, null, true, null, new Type(Type::BUILTIN_TYPE_OBJECT, false, $resourceClass)), $resourceClass, false, $queryName, null)) {
119
            $args = $this->resolveResourceArgs($configuration['args'] ?? [], $queryName, $shortName);
0 ignored issues
show
Bug introduced by Alan Poulain
It seems like $shortName can also be of type null; however, parameter $shortName of ApiPlatform\Core\GraphQl...::resolveResourceArgs() 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

119
            $args = $this->resolveResourceArgs($configuration['args'] ?? [], $queryName, /** @scrutinizer ignore-type */ $shortName);
Loading history...
120
            $configuration['args'] = $args ?: $configuration['args'] ?? $fieldConfiguration['args'];
121
122
            return [Inflector::pluralize($fieldName) => array_merge($fieldConfiguration, $configuration)];
123
        }
124
125
        return [];
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131
    public function getMutationFields(string $resourceClass, ResourceMetadata $resourceMetadata, string $mutationName): array
132
    {
133
        $mutationFields = [];
134
        $shortName = $resourceMetadata->getShortName();
135
        $resourceType = new Type(Type::BUILTIN_TYPE_OBJECT, true, $resourceClass);
136
        $deprecationReason = $resourceMetadata->getGraphqlAttribute($mutationName, 'deprecation_reason', '', true);
137
138
        if ($fieldConfiguration = $this->getResourceFieldConfiguration(null, ucfirst("{$mutationName}s a $shortName."), $deprecationReason, $resourceType, $resourceClass, false, null, $mutationName)) {
139
            $fieldConfiguration['args'] += ['input' => $this->getResourceFieldConfiguration(null, null, $deprecationReason, $resourceType, $resourceClass, true, null, $mutationName)];
140
141
            if (!$this->typeBuilder->isCollection($resourceType)) {
142
                $fieldConfiguration['resolve'] = ($this->itemMutationResolverFactory)($resourceClass, null, $mutationName);
143
            }
144
        }
145
146
        $mutationFields[$mutationName.$resourceMetadata->getShortName()] = $fieldConfiguration ?? [];
147
148
        return $mutationFields;
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154
    public function getResourceObjectTypeFields(?string $resourceClass, ResourceMetadata $resourceMetadata, bool $input, ?string $queryName, ?string $mutationName, int $depth = 0, ?array $ioMetadata = null): array
155
    {
156
        $fields = [];
157
        $idField = ['type' => GraphQLType::nonNull(GraphQLType::id())];
158
        $clientMutationId = GraphQLType::string();
159
160
        if (null !== $ioMetadata && \array_key_exists('class', $ioMetadata) && null === $ioMetadata['class']) {
161
            if ($input) {
162
                return ['clientMutationId' => $clientMutationId];
163
            }
164
165
            return [];
166
        }
167
168
        if ('delete' === $mutationName) {
169
            $fields = [
170
                'id' => $idField,
171
            ];
172
173
            if ($input) {
174
                $fields['clientMutationId'] = $clientMutationId;
175
            }
176
177
            return $fields;
178
        }
179
180
        if (!$input || 'create' !== $mutationName) {
181
            $fields['id'] = $idField;
182
        }
183
184
        ++$depth; // increment the depth for the call to getResourceFieldConfiguration.
185
186
        if (null !== $resourceClass) {
187
            foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $property) {
188
                $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $property, ['graphql_operation_name' => $mutationName ?? $queryName]);
189
                if (
190
                    null === ($propertyType = $propertyMetadata->getType())
191
                    || (!$input && false === $propertyMetadata->isReadable())
192
                    || ($input && null !== $mutationName && false === $propertyMetadata->isWritable())
193
                ) {
194
                    continue;
195
                }
196
197
                if ($fieldConfiguration = $this->getResourceFieldConfiguration($property, $propertyMetadata->getDescription(), $propertyMetadata->getAttribute('deprecation_reason', ''), $propertyType, $resourceClass, $input, $queryName, $mutationName, $depth)) {
198
                    $fields['id' === $property ? '_id' : $this->normalizePropertyName($property)] = $fieldConfiguration;
199
                }
200
            }
201
        }
202
203
        if (null !== $mutationName && $input) {
204
            $fields['clientMutationId'] = $clientMutationId;
205
        }
206
207
        return $fields;
208
    }
209
210
    /**
211
     * {@inheritdoc}
212
     */
213
    public function resolveResourceArgs(array $args, string $operationName, string $shortName): array
214
    {
215
        foreach ($args as $id => $arg) {
216
            if (!isset($arg['type'])) {
217
                throw new \InvalidArgumentException(sprintf('The argument "%s" of the custom operation "%s" in %s needs a "type" option.', $id, $operationName, $shortName));
218
            }
219
220
            $args[$id]['type'] = $this->typeConverter->resolveType($arg['type']);
221
        }
222
223
        return $args;
224
    }
225
226
    /**
227
     * Get the field configuration of a resource.
228
     *
229
     * @see http://webonyx.github.io/graphql-php/type-system/object-types/
230
     */
231
    private function getResourceFieldConfiguration(?string $property, ?string $fieldDescription, string $deprecationReason, Type $type, string $rootResource, bool $input, ?string $queryName, ?string $mutationName, int $depth = 0): ?array
232
    {
233
        try {
234
            $resourceClass = $this->typeBuilder->isCollection($type) && ($collectionValueType = $type->getCollectionValueType()) ? $collectionValueType->getClassName() : $type->getClassName();
235
236
            if (null === $graphqlType = $this->convertType($type, $input, $queryName, $mutationName, $resourceClass ?? '', $rootResource, $property, $depth)) {
0 ignored issues
show
introduced by Alan Poulain
The condition null === $graphqlType = ...rce, $property, $depth) is always false.
Loading history...
237
                return null;
238
            }
239
240
            $graphqlWrappedType = $graphqlType instanceof WrappingType ? $graphqlType->getWrappedType() : $graphqlType;
241
            $isStandardGraphqlType = \in_array($graphqlWrappedType, GraphQLType::getStandardTypes(), true);
242
            if ($isStandardGraphqlType) {
243
                $resourceClass = '';
244
            }
245
246
            $resourceMetadata = null;
247
            if (!empty($resourceClass)) {
248
                try {
249
                    $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
250
                } catch (ResourceClassNotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by soyuka
Consider adding a comment why this CATCH block is empty.
Loading history...
251
                }
252
            }
253
254
            $args = [];
255
            if (!$input && null === $mutationName && !$isStandardGraphqlType && $this->typeBuilder->isCollection($type)) {
256
                if ($this->pagination->isGraphQlEnabled($resourceClass, $queryName)) {
257
                    $args = $this->getGraphQlPaginationArgs($resourceClass, $queryName);
0 ignored issues
show
Bug introduced by raoul clais
It seems like $resourceClass can also be of type null; however, parameter $resourceClass of ApiPlatform\Core\GraphQl...GraphQlPaginationArgs() 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

257
                    $args = $this->getGraphQlPaginationArgs(/** @scrutinizer ignore-type */ $resourceClass, $queryName);
Loading history...
Bug introduced by raoul clais
It seems like $queryName can also be of type null; however, parameter $queryName of ApiPlatform\Core\GraphQl...GraphQlPaginationArgs() 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

257
                    $args = $this->getGraphQlPaginationArgs($resourceClass, /** @scrutinizer ignore-type */ $queryName);
Loading history...
258
                }
259
260
                $args = $this->getFilterArgs($args, $resourceClass, $resourceMetadata, $rootResource, $property, $queryName, $mutationName, $depth);
261
            }
262
263
            if ($isStandardGraphqlType || $input) {
264
                $resolve = null;
265
            } elseif ($this->typeBuilder->isCollection($type)) {
266
                $resolve = ($this->collectionResolverFactory)($resourceClass, $rootResource, $queryName);
267
            } else {
268
                $resolve = ($this->itemResolverFactory)($resourceClass, $rootResource, $queryName);
269
            }
270
271
            return [
272
                'type' => $graphqlType,
273
                'description' => $fieldDescription,
274
                'args' => $args,
275
                'resolve' => $resolve,
276
                'deprecationReason' => $deprecationReason,
277
            ];
278
        } catch (InvalidTypeException $e) {
279
            // just ignore invalid types
280
        }
281
282
        return null;
283
    }
284
285
    private function getGraphQlPaginationArgs(string $resourceClass, string $queryName): array
286
    {
287
        $paginationType = $this->pagination->getGraphQlPaginationType($resourceClass, $queryName);
288
289
        if ('cursor' === $paginationType) {
290
            return [
291
                'first' => [
292
                    'type' => GraphQLType::int(),
293
                    'description' => 'Returns the first n elements from the list.',
294
                ],
295
                'last' => [
296
                    'type' => GraphQLType::int(),
297
                    'description' => 'Returns the last n elements from the list.',
298
                ],
299
                'before' => [
300
                    'type' => GraphQLType::string(),
301
                    'description' => 'Returns the elements in the list that come before the specified cursor.',
302
                ],
303
                'after' => [
304
                    'type' => GraphQLType::string(),
305
                    'description' => 'Returns the elements in the list that come after the specified cursor.',
306
                ],
307
            ];
308
        }
309
310
        $paginationOptions = $this->pagination->getOptions();
311
312
        $args = [
313
            $paginationOptions['page_parameter_name'] => [
314
                'type' => GraphQLType::int(),
315
                'description' => 'Returns the current page.',
316
            ],
317
        ];
318
319
        if ($paginationOptions['client_items_per_page']) {
320
            $args[$paginationOptions['items_per_page_parameter_name']] = [
321
                'type' => GraphQLType::int(),
322
                'description' => 'Returns the number of items per page.',
323
            ];
324
        }
325
326
        return $args;
327
    }
328
329
    private function getFilterArgs(array $args, ?string $resourceClass, ?ResourceMetadata $resourceMetadata, string $rootResource, ?string $property, ?string $queryName, ?string $mutationName, int $depth): array
330
    {
331
        if (null === $resourceMetadata || null === $resourceClass) {
332
            return $args;
333
        }
334
335
        foreach ($resourceMetadata->getGraphqlAttribute($queryName, 'filters', [], true) as $filterId) {
0 ignored issues
show
Bug introduced by Alan Poulain
It seems like $queryName can also be of type null; however, parameter $operationName of ApiPlatform\Core\Metadat...::getGraphqlAttribute() 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

335
        foreach ($resourceMetadata->getGraphqlAttribute(/** @scrutinizer ignore-type */ $queryName, 'filters', [], true) as $filterId) {
Loading history...
336
            if (null === $this->filterLocator || !$this->filterLocator->has($filterId)) {
337
                continue;
338
            }
339
340
            foreach ($this->filterLocator->get($filterId)->getDescription($resourceClass) as $key => $value) {
341
                $nullable = isset($value['required']) ? !$value['required'] : true;
342
                $filterType = \in_array($value['type'], Type::$builtinTypes, true) ? new Type($value['type'], $nullable) : new Type('object', $nullable, $value['type']);
343
                $graphqlFilterType = $this->convertType($filterType, false, $queryName, $mutationName, $resourceClass, $rootResource, $property, $depth);
344
345
                if ('[]' === substr($key, -2)) {
346
                    $graphqlFilterType = GraphQLType::listOf($graphqlFilterType);
347
                    $key = substr($key, 0, -2).'_list';
348
                }
349
350
                /** @var string $key */
351
                $key = str_replace('.', $this->nestingSeparator, $key);
352
353
                parse_str($key, $parsed);
354
                if (\array_key_exists($key, $parsed) && \is_array($parsed[$key])) {
355
                    $parsed = [$key => ''];
356
                }
357
                array_walk_recursive($parsed, function (&$value) use ($graphqlFilterType) {
358
                    $value = $graphqlFilterType;
359
                });
360
                $args = $this->mergeFilterArgs($args, $parsed, $resourceMetadata, $key);
361
            }
362
        }
363
364
        return $this->convertFilterArgsToTypes($args);
365
    }
366
367
    private function mergeFilterArgs(array $args, array $parsed, ResourceMetadata $resourceMetadata = null, $original = ''): array
368
    {
369
        foreach ($parsed as $key => $value) {
370
            // Never override keys that cannot be merged
371
            if (isset($args[$key]) && !\is_array($args[$key])) {
372
                continue;
373
            }
374
375
            if (\is_array($value)) {
376
                $value = $this->mergeFilterArgs($args[$key] ?? [], $value);
377
                if (!isset($value['#name'])) {
378
                    $name = (false === $pos = strrpos($original, '[')) ? $original : substr($original, 0, (int) $pos);
379
                    $value['#name'] = ($resourceMetadata ? $resourceMetadata->getShortName() : '').'Filter_'.strtr($name, ['[' => '_', ']' => '', '.' => '__']);
380
                }
381
            }
382
383
            $args[$key] = $value;
384
        }
385
386
        return $args;
387
    }
388
389
    private function convertFilterArgsToTypes(array $args): array
390
    {
391
        foreach ($args as $key => $value) {
392
            if (strpos($key, '.')) {
393
                // Declare relations/nested fields in a GraphQL compatible syntax.
394
                $args[str_replace('.', $this->nestingSeparator, $key)] = $value;
395
                unset($args[$key]);
396
            }
397
        }
398
399
        foreach ($args as $key => $value) {
400
            if (!\is_array($value) || !isset($value['#name'])) {
401
                continue;
402
            }
403
404
            $name = $value['#name'];
405
406
            if ($this->typesContainer->has($name)) {
407
                $args[$key] = $this->typesContainer->get($name);
408
                continue;
409
            }
410
411
            unset($value['#name']);
412
413
            $filterArgType = new InputObjectType([
414
                'name' => $name,
415
                'fields' => $this->convertFilterArgsToTypes($value),
416
            ]);
417
418
            $this->typesContainer->set($name, $filterArgType);
419
420
            $args[$key] = $filterArgType;
421
        }
422
423
        return $args;
424
    }
425
426
    /**
427
     * Converts a built-in type to its GraphQL equivalent.
428
     *
429
     * @throws InvalidTypeException
430
     */
431
    private function convertType(Type $type, bool $input, ?string $queryName, ?string $mutationName, string $resourceClass, string $rootResource, ?string $property, int $depth)
432
    {
433
        $graphqlType = $this->typeConverter->convertType($type, $input, $queryName, $mutationName, $resourceClass, $rootResource, $property, $depth);
434
435
        if (null === $graphqlType) {
436
            throw new InvalidTypeException(sprintf('The type "%s" is not supported.', $type->getBuiltinType()));
437
        }
438
439
        if (\is_string($graphqlType)) {
440
            if (!$this->typesContainer->has($graphqlType)) {
441
                throw new InvalidTypeException(sprintf('The GraphQL type %s is not valid. Valid types are: %s. Have you registered this type by implementing %s?', $graphqlType, implode(', ', array_keys($this->typesContainer->all())), TypeInterface::class));
442
            }
443
444
            $graphqlType = $this->typesContainer->get($graphqlType);
445
        }
446
447
        if ($this->typeBuilder->isCollection($type)) {
448
            $operationName = $queryName ?? $mutationName;
449
450
            return $this->pagination->isGraphQlEnabled($resourceClass, $operationName) && !$input ? $this->typeBuilder->getResourcePaginatedCollectionType($graphqlType, $resourceClass, $operationName) : GraphQLType::listOf($graphqlType);
0 ignored issues
show
Bug introduced by raoul clais
It seems like $operationName can also be of type null; however, parameter $operationName of ApiPlatform\Core\GraphQl...ginatedCollectionType() 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

450
            return $this->pagination->isGraphQlEnabled($resourceClass, $operationName) && !$input ? $this->typeBuilder->getResourcePaginatedCollectionType($graphqlType, $resourceClass, /** @scrutinizer ignore-type */ $operationName) : GraphQLType::listOf($graphqlType);
Loading history...
451
        }
452
453
        return !$graphqlType instanceof NullableType || $type->isNullable() || (null !== $mutationName && 'update' === $mutationName)
454
            ? $graphqlType
455
            : GraphQLType::nonNull($graphqlType);
456
    }
457
458
    private function normalizePropertyName(string $property): string
459
    {
460
        return null !== $this->nameConverter ? $this->nameConverter->normalize($property) : $property;
461
    }
462
}
463