checkMethodAwareServiceIdList()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 3
crap 3
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
 * @see \Yoanm\SymfonyJsonRpcHttpServer\DependencyInjection\Configuration
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
    public const ENDPOINT_PATH_CONTAINER_PARAM_ID = self::EXTENSION_IDENTIFIER.'.http_endpoint_path';
25
26
    /** Tags */
27
    /**** Methods tags **/
28
    // Use this tag to inject your JSON-RPC methods into the default method resolver
29
    public const JSONRPC_METHOD_TAG = 'json_rpc_http_server.jsonrpc_method';
30
    // And add an attribute with following key
31
    public const JSONRPC_METHOD_TAG_METHOD_NAME_KEY = 'method';
32
    /**** END - Methods tags **/
33
34
    // Server dispatcher - Use this tag and server dispatcher will be injected
35
    public const JSONRPC_SERVER_DISPATCHER_AWARE_TAG = 'json_rpc_http_server.server_dispatcher_aware';
36
37
    // JSON-RPC Methods mapping - Use this tag and all JSON-RPC method instance will be injected
38
    // Useful for documentation for instance
39
    public const JSONRPC_METHOD_AWARE_TAG = 'json_rpc_http_server.method_aware';
40
41
42
    private const PARAMS_VALIDATOR_ALIAS = 'json_rpc_http_server.alias.params_validator';
43
    private const REQUEST_HANDLER_SERVICE_ID = 'json_rpc_server_sdk.app.handler.jsonrpc_request';
44
45
    /**
46
     * {@inheritdoc}
47
     */
48 11
    public function load(array $configs, ContainerBuilder $container)
