Passed
Push — master ( 1c2a4d...909dca )
by Yo
03:22
created

checkMethodAwareServiceIdList()   A

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
 * 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
    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 8
    public function load(array $configs, ContainerBuilder $container)
49
    {
50 8
        $this->compileAndProcessConfigurations($configs, $container);
51
52 8
        $loader = new YamlFileLoader(
53 8
            $container,
54 8
            new FileLocator(__DIR__.'/../Resources/config')
55
        );
56 8
        $loader->load('sdk.services.app.yaml');
57 8
        $loader->load('sdk.services.infra.yaml');
58 8
        $loader->load('services.private.yaml');
59 8
        $loader->load('services.public.yaml');
60 8
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65 8
    public function process(ContainerBuilder $container)
66
    {
67 8
        $this->bindJsonRpcServerDispatcher($container);
68 7
        $this->bindValidatorIfDefined($container);
69 7
        $this->binJsonRpcMethods($container);
70 6
    }
71
72
    /**
73
     * {@inheritdoc}
74
     */
75 9
    public function getNamespace()
76
    {
77 9
        return 'http://example.org/schema/dic/'.$this->getAlias();
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83 1
    public function getXsdValidationBasePath()
84
    {
85 1
        return '';
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91 9
    public function getAlias()
92
    {
93 9
        return self::EXTENSION_IDENTIFIER;
94
    }
95
96
    /**
97
     * @param array            $configs
98
     * @param ContainerBuilder $container
99
     */
100 8
    private function compileAndProcessConfigurations(array $configs, ContainerBuilder $container) : void
101
    {
102 8
        $configuration = new Configuration();
103 8
        $config = (new Processor())->processConfiguration($configuration, $configs);
104
105 8
        $httpEndpointPath = $config['endpoint'];
106
107 8
        $container->setParameter(self::ENDPOINT_PATH_CONTAINER_PARAM_ID, $httpEndpointPath);
108 8
    }
109
110
    /**
111
     * @param ContainerBuilder $container
112
     */
113 8
    private function bindJsonRpcServerDispatcher(ContainerBuilder $container) : void
114
    {
115 8
        $dispatcherRef = new Reference('json_rpc_http_server.dispatcher.server');
116 8
        $dispatcherAwareServiceList = $container->findTaggedServiceIds(self::JSONRPC_SERVER_DISPATCHER_AWARE_TAG);
117 8
        foreach ($dispatcherAwareServiceList as $serviceId => $tagAttributeList) {
118 8
            $definition = $container->getDefinition($serviceId);
119
120 8
            if (!in_array(JsonRpcServerDispatcherAwareTrait::class, class_uses($definition->getClass()))) {
121 1
                throw new LogicException(
122 1
                    sprintf(
123 1
                        'Service "%s" is taggued with "%s" but does not use "%s"',
124 1
                        $serviceId,
125 1
                        self::JSONRPC_SERVER_DISPATCHER_AWARE_TAG,
126 1
                        JsonRpcServerDispatcherAwareTrait::class
127
                    )
128
                );
129
            }
130
131 7
            $definition->addMethodCall('setJsonRpcServerDispatcher', [$dispatcherRef]);
132
        }
133 7
    }
134
135 7
    private function bindValidatorIfDefined(ContainerBuilder $container) : void
136
    {
137 7
        if ($container->hasAlias(self::PARAMS_VALIDATOR_ALIAS)) {
138 1
            $container->getDefinition(self::REQUEST_HANDLER_SERVICE_ID)
139 1
                ->addMethodCall(
140 1
                    'setMethodParamsValidator',
141
                    [
142 1
                        new Reference(self::PARAMS_VALIDATOR_ALIAS)
143
                    ]
144
                )
145
            ;
146
        }
147 7
    }
148
149
    /**
150
     * @param ContainerBuilder $container
151
     */
152 7
    private function binJsonRpcMethods(ContainerBuilder $container) : void
153
    {
154 7
        $mappingAwareServiceDefinitionList = $this->findAndValidateMappingAwareDefinitionList($container);
155
156 6
        if (0 === count($mappingAwareServiceDefinitionList)) {
157 5
            return;
158
        }
159
160 1
        $jsonRpcMethodDefinitionList = (new JsonRpcMethodDefinitionHelper())
161 1
            ->findAndValidateJsonRpcMethodDefinition($container);
162
163 1
        $methodMappingList = [];
164 1
        foreach ($jsonRpcMethodDefinitionList as $jsonRpcMethodServiceId => $methodNameList) {
165 1
            foreach ($methodNameList as $methodName) {
166 1
                $methodMappingList[$methodName] = new Reference($jsonRpcMethodServiceId);
167 1
                $this->bindJsonRpcMethod($methodName, $jsonRpcMethodServiceId, $mappingAwareServiceDefinitionList);
168
            }
169
        }
170
171
        // Service locator for method resolver
172
        // => first argument is an array of wanted service with keys as alias for internal use
173 1
        $container->getDefinition('json_rpc_http_server.service_locator.method_resolver')
174 1
            ->setArgument(0, $methodMappingList);
175 1
    }
176
177
    /**
178
     * @param string       $methodName
179
     * @param string       $jsonRpcMethodServiceId
180
     * @param Definition[] $mappingAwareServiceDefinitionList
181
     */
182 1
    private function bindJsonRpcMethod(
183
        string $methodName,
184
        string $jsonRpcMethodServiceId,
185
        array $mappingAwareServiceDefinitionList
186
    ) : void {
187 1
        foreach ($mappingAwareServiceDefinitionList as $methodAwareServiceDefinition) {
188 1
            $methodAwareServiceDefinition->addMethodCall(
189 1
                'addJsonRpcMethod',
190 1
                [$methodName, new Reference($jsonRpcMethodServiceId)]
191
            );
192
        }
193 1
    }
194
195
    /**
196
     * @param ContainerBuilder $container
197
     *
198
     * @return array
199
     */
200 7
    private function findAndValidateMappingAwareDefinitionList(ContainerBuilder $container): array
201
    {
202 7
        $mappingAwareServiceDefinitionList = [];
203 7
        $methodAwareServiceIdList = array_keys($container->findTaggedServiceIds(self::JSONRPC_METHOD_AWARE_TAG));
204 7
        foreach ($methodAwareServiceIdList as $serviceId) {
205 2
            $definition = $container->getDefinition($serviceId);
206
207 2
            $this->checkMethodAwareServiceIdList($definition, $serviceId, $container);
208
209 1
            $mappingAwareServiceDefinitionList[$serviceId] = $definition;
210
        }
211
212 6
        return $mappingAwareServiceDefinitionList;
213
    }
214
215 2
    private function checkMethodAwareServiceIdList(
216
        Definition $definition,
217
        string $serviceId,
218
        ContainerBuilder $container
219
    ) : void {
220 2
        $class = $container->getReflectionClass($definition->getClass());
221
222 2
        if (null !== $class && !$class->implementsInterface(JsonRpcMethodAwareInterface::class)) {
223 1
            throw new LogicException(sprintf(
224 1
                'Service "%s" is taggued as JSON-RPC method aware but does not implement %s',
225 1
                $serviceId,
226 1
                JsonRpcMethodAwareInterface::class
227
            ));
228
        }
229 1
    }
230
}
231