UserErrorsType::create()   A
last analyzed

Complexity

Conditions 6
Paths 12

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 6
eloc 13
c 2
b 0
f 0
nc 12
nop 4
dl 0
loc 22
ccs 14
cts 14
cp 1
crap 6
rs 9.2222
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Type\Definition;
6
7
use Exception;
8
use GraphQL;
9
use function array_map;
10
use function array_merge;
11
use function count;
12
use function gettype;
13
use function implode;
14
use function is_callable;
15
use function ucfirst;
16
17
final class UserErrorsType extends ObjectType
18
{
19
    public const SUBERRORS_NAME = 'suberrors';
20
    protected const CODE_NAME = 'code';
21
    protected const MESSAGE_NAME = 'msg';
22
23
    /**
24
     * @param mixed[]  $config
25
     * @param string[] $path
26
     */
27 37
    public function __construct(array $config, array $path, bool $isParentList = false)
28
    {
29 37
        $finalFields = $config['fields'] ?? [];
30
31 37
        if (!isset($config['type'])) {
32 1
            throw new Exception('You must specify a type for your field');
33
        }
34
35
36 36
        $this->_addErrorCodes($config, $finalFields, $path);
37
38 36
        $type = $this->_getType($config);
39 36
        if ($type instanceof InputObjectType) {
40 28
            $this->_buildInputObjectType($type, $config, $path, $finalFields, $isParentList);
41
        }
42
43 35
        if ($isParentList) {
44 11
            $this->_addPathField($finalFields);
45
        }
46
47 35
        parent::__construct([
48 35
            'name' => $this->_nameFromPath(array_merge($path)) . ucfirst('error'),
49 35
            'description' => 'User errors for ' . ucfirst($path[count($path)-1]),
50 35
            'fields' => $finalFields,
51
        ]);
52 35
    }
53
54 36
    protected function _getType($config) {
55 36
        $type = $config['type'];
56 36
        if (is_callable($type)) {
57 1
        	$type = $type();
58
		}
59
60 36
        if ($type instanceof WrappingType) {
61 16
            $type = $type->getInnermostType();
62
        }
63 36
        return $type;
64
    }
65
66 28
    static public function needSuberrors(array $config, bool $isParentList) : bool {
67 28
        return !empty($config['validate']) || !empty($config['isRoot']) || $isParentList;
68
    }
69
70 28
    protected function _buildInputObjectFields(InputObjectType $type, $config, $path) {
71 28
        $fields = [];
72 28
        foreach ($type->getFields() as $key => $field) {
73 26
            $fieldType = $this->_getType($field->config);
74 26
            if ($newType = static::create(
75
                [
76 26
                    'validate' => $field->config['validate'] ?? null,
77 26
                    'errorCodes' => $field->config['errorCodes'] ?? null,
78 26
                    'type' => $fieldType,
79 26
                    'typeSetter' => $config['typeSetter'] ?? null
80
                ],
81 26
                array_merge($path, [$key]),
82 26
                $field->getType() instanceof ListOfType
83
            )) {
84 21
                $fields[$key] = [
85 21
                    'description' => 'Error for ' . $key,
86 21
                    'type' => $field->getType() instanceof ListOfType ? Type::listOf($newType) : $newType,
87
                    'resolve' => static function ($value) use ($key) {
88 10
                        return $value[$key] ?? null;
89 25
                    },
90
                ];
91
            }
92
        }
93 27
        return $fields;
94
    }
95
96 28
    protected function _buildInputObjectType(InputObjectType $type, $config, $path, &$finalFields, $isParentList) {
97 28
        $createSubErrors = static::needSuberrors($config, $isParentList);
98
99 28
        $fields = $this->_buildInputObjectFields($type, $config, $path);
100
101 27
        if ($createSubErrors && count($fields)) {
102
            /**
103
             * suberrors property
104
             */
105 18
            $finalFields[static::SUBERRORS_NAME] = [
106 18
                'type' => $this->_set(new ObjectType([
107 18
                    'name' => $this->_nameFromPath(array_merge($path, ['fieldErrors'])),
108 18
                    'description' => 'User Error',
109 18
                    'fields' => $fields,
110 18
                ]), $config),
111 18
                'description' => 'Validation errors for ' . ucfirst($path[count($path)-1]),
112
                'resolve' => static function (array $value) {
113 12
                    return $value[static::SUBERRORS_NAME] ?? null;
114 18
                },
115
            ];
116
        }
117
        else {
118 10
            $finalFields += $fields;
119
        }
120 27
    }
121
122 36
    protected function _addErrorCodes($config, &$finalFields, $path) {
123 36
        if (isset($config['errorCodes'])) {
124 17
            if (!isset($config['validate'])) {
125 1
                throw new Exception('If you specify errorCodes, you must also provide a validate callback');
126
            }
127
128
            /** code property */
129 16
            $finalFields[static::CODE_NAME] = [
130 16
                'type' => $this->_set(new EnumType([
131 16
                    'name' => $this->_nameFromPath(array_merge($path)) . 'ErrorCode',
132 16
                    'description' => 'Error code',
133 16
                    'values' => $config['errorCodes'],
134 16
                ]), $config),
135 16
                'description' => 'An error code',
136
                'resolve' => static function ($value) {
137 6
                    return $value['error'][0] ?? null;
138 16
                },
139
            ];
140
141
            /**
142
             * msg property
143
             */
144 16
            $finalFields[static::MESSAGE_NAME] = [
145 16
                'type' => Type::string(),
146 16
                'description' => 'A natural language description of the issue',
147
                'resolve' => static function ($value) {
148 6
                    return $value['error'][1] ?? null;
149 16
                },
150
            ];
151
        }
152 36
    }
153
154 11
    protected function _addPathField(&$finalFields) {
155 11
        if (!empty($finalFields['code']) || !empty($finalFields['suberrors'])) {
156 11
            $finalFields['path'] = [
157 11
                'type' => Type::listOf(Type::int()),
158 11
                'description' => 'A path describing this items\'s location in the nested array',
159
                'resolve' => static function ($value) {
160 3
                    return $value['path'];
161 11
                },
162
            ];
163
        }
164 11
    }
165
166
    /**
167
     * @param Type $type
168
     * @param mixed[] $config
169
     * @return Type
170
     */
171 24
    protected function _set(Type $type, array $config)
172
    {
173 24
        if (is_callable($config['typeSetter'] ?? null)) {
174 2
            $config['typeSetter']($type);
175
        }
176 24
        return $type;
177
    }
178
179
    /**
180
     * @param mixed[]  $config
181
     * @param string[] $path
182
     * @param string   $name
183
     *
184
     * @return static|null
185
     */
186 32
    public static function create(array $config, array $path, bool $isParentList = false, string $name = '') : ?self
187
    {
188 32
        $config['fields'] = $config['fields'] ?? [];
189 32
        if (is_callable($config['validate'] ?? null)) {
190 29
            $config['fields'][static::CODE_NAME] = $config['fields'][static::CODE_NAME] ?? static::_generateIntCodeType();
191 29
            $config['fields'][static::MESSAGE_NAME] = $config['fields'][static::MESSAGE_NAME] ?? static::_generateMessageType();
192
        }
193
194 32
        $userErrorType = new static($config, $path, $isParentList);
195 30
        if ($userErrorType->getFields()) {
196 28
            $userErrorType->name = !empty($name) ? $name : $userErrorType->name;
197 28
            if (is_callable($config['typeSetter'] ?? null)) {
198 1
                $config['typeSetter']($userErrorType);
199
            }
200 28
            return $userErrorType;
201
        }
202
203 4
        if(count($path) == 1) {
204 1
            throw new Exception("You must specify at least one 'validate' callback somewhere");
205
        }
206
207 4
        return null;
208
    }
209
210 29
    protected static function _generateIntCodeType() {
211
        return [
212 29
            'type' => Type::int(),
213 29
            'description' => 'A numeric error code. 0 on success, non-zero on failure.',
214
            'resolve' => static function ($value) {
215 4
                $error = $value['error'] ?? null;
216 4
                switch (gettype($error)) {
217 4
                    case 'integer':
218 1
                        return $error;
219
                }
220 3
                return $error[0];
221 29
            },
222
        ];
223
    }
224
225 29
    protected static function _generateMessageType() {
226
        return [
227 29
            'type' => Type::string(),
228 29
            'description' => 'An error message.',
229
            'resolve' => static function ($value) {
230 4
                $error = $value['error'] ?? null;
231 4
                switch (gettype($error)) {
232 4
                    case 'integer':
233 1
                        return '';
234
                }
235 3
                return $error[1];
236 29
            },
237
        ];
238
    }
239
240
    /**
241
     * @param string[] $path
242
     */
243 35
    protected function _nameFromPath(array $path) : string
244
    {
245 35
        return implode('_', array_map('ucfirst', $path));
246
    }
247
}
248