Completed
Pull Request — master (#2)
by Yo
02:01
created

JsonRpcHttpServerExtension   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 256
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 256
ccs 109
cts 109
cp 1
rs 10
c 0
b 0
f 0
wmc 15

9 Methods

Rating   Name   Duplication   Size   Complexity  
A prependServiceName() 0 3 1
A createPublicServiceDefinitions() 0 16 1
A getMethodResolverServiceId() 0 22 3
B createInfraServiceDefinitions() 0 44 1
A load() 0 7 1
A validateJsonRpcMethodTag() 0 8 3
B createAppServiceDefinitions() 0 42 1
A loadJsonRpcMethodsFromTag() 0 17 2
A checkJsonRpcMethodService() 0 8 2
1
<?php
2
namespace Yoanm\SymfonyJsonRpcHttpServer\Infra\Symfony\DependencyInjection;
3
4
use Symfony\Component\DependencyInjection\ContainerBuilder;
5
use Symfony\Component\DependencyInjection\Definition;
6
use Symfony\Component\DependencyInjection\Exception\LogicException;
7
use Symfony\Component\DependencyInjection\Extension\Extension;
8
use Symfony\Component\DependencyInjection\Reference;
9
use Yoanm\JsonRpcServer\App\Creator\CustomExceptionCreator;
10
use Yoanm\JsonRpcServer\App\Creator\ResponseCreator;
11
use Yoanm\JsonRpcServer\App\Manager\MethodManager;
12
use Yoanm\JsonRpcServer\App\RequestHandler;
13
use Yoanm\JsonRpcServer\App\Serialization\RequestDenormalizer;
14
use Yoanm\JsonRpcServer\App\Serialization\ResponseNormalizer;
15
use Yoanm\JsonRpcServer\Infra\Endpoint\JsonRpcEndpoint;
16
use Yoanm\JsonRpcServer\Infra\Serialization\RawRequestSerializer;
17
use Yoanm\JsonRpcServer\Infra\Serialization\RawResponseSerializer;
18
use Yoanm\JsonRpcServerPsr11Resolver\Infra\Resolver\ContainerMethodResolver;
19
use Yoanm\SymfonyJsonRpcHttpServer\Infra\Endpoint\JsonRpcHttpEndpoint;
20
use Yoanm\SymfonyJsonRpcHttpServer\Infra\Resolver\ServiceNameResolver;
21
22
/**
23
 * Class JsonRpcHttpServerExtension
24
 *
25
 * /!\ In case you use the default resolver (yoanm/jsonrpc-server-sdk-psr11-resolver),
26
 * your JSON-RPC method services must be public in order to retrieve it later from container
27
 */
28
class JsonRpcHttpServerExtension extends Extension
29
{
30
    // Use this service to inject string request
31
    const ENDPOINT_SERVICE_NAME = 'yoanm.jsonrpc_http_server.endpoint';
32
    // Use this tag to inject your own resolver
33
    const METHOD_RESOLVER_TAG = 'yoanm.jsonrpc_http_server.method_resolver';
34
    // Use this tag to inject your JSON-RPC methods into the default method resolver
35
    const JSONRPC_METHOD_TAG = 'yoanm.jsonrpc_http_server.jsonrpc_method';
36
    // In case you want to add mapping for a method, use the following service
37
    const SERVICE_NAME_RESOLVER_SERVICE_NAME = 'yoanm.jsonrpc_http_server.resolver.service_name';
38
39
40
    const JSONRPC_METHOD_TAG_METHOD_NAME_KEY = 'method';
41
42
43
    private $sdkAppResponseCreatorServiceId        = 'sdk.app.creator.response';
44
    private $sdkAppCustomExceptionCreatorServiceId = 'sdk.app.creator.custom_exception';
45
    private $sdkAppRequestDenormalizerServiceId    = 'sdk.app.serialization.request_denormalizer';
46
    private $sdkAppResponseNormalizerServiceId     = 'sdk.app.serialization.response_normalizer';
47
    private $sdkAppMethodManagerServiceId          = 'sdk.app.manager.method';
48
    private $sdkAppRequestHandlerServiceId         = 'sdk.app.handler.request';
49
50
    private $sdkInfraEndpointServiceId          = 'sdk.infra.endpoint';
51
    private $sdkInfraRawReqSerializerServiceId  = 'sdk.infra.serialization.raw_request_serializer';
52
    private $sdkInfraRawRespSerializerServiceId = 'sdk.infra.serialization.raw_response_serializer';
53
54
    private $psr11InfraMethodResolverServiceId = 'psr11.infra.resolver.method';
55
56
    /**
57
     * {@inheritdoc}
58
     */
59 9
    public function load(array $configs, ContainerBuilder $container)
60
    {
61
        // Use only references to avoid class instantiation
62
        // And don't use file configuration in order to not add Symfony\Component\Config as dependency
63 9
        $this->createPublicServiceDefinitions($container);
64 9
        $this->createInfraServiceDefinitions($container);
65 9
        $this->createAppServiceDefinitions($container);
66 6
    }
67
68
    /**
69
     * @param ContainerBuilder $container
70
     */
71 9
    protected function createAppServiceDefinitions(ContainerBuilder $container)
72
    {
73
        // RequestDenormalizer
74 9
        $container->setDefinition(
75 9
            $this->prependServiceName($this->sdkAppRequestDenormalizerServiceId),
76 9
            new Definition(RequestDenormalizer::class)
77
        );
78
        // ResponseNormalizer
79 9
        $container->setDefinition(
80 9
            $this->prependServiceName($this->sdkAppResponseNormalizerServiceId),
81 9
            new Definition(ResponseNormalizer::class)
82
        );
83
        // ResponseCreator
84 9
        $container->setDefinition(
85 9
            $this->prependServiceName($this->sdkAppResponseCreatorServiceId),
86 9
            new Definition(ResponseCreator::class)
87
        );
88
        // CustomExceptionCreator
89 9
        $container->setDefinition(
90 9
            $this->prependServiceName($this->sdkAppCustomExceptionCreatorServiceId),
91 9
            new Definition(CustomExceptionCreator::class)
92
        );
93
94
        // MethodManager
95 9
        $container->setDefinition(
96 9
            $this->prependServiceName($this->sdkAppMethodManagerServiceId),
97 9
            new Definition(
98 9
                MethodManager::class,
99
                [
100 9
                    new Reference($this->getMethodResolverServiceId($container)),
101 6
                    new Reference($this->prependServiceName($this->sdkAppCustomExceptionCreatorServiceId))
102
                ]
103
            )
104
        );
105
        // RequestHandler
106 6
        $container->setDefinition(
107 6
            $this->prependServiceName($this->sdkAppRequestHandlerServiceId),
108 6
            new Definition(
109 6
                RequestHandler::class,
110
                [
111 6
                    new Reference($this->prependServiceName($this->sdkAppMethodManagerServiceId)),
112 6
                    new Reference($this->prependServiceName($this->sdkAppResponseCreatorServiceId))
113
                ]
114
            )
115
        );
116 6
    }
117
118
    /**
119
     * @param ContainerBuilder $container
120
     */
121 9
    protected function createInfraServiceDefinitions(ContainerBuilder $container)
122
    {
123
        // RawRequestSerializer
124 9
        $container->setDefinition(
125 9
            $this->prependServiceName($this->sdkInfraRawReqSerializerServiceId),
126 9
            new Definition(
127 9
                RawRequestSerializer::class,
128 9
                [new Reference($this->prependServiceName($this->sdkAppRequestDenormalizerServiceId))]
129
            )
130
        );
131
132
        // RawResponseSerializer
133 9
        $container->setDefinition(
134 9
            $this->prependServiceName($this->sdkInfraRawRespSerializerServiceId),
135 9
            new Definition(
136 9
                RawResponseSerializer::class,
137 9
                [new Reference($this->prependServiceName($this->sdkAppResponseNormalizerServiceId))]
138
            )
139
        );
140
        // JsonRpcEndpoint
141 9
        $container->setDefinition(
142 9
            $this->prependServiceName($this->sdkInfraEndpointServiceId),
143 9
            new Definition(
144 9
                JsonRpcEndpoint::class,
145
                [
146 9
                    new Reference($this->prependServiceName($this->sdkInfraRawReqSerializerServiceId)),
147 9
                    new Reference($this->prependServiceName($this->sdkAppRequestHandlerServiceId)),
148 9
                    new Reference($this->prependServiceName($this->sdkInfraRawRespSerializerServiceId)),
149 9
                    new Reference($this->prependServiceName($this->sdkAppResponseCreatorServiceId))
150
                ]
151
            )
152
        );
153
        // ContainerMethodResolver
154 9
        $container->setDefinition(
155 9
            $this->prependServiceName($this->psr11InfraMethodResolverServiceId),
156 9
            (new Definition(
157 9
                ContainerMethodResolver::class,
158
                [
159 9
                    new Reference('service_container')
160
                ]
161 9
            ))->addMethodCall(
162 9
                'setServiceNameResolver',
163
                [
164 9
                    new Reference(self::SERVICE_NAME_RESOLVER_SERVICE_NAME)
165
                ]
166
            )
167
        );
168 9
    }
169
170
    /**
171
     * @param ContainerBuilder $container
172
     */
173 9
    protected function createPublicServiceDefinitions(ContainerBuilder $container)
174
    {
175
        // JsonRpcHttpEndpoint
176 9
        $container->setDefinition(
177 9
            self::ENDPOINT_SERVICE_NAME,
178 9
            (new Definition(
179 9
                JsonRpcHttpEndpoint::class,
180
                [
181 9
                    new Reference($this->prependServiceName($this->sdkInfraEndpointServiceId))
182
                ]
183 9
            ))->setPrivate(false)
184
        );
185
        // ServiceNameResolver
186 9
        $container->setDefinition(
187 9
            self::SERVICE_NAME_RESOLVER_SERVICE_NAME,
188 9
            (new Definition(ServiceNameResolver::class))->setPrivate(false)
189
        );
190 9
    }
191
192
    /**
193
     * @param ContainerBuilder $container
194
     *
195
     * @return string
196
     */
197 9
    private function getMethodResolverServiceId(ContainerBuilder $container)
198
    {
199 9
        $serviceIdList = array_keys($container->findTaggedServiceIds(self::METHOD_RESOLVER_TAG));
200 9
        $serviceCount = count($serviceIdList);
201 9
        if ($serviceCount > 0) {
202 3
            if ($serviceCount > 1) {
203 1
                throw new LogicException(
204 1
                    sprintf(
205 1
                        'Only one method resolver could be defined, found following services : %s',
206 1
                        implode(', ', $serviceIdList)
207
                    )
208
                );
209
            }
210
            // Use the first result
211 2
            $resolverServiceId = array_shift($serviceIdList);
212
        } else {
213
            // Use ArrayMethodResolver as default resolver
214 6
            $resolverServiceId = $this->prependServiceName($this->psr11InfraMethodResolverServiceId);
215 6
            $this->loadJsonRpcMethodsFromTag($container);
216
        }
217
218 6
        return $resolverServiceId;
219
    }
220
221
    /**
222
     * @param ContainerBuilder $container
223
     */
224 6
    private function loadJsonRpcMethodsFromTag(ContainerBuilder $container)
225
    {
226
        // Check if methods have been defined by tags
227 6
        $methodServiceList = $container->findTaggedServiceIds(self::JSONRPC_METHOD_TAG);
228 6
        $defaultResolverDefinition = $container->getDefinition(self::SERVICE_NAME_RESOLVER_SERVICE_NAME);
229
230 6
        foreach ($methodServiceList as $serviceId => $tags) {
231 3
            $firstTag = array_shift($tags);
232
233 3
            $this->validateJsonRpcMethodTag($firstTag, $serviceId);
234 3
            $this->checkJsonRpcMethodService($container, $serviceId);
235
236 3
            $defaultResolverDefinition->addMethodCall(
237 3
                'addMethodMapping',
238
                [
239 3
                    $firstTag[self::JSONRPC_METHOD_TAG_METHOD_NAME_KEY],
240 3
                    $serviceId
241
                ]
242
            );
243
        }
244 4
    }
245
246
    /**
247
     * @param string $serviceName
248
     *
249
     * @return string
250
     */
251 9
    private function prependServiceName(string $serviceName) : string
252
    {
253 9
        return sprintf('yoanm.jsonrpc_http_server.%s', $serviceName);
254
    }
255
256
    /**
257
     * @param array  $tag
258
     * @param string $serviceId
259
     */
260 3
    private function validateJsonRpcMethodTag(array $tag, string $serviceId)
261
    {
262 3
        if (!is_array($tag) || !array_key_exists(self::JSONRPC_METHOD_TAG_METHOD_NAME_KEY, $tag)) {
0 ignored issues
show
introduced by
The condition is_array($tag) is always true.
Loading history...
263 1
            throw new LogicException(sprintf(
264
                'Service %s is taggued as JSON-RPC method but does not have'
265 1
                . 'method name defined under "%s" tag attribute key',
266 1
                $serviceId,
267 1
                self::JSONRPC_METHOD_TAG_METHOD_NAME_KEY
268
            ));
269
        }
270 3
    }
271
272
    /**
273
     * @param ContainerBuilder $container
274
     * @param string           $serviceId
275
     */
276 3
    private function checkJsonRpcMethodService(ContainerBuilder $container, string $serviceId)
277
    {
278
        // Check if given service is public => must be public in order to get it from container later
279 3
        if ($container->getDefinition($serviceId)->isPrivate()) {
280 1
            throw new LogicException(sprintf(
281
                'Service %s is taggued as JSON-RPC method but is not public. Service must be public in order'
282 1
                . ' to retrieve it later',
283 1
                $serviceId
284
            ));
285
        }
286 3
    }
287
}
288