1
|
|
|
<?php |
2
|
|
|
namespace Metfan\RabbitSetup\Configuration; |
3
|
|
|
|
4
|
|
|
use Symfony\Component\Config\Definition\Builder\TreeBuilder; |
5
|
|
|
use Symfony\Component\Config\Definition\ConfigurationInterface; |
6
|
|
|
use Symfony\Component\Config\Definition\Exception\InvalidTypeException; |
7
|
|
|
|
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Declaration of configuration to validate and normalize config file |
11
|
|
|
* |
12
|
|
|
* @author Ulrich |
13
|
|
|
* @package Metfan\RabbitSetup\Configuration |
14
|
|
|
*/ |
15
|
|
|
class ConfigExpertConfiguration implements ConfigurationInterface |
16
|
|
|
{ |
17
|
|
|
public function getConfigTreeBuilder() |
18
|
|
|
{ |
19
|
|
|
$treeBuilder = new TreeBuilder(); |
20
|
|
|
$rootNode = $treeBuilder->root('rabbit_setup'); |
21
|
|
|
|
22
|
|
|
$rootNode |
23
|
|
|
->validate() |
24
|
|
|
->always() |
25
|
|
|
->then(function($v){ |
26
|
|
|
/** |
27
|
|
|
* ensure that connections are correctly declared between vhosts and connection |
28
|
|
|
*/ |
29
|
|
|
foreach ($v['vhosts'] as $name => $config) { |
30
|
|
|
if (!isset($v['connections'][$config['connection']])) { |
31
|
|
|
throw new InvalidTypeException(sprintf( |
32
|
|
|
'Connection name "%s" for vhost %s have to be declared in "connections" section.', |
33
|
|
|
$config['connection'], |
34
|
|
|
$name |
35
|
|
|
)); |
36
|
|
|
} |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
return $v; |
40
|
|
|
}) |
41
|
|
|
->end() |
42
|
|
|
->children() |
43
|
|
|
->arrayNode('connections') #definition of connections |
44
|
|
|
->requiresAtLeastOneElement() |
45
|
|
|
->useAttributeAsKey('name') #use name as identifier |
46
|
|
|
->prototype('array') |
47
|
|
|
->children() |
48
|
|
|
->scalarNode('user')->defaultValue('guest')->end() |
49
|
|
|
->scalarNode('password')->defaultValue('guest')->end() |
50
|
|
|
->scalarNode('host')->defaultValue('127.0.0.1')->end() |
51
|
|
|
->scalarNode('port')->defaultValue(15672)->end() |
52
|
|
|
->end() |
53
|
|
|
->end() |
54
|
|
|
->end() |
55
|
|
|
->arrayNode('vhosts') # definition of vhosts |
56
|
|
|
->requiresAtLeastOneElement() |
57
|
|
|
->useAttributeAsKey('name') |
58
|
|
|
->prototype('array') |
59
|
|
|
->validate() |
60
|
|
|
->ifTrue(function($v){ return count($v['queues']) > 0;}) |
61
|
|
|
->then(function($v) { |
62
|
|
|
foreach ($v['queues'] as $name => $config) { |
63
|
|
|
if (isset($config['bindings'])) { |
64
|
|
|
foreach ($config['bindings'] as $binding) { |
65
|
|
|
if (!isset($v['exchanges'][$binding['exchange']])) { |
66
|
|
|
throw new InvalidTypeException(sprintf( |
67
|
|
|
'Exchange "%s" use in binding of queue %s must be declared under exchanges key configuration.', |
68
|
|
|
$binding['exchange'], |
69
|
|
|
$name |
70
|
|
|
)); |
71
|
|
|
} |
72
|
|
|
} |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
if (isset($config['arguments']) |
76
|
|
|
&& isset($config['arguments']['x-dead-letter-exchange']) |
77
|
|
|
&& !isset($v['exchanges'][$config['arguments']['x-dead-letter-exchange']])) { |
78
|
|
|
throw new InvalidTypeException(sprintf( |
79
|
|
|
'Exchange "%s" use in x-dead-letter-exchange of queue %s must be declared under exchanges key configuration.', |
80
|
|
|
$config['arguments']['x-dead-letter-exchange'], |
81
|
|
|
$name |
82
|
|
|
)); |
83
|
|
|
} |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
return $v; |
87
|
|
|
}) |
88
|
|
|
->end() |
89
|
|
|
->children() |
90
|
|
|
->scalarNode('connection')->isRequired()->cannotBeEmpty()->end() #connection to use |
91
|
|
|
->append($this->addParameters()) #add parameters definition |
92
|
|
|
->append($this->addPolicies()) #add policies definition |
93
|
|
|
->append($this->addExchanges()) #add exchanges definition |
94
|
|
|
->append($this->addQueues()) #add queues definition |
95
|
|
|
->end() |
96
|
|
|
->end() |
97
|
|
|
->end() |
98
|
|
|
->end(); |
99
|
|
|
|
100
|
|
|
return $treeBuilder; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
public function addParameters() |
104
|
|
|
{ |
105
|
|
|
$treeBuilder = new TreeBuilder(); |
106
|
|
|
$node = $treeBuilder->root('parameters'); |
107
|
|
|
|
108
|
|
|
$node |
109
|
|
|
->validate() |
110
|
|
|
->ifTrue(function($v) {return isset($v['federation-upstream-set']);}) |
111
|
|
|
->then(function($v){ |
112
|
|
|
/** |
113
|
|
|
* Verify all server name use in federation-upstream-set |
114
|
|
|
* were declared in federation-upstream |
115
|
|
|
*/ |
116
|
|
|
foreach ($v['federation-upstream-set'] as $name => $federationSet) { |
117
|
|
|
foreach ($federationSet as $config) { |
118
|
|
|
if ('all' != $config['upstream'] |
119
|
|
|
&& !array_key_exists($config['upstream'], $v['federation-upstream'])) { |
120
|
|
|
throw new InvalidTypeException(sprintf( |
121
|
|
|
'Unknown upstream server name: "%s" in upstream set: "%s".', |
122
|
|
|
$config['upstream'], |
123
|
|
|
$name)); |
124
|
|
|
} |
125
|
|
|
} |
126
|
|
|
} |
127
|
|
|
return $v; |
128
|
|
|
}) |
129
|
|
|
->end() |
130
|
|
|
|
131
|
|
|
|
132
|
|
|
->normalizeKeys(false) # don't transforme - into _ |
133
|
|
|
->children() |
134
|
|
|
->arrayNode('federation-upstream') |
135
|
|
|
->useAttributeAsKey('name') |
136
|
|
|
->prototype('array') |
137
|
|
|
->normalizeKeys(false) # don't transforme - into _ |
138
|
|
|
->children() |
139
|
|
|
->scalarNode('uri') |
140
|
|
|
->isRequired() |
141
|
|
|
->cannotBeEmpty() |
142
|
|
|
->end() |
143
|
|
|
->scalarNode('expires') |
144
|
|
|
->validate() |
145
|
|
|
->always() |
146
|
|
View Code Duplication |
->then(function($value){ |
|
|
|
|
147
|
|
|
if (!$value) { |
148
|
|
|
return null; |
149
|
|
|
} elseif (is_int($value)){ |
150
|
|
|
return (int) $value; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
throw new InvalidTypeException(sprintf( |
154
|
|
|
'Invalid value for path "expires". Expected integer or null, but got %s.', |
155
|
|
|
gettype($value))); |
156
|
|
|
}) |
157
|
|
|
->end() |
158
|
|
|
->info('# in ms, leave blank mean forever') |
159
|
|
|
->defaultValue(null) |
160
|
|
|
->end() |
161
|
|
|
->scalarNode('message-ttl') |
162
|
|
|
->validate() |
163
|
|
|
->always() |
164
|
|
View Code Duplication |
->then(function($value){ |
|
|
|
|
165
|
|
|
if (!$value) { |
166
|
|
|
return null; |
167
|
|
|
} elseif (is_int($value)){ |
168
|
|
|
return (int) $value; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
throw new InvalidTypeException(sprintf( |
172
|
|
|
'Invalid value for path "expires". Expected integer or null, but got %s.', |
173
|
|
|
gettype($value))); |
174
|
|
|
}) |
175
|
|
|
->end() |
176
|
|
|
->info('# in ms, leave blank mean forever') |
177
|
|
|
->defaultValue(null) |
178
|
|
|
->end() |
179
|
|
|
->integerNode('max-hops') |
180
|
|
|
->min(1) |
181
|
|
|
->defaultValue(1) |
182
|
|
|
->end() |
183
|
|
|
->integerNode('prefetch-count') |
184
|
|
|
->min(0) |
185
|
|
|
->defaultValue(1000) |
186
|
|
|
->end() |
187
|
|
|
->integerNode('reconnect-delay') |
188
|
|
|
->min(0) |
189
|
|
|
->info('# in s') |
190
|
|
|
->defaultValue(5) |
191
|
|
|
->end() |
192
|
|
|
->enumNode('ack-mode') |
193
|
|
|
->values(['on-confirm', 'on-publish', 'no-ack']) |
194
|
|
|
->defaultValue('on-confirm') |
195
|
|
|
->end() |
196
|
|
|
->booleanNode('trust-user-id') |
197
|
|
|
->treatNullLike(false) |
198
|
|
|
->defaultValue(false) |
199
|
|
|
->end() |
200
|
|
|
->end() |
201
|
|
|
->end() |
202
|
|
|
->end() |
203
|
|
|
->arrayNode('federation-upstream-set') |
204
|
|
|
->useAttributeAsKey('name') |
205
|
|
|
->prototype('array') |
206
|
|
|
->prototype('array') |
207
|
|
|
->children() |
208
|
|
|
->scalarNode('upstream') |
209
|
|
|
->end() |
210
|
|
|
->end() |
211
|
|
|
->end() |
212
|
|
|
->end() |
213
|
|
|
->end() |
214
|
|
|
->end(); |
215
|
|
|
|
216
|
|
|
return $node; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
public function addPolicies() |
220
|
|
|
{ |
221
|
|
|
$treeBuilder = new TreeBuilder(); |
222
|
|
|
$node = $treeBuilder->root('policies'); |
223
|
|
|
|
224
|
|
|
$node |
225
|
|
|
->validate() |
226
|
|
|
->always() |
227
|
|
|
->then(function($v) { |
228
|
|
|
/** |
229
|
|
|
* When applying policy to queues or exchanges some options can't be use |
230
|
|
|
* depending of apply type. |
231
|
|
|
*/ |
232
|
|
|
foreach ($v as $policyName => $policy) { |
233
|
|
|
$definitionsUsed = array_keys($policy['definition']); |
234
|
|
|
$intersect = []; |
235
|
|
|
if ('exchanges' == $policy['apply-to']) { |
236
|
|
|
$forbid = [ |
237
|
|
|
'message-ttl', |
238
|
|
|
'expires', |
239
|
|
|
'max-length', |
240
|
|
|
'max-length-bytes', |
241
|
|
|
'dead-letter-exchange', |
242
|
|
|
'dead-letter-routing-key']; |
243
|
|
|
$intersect = array_intersect($definitionsUsed, $forbid); |
244
|
|
|
} elseif ('queues' == $policy['apply-to']) { |
245
|
|
|
$intersect = array_intersect($definitionsUsed, ['alternate-exchange']); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
if (count($intersect) > 0) { |
249
|
|
|
throw new InvalidTypeException(sprintf( |
250
|
|
|
'You can\'t use "%s" with %s in policy: %s', |
251
|
|
|
implode(', ', $intersect), |
252
|
|
|
$policy['apply-to'], |
253
|
|
|
$policyName |
254
|
|
|
)); |
255
|
|
|
} |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
return $v; |
259
|
|
|
}) |
260
|
|
|
->end() |
261
|
|
|
|
262
|
|
|
->useAttributeAsKey('name') |
263
|
|
|
->prototype('array') |
264
|
|
|
->normalizeKeys(false) # don't transforme - into _ |
265
|
|
|
->children() |
266
|
|
|
->scalarNode('pattern') |
267
|
|
|
->isRequired() |
268
|
|
|
->cannotBeEmpty() |
269
|
|
|
->end() |
270
|
|
|
->integerNode('priority') |
271
|
|
|
->defaultValue(0) |
272
|
|
|
->end() |
273
|
|
|
->enumNode('apply-to') |
274
|
|
|
->values(['exchanges', 'queues', 'all']) |
275
|
|
|
->defaultValue('all') |
276
|
|
|
->end() |
277
|
|
|
->arrayNode('definition') |
278
|
|
|
->isRequired() |
279
|
|
|
->normalizeKeys(false) # don't transforme - into _ |
280
|
|
|
->children() |
281
|
|
|
->integerNode('message-ttl')->end() |
282
|
|
|
->integerNode('expires')->end() |
283
|
|
|
->integerNode('max-length')->end() |
284
|
|
|
->integerNode('max-length-bytes')->end() |
285
|
|
|
->scalarNode('alternate-exchange')->end() |
286
|
|
|
->scalarNode('dead-letter-exchange')->end() |
287
|
|
|
->scalarNode('dead-letter-routing-key')->end() |
288
|
|
|
->scalarNode('federation-upstream-set')->end() |
289
|
|
|
->scalarNode('federation-upstream')->end() |
290
|
|
|
->end() |
291
|
|
|
->end() |
292
|
|
|
->end() |
293
|
|
|
->end(); |
294
|
|
|
|
295
|
|
|
return $node; |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
public function addExchanges() |
299
|
|
|
{ |
300
|
|
|
$treeBuilder = new TreeBuilder(); |
301
|
|
|
$node = $treeBuilder->root('exchanges'); |
302
|
|
|
|
303
|
|
|
$node |
304
|
|
|
->useAttributeAsKey('name') |
305
|
|
|
->prototype('array') |
306
|
|
|
->normalizeKeys(false) # don't transforme - into _ |
307
|
|
|
->children() |
308
|
|
|
->enumNode('type') |
309
|
|
|
->values(['topic', 'fanout', 'direct', 'headers']) |
310
|
|
|
->isRequired() |
311
|
|
|
->end() |
312
|
|
|
->booleanNode('durable') |
313
|
|
|
->defaultValue(true) |
314
|
|
|
->end() |
315
|
|
|
->booleanNode('auto-delete') |
316
|
|
|
->defaultValue(false) |
317
|
|
|
->end() |
318
|
|
|
->booleanNode('internal') |
319
|
|
|
->defaultValue(false) |
320
|
|
|
->end() |
321
|
|
|
->scalarNode('alternate-exchange') |
322
|
|
|
->end() |
323
|
|
|
->end() |
324
|
|
|
->end(); |
325
|
|
|
|
326
|
|
|
return $node; |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
public function addQueues() |
330
|
|
|
{ |
331
|
|
|
$treeBuilder = new TreeBuilder(); |
332
|
|
|
$node = $treeBuilder->root('queues'); |
333
|
|
|
|
334
|
|
|
$node |
335
|
|
|
->useAttributeAsKey('name') |
336
|
|
|
->prototype('array') |
337
|
|
|
->normalizeKeys(false) # don't transforme - into _ |
338
|
|
|
->children() |
339
|
|
|
->booleanNode('durable') |
340
|
|
|
->defaultValue(true) |
341
|
|
|
->end() |
342
|
|
|
->booleanNode('auto-delete') |
343
|
|
|
->defaultValue(false) |
344
|
|
|
->end() |
345
|
|
|
->arrayNode('arguments') |
346
|
|
|
->normalizeKeys(false) # don't transforme - into _ |
347
|
|
|
->children() |
348
|
|
|
->integerNode('x-message-ttl')->end() |
349
|
|
|
->integerNode('x-expires')->end() |
350
|
|
|
->integerNode('x-max-length')->end() |
351
|
|
|
->integerNode('x-max-length-bytes')->end() |
352
|
|
|
->integerNode('x-max-priority')->end() |
353
|
|
|
->scalarNode('x-dead-letter-exchange')->end() |
354
|
|
|
->scalarNode('x-dead-letter-routing-key')->end() |
355
|
|
|
->end() |
356
|
|
|
->end() |
357
|
|
|
->arrayNode('bindings') |
358
|
|
|
->prototype('array') |
359
|
|
|
->children() |
360
|
|
|
->scalarNode('exchange') |
361
|
|
|
->isRequired() |
362
|
|
|
->end() |
363
|
|
|
->scalarNode('routing_key') |
364
|
|
|
->isRequired() |
365
|
|
|
->end() |
366
|
|
|
->end() |
367
|
|
|
->end() |
368
|
|
|
->end() |
369
|
|
|
->end() |
370
|
|
|
->end(); |
371
|
|
|
|
372
|
|
|
return $node; |
373
|
|
|
} |
374
|
|
|
} |
375
|
|
|
|
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.