Configuration::getConfigTreeBuilder()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
nc 1
nop 0
dl 0
loc 15
c 0
b 0
f 0
cc 1
rs 9.9332
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Damax\ChargeableApi\Bridge\Symfony\Bundle\DependencyInjection;
6
7
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
8
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
9
use Symfony\Component\Config\Definition\ConfigurationInterface;
10
11
final class Configuration implements ConfigurationInterface
12
{
13
    public const WALLET_REDIS = 'redis';
14
    public const WALLET_MONGO = 'mongo';
15
    public const WALLET_FIXED = 'fixed';
16
    public const WALLET_SERVICE = 'service';
17
18
    public const IDENTITY_FIXED = 'fixed';
19
    public const IDENTITY_SECURITY = 'security';
20
    public const IDENTITY_SERVICE = 'service';
21
22
    public function getConfigTreeBuilder(): TreeBuilder
23
    {
24
        $treeBuilder = new TreeBuilder('damax_chargeable_api');
25
26
        $rootNode = $treeBuilder->getRootNode();
27
        $rootNode
28
            ->children()
29
                ->append($this->walletNode('wallet'))
30
                ->append($this->identityNode('identity'))
31
                ->append($this->productNode('product'))
32
                ->append($this->listenerNode('listener'))
33
            ->end()
34
        ;
35
36
        return $treeBuilder;
37
    }
38
39
    private function walletNode(string $name): ArrayNodeDefinition
40
    {
41
        return (new ArrayNodeDefinition($name))
42
            ->addDefaultsIfNotSet()
43
            ->beforeNormalization()
44
                ->ifTrue(function (array $config): bool {
45
                    return !isset($config['type']);
46
                })
47
                ->then(function (array $config): array {
48
                    return ['type' => self::WALLET_FIXED, 'accounts' => $config];
49
                })
50
            ->end()
51
            ->validate()
52
                ->ifTrue(function (array $config): bool {
53
                    return self::WALLET_SERVICE === $config['type'] && empty($config['factory_service_id']);
54
                })
55
                ->thenInvalid('Service id must be specified.')
56
            ->end()
57
            ->validate()
58
                ->ifTrue(function (array $config): bool {
59
                    return self::WALLET_REDIS === $config['type'] && (empty($config['redis_client_id']) || empty($config['wallet_key']));
60
                })
61
                ->thenInvalid('Wallet key and Redis client must be specified.')
62
            ->end()
63
            ->validate()
64
                ->ifTrue(function (array $config) {
65
                    return self::WALLET_MONGO === $config['type'] && (empty($config['mongo_client_id']) || empty($config['db_name']) || empty($config['collection_name']));
66
                })
67
                ->thenInvalid('Mongo client, database and collection name must be specified.')
68
            ->end()
69
            ->children()
70
                ->enumNode('type')
71
                    ->values([self::WALLET_REDIS, self::WALLET_MONGO, self::WALLET_FIXED, self::WALLET_SERVICE])
72
                    ->defaultValue(self::WALLET_FIXED)
73
                ->end()
74
75
                // Redis
76
                ->scalarNode('redis_client_id')->cannotBeEmpty()->end()
77
                ->scalarNode('wallet_key')->cannotBeEmpty()->end()
78
79
                // Mongo
80
                ->scalarNode('mongo_client_id')->cannotBeEmpty()->end()
81
                ->scalarNode('db_name')->cannotBeEmpty()->end()
82
                ->scalarNode('collection_name')->cannotBeEmpty()->end()
83
84
                // Fixed
85
                ->arrayNode('accounts')
86
                    ->useAttributeAsKey(true)
87
                    ->requiresAtLeastOneElement()
88
                    ->scalarPrototype()
89
                        ->isRequired()
90
                    ->end()
91
                ->end()
92
93
                // Service
94
                ->scalarNode('factory_service_id')->cannotBeEmpty()->end()
95
            ->end()
96
        ;
97
    }
98
99
    private function identityNode(string $name): ArrayNodeDefinition
100
    {
101
        return (new ArrayNodeDefinition($name))
102
            ->addDefaultsIfNotSet()
103
            ->beforeNormalization()
104
                ->ifString()
105
                ->then(function (string $config): array {
106
                    return ['type' => self::IDENTITY_FIXED, 'identity' => $config];
107
                })
108
            ->end()
109
            ->validate()
110
                ->ifTrue(function (array $config): bool {
111
                    return self::IDENTITY_SERVICE === $config['type'] && empty($config['factory_service_id']);
112
                })
113
                ->thenInvalid('Service id must be specified.')
114
            ->end()
115
            ->validate()
116
                ->ifTrue(function (array $config): bool {
117
                    return self::IDENTITY_FIXED === $config['type'] && empty($config['identity']);
118
                })
119
                ->thenInvalid('Identity must be specified.')
120
            ->end()
121
            ->children()
122
                ->enumNode('type')
123
                    ->values([self::IDENTITY_FIXED, self::IDENTITY_SECURITY, self::IDENTITY_SERVICE])
124
                    ->defaultValue(self::IDENTITY_SECURITY)
125
                ->end()
126
                ->scalarNode('identity')->cannotBeEmpty()->end()
127
                ->scalarNode('factory_service_id')->cannotBeEmpty()->end()
128
            ->end()
129
        ;
130
    }
131
132
    private function productNode(string $name): ArrayNodeDefinition
133
    {
134
        $defaults = ['name' => 'API', 'price' => 1];
135
136
        return (new ArrayNodeDefinition($name))
137
            ->requiresAtLeastOneElement()
138
            ->beforeNormalization()
139
                ->ifString()
140
                ->then(function (string $config) use ($defaults): array {
141
                    return [array_replace($defaults, ['name' => $config])];
142
                })
143
            ->end()
144
            ->beforeNormalization()
145
                ->ifTrue(function ($config): bool {
146
                    return is_numeric($config);
147
                })
148
                ->then(function (int $config) use ($defaults): array {
149
                    return [array_replace($defaults, ['price' => $config])];
150
                })
151
            ->end()
152
            ->defaultValue([$defaults])
153
            ->arrayPrototype()
154
                ->children()
155
                    ->scalarNode('name')
156
                        ->isRequired()
157
                        ->cannotBeEmpty()
158
                    ->end()
159
                    ->integerNode('price')
160
                        ->min(1)
161
                        ->defaultValue(1)
162
                    ->end()
163
                    ->append($this->matcherNode('matcher'))
164
                ->end()
165
            ->end()
166
        ;
167
    }
168
169
    private function listenerNode(string $name): ArrayNodeDefinition
170
    {
171
        return (new ArrayNodeDefinition($name))
172
            ->addDefaultsIfNotSet()
173
            ->children()
174
                ->integerNode('priority')
175
                    ->max(7)
176
                    ->defaultValue(4)
177
                ->end()
178
                ->append($this->matcherNode('matcher'))
179
            ->end()
180
        ;
181
    }
182
183
    private function matcherNode(string $name): ArrayNodeDefinition
184
    {
185
        return (new ArrayNodeDefinition($name))
186
            ->beforeNormalization()
187
                ->ifString()
188
                ->then(function (string $config): array {
189
                    return ['path' => $config];
190
                })
191
            ->end()
192
            ->children()
193
                ->scalarNode('path')
194
                    ->cannotBeEmpty()
195
                ->end()
196
                ->arrayNode('ips')
197
                    ->scalarPrototype()->cannotBeEmpty()->end()
198
                ->end()
199
                ->arrayNode('methods')
200
                    ->scalarPrototype()->cannotBeEmpty()->end()
201
                ->end()
202
            ->end()
203
        ;
204
    }
205
}
206