49
    {
50 11
        $this->compileAndProcessConfigurations($configs, $container);
51
52 11
        $loader = new YamlFileLoader(
53 11
            $container,
54 11
            new FileLocator(__DIR__.'/../Resources/config')
55 11
        );
56 11
        $loader->load('sdk.services.app.yaml');
57 11
        $loader->load('sdk.services.infra.yaml');
58 11
        $loader->load('services.private.yaml');
59 11
        $loader->load('services.public.yaml');
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65 11
    public function process(ContainerBuilder $container)
66
    {
67 11
        $this->bindJsonRpcServerDispatcher($container);
68 10
        $this->bindValidatorIfDefined($container);
69 10
        $this->bindJsonRpcMethods($container);
70 9
        $this->bindDebug($container);
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 12
    public function getNamespace()
77
    {
78 12
        return 'http://example.org/schema/dic/'.$this->getAlias();
79
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84 1
    public function getXsdValidationBasePath()
85
    {
86 1
        return '';
87
    }
88
89
    /**
90
     * {@inheritdoc}
91
     */
92 12
    public function getAlias()
93
    {
94 12
        return self::EXTENSION_IDENTIFIER;
95
    }
96
97
    /**
98
     * @param array            $configs
99
     * @param ContainerBuilder $container
100
     */
101 11
    private function compileAndProcessConfigurations(array $configs, ContainerBuilder $container) : void
102
    {
103 11
        $configuration = new Configuration();
104 11
        $config = (new Processor())->processConfiguration($configuration, $configs);
105
106 11
        $container->setParameter(self::ENDPOINT_PATH_CONTAINER_PARAM_ID, $config['endpoint']);
107
108 11
        foreach ($config['debug'] as $name => $value) {
109 11
            $container->setParameter(self::EXTENSION_IDENTIFIER.'.debug.'.$name, $value);
110
        }
111
    }
112
113
    /**
114
     * @param ContainerBuilder $container
115
     */
116 11
    private function bindJsonRpcServerDispatcher(ContainerBuilder $container) : void
117
    {
118 11
        $dispatcherRef = new Reference(self::EXTENSION_IDENTIFIER.'.dispatcher.server');
119 11
        $dispatcherAwareServiceList = $container->findTaggedServiceIds(self::JSONRPC_SERVER_DISPATCHER_AWARE_TAG);
120 11
        foreach ($dispatcherAwareServiceList as $serviceId => $tagAttributeList) {
121 11
            $definition = $container->getDefinition($serviceId);
122
123 11
            if (!in_array(JsonRpcServerDispatcherAwareTrait::class, class_uses($definition->getClass()))) {
124 1
                throw new LogicException(
125 1
                    sprintf(
126 1
                        'Service "%s" is taggued with "%s" but does not use "%s"',
127 1
                        $serviceId,
128 1
                        self::JSONRPC_SERVER_DISPATCHER_AWARE_TAG,
129 1
                        JsonRpcServerDispatcherAwareTrait::class
130 1
                    )
131 1
                );
132
            }
133
134 10
            $definition->addMethodCall('setJsonRpcServerDispatcher', [$dispatcherRef]);
135
        }
136
    }
137
138 10
    private function bindValidatorIfDefined(ContainerBuilder $container) : void
139
    {
140 10
        if ($container->hasAlias(self::PARAMS_VALIDATOR_ALIAS)) {
141 1
            $container->getDefinition(self::REQUEST_HANDLER_SERVICE_ID)
142 1
                ->addMethodCall(
143 1
                    'setMethodParamsValidator',
144 1
                    [
145 1
                        new Reference(self::PARAMS_VALIDATOR_ALIAS)
146 1
                    ]
147 1
                )
148 1
            ;
149
        }
150
    }
151
152
    /**
153
     * @param ContainerBuilder $container
154
     */
155 10
    private function bindJsonRpcMethods(ContainerBuilder $container) : void
156
    {
157 10
        $mappingAwareServiceDefinitionList = $this->findAndValidateMappingAwareDefinitionList($container);
158
159 9
        $jsonRpcMethodDefinitionList = (new JsonRpcMethodDefinitionHelper())
160 9
            ->findAndValidateJsonRpcMethodDefinition($container);
161
162 9
        $methodMappingList = [];
163 9
        foreach ($jsonRpcMethodDefinitionList as $jsonRpcMethodServiceId => $methodNameList) {
164 3
            foreach ($methodNameList as $methodName) {
165 3
                $methodMappingList[$methodName] = new Reference($jsonRpcMethodServiceId);
166 3
                $this->bindJsonRpcMethod($methodName, $jsonRpcMethodServiceId, $mappingAwareServiceDefinitionList);
167
            }
168
        }
169
170
        // Service locator for method resolver
171
        // => first argument is an array of wanted service with keys as alias for internal use
172 9
        $container->getDefinition(self::EXTENSION_IDENTIFIER.'.service_locator.method_resolver')
173 9
            ->setArgument(0, $methodMappingList);
174
    }
175
176
    /**
177
     * @param string       $methodName
178
     * @param string       $jsonRpcMethodServiceId
179
     * @param Definition[] $mappingAwareServiceDefinitionList
180
     */
181 3
    private function bindJsonRpcMethod(
182
        string $methodName,
183
        string $jsonRpcMethodServiceId,
184
        array $mappingAwareServiceDefinitionList
185
    ) : void {
186 3
        foreach ($mappingAwareServiceDefinitionList as $methodAwareServiceDefinition) {
187 2
            $methodAwareServiceDefinition->addMethodCall(
188 2
                'addJsonRpcMethod',
189 2
                [$methodName, new Reference($jsonRpcMethodServiceId)]
190 2
            );
191
        }
192
    }
193
194
    /**
195
     * @param ContainerBuilder $container
196
     *
197
     * @return array
198
     */
199 10
    private function findAndValidateMappingAwareDefinitionList(ContainerBuilder $container): array
200
    {
201 10
        $mappingAwareServiceDefinitionList = [];
202 10
        $methodAwareServiceIdList = array_keys($container->findTaggedServiceIds(self::JSONRPC_METHOD_AWARE_TAG));
203 10
        foreach ($methodAwareServiceIdList as $serviceId) {
204 3
            $definition = $container->getDefinition($serviceId);
205
206 3
            $this->checkMethodAwareServiceIdList($definition, $serviceId, $container);
207
208 2
            $mappingAwareServiceDefinitionList[$serviceId] = $definition;
209
        }
210
211 9
        return $mappingAwareServiceDefinitionList;
212
    }
213
214 3
    private function checkMethodAwareServiceIdList(
215
        Definition $definition,
216
        string $serviceId,
217
        ContainerBuilder $container
218
    ) : void {
219 3
        $class = $container->getReflectionClass($definition->getClass());
220
221 3
        if (null !== $class && !$class->implementsInterface(JsonRpcMethodAwareInterface::class)) {
222 1
            throw new LogicException(sprintf(
223 1
                'Service "%s" is tagged as JSON-RPC method aware but does not implement %s',
224 1
                $serviceId,
225 1
                JsonRpcMethodAwareInterface::class
226 1
            ));
227
        }
228
    }
229
230 9
    private function bindDebug(ContainerBuilder $container) : void
231
    {
232 9
        if ($container->getParameter(self::EXTENSION_IDENTIFIER.'.debug.enabled')) {
233 1
            $container->getDefinition('json_rpc_server_sdk.app.serialization.jsonrpc_response_normalizer')
234 1
                ->addArgument(new Reference('json_rpc_server_sdk.app.serialization.jsonrpc_response_error_normalizer'));
235
        }
236
    }
237
}
238