Failed Conditions
Pull Request — master (#2)
by Yo
02:05
created

getMethodResolverServiceId()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 13
nc 3
nop 1
dl 0
loc 22
ccs 0
cts 19
cp 0
crap 12
rs 9.2
c 0
b 0
f 0
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
class JsonRpcHttpServerExtension extends Extension
26
{
27
    // Use this service to inject string request
28
    const ENDPOINT_SERVICE_NAME = 'yoanm.jsonrpc_http_server.endpoint';
29
    // Use this tag to inject your own resolver
30
    const METHOD_RESOLVER_TAG = 'yoanm.jsonrpc_http_server.method_resolver';
31
    // Use this tag to inject your JSON-RPC methods into the default method resolver
32
    const JSONRPC_METHOD_TAG = 'yoanm.jsonrpc_http_server.jsonrpc_method';
33
    // In case you want to add mapping for a method, use the following service
34
    const SERVICE_NAME_RESOLVER_SERVICE_NAME = 'yoanm.jsonrpc_http_server.resolver.service_name';
35
36
37
    const JSONRPC_METHOD_TAG_METHOD_NAME_KEY = 'method';
38
39
40
    private $sdkAppResponseCreatorServiceId        = 'sdk.app.creator.response';
41
    private $sdkAppCustomExceptionCreatorServiceId = 'sdk.app.creator.custom_exception';
42
    private $sdkAppRequestDenormalizerServiceId    = 'sdk.app.serialization.request_denormalizer';
43
    private $sdkAppResponseNormalizerServiceId     = 'sdk.app.serialization.response_normalizer';
44
    private $sdkAppMethodManagerServiceId          = 'sdk.app.manager.method';
45
    private $sdkAppRequestHandlerServiceId         = 'sdk.app.handler.request';
46
47
    private $sdkInfraEndpointServiceId          = 'sdk.infra.endpoint';
48
    private $sdkInfraRawReqSerializerServiceId  = 'sdk.infra.serialization.raw_request_serializer';
49
    private $sdkInfraRawRespSerializerServiceId = 'sdk.infra.serialization.raw_response_serializer';
50
51
    private $psr11InfraMethodResolverServiceId = 'psr11.infra.resolver.method';
52
53
    /**
54
     * {@inheritdoc}
55
     */
56
    public function load(array $configs, ContainerBuilder $container)
57
    {
58
        // Use only references to avoid class instantiation
59
        // And don't use file configuration in order to not add Symfony\Component\Config as dependency
60
        $this->createPublicServiceDefinitions($container);
61
        $this->createInfraServiceDefinitions($container);
62
        $this->createAppServiceDefinitions($container);
63
    }
64
65
    /**
66
     * @param ContainerBuilder $container
67
     */
68
    protected function createAppServiceDefinitions(ContainerBuilder $container)
69
    {
70
        // RequestDenormalizer
71
        $container->setDefinition(
72
            $this->prependServiceName($this->sdkAppRequestDenormalizerServiceId),
73
            new Definition(RequestDenormalizer::class)
74
        );
75
        // ResponseNormalizer
76
        $container->setDefinition(
77
            $this->prependServiceName($this->sdkAppResponseNormalizerServiceId),
78
            new Definition(ResponseNormalizer::class)
79
        );
80
        // ResponseCreator
81
        $container->setDefinition(
82
            $this->prependServiceName($this->sdkAppResponseCreatorServiceId),
83
            new Definition(ResponseCreator::class)
84
        );
85
        // CustomExceptionCreator
86
        $container->setDefinition(
87
            $this->prependServiceName($this->sdkAppCustomExceptionCreatorServiceId),
88
            new Definition(CustomExceptionCreator::class)
89
        );
90
91
        // MethodManager
92
        $container->setDefinition(
93
            $this->prependServiceName($this->sdkAppMethodManagerServiceId),
94
            new Definition(
95
                MethodManager::class,
96
                [
97
                    new Reference($this->getMethodResolverServiceId($container)),
98
                    new Reference($this->prependServiceName($this->sdkAppCustomExceptionCreatorServiceId))
99
                ]
100
            )
101
        );
102
        // RequestHandler
103
        $container->setDefinition(
104
            $this->prependServiceName($this->sdkAppRequestHandlerServiceId),
105
            new Definition(
106
                RequestHandler::class,
107
                [
108
                    new Reference($this->prependServiceName($this->sdkAppMethodManagerServiceId)),
109
                    new Reference($this->prependServiceName($this->sdkAppResponseCreatorServiceId))
110
                ]
111
            )
112
        );
113
    }
114
115
    /**
116
     * @param ContainerBuilder $container
117
     */
118
    protected function createInfraServiceDefinitions(ContainerBuilder $container)
119
    {
120
        // RawRequestSerializer
121
        $container->setDefinition(
122
            $this->prependServiceName($this->sdkInfraRawReqSerializerServiceId),
123
            new Definition(
124
                RawRequestSerializer::class,
125
                [new Reference($this->prependServiceName($this->sdkAppRequestDenormalizerServiceId))]
126
            )
127
        );
128
129
        // RawResponseSerializer
130
        $container->setDefinition(
131
            $this->prependServiceName($this->sdkInfraRawRespSerializerServiceId),
132
            new Definition(
133
                RawResponseSerializer::class,
134
                [new Reference($this->prependServiceName($this->sdkAppResponseNormalizerServiceId))]
135
            )
136
        );
137
        // JsonRpcEndpoint
138
        $container->setDefinition(
139
            $this->prependServiceName($this->sdkInfraEndpointServiceId),
140
            new Definition(
141
                JsonRpcEndpoint::class,
142
                [
143
                    new Reference($this->prependServiceName($this->sdkInfraRawReqSerializerServiceId)),
144
                    new Reference($this->prependServiceName($this->sdkAppRequestHandlerServiceId)),
145
                    new Reference($this->prependServiceName($this->sdkInfraRawRespSerializerServiceId)),
146
                    new Reference($this->prependServiceName($this->sdkAppResponseCreatorServiceId))
147
                ]
148
            )
149
        );
150
        // ContainerMethodResolver
151
        $container->setDefinition(
152
            $this->prependServiceName($this->psr11InfraMethodResolverServiceId),
153
            (new Definition(
154
                ContainerMethodResolver::class,
155
                [
156
                    new Reference('container')
157
                ]
158
            ))->addMethodCall(
159
                'setServiceNameResolver',
160
                [
161
                    new Reference(self::SERVICE_NAME_RESOLVER_SERVICE_NAME)
162
                ]
163
            )
164
        );
165
    }
166
167
    /**
168
     * @param ContainerBuilder $container
169
     */
170
    protected function createPublicServiceDefinitions(ContainerBuilder $container)
171
    {
172
        // JsonRpcHttpEndpoint
173
        $container->setDefinition(
174
            self::ENDPOINT_SERVICE_NAME,
175
            (new Definition(
176
                JsonRpcHttpEndpoint::class,
177
                [
178
                    new Reference($this->prependServiceName($this->sdkInfraEndpointServiceId))
179
                ]
180
            ))->setPrivate(false)
181
        );
182
        // ServiceNameResolver
183
        $container->setDefinition(
184
            self::SERVICE_NAME_RESOLVER_SERVICE_NAME,
185
            new Definition(ServiceNameResolver::class)
186
        );
187
    }
188
189
    /**
190
     * @param ContainerBuilder $container
191
     *
192
     * @return string
193
     */
194
    private function getMethodResolverServiceId(ContainerBuilder $container)
195
    {
196
        $serviceIdList = array_keys($container->findTaggedServiceIds(self::METHOD_RESOLVER_TAG));
197
        $serviceCount = count($serviceIdList);
198
        if ($serviceCount > 0) {
199
            if ($serviceCount > 1) {
200
                throw new LogicException(
201
                    sprintf(
202
                        'Only one method resolver could be defined, found following services : %s',
203
                        implode(', ', $serviceIdList)
204
                    )
205
                );
206
            }
207
            // Use the first result
208
            $resolverServiceId = array_shift($serviceIdList);
209
        } else {
210
            // Use ArrayMethodResolver as default resolver
211
            $resolverServiceId = $this->prependServiceName($this->psr11InfraMethodResolverServiceId);
212
            $this->loadJsonRpcMethodsFromTag($container);
213
        }
214
215
        return $resolverServiceId;
216
    }
217
218
    /**
219
     * @param ContainerBuilder $container
220
     */
221
    private function loadJsonRpcMethodsFromTag(ContainerBuilder $container)
222
    {
223
        // Check if methods have been defined by tags
224
        $methodServiceList = $container->findTaggedServiceIds(self::JSONRPC_METHOD_TAG);
225
        $defaultResolverDefinition = $container->getDefinition(self::SERVICE_NAME_RESOLVER_SERVICE_NAME);
226
227
        foreach ($methodServiceList as $serviceId => $tags) {
228
            $firstTag = array_shift($tags);
229
            if (!is_array($firstTag) || !array_key_exists(self::JSONRPC_METHOD_TAG_METHOD_NAME_KEY, $firstTag)) {
230
                throw new LogicException(sprintf(
231
                    'Service %s is taggued as JSON-RPC method but does not have'
232
                    .'method name defined under "%s" tag attribute key',
233
                    $serviceId,
234
                    self::JSONRPC_METHOD_TAG_METHOD_NAME_KEY
235
                ));
236
            }
237
            $defaultResolverDefinition->addMethodCall(
238
                'addMethodMapping',
239
                [
240
                    $firstTag[self::JSONRPC_METHOD_TAG_METHOD_NAME_KEY],
241
                    $serviceId
242
                ]
243
            );
244
        }
245
    }
246
247
    private function prependServiceName($serviceName)
248
    {
249
        return sprintf('yoanm.jsonrpc_http_server.%s', $serviceName);
250
    }
251
}
252