Passed
Push — master ( cc4f3c...e533d6 )
by Kévin
05:08 queued 01:54
created

ItemMutationResolverFactory::__invoke()   C

Complexity

Conditions 12
Paths 1

Size

Total Lines 56
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 37
dl 0
loc 56
rs 6.9666
c 0
b 0
f 0
cc 12
nc 1
nop 3

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
/*
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\Resolver\Factory;
15
16
use ApiPlatform\Core\Api\IriConverterInterface;
17
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
18
use ApiPlatform\Core\Exception\InvalidArgumentException;
19
use ApiPlatform\Core\Exception\ItemNotFoundException;
20
use ApiPlatform\Core\GraphQl\Resolver\FieldsToAttributesTrait;
21
use ApiPlatform\Core\GraphQl\Resolver\ResourceAccessCheckerTrait;
22
use ApiPlatform\Core\GraphQl\Serializer\ItemNormalizer;
23
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
24
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
25
use ApiPlatform\Core\Security\ResourceAccessCheckerInterface;
26
use ApiPlatform\Core\Util\ClassInfoTrait;
27
use ApiPlatform\Core\Validator\Exception\ValidationException;
28
use ApiPlatform\Core\Validator\ValidatorInterface;
29
use GraphQL\Error\Error;
30
use GraphQL\Type\Definition\ResolveInfo;
31
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
32
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
33
34
/**
35
 * Creates a function resolving a GraphQL mutation of an item.
36
 *
37
 * @experimental
38
 *
39
 * @author Alan Poulain <[email protected]>
40
 */
41
final class ItemMutationResolverFactory implements ResolverFactoryInterface
42
{
43
    use ClassInfoTrait;
44
    use FieldsToAttributesTrait;
45
    use ResourceAccessCheckerTrait;
0 ignored issues
show
introduced by
The trait ApiPlatform\Core\GraphQl...ourceAccessCheckerTrait requires some properties which are not provided by ApiPlatform\Core\GraphQl...MutationResolverFactory: $fieldNodes, $path
Loading history...
46
47
    private $iriConverter;
48
    private $dataPersister;
49
    private $normalizer;
50
    private $resourceMetadataFactory;
51
    private $resourceAccessChecker;
52
    private $validator;
53
54
    public function __construct(IriConverterInterface $iriConverter, DataPersisterInterface $dataPersister, NormalizerInterface $normalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceAccessCheckerInterface $resourceAccessChecker = null, ValidatorInterface $validator = null)
55
    {
56
        if (!$normalizer instanceof DenormalizerInterface) {
57
            throw new InvalidArgumentException(sprintf('The normalizer must implements the "%s" interface', DenormalizerInterface::class));
58
        }
59
60
        $this->iriConverter = $iriConverter;
61
        $this->dataPersister = $dataPersister;
62
        $this->normalizer = $normalizer;
63
        $this->resourceMetadataFactory = $resourceMetadataFactory;
64
        $this->resourceAccessChecker = $resourceAccessChecker;
65
        $this->validator = $validator;
66
    }
67
68
    public function __invoke(string $resourceClass = null, string $rootClass = null, string $operationName = null): callable
69
    {
70
        return function ($root, $args, $context, ResolveInfo $info) use ($resourceClass, $operationName) {
71
            if (null === $resourceClass) {
72
                return null;
73
            }
74
75
            $data = ['clientMutationId' => $args['input']['clientMutationId'] ?? null];
76
            $item = null;
77
78
            $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
79
            $normalizationContext = $resourceMetadata->getGraphqlAttribute($operationName ?? '', 'normalization_context', [], true);
80
            $normalizationContext['attributes'] = $this->fieldsToAttributes($info);
81
82
            if (isset($args['input']['id'])) {
83
                try {
84
                    $item = $this->iriConverter->getItemFromIri($args['input']['id'], $normalizationContext);
85
                } catch (ItemNotFoundException $e) {
86
                    throw Error::createLocatedError(sprintf('Item "%s" not found.', $args['input']['id']), $info->fieldNodes, $info->path);
87
                }
88
89
                if ($resourceClass !== $this->getObjectClass($item)) {
90
                    throw Error::createLocatedError(sprintf('Item "%s" did not match expected type "%s".', $args['input']['id'], $resourceClass), $info->fieldNodes, $info->path);
91
                }
92
            }
93
94
            $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
95
            $this->canAccess($this->resourceAccessChecker, $resourceMetadata, $resourceClass, $info, $item, $operationName);
96
            if (false === $resourceClass = $resourceMetadata->getAttribute('input_class', $resourceClass)) {
97
                return null;
98
            }
99
100
            switch ($operationName) {
101
                case 'create':
102
                case 'update':
103
                    $context = null === $item ? ['resource_class' => $resourceClass] : ['resource_class' => $resourceClass, 'object_to_populate' => $item];
104
                    $context += $resourceMetadata->getGraphqlAttribute($operationName, 'denormalization_context', [], true);
0 ignored issues
show
Bug introduced by
It seems like $operationName 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

104
                    $context += $resourceMetadata->getGraphqlAttribute(/** @scrutinizer ignore-type */ $operationName, 'denormalization_context', [], true);
Loading history...
105
                    $item = $this->normalizer->denormalize($args['input'], $resourceClass, ItemNormalizer::FORMAT, $context);
106
                    $this->validate($item, $info, $resourceMetadata, $operationName);
107
                    $persistResult = $this->dataPersister->persist($item);
108
109
                    if (null === $persistResult) {
110
                        @trigger_error(sprintf('Returning void from %s::persist() is deprecated since API Platform 2.3 and will not be supported in API Platform 3, an object should always be returned.', DataPersisterInterface::class), E_USER_DEPRECATED);
111
                    }
112
113
                    return $this->normalizer->normalize($persistResult ?? $item, ItemNormalizer::FORMAT, $normalizationContext) + $data;
114
                case 'delete':
115
                    if ($item) {
116
                        $this->dataPersister->remove($item);
117
                        $data['id'] = $args['input']['id'];
118
                    } else {
119
                        $data['id'] = null;
120
                    }
121
            }
122
123
            return $data;
124
        };
125
    }
126
127
    /**
128
     * @param object $item
129
     *
130
     * @throws Error
131
     */
132
    private function validate($item, ResolveInfo $info, ResourceMetadata $resourceMetadata, string $operationName = null)
133
    {
134
        if (null === $this->validator) {
135
            return;
136
        }
137
138
        $validationGroups = $resourceMetadata->getGraphqlAttribute($operationName, 'validation_groups', null, true);
0 ignored issues
show
Bug introduced by
It seems like $operationName 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

138
        $validationGroups = $resourceMetadata->getGraphqlAttribute(/** @scrutinizer ignore-type */ $operationName, 'validation_groups', null, true);
Loading history...
139
        try {
140
            $this->validator->validate($item, ['groups' => $validationGroups]);
141
        } catch (ValidationException $e) {
142
            throw Error::createLocatedError($e->getMessage(), $info->fieldNodes, $info->path);
143
        }
144
    }
145
}
146