Passed
Pull Request — release/2.0.0 (#29)
by Yo
01:36
created

JsonRpcHttpServerExtension   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 201
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 22
eloc 67
dl 0
loc 201
ccs 78
cts 78
cp 1
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A bindValidatorIfDefined() 0 8 2
A checkMethodAwareServiceIdList() 0 12 3
A binJsonRpcMethods() 0 14 4
A process() 0 5 1
A getAlias() 0 3 1
A bindJsonRpcMethod() 0 9 2
A getXsdValidationBasePath() 0 3 1
A bindJsonRpcServerDispatcher() 0 19 3
A compileAndProcessConfigurations() 0 8 1
A getNamespace() 0 3 1
A load() 0 11 1
A findAndValidateMappingAwareDefinitionList() 0 13 2
1
<?php
2
namespace Yoanm\SymfonyJsonRpcHttpServer\DependencyInjection;
3
4
use Symfony\Component\Config\Definition\Processor;
5
use Symfony\Component\Config\FileLocator;
6
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
7
use Symfony\Component\DependencyInjection\ContainerBuilder;
8
use Symfony\Component\DependencyInjection\Definition;
9
use Symfony\Component\DependencyInjection\Exception\LogicException;
10
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
11
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
12
use Symfony\Component\DependencyInjection\Reference;
13
use Yoanm\JsonRpcServer\App\Dispatcher\JsonRpcServerDispatcherAwareTrait;
14
use Yoanm\JsonRpcServer\Domain\JsonRpcMethodAwareInterface;
15
16
/**
17
 * Class JsonRpcHttpServerExtension
18
 */
19
class JsonRpcHttpServerExtension implements ExtensionInterface, CompilerPassInterface
20
{
21
    // Extension identifier (used in configuration for instance)
22
    public const EXTENSION_IDENTIFIER = 'json_rpc_http_server';
23
24
    /** Tags */
25
    /**** Methods tags **/
26
    // Use this tag to inject your JSON-RPC methods into the default method resolver
27
    public const JSONRPC_METHOD_TAG = 'json_rpc_http_server.jsonrpc_method';
28
    // And add an attribute with following key
29
    public const JSONRPC_METHOD_TAG_METHOD_NAME_KEY = 'method';
30
    /**** END - Methods tags **/
31
32
    // Server dispatcher - Use this tag and server dispatcher will be injected
33
    public const JSONRPC_SERVER_DISPATCHER_AWARE_TAG = 'json_rpc_http_server.server_dispatcher_aware';
34
35
    // JSON-RPC Methods mapping - Use this tag and all JSON-RPC method instance will be injected
36
    // Useful for documentation for instance
37
    public const JSONRPC_METHOD_AWARE_TAG = 'json_rpc_http_server.method_aware';
38
39
40
    private const PARAMS_VALIDATOR_ALIAS = 'json_rpc_http_server.alias.params_validator';
41
    private const REQUEST_HANDLER_SERVICE_ID = 'json_rpc_server_sdk.app.handler.jsonrpc_request';
42
43
    /**
44
     * {@inheritdoc}
45
     */
46 8
    public function load(array $configs, ContainerBuilder $container)
47
    {
48 8
        $this->compileAndProcessConfigurations($configs, $container);
49
50 8
        $loader = new YamlFileLoader(
51 8
            $container,
52 8
            new FileLocator(__DIR__.'/../Resources/config')
53
        );
54 8
        $loader->load('sdk.services.app.yaml');
55 8
        $loader->load('sdk.services.infra.yaml');
56 8
        $loader->load('services.public.yaml');
57 8
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62 8
    public function process(ContainerBuilder $container)
63
    {
64 8
        $this->bindJsonRpcServerDispatcher($container);
65 7
        $this->bindValidatorIfDefined($container);
66 7
        $this->binJsonRpcMethods($container);
67 6
    }
68
69
    /**
70
     * {@inheritdoc}
71
     */
72 9
    public function getNamespace()
73
    {
74 9
        return 'http://example.org/schema/dic/'.$this->getAlias();
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80 1
    public function getXsdValidationBasePath()
81
    {
82 1
        return '';
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88 9
    public function getAlias()
89
    {
90 9
        return self::EXTENSION_IDENTIFIER;
91
    }
92
93
    /**
94
     * @param array            $configs
95
     * @param ContainerBuilder $container
96
     */
97 8
    private function compileAndProcessConfigurations(array $configs, ContainerBuilder $container) : void
98
    {
99 8
        $configuration = new Configuration();
100 8
        $config = (new Processor())->processConfiguration($configuration, $configs);
101
102 8
        $httpEndpointPath = $config['endpoint'];
103
104 8
        $container->setParameter(self::EXTENSION_IDENTIFIER.'.http_endpoint_path', $httpEndpointPath);
105 8
    }
106
107
    /**
108
     * @param ContainerBuilder $container
109
     *
110
     * @return Reference|null Null in case no dispatcher found
111
     */
112 8
    private function bindJsonRpcServerDispatcher(ContainerBuilder $container) : void
113
    {
114 8
        $dispatcherRef = new Reference('json_rpc_http_server.dispatcher.server');
115 8
        $dispatcherAwareServiceList = $container->findTaggedServiceIds(self::JSONRPC_SERVER_DISPATCHER_AWARE_TAG);
116 8
        foreach ($dispatcherAwareServiceList as $serviceId => $tagAttributeList) {
117 8
            $definition = $container->getDefinition($serviceId);
118
119 8
            if (!in_array(JsonRpcServerDispatcherAwareTrait::class, class_uses($definition->getClass()))) {
120 1
                throw new LogicException(
121 1
                    sprintf(
122 1
                        'Service "%s" is taggued with "%s" but does not use "%s"',
123 1
                        $serviceId,
124 1
                        self::JSONRPC_SERVER_DISPATCHER_AWARE_TAG,
125 1
                        JsonRpcServerDispatcherAwareTrait::class
126
                    )
127
                );
128
            }
129
130 7
            $definition->addMethodCall('setJsonRpcServerDispatcher', [$dispatcherRef]);
131
        }
132 7
    }
133
134 7
    private function bindValidatorIfDefined(ContainerBuilder $container) : void
135
    {
136 7
        if ($container->hasAlias(self::PARAMS_VALIDATOR_ALIAS)) {
137 1
            $container->getDefinition(self::REQUEST_HANDLER_SERVICE_ID)
138 1
                ->addMethodCall(
139 1
                    'setMethodParamsValidator',
140
                    [
141 1
                        new Reference(self::PARAMS_VALIDATOR_ALIAS)
142
                    ]
143
                )
144
            ;
145
        }
146 7
    }
147
148
    /**
149
     * @param ContainerBuilder $container
150
     */
151 7
    private function binJsonRpcMethods(ContainerBuilder $container) : void
152
    {
153 7
        $mappingAwareServiceDefinitionList = $this->findAndValidateMappingAwareDefinitionList($container);
154
155 6
        if (0 === count($mappingAwareServiceDefinitionList)) {
156 5
            return;
157
        }
158
159 1
        $jsonRpcMethodDefinitionList = (new JsonRpcMethodDefinitionHelper())
160 1
            ->findAndValidateJsonRpcMethodDefinition($container);
161
162 1
        foreach ($jsonRpcMethodDefinitionList as $jsonRpcMethodServiceId => $methodNameList) {
163 1
            foreach ($methodNameList as $methodName) {
164 1
                $this->bindJsonRpcMethod($methodName, $jsonRpcMethodServiceId, $mappingAwareServiceDefinitionList);
165
            }
166
        }
167 1
    }
168
169
    /**
170
     * @param string       $methodName
171
     * @param Definition   $jsonRpcMethodDefinition
172
     * @param Definition[] $mappingAwareServiceDefinitionList
173
     */
174 1
    private function bindJsonRpcMethod(
175
        string $methodName,
176
        string $jsonRpcMethodServiceId,
177
        array $mappingAwareServiceDefinitionList
178
    ) : void {
179 1
        foreach ($mappingAwareServiceDefinitionList as $methodAwareServiceDefinition) {
180 1
            $methodAwareServiceDefinition->addMethodCall(
181 1
                'addJsonRpcMethod',
182 1
                [$methodName, new Reference($jsonRpcMethodServiceId)]
183
            );
184
        }
185 1
    }
186
187
    /**
188
     * @param ContainerBuilder $container
189
     *
190
     * @return array
191
     * @throws \ReflectionException
192
     */
193 7
    private function findAndValidateMappingAwareDefinitionList(ContainerBuilder $container): array
194
    {
195 7
        $mappingAwareServiceDefinitionList = [];
196 7
        $methodAwareServiceIdList = array_keys($container->findTaggedServiceIds(self::JSONRPC_METHOD_AWARE_TAG));
197 7
        foreach ($methodAwareServiceIdList as $serviceId) {
198 2
            $definition = $container->getDefinition($serviceId);
199
200 2
            $this->checkMethodAwareServiceIdList($definition, $serviceId, $container);
201
202 1
            $mappingAwareServiceDefinitionList[$serviceId] = $definition;
203
        }
204
205 6
        return $mappingAwareServiceDefinitionList;
206
    }
207
208 2
    private function checkMethodAwareServiceIdList(
209
        Definition $definition,
210
        string $serviceId,
211
        ContainerBuilder $container
212
    ) : void {
213 2
        $class = $container->getReflectionClass($definition->getClass());
214
215 2
        if (null !== $class && !$class->implementsInterface(JsonRpcMethodAwareInterface::class)) {
216 1
            throw new LogicException(sprintf(
217 1
                'Service "%s" is taggued as JSON-RPC method aware but does not implement %s',
218 1
                $serviceId,
219 1
                JsonRpcMethodAwareInterface::class
220
            ));
221
        }
222 1
    }
223
}
224