Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Pull Request — master (#643)
by Stepan
11:56
created

ArgumentsTransformer   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Test Coverage

Coverage 97.18%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
wmc 34
eloc 72
c 3
b 1
f 0
dl 0
loc 189
ccs 69
cts 71
cp 0.9718
rs 9.68

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getType() 0 3 1
A getTypeClassInstance() 0 5 3
A __construct() 0 5 1
A getArguments() 0 19 4
C populateObject() 0 57 13
C getInstanceAndValidate() 0 29 12
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Overblog\GraphQLBundle\Transformer;
6
7
use GraphQL\Type\Definition\EnumType;
8
use GraphQL\Type\Definition\InputObjectType;
9
use GraphQL\Type\Definition\ListOfType;
10
use GraphQL\Type\Definition\NonNull;
11
use GraphQL\Type\Definition\ResolveInfo;
12
use GraphQL\Type\Definition\Type;
13
use Overblog\GraphQLBundle\Error\InvalidArgumentError;
14
use Overblog\GraphQLBundle\Error\InvalidArgumentsError;
15
use Symfony\Component\PropertyAccess\PropertyAccess;
16
use Symfony\Component\PropertyAccess\PropertyAccessor;
17
use Symfony\Component\Validator\ConstraintViolationList;
18
use Symfony\Component\Validator\Validator\ValidatorInterface;
19
20
class ArgumentsTransformer
21
{
22
    /**
23
     * @var ValidatorInterface
24
     */
25
    protected $validator;
26
27
    /**
28
     * @var array
29
     */
30
    protected $classesMap;
31
32
    /**
33
     * @var PropertyAccessor
34
     */
35
    protected $accessor;
36
37 147
    public function __construct(ValidatorInterface $validator = null, $classesMap = [])
38
    {
39 147
        $this->validator = $validator;
40 147
        $this->accessor = PropertyAccess::createPropertyAccessor();
41 147
        $this->classesMap = $classesMap;
42 147
    }
43
44
    /**
45
     * Get the PHP class for a given type.
46
     *
47
     * @param string $type
48
     *
49
     * @return object|false
50
     */
51 4
    private function getTypeClassInstance(string $type)
52
    {
53 4
        $classname = isset($this->classesMap[$type]) ? $this->classesMap[$type]['class'] : false;
54
55 4
        return $classname ? new $classname() : false;
56
    }
57
58
    /**
59
     * Extract given type from Resolve Info.
60
     *
61
     * @param string      $type
62
     * @param ResolveInfo $info
63
     *
64
     * @return Type
65
     */
66 4
    private function getType(string $type, ResolveInfo $info): Type
67
    {
68 4
        return $info->schema->getType($type);
69
    }
70
71
    /**
72
     * Populate an object based on type with given data.
73
     *
74
     * @param Type        $type
75
     * @param mixed       $data
76
     * @param bool        $multiple
77
     * @param ResolveInfo $info
78
     *
79
     * @return mixed
80
     */
81 4
    private function populateObject(Type $type, $data, bool $multiple, ResolveInfo $info)
82
    {
83 4
        if (null === $data) {
84 4
            if ($type instanceof InputObjectType) {
85 2
                return $this->getTypeClassInstance($type->name) ?: null;
86
            }
87 4
            return $data;
88
        }
89
90 4
        if ($type instanceof NonNull) {
91
            $type = $type->getWrappedType();
92
        }
93
94 4
        if ($multiple) {
95
            return \array_map(function ($data) use ($type, $info) {
96 4
                return $this->populateObject($type, $data, false, $info);
97 4
            }, $data);
98
        }
99
100 4
        if ($type instanceof EnumType) {
101 4
            $instance = $this->getTypeClassInstance($type->name);
102 4
            if ($instance) {
103 2
                $this->accessor->setValue($instance, 'value', $data);
104
105 2
                return $instance;
106
            } else {
107 3
                return $data;
108
            }
109 4
        } elseif ($type instanceof InputObjectType) {
110 4
            $instance = $this->getTypeClassInstance($type->name);
111 4
            if (!$instance) {
112
                return $data;
113
            }
114
115 4
            $fields = $type->getFields();
116
117 4
            foreach ($fields as $name => $field) {
118 4
                $fieldData = $this->accessor->getValue($data, \sprintf('[%s]', $name));
119 4
                $fieldType = $field->getType();
120
121 4
                if ($fieldType instanceof NonNull) {
122 4
                    $fieldType = $fieldType->getWrappedType();
123
                }
124
125 4
                if ($fieldType instanceof ListOfType) {
126 4
                    $fieldValue = $this->populateObject($fieldType->getWrappedType(), $fieldData, true, $info);
127
                } else {
128 4
                    $fieldValue = $this->populateObject($fieldType, $fieldData, false, $info);
129
                }
130
131 4
                $this->accessor->setValue($instance, $name, $fieldValue);
132
            }
133
134 4
            return $instance;
135
        }
136
137 4
        return $data;
138
    }
139
140
    /**
141
     * Given a GraphQL type and an array of data, populate corresponding object recursively
142
     * using annoted classes.
143
     *
144
     * @param string      $argType
145
     * @param mixed       $data
146
     * @param ResolveInfo $info
147
     *
148
     * @return mixed
149
     */
150 4
    public function getInstanceAndValidate(string $argType, $data, ResolveInfo $info, string $argName)
151
    {
152 4
        $isRequired = '!' === $argType[\strlen($argType) - 1];
153 4
        $isMultiple = '[' === $argType[0];
154 4
        $endIndex = ($isRequired ? 1 : 0) + ($isMultiple ? 1 : 0);
155 4
        $type = \substr($argType, $isMultiple ? 1 : 0, $endIndex > 0 ? -$endIndex : \strlen($argType));
156
157 4
        $result = $this->populateObject($this->getType($type, $info), $data, $isMultiple, $info);
158 4
        $errors = new ConstraintViolationList();
159 4
        if ($this->validator) {
160 4
            if (\is_object($result)) {
161 3
                $errors = $this->validator->validate($result);
162
            }
163 4
            if (\is_array($result) && $isMultiple) {
164 1
                foreach ($result as $element) {
165 1
                    if (\is_object($element)) {
166 1
                        $errors->addAll(
167 1
                            $this->validator->validate($element)
168
                        );
169
                    }
170
                }
171
            }
172
        }
173
174 4
        if (\count($errors) > 0) {
175 2
            throw new InvalidArgumentError($argName, $errors);
176
        }
177
178 2
        return $result;
179
    }
180
181
    /**
182
     * Transform a list of arguments into their corresponding php class and validate them.
183
     *
184
     * @param array       $mapping
185
     * @param mixed       $data
186
     * @param ResolveInfo $info
187
     *
188
     * @return array
189
     */
190 4
    public function getArguments(array $mapping, $data, ResolveInfo $info)
191
    {
192 4
        $args = [];
193 4
        $exceptions = [];
194
195 4
        foreach ($mapping as $name => $type) {
196
            try {
197 4
                $value = $this->getInstanceAndValidate($type, $data[$name], $info, $name);
198 2
                $args[] = $value;
199 2
            } catch (InvalidArgumentError $exception) {
200 2
                $exceptions[] = $exception;
201
            }
202
        }
203
204 4
        if (!empty($exceptions)) {
205 2
            throw new InvalidArgumentsError($exceptions);
206
        }
207
208 2
        return $args;
209
    }
210
}
211