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

Completed
Push — annotations ( be29b8...8e920d )
by Jérémiah
17:31
created

BuilderProcessor::process()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 38
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 7

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 38
ccs 22
cts 22
cp 1
rs 8.6506
c 0
b 0
f 0
cc 7
nc 7
nop 1
crap 7
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Overblog\GraphQLBundle\Config\Processor;
6
7
use Overblog\GraphQLBundle\Definition\Builder\MappingInterface;
8
use Overblog\GraphQLBundle\Relay\Builder\RelayConnectionFieldsBuilder;
9
use Overblog\GraphQLBundle\Relay\Builder\RelayEdgeFieldsBuilder;
10
use Overblog\GraphQLBundle\Relay\Connection\BackwardConnectionArgsDefinition;
11
use Overblog\GraphQLBundle\Relay\Connection\ConnectionArgsDefinition;
12
use Overblog\GraphQLBundle\Relay\Connection\ForwardConnectionArgsDefinition;
13
use Overblog\GraphQLBundle\Relay\Mutation\MutationFieldDefinition;
14
use Overblog\GraphQLBundle\Relay\Node\GlobalIdFieldDefinition;
15
use Overblog\GraphQLBundle\Relay\Node\NodeFieldDefinition;
16
use Overblog\GraphQLBundle\Relay\Node\PluralIdentifyingRootFieldDefinition;
17
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
18
19
final class BuilderProcessor implements ProcessorInterface
20
{
21
    public const BUILDER_FIELD_TYPE = 'field';
22
    public const BUILDER_FIELDS_TYPE = 'fields';
23
    public const BUILDER_ARGS_TYPE = 'args';
24
25
    public const BUILDER_TYPES = [
26
        self::BUILDER_FIELD_TYPE,
27
        self::BUILDER_FIELDS_TYPE,
28
        self::BUILDER_ARGS_TYPE,
29
    ];
30
31
    /** @var MappingInterface[] */
32
    private static $builderClassMap = [
33
        self::BUILDER_ARGS_TYPE => [
34
            'Relay::ForwardConnection' => ForwardConnectionArgsDefinition::class,
35
            'Relay::BackwardConnection' => BackwardConnectionArgsDefinition::class,
36
            'Relay::Connection' => ConnectionArgsDefinition::class,
37
        ],
38
        self::BUILDER_FIELD_TYPE => [
39
            'Relay::Mutation' => MutationFieldDefinition::class,
40
            'Relay::GlobalId' => GlobalIdFieldDefinition::class,
41
            'Relay::Node' => NodeFieldDefinition::class,
42
            'Relay::PluralIdentifyingRoot' => PluralIdentifyingRootFieldDefinition::class,
43
        ],
44
        self::BUILDER_FIELDS_TYPE => [
45
            'relay-connection' => RelayConnectionFieldsBuilder::class,
46
            'relay-edge' => RelayEdgeFieldsBuilder::class,
47
        ],
48
    ];
49
50
    /**
51
     * {@inheritdoc}
52
     */
53 43
    public static function process(array $configs): array
54
    {
55 43
        $addedTypes = [];
56
        // map: "type name" => "provided by" for better DX, while debugging accidental type overrides in builders
57 43
        $reservedTypesMap = \array_combine(
58 43
            \array_keys($configs),
59 43
            \array_fill(0, \count($configs), 'configs')
60
        );
61
62 43
        foreach ($configs as &$config) {
63 43
            if (isset($config['config']['builders']) && \is_array($config['config']['builders'])) {
64 3
                ['fields' => $buildersFields, 'types' => $buildersTypes] = self::processFieldsBuilders(
65 3
                    $config['config']['builders'],
66 3
                    $reservedTypesMap
0 ignored issues
show
Bug introduced by
It seems like $reservedTypesMap can also be of type false; however, parameter $reservedTypesMap of Overblog\GraphQLBundle\C...processFieldsBuilders() does only seem to accept array, 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

66
                    /** @scrutinizer ignore-type */ $reservedTypesMap
Loading history...
67
                );
68
69 2
                $config['config']['fields'] = isset($config['config']['fields'])
70 1
                    ? \array_merge($buildersFields, $config['config']['fields'])
71 2
                    : $buildersFields;
72
73 2
                $addedTypes = \array_merge($addedTypes, $buildersTypes);
74
75 2
                unset($config['config']['builders']);
76
            }
77
78 42
            if (isset($config['config']['fields']) && \is_array($config['config']['fields'])) {
79 37
                ['fields' => $buildersFields, 'types' => $buildersTypes] = self::processFieldBuilders(
80 37
                    $config['config']['fields'],
81 37
                    $reservedTypesMap
82
                );
83
84 33
                $config['config']['fields'] = $buildersFields;
85
86 33
                $addedTypes = \array_merge($addedTypes, $buildersTypes);
87
            }
88
        }
89
90 37
        return \array_merge($configs, $addedTypes);
91
    }
92
93 15
    public static function addBuilderClass($name, $type, $builderClass): void
94
    {
95 15
        self::checkBuilderClass($builderClass, $type);
96 5
        self::$builderClassMap[$type][$name] = $builderClass;
97 5
    }
98
99
    /**
100
     * @param string $builderClass
101
     * @param string $type
102
     */
103 15
    private static function checkBuilderClass($builderClass, $type): void
104
    {
105 15
        $interface = MappingInterface::class;
106
107 15
        if (!\is_string($builderClass)) {
0 ignored issues
show
introduced by
The condition is_string($builderClass) is always true.
Loading history...
108 5
            throw new \InvalidArgumentException(
109 5
                \sprintf('%s builder class should be string, but %s given.', \ucfirst($type), \gettype($builderClass))
110
            );
111
        }
112
113 10
        if (!\class_exists($builderClass)) {
114 2
            throw new \InvalidArgumentException(
115 2
                \sprintf('%s builder class "%s" not found.', \ucfirst($type), $builderClass)
116
            );
117
        }
118
119 8
        if (!\is_subclass_of($builderClass, $interface)) {
120 3
            throw new \InvalidArgumentException(
121 3
                \sprintf(
122 3
                    '%s builder class should implement "%s", but "%s" given.',
123 3
                    \ucfirst($type),
124 3
                    $interface,
125 3
                    $builderClass
126
                )
127
            );
128
        }
129 5
    }
130
131 37
    private static function processFieldBuilders(array $fields, array &$reservedTypesMap)
132
    {
133 37
        $newTypes = [];
134
135 37
        foreach ($fields as &$field) {
136 37
            $fieldBuilderName = null;
137
138 37
            if (isset($field['builder']) && \is_string($field['builder'])) {
139 11
                $fieldBuilderName = $field['builder'];
140 11
                unset($field['builder']);
141 34
            } elseif (\is_string($field)) {
142 2
                @\trigger_error(
143
                    'The builder short syntax (Field: Builder => Field: {builder: Builder}) is deprecated as of 0.7 and will be removed in 0.12. '.
144 2
                    'It will be replaced by the field type short syntax (Field: Type => Field: {type: Type})',
145 2
                    \E_USER_DEPRECATED
146
                );
147 2
                $fieldBuilderName = $field;
148
            }
149
150 37
            $builderConfig = [];
151 37
            if (isset($field['builderConfig'])) {
152 10
                if (\is_array($field['builderConfig'])) {
153 10
                    $builderConfig = $field['builderConfig'];
154
                }
155 10
                unset($field['builderConfig']);
156
            }
157
158 37
            if ($fieldBuilderName) {
159 11
                $mapping = self::getFieldBuilderMapping($fieldBuilderName, self::BUILDER_FIELD_TYPE, $builderConfig, $reservedTypesMap);
160
161 9
                $fieldMapping = $mapping['field'];
162 9
                $field = \is_array($field) ? \array_merge($fieldMapping, $field) : $fieldMapping;
163 9
                $newTypes = \array_merge($newTypes, $mapping['types']);
164
            }
165 35
            if (isset($field['argsBuilder'])) {
166 15
                $field = self::processFieldArgumentsBuilders($field);
167
            }
168
        }
169
170
        return [
171 33
            'fields' => $fields,
172 33
            'types' => $newTypes,
173
        ];
174
    }
175
176 3
    private static function processFieldsBuilders(array $builders, array &$reservedTypesMap)
177
    {
178 3
        $fields = [];
179 3
        $newTypes = [];
180
181 3
        foreach ($builders as $builder) {
182 3
            $builderName = $builder['builder'];
183 3
            $builderConfig = $builder['builderConfig'] ?? [];
184
185 3
            $mapping = self::getFieldBuilderMapping($builderName, self::BUILDER_FIELDS_TYPE, $builderConfig, $reservedTypesMap);
186
187 2
            $fields = \array_merge($fields, $mapping['fields']);
188 2
            $newTypes = \array_merge($newTypes, $mapping['types']);
189
        }
190
191
        return [
192 2
            'fields' => $fields,
193 2
            'types' => $newTypes,
194
        ];
195
    }
196
197
    /**
198
     * @param string $builderName
199
     * @param string $builderType
200
     * @param array  $builderConfig
201
     * @param array  $reservedTypesMap
202
     *
203
     * @return array
204
     *
205
     * @throws InvalidConfigurationException
206
     */
207 13
    private static function getFieldBuilderMapping(string $builderName, string $builderType, array $builderConfig, array &$reservedTypesMap)
208
    {
209 13
        $builder = self::getBuilder($builderName, $builderType);
210 12
        $mapping = $builder->toMappingDefinition($builderConfig);
211
212 12
        $fieldMappingKey = null;
213
214 12
        if (self::BUILDER_FIELD_TYPE === $builderType) {
215 10
            $fieldMappingKey = 'field';
216 3
        } elseif (self::BUILDER_FIELDS_TYPE === $builderType) {
217 3
            $fieldMappingKey = 'fields';
218
        }
219
220 12
        $fieldMapping = $mapping[$fieldMappingKey] ?? $mapping;
221 12
        $typesMapping = [];
222
223 12
        if (isset($mapping[$fieldMappingKey], $mapping['types'])) {
224 5
            $builderClass = \get_class($builder);
225
226 5
            foreach ($mapping['types'] as $typeName => $typeConfig) {
227 5
                if (isset($reservedTypesMap[$typeName])) {
228 4
                    throw new InvalidConfigurationException(\sprintf(
229 4
                        'Type "%s" emitted by builder "%s" already exists. Type was provided by "%s". Builder may only emit new types. Overriding is not allowed.',
230 4
                        $typeName,
231 4
                        $builderClass,
232 4
                        $reservedTypesMap[$typeName]
233
                    ));
234
                }
235
236 3
                $reservedTypesMap[$typeName] = $builderClass;
237 3
                $typesMapping[$typeName] = $typeConfig;
238
            }
239
        }
240
241
        return [
242 10
            $fieldMappingKey => $fieldMapping,
243 10
            'types' => $typesMapping,
244
        ];
245
    }
246
247
    /**
248
     * @param string $name
249
     * @param string $type
250
     *
251
     * @return MappingInterface
252
     *
253
     * @throws InvalidConfigurationException if builder class not define
254
     */
255 26
    private static function getBuilder($name, $type)
256
    {
257 26
        static $builders = [];
258 26
        if (isset($builders[$type][$name])) {
259 21
            return $builders[$type][$name];
260
        }
261
262 12
        $builderClassMap = self::$builderClassMap[$type];
263
264 12
        if (isset($builderClassMap[$name])) {
265 9
            return $builders[$type][$name] = new $builderClassMap[$name]();
266
        }
267
        // deprecated relay builder name ?
268 3
        $newName = 'Relay::'.\rtrim($name, 'Args');
269 3
        if (isset($builderClassMap[$newName])) {
270 1
            @\trigger_error(
271 1
                \sprintf('The "%s" %s builder is deprecated as of 0.7 and will be removed in 0.12. Use "%s" instead.', $name, $type, $newName),
272 1
                \E_USER_DEPRECATED
273
            );
274
275 1
            return $builders[$type][$newName] = new $builderClassMap[$newName]();
276
        }
277
278 2
        throw new InvalidConfigurationException(\sprintf('%s builder "%s" not found.', \ucfirst($type), $name));
279
    }
280
281 15
    private static function processFieldArgumentsBuilders(array $field)
282
    {
283 15
        $argsBuilderName = null;
284
285 15
        if (\is_string($field['argsBuilder'])) {
286 15
            $argsBuilderName = $field['argsBuilder'];
287 1
        } elseif (isset($field['argsBuilder']['builder']) && \is_string($field['argsBuilder']['builder'])) {
288 1
            $argsBuilderName = $field['argsBuilder']['builder'];
289
        }
290
291 15
        $builderConfig = [];
292 15
        if (isset($field['argsBuilder']['config']) && \is_array($field['argsBuilder']['config'])) {
293 1
            $builderConfig = $field['argsBuilder']['config'];
294
        }
295
296 15
        if ($argsBuilderName) {
297 15
            $args = self::getBuilder($argsBuilderName, self::BUILDER_ARGS_TYPE)->toMappingDefinition($builderConfig);
298 14
            $field['args'] = isset($field['args']) && \is_array($field['args']) ? \array_merge($args, $field['args']) : $args;
299
        }
300
301 14
        unset($field['argsBuilder']);
302
303 14
        return $field;
304
    }
305
}
306