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() |
|
|
|
|
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() |
|
|
|
|
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'])) { |
|
|
|
|
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'])) { |
|
|
|
|
392
|
|
|
throw new InvalidConfigurationException(sprintf('You cannot set both "type" and "service" for "%s" - please choose one.', $path)); |
393
|
|
|
} |
394
|
|
|
} |
395
|
|
|
} |
396
|
|
|
|
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.