Completed
Push — master ( aa6181...9c1eab )
by Joshua
8s
created

Configuration::getRestNode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 18
rs 9.4285
cc 1
eloc 13
nc 1
nop 0
1
<?php
2
3
namespace As3\Bundle\ModlrBundle\DependencyInjection;
4
5
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
6
use Symfony\Component\Config\Definition\ConfigurationInterface;
7
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
8
9
/**
10
 * Validates and merges configuration for Modlr.
11
 *
12
 * @author  Jacob Bare <[email protected]>
13
 */
14
class Configuration implements ConfigurationInterface
15
{
16
    /**
17
     * {@inheritDoc}
18
     */
19
    public function getConfigTreeBuilder()
20
    {
21
        $treeBuilder = new TreeBuilder();
22
        $treeBuilder->root('as3_modlr')
23
            ->children()
24
                ->append($this->getAdapterNode())
25
                ->append($this->getMetadataNode())
26
                ->append($this->getPersistersNode())
27
                ->append($this->getRestNode())
28
                ->append($this->getSearchClientsNode())
29
            ->end()
30
        ;
31
        return $treeBuilder;
32
    }
33
34
    /**
35
     * Creates a root config node with the provided key.
36
     *
37
     * @param   string $key
38
     * @return  \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition
39
     */
40
    private function createRootNode($key)
41
    {
42
        $treeBuilder = new TreeBuilder();
43
        return $treeBuilder->root($key);
44
    }
45
46
    /**
47
     * Formats the root REST endpoint.
48
     *
49
     * @param   string  $endpoint
50
     * @return  string
51
     */
52
    private function formatRestEndpoint($endpoint)
53
    {
54
        return sprintf('%s', trim($endpoint, '/'));
55
    }
56
57
    /**
58
     * Gets the api adapter configuration node.
59
     *
60
     * @return  \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition
61
     */
62
    private function getAdapterNode()
63
    {
64
        $node = $this->createRootNode('adapter');
65
        return $node
66
            ->isRequired()
67
            ->addDefaultsIfNotSet()
68
            ->children()
69
                ->enumNode('type')
70
                    ->values(['jsonapiorg', null])
71
                ->end()
72
                ->scalarNode('service')->cannotBeEmpty()->end()
73
            ->end()
74
            ->validate()
75
                ->always(function($v) {
76
                    $this->validateAdapter($v);
77
                    return $v;
78
                })
79
            ->end()
80
        ;
81
    }
82
83
    /**
84
     * Gets the metadata configuration node.
85
     *
86
     * @return  \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition
87
     */
88
    private function getMetadataNode()
89
    {
90
        $node = $this->createRootNode('metadata');
91
        return $node
92
            ->addDefaultsIfNotSet()
93
            ->children()
94
                ->arrayNode('drivers')
95
                    ->defaultValue(['default' => ['type' => 'yml']])
96
                    ->useAttributeAsKey('name')
97
                    ->prototype('array')
98
                        ->performNoDeepMerging()
99
                        ->children()
100
101
                            ->enumNode('type')->defaultValue('yml')
102
                                ->values(['yml', null])
103
                            ->end()
104
                            ->scalarNode('service')->cannotBeEmpty()->end()
105
106
                            ->arrayNode('parameters')
107
                                ->prototype('variable')->end()
108
                            ->end()
109
110
                        ->end()
111
                    ->end()
112
                    ->validate()
113
                        ->always(function($v) {
114
                            $this->validateMetadataDrivers($v);
115
                            return $v;
116
                        })
117
                    ->end()
118
                ->end()
119
120
                ->arrayNode('cache')
121
                    ->canBeDisabled()
122
                    ->children()
123
124
                        ->enumNode('type')->defaultValue('file')
125
                            ->values(['file', 'binary_file', 'redis', null])
126
                        ->end()
127
                        ->scalarNode('service')->cannotBeEmpty()->end()
128
129
                        ->arrayNode('parameters')
130
                            ->prototype('variable')->end()
131
                        ->end()
132
133
                    ->end()
134
135
                    ->validate()
136
                        ->always(function($v) {
137
                            $this->validateMetadataCache($v);
138
                            return $v;
139
                        })
140
                    ->end()
141
                ->end()
142
            ->end()
143
        ;
144
    }
145
146
    /**
147
     * Gets the persisters configuration node.
148
     *
149
     * @return  \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition
150
     */
151 View Code Duplication
    private function getPersistersNode()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
152
    {
153
        $node = $this->createRootNode('persisters');
154
        return $node
155
            ->isRequired()
156
            ->useAttributeAsKey('name')
157
            ->prototype('array')
158
                ->children()
159
160
                    ->enumNode('type')
161
                        ->values(['mongodb', null])
162
                    ->end()
163
                    ->scalarNode('service')->cannotBeEmpty()->end()
164
165
                    ->arrayNode('parameters')
166
                        ->prototype('variable')->end()
167
                    ->end()
168
                ->end()
169
            ->end()
170
            ->validate()
171
                ->always(function($v) {
172
                    $this->validatePersisters($v);
173
                    return $v;
174
                })
175
            ->end()
176
        ;
177
    }
178
179
    /**
180
     * Gets the rest configuration node.
181
     *
182
     * @return  \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition
183
     */
184
    private function getRestNode()
185
    {
186
        $node = $this->createRootNode('rest');
187
        return $node
188
            ->addDefaultsIfNotSet()
189
            ->children()
190
                ->scalarNode('root_endpoint')->isRequired()->cannotBeEmpty()->defaultValue('modlr/api')
191
                    ->validate()
192
                        ->always(function($v) {
193
                            $v = $this->formatRestEndpoint($v);
194
                            return $v;
195
                        })
196
                    ->end()
197
                ->end()
198
            ->end()
199
200
        ;
201
    }
202
203
    /**
204
     * Gets the search clients configuration node.
205
     *
206
     * @return  \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition
207
     */
208 View Code Duplication
    private function getSearchClientsNode()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
209
    {
210
        $node = $this->createRootNode('search_clients');
211
        return $node
212
            ->isRequired()
213
            ->useAttributeAsKey('name')
214
            ->prototype('array')
215
                ->children()
216
217
                    ->enumNode('type')
218
                        ->values(['elastic', null])
219
                    ->end()
220
                    ->scalarNode('service')->cannotBeEmpty()->end()
221
222
                    ->arrayNode('parameters')
223
                        ->prototype('variable')->end()
224
                    ->end()
225
                ->end()
226
            ->end()
227
            ->validate()
228
                ->always(function($v) {
229
                    $this->validateSearchClients($v);
230
                    return $v;
231
                })
232
            ->end()
233
        ;
234
    }
235
236
    /**
237
     * Validates the api adapter config.
238
     *
239
     * @param   array   $adapter
240
     * @return  self
241
     * @throws  InvalidConfigurationException
242
     */
243
    private function validateAdapter(array $adapter)
244
    {
245
        $this->validateTypeAndService($adapter, 'as3_modlr.adapter');
246
        switch ($adapter['type']) {
247
            case 'jsonapiorg':
248
                $this->validateLibClassExists('Api\JsonApiOrg\Adapter', 'as3_modlr.adapter.type');
249
                break;
250
            default:
251
                break;
252
        }
253
        return $this;
254
    }
255
256
    /**
257
     * Validates that a library class name exists.
258
     *
259
     * @param   string  $subClass
260
     * @param   string  $path
261
     * @throws  InvalidConfigurationException
262
     */
263
    private function validateLibClassExists($subClass, $path)
264
    {
265
        $class = Utility::getLibraryClass($subClass);
266
        if (false === class_exists($class)) {
267
            throw new InvalidConfigurationException(sprintf('The library class "%s" was not found for "%s" - was the library installed?', $class, $path));
268
        }
269
    }
270
271
    /**
272
     * Validates the metadata cache config.
273
     *
274
     * @param   array   $config
275
     * @return  self
276
     * @throws  InvalidConfigurationException
277
     */
278
    private function validateMetadataCache(array $config)
279
    {
280
        if (false === $config['enabled']) {
281
            return $this;
282
        }
283
284
        $this->validateTypeAndService($config, 'as3_modlr.metadata.cache');
285
        switch ($config['type']) {
286
            case 'redis':
287
                $this->validateMetadataCacheRedis($config);
288
                break;
289
            default:
290
                break;
291
        }
292
        return $this;
293
    }
294
295
    /**
296
     * Validates the Redis metadata cache config.
297
     *
298
     * @param   array   $config
299
     * @throws  InvalidConfigurationException
300
     */
301
    private function validateMetadataCacheRedis(array $config)
302
    {
303
        if (!isset($config['parameters']['handler'])) {
304
            throw new InvalidConfigurationException('A Redis handler service name must be defined for "as3_modlr.metadata.cache.parameters.handler"');
305
        }
306
    }
307
308
    /**
309
     * Validates the metadata drivers config.
310
     *
311
     * @param   array   $drivers
312
     * @return  self
313
     * @throws  InvalidConfigurationException
314
     */
315
    private function validateMetadataDrivers(array $drivers)
316
    {
317
        foreach ($drivers as $name => $config) {
318
            $this->validateTypeAndService($config, sprintf('as3_modlr.metadata.drivers.%s', $name));
319
        }
320
        return $this;
321
    }
322
323
    /**
324
     * Validates the MongoDb persister config.
325
     *
326
     * @param   array   $config
327
     * @throws  InvalidConfigurationException
328
     */
329
    private function validatePersisterMongoDb(array $config)
330
    {
331
        if (!isset($config['parameters']['host'])) {
332
            throw new InvalidConfigurationException(sprintf('The MongoDB persister requires a value for "as3_modlr.persisters.%s.parameters.host"', $name));
333
        }
334
    }
335
336
    /**
337
     * Validates the persisters config.
338
     *
339
     * @param   array   $persisters
340
     * @return  self
341
     * @throws  InvalidConfigurationException
342
     */
343
    private function validatePersisters(array $persisters)
344
    {
345
        foreach ($persisters as $name => $config) {
346
            $this->validateTypeAndService($config, sprintf('as3_modlr.persisters.%s', $name));
347
            switch ($config['type']) {
348
                case 'mongodb':
349
                    $this->validateLibClassExists('Persister\MongoDb\Persister', sprintf('as3_modlr.persisters.%s.type', $name));
350
                    $this->validatePersisterMongoDb($config);
351
                    break;
352
                default:
353
                    break;
354
            }
355
        }
356
    }
357
358
    /**
359
     * Validates the search clients config.
360
     *
361
     * @param   array   $clients
362
     * @return  self
363
     * @throws  InvalidConfigurationException
364
     */
365
    private function validateSearchClients(array $clients)
366
    {
367
        foreach ($clients as $name => $config) {
368
            $this->validateTypeAndService($config, sprintf('as3_modlr.search_clients.%s', $name));
369
            switch ($config['type']) {
370
                case 'elastic':
371
                    $this->validateLibClassExists('Search\Elastic\Client', sprintf('as3_modlr.search_clients.%s.type', $name));
372
                    break;
373
                default:
374
                    break;
375
            }
376
        }
377
    }
378
379
    /**
380
     * Validates a configuration that uses type and service as options.
381
     *
382
     * @param   array   $config
383
     * @param   string  $path
384
     * @throws  InvalidArgumentException
385
     */
386
    private function validateTypeAndService(array $config, $path)
387
    {
388 View Code Duplication
        if (!isset($config['type']) && !isset($config['service'])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
389
            throw new InvalidConfigurationException(sprintf('You must set one of "type" or "service" for "%s"', $path));
390
        }
391 View Code Duplication
        if (isset($config['type']) && isset($config['service'])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
392
            throw new InvalidConfigurationException(sprintf('You cannot set both "type" and "service" for "%s" - please choose one.', $path));
393
        }
394
    }
395
}
396