Completed
Push — master ( 3e44d5...49b4bd )
by Rafael
06:52
created

NamespaceDefinitionPlugin::configure()   F

Complexity

Conditions 20
Paths 325

Size

Total Lines 62
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 20.2312

Importance

Changes 0
Metric Value
eloc 36
dl 0
loc 62
ccs 33
cts 36
cp 0.9167
rs 1.7708
c 0
b 0
f 0
cc 20
nc 325
nop 3
crap 20.2312

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
 *  This file is part of the GraphQL Bundle package.
4
 *
5
 *  (c) YnloUltratech <[email protected]>
6
 *
7
 *  For the full copyright and license information, please view the LICENSE
8
 *  file that was distributed with this source code.
9
 ******************************************************************************/
10
11
namespace Ynlo\GraphQLBundle\Definition\Plugin;
12
13
use Doctrine\Common\Util\Inflector;
14
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
15
use Ynlo\GraphQLBundle\Definition\DefinitionInterface;
16
use Ynlo\GraphQLBundle\Definition\ExecutableDefinitionInterface;
17
use Ynlo\GraphQLBundle\Definition\FieldDefinition;
18
use Ynlo\GraphQLBundle\Definition\FieldsAwareDefinitionInterface;
19
use Ynlo\GraphQLBundle\Definition\MutationDefinition;
20
use Ynlo\GraphQLBundle\Definition\NodeAwareDefinitionInterface;
21
use Ynlo\GraphQLBundle\Definition\ObjectDefinition;
22
use Ynlo\GraphQLBundle\Definition\ObjectDefinitionInterface;
23
use Ynlo\GraphQLBundle\Definition\Registry\Endpoint;
24
use Ynlo\GraphQLBundle\Resolver\EmptyObjectResolver;
25
26
/**
27
 * This extension configure namespace in definitions
28
 * using definition node and bundle in the node
29
 */
30
class NamespaceDefinitionPlugin extends AbstractDefinitionPlugin
31
{
32
    protected $globalConfig = [];
33
34
    /**
35
     * NamespaceDefinitionPlugin constructor.
36
     *
37
     * Configuration:
38
     *
39
     * # Group each bundle into a separate schema definition
40
     * bundles:
41
     *      enabled:              true
42
     *
43
     *      # The following suffix will be used for bundle query groups
44
     *      query_suffix:         BundleQuery
45
     *
46
     *      # The following suffix will be used for bundle mutation groups
47
     *      mutation_suffix:      BundleMutation
48
     *
49
     *      # The following bundles will be ignore for grouping, all definitions will be placed in the root query or mutation
50
     *      ignore:
51
     *
52
     *      # Default:
53
     *      - AppBundle
54
     *
55
     *      # Define aliases for bundles to set definitions inside other desired bundle name.
56
     *      # Can be used to group multiple bundles or publish a bundle with a different name
57
     *      aliases:              # Example: SecurityBundle: AppBundle
58
     *
59
     *          # Prototype
60
     *          name:                 ~
61
     *
62
     * # Group queries and mutations of the same node into a node specific schema definition.
63
     * nodes:
64
     *      enabled:              true
65
     *
66
     *      # The following suffix will be used to create the name for queries to the same node
67
     *      query_suffix:         Query
68
     *
69
     *      # The following suffix will be used to create the name for mutations to the same node
70
     *      mutation_suffix:      Mutation
71
     *
72
     *      # The following nodes will be ignore for grouping, all definitions will be placed in the root query or mutation
73
     *      ignore:
74
     *
75
     *      # Default:
76
     *      - Node
77
     *
78
     *      # Define aliases for nodes to set definitions inside other desired node name.
79
     *      # Can be used to group multiple nodes or publish a node with a different group name
80
     *      aliases:              # Example: InvoiceItem: Invoice
81
     *
82
     *          # Prototype
83
     *          name:
84
     *
85
     * @param array $config
86
     */
87 2
    public function __construct(array $config = [])
88
    {
89 2
        $this->globalConfig = $config;
90 2
    }
91
92
    /**
93
     * {@inheritDoc}
94
     */
95
    public function buildConfig(ArrayNodeDefinition $root): void
96
    {
97
        $config = $root
98
            ->info('Enable/Disable namespace for queries and mutations')
99
            ->canBeDisabled()
100
            ->children();
101
102
        $config->scalarNode('namespace');
103
        $config->scalarNode('alias');
104
        $config->scalarNode('node');
105
        $config->scalarNode('bundle');
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111 2
    public function configure(DefinitionInterface $definition, Endpoint $endpoint, array $config): void
112
    {
113 2
        $node = null;
114 2
        $nodeClass = null;
115
116 2
        if (!($config['enabled'] ?? true)) {
117
            return;
118
        }
119
120 2
        if ($definition instanceof NodeAwareDefinitionInterface && isset($this->globalConfig['nodes']['enabled']) && $definition->getNode()) {
121 2
            $node = $definition->getNode();
122
123 2
            if (class_exists($node)) {
124
                $nodeClass = $node;
125
            } else {
126 2
                $nodeClass = $endpoint->getClassForType($node);
127
            }
128
129 2
            if (isset($this->globalConfig['nodes']['aliases'][$node])) {
130 1
                $node = $this->globalConfig['nodes']['aliases'][$node];
131
            }
132
133 2
            if ($node && \in_array($node, $this->globalConfig['nodes']['ignore'] ?? [], true)) {
134
                $node = null;
135
            }
136
        }
137
138 2
        $bundle = null;
139 2
        if ($this->globalConfig['bundles']['enabled'] ?? false) {
140 2
            if ($node && $nodeClass && $endpoint->hasType($node)) {
141 2
                preg_match_all('/\\\\?(\w+Bundle)\\\\/', $nodeClass, $matches);
142 2
                if ($matches) {
143 2
                    $bundle = current(array_reverse($matches[1]));
144
                }
145
146 2
                if (isset($this->globalConfig['bundles']['aliases'][$bundle])) {
147 2
                    $bundle = $this->globalConfig['bundles']['aliases'][$bundle];
148
                }
149
150 2
                if ($bundle && \in_array($bundle, $this->globalConfig['bundles']['ignore'] ?? [], true)) {
151 2
                    $bundle = null;
152
                }
153
154 2
                if ($bundle) {
155 2
                    $bundle = preg_replace('/Bundle$/', null, $bundle);
156
                }
157
            }
158
        }
159
160 2
        $node = $config['node'] ?? $node;
161 2
        $bundle = $config['bundle'] ?? $bundle;
162
163 2
        if ($bundle || $node) {
164 2
            $config = $definition->getMeta('namespace', []);
165 2
            $definition->setMeta(
166 2
                'namespace',
167 2
                array_merge(
168
                    [
169 2
                        'bundle' => $bundle,
170 2
                        'node' => $node,
171
                    ],
172 2
                    $config
173
                )
174
            );
175
        }
176 2
    }
177
178
    /**
179
     * {@inheritDoc}
180
     */
181 1
    public function configureEndpoint(Endpoint $endpoint): void
182
    {
183 1
        $groupByBundle = $this->globalConfig['bundles']['enabled'] ?? false;
184 1
        $groupByNode = $this->globalConfig['bundles']['enabled'] ?? false;
185 1
        if ($groupByBundle || $groupByNode) {
186 1
            $endpoint->setQueries($this->namespaceDefinitions($endpoint->allQueries(), $endpoint));
187 1
            $endpoint->setMutations($this->namespaceDefinitions($endpoint->allMutations(), $endpoint));
188
        }
189 1
    }
190
191
    /**
192
     * @param array    $definitions
193
     * @param Endpoint $endpoint
194
     *
195
     * @return array
196
     */
197 1
    private function namespaceDefinitions(array $definitions, Endpoint $endpoint): array
198
    {
199 1
        $namespacedDefinitions = [];
200
        /** @var DefinitionInterface $definition */
201 1
        foreach ($definitions as $definition) {
202 1
            if (!$definition->hasMeta('namespace') || !$definition->getMeta('namespace')) {
203 1
                $namespacedDefinitions[$definition->getName()] = $definition;
204 1
                continue;
205
            }
206
207 1
            $root = null;
208 1
            $parent = null;
209 1
            $namespaceConfig = $definition->getMeta('namespace');
210 1
            $namespacePath = $namespaceConfig['namespace'] ?? null;
211 1
            if ($namespacePath) {
212 1
                $querySuffix = $this->globalConfig['nodes']['query_suffix'] ?? 'Query';
213 1
                $mutationSuffix = $this->globalConfig['nodes']['mutation_suffix'] ?? 'Mutation';
214 1
                $namespaces = explode('/', $namespacePath);
215 1
                foreach ($namespaces as $namespace) {
216 1
                    $name = lcfirst($namespace);
217 1
                    $typeName = ucfirst($namespace).(($definition instanceof MutationDefinition) ? $mutationSuffix : $querySuffix);
218 1
                    if (!$root) {
219 1
                        if (isset($namespacedDefinitions[$name])) {
220
                            $root = $namespacedDefinitions[$name];
221
                        } else {
222 1
                            $root = $this->createRootNamespace(\get_class($definition), $name, $typeName, $endpoint);
223
                        }
224 1
                        $parent = $endpoint->getType($root->getType());
225 1
                        $namespacedDefinitions[$root->getName()] = $root;
226
                    } else {
227 1
                        $parent = $this->createChildNamespace($parent, $name, $typeName, $endpoint);
228
                    }
229
                }
230 1
                if ($alias = $namespaceConfig['alias'] ?? null) {
231
                    $definition->setName($alias);
232
                }
233 1
                $this->addDefinitionToNamespace($parent, $definition);
234 1
                continue;
235
            }
236
237 1
            if ($bundle = $namespaceConfig['bundle'] ?? null) {
238 1
                $bundleQuerySuffix = $this->globalConfig['bundle']['query_suffix'] ?? 'BundleQuery';
239 1
                $bundleMutationSuffix = $this->globalConfig['bundle']['mutation_suffix'] ?? 'BundleMutation';
240
241 1
                $name = lcfirst($bundle);
242 1
                $typeName = ucfirst($name).(($definition instanceof MutationDefinition) ? $bundleMutationSuffix : $bundleQuerySuffix);
243 1
                $root = $this->createRootNamespace(\get_class($definition), $name, $typeName, $endpoint);
244 1
                $parent = $endpoint->getType($root->getType());
245
            }
246
247 1
            if ($nodeName = $namespaceConfig['node'] ?? null) {
248 1
                if ($endpoint->hasTypeForClass($nodeName)) {
249 1
                    $nodeName = $endpoint->getTypeForClass($nodeName);
250
                }
251
252 1
                $name = Inflector::pluralize(lcfirst($nodeName));
253
254 1
                $querySuffix = $this->globalConfig['nodes']['query_suffix'] ?? 'Query';
255 1
                $mutationSuffix = $this->globalConfig['nodes']['mutation_suffix'] ?? 'Mutation';
256
257 1
                $typeName = ucfirst($nodeName).(($definition instanceof MutationDefinition) ? $mutationSuffix : $querySuffix);
258 1
                if (!$root) {
259 1
                    $root = $this->createRootNamespace(\get_class($definition), $name, $typeName, $endpoint);
260 1
                    $parent = $endpoint->getType($root->getType());
261 1
                } elseif ($parent) {
262 1
                    $parent = $this->createChildNamespace($parent, $name, $typeName, $endpoint);
263
                }
264
265 1
                if ($alias = $namespaceConfig['alias'] ?? null) {
266
                    $definition->setName($alias);
267
                } else {
268
                    //remove node suffix on namespaced definitions
269 1
                    $definition->setName(preg_replace(sprintf("/(\w+)%s$/", $nodeName), '$1', $definition->getName()));
270 1
                    $definition->setName(preg_replace(sprintf("/(\w+)%s$/", Inflector::pluralize($nodeName)), '$1', $definition->getName()));
271
                }
272
            }
273
274 1
            if ($root && $parent) {
275 1
                $this->addDefinitionToNamespace($parent, $definition);
276 1
                $namespacedDefinitions[$root->getName()] = $root;
277
            } else {
278 1
                $namespacedDefinitions[$definition->getName()] = $definition;
279
            }
280
        }
281
282 1
        return $namespacedDefinitions;
283
    }
284
285
    /**
286
     * @param FieldsAwareDefinitionInterface $fieldsAwareDefinition
287
     * @param ExecutableDefinitionInterface  $definition
288
     */
289 1
    private function addDefinitionToNamespace(FieldsAwareDefinitionInterface $fieldsAwareDefinition, ExecutableDefinitionInterface $definition): void
290
    {
291 1
        $field = new FieldDefinition();
292 1
        $field->setName($definition->getName());
293 1
        $field->setDescription($definition->getDescription());
294 1
        $field->setDeprecationReason($definition->getDeprecationReason());
295 1
        $field->setType($definition->getType());
296 1
        $field->setResolver($definition->getResolver());
297 1
        $field->setArguments($definition->getArguments());
298 1
        $field->setList($definition->isList());
299 1
        $field->setMetas($definition->getMetas());
300 1
        $field->setNode($definition->getNode());
301 1
        $field->setComplexity($definition->getComplexity());
0 ignored issues
show
Bug introduced by
The method getComplexity() does not exist on Ynlo\GraphQLBundle\Defin...ableDefinitionInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Ynlo\GraphQLBundle\Defin...ableDefinitionInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

301
        $field->setComplexity($definition->/** @scrutinizer ignore-call */ getComplexity());
Loading history...
302 1
        $fieldsAwareDefinition->addField($field);
303 1
    }
304
305
    /**
306
     * @param ObjectDefinitionInterface $parent   parent definition to add a child field
307
     * @param string                    $name     name of the field
308
     * @param string                    $typeName name of the type to create
309
     * @param Endpoint                  $endpoint Endpoint instance to extract definitions
310
     *
311
     * @return ObjectDefinition
312
     */
313 1
    private function createChildNamespace(ObjectDefinitionInterface $parent, string $name, string $typeName, Endpoint $endpoint): ObjectDefinition
314
    {
315 1
        $child = new FieldDefinition();
316 1
        $child->setName($name);
317 1
        $child->setResolver(EmptyObjectResolver::class);
318
319 1
        $type = new ObjectDefinition();
320 1
        $type->setName($typeName);
321 1
        if ($endpoint->hasType($type->getName())) {
322 1
            $type = $endpoint->getType($type->getName());
323
        } else {
324 1
            $endpoint->add($type);
325
        }
326
327 1
        $child->setType($type->getName());
328 1
        $parent->addField($child);
329
330 1
        return $type;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $type could return the type Ynlo\GraphQLBundle\Definition\DefinitionInterface which includes types incompatible with the type-hinted return Ynlo\GraphQLBundle\Definition\ObjectDefinition. Consider adding an additional type-check to rule them out.
Loading history...
331
    }
332
333
    /**
334
     * @param string   $rootType Class of the root type to create QueryDefinition or MutationDefinition
335
     * @param string   $name     name of the root field
336
     * @param string   $typeName name for the root type
337
     * @param Endpoint $endpoint Endpoint interface to extract existent definitions
338
     *
339
     * @return ExecutableDefinitionInterface
340
     */
341 1
    private function createRootNamespace($rootType, $name, $typeName, Endpoint $endpoint): ExecutableDefinitionInterface
342
    {
343
        /** @var ExecutableDefinitionInterface $rootDefinition */
344 1
        $rootDefinition = new $rootType();
345 1
        $rootDefinition->setName($name);
346 1
        $rootDefinition->setResolver(EmptyObjectResolver::class);
347
348 1
        $type = new ObjectDefinition();
349 1
        $type->setName($typeName);
350 1
        if ($endpoint->hasType($type->getName())) {
351 1
            $type = $endpoint->getType($type->getName());
352
        } else {
353 1
            $endpoint->add($type);
354
        }
355
356 1
        $rootDefinition->setType($type->getName());
357
358 1
        return $rootDefinition;
359
    }
360
}
361