Completed
Push — feature/service-wrapper-for-sw... ( 7b5228...6a2d19 )
by Samuel
08:23
created

ProxyController::proxyAction()   B

Complexity

Conditions 4
Paths 30

Size

Total Lines 50
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 2
Metric Value
c 4
b 1
f 2
dl 0
loc 50
rs 8.8571
cc 4
eloc 36
nc 30
nop 1
1
<?php
2
/**
3
 * ProxyController
4
 */
5
6
namespace Graviton\ProxyBundle\Controller;
7
8
use Graviton\ProxyBundle\Service\ApiDefinitionLoader;
9
use Graviton\ProxyBundle\Service\TransformationHandler;
10
use GuzzleHttp\Exception\ClientException;
11
use GuzzleHttp\Exception\ServerException;
12
use Proxy\Proxy;
13
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
14
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
15
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
16
use Symfony\Component\HttpFoundation\Request;
17
use Symfony\Component\HttpFoundation\Response;
18
19
/**
20
 * general controller for all proxy staff
21
 *
22
 * @package Graviton\ProxyBundle\Controller
23
 * @author  List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
24
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
25
 * @link    http://swisscom.ch
26
 */
27
class ProxyController
28
{
29
    /**
30
     * @var Proxy
31
     */
32
    private $proxy;
33
34
    /**
35
     * @var EngineInterface
36
     */
37
    private $templating;
38
39
    /**
40
     * @var ApiDefinitionLoader
41
     */
42
    private $diactorosFactory;
43
44
    /**
45
     * @var DiactorosFactory
46
     */
47
    private $apiLoader;
48
49
    /**
50
     * @var HttpFoundationFactory
51
     */
52
    private $httpFoundationFactory;
53
54
    /**
55
     * @var array
56
     */
57
    private $proxySourceConfiguration;
58
59
    /**
60
     * @var TransformationHandler
61
     */
62
    private $transformationHandler;
63
64
    /**
65
     * Constructor
66
     *
67
     * @param Proxy                 $proxy                    proxy
68
     * @param EngineInterface       $templating               twig templating engine
69
     * @param ApiDefinitionLoader   $loader                   definition loader
70
     * @param DiactorosFactory      $diactorosFactory         convert HttpFoundation objects to PSR-7
71
     * @param HttpFoundationFactory $httpFoundationFactory    convert PSR-7 interfaces to HttpFoundation
72
     * @param TransformationHandler $transformationHandler    transformation handler
73
     * @param array                 $proxySourceConfiguration Set of sources to be recognized by the controller.
74
     */
75
    public function __construct(
76
        Proxy $proxy,
77
        EngineInterface $templating,
78
        ApiDefinitionLoader $loader,
79
        DiactorosFactory $diactorosFactory,
80
        HttpFoundationFactory $httpFoundationFactory,
81
        TransformationHandler $transformationHandler,
82
        array $proxySourceConfiguration
83
    ) {
84
        $this->proxy = $proxy;
85
        $this->templating = $templating;
86
        $this->apiLoader = $loader;
0 ignored issues
show
Documentation Bug introduced by
It seems like $loader of type object<Graviton\ProxyBun...ce\ApiDefinitionLoader> is incompatible with the declared type object<Symfony\Bridge\Ps...ctory\DiactorosFactory> of property $apiLoader.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
87
        $this->diactorosFactory = $diactorosFactory;
0 ignored issues
show
Documentation Bug introduced by
It seems like $diactorosFactory of type object<Symfony\Bridge\Ps...ctory\DiactorosFactory> is incompatible with the declared type object<Graviton\ProxyBun...ce\ApiDefinitionLoader> of property $diactorosFactory.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
88
        $this->httpFoundationFactory = $httpFoundationFactory;
89
        $this->proxySourceConfiguration = $proxySourceConfiguration;
90
        $this->transformationHandler = $transformationHandler;
91
    }
92
93
    /**
94
     * action for routing all requests directly to the third party API
95
     *
96
     * @param Request $request request
97
     *
98
     * @return \Psr\Http\Message\ResponseInterface|Response
99
     */
100
    public function proxyAction(Request $request)
101
    {
102
        $api = $this->decideApiAndEndpoint($request->getUri());
103
        $this->registerProxySources();
104
105
        $url = $this->apiLoader->getEndpoint($api['endpoint'], true);
0 ignored issues
show
Bug introduced by
The method getEndpoint() does not seem to exist on object<Symfony\Bridge\Ps...ctory\DiactorosFactory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
106
        if (parse_url($url, PHP_URL_SCHEME) === false) {
107
            $scheme = $request->getScheme();
108
            $url = $scheme.'://'.$url;
109
        }
110
        $response = null;
111
        try {
112
            $newRequest = Request::create(
113
                $url,
114
                $request->getMethod(),
115
                array (),
116
                array (),
117
                array (),
118
                array (),
119
                $request->getContent(false)
0 ignored issues
show
Bug introduced by
It seems like $request->getContent(false) targeting Symfony\Component\HttpFo...n\Request::getContent() can also be of type resource; however, Symfony\Component\HttpFoundation\Request::create() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
120
            );
121
            $newRequest->headers->add($request->headers->all());
122
123
124
125
            $newRequest = $this->transformationHandler->transformRequest(
126
                $api['apiName'],
127
                $api['endpoint'],
128
                $request,
129
                $newRequest
130
            );
131
132
            $psrRequest = $this->diactorosFactory->createRequest($newRequest);
0 ignored issues
show
Bug introduced by
The method createRequest() does not seem to exist on object<Graviton\ProxyBun...ce\ApiDefinitionLoader>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
133
            $psrResponse = $this->proxy->forward($psrRequest)->to($this->getHostWithScheme($url));
134
135
            $response = $this->httpFoundationFactory->createResponse($psrResponse);
136
            $this->transformationHandler->transformResponse(
137
                $api['apiName'],
138
                $api['endpoint'],
139
                $response,
140
                clone $response
141
            );
142
        } catch (ClientException $e) {
143
            $response = $e->getResponse();
144
        } catch (ServerException $serverException) {
145
            $response = $serverException->getResponse();
146
        }
147
148
        return $response;
149
    }
150
151
    /**
152
     * get schema info
153
     *
154
     * @param Request $request request
155
     *
156
     * @return Response
157
     */
158
    public function schemaAction(Request $request)
159
    {
160
        $api = $this->decideApiAndEndpoint($request->getUri());
161
        $this->registerProxySources();
162
        $schema = $this->apiLoader->getEndpointSchema(urldecode($api['endpoint']));
0 ignored issues
show
Bug introduced by
The method getEndpointSchema() does not seem to exist on object<Symfony\Bridge\Ps...ctory\DiactorosFactory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
163
        $schema = $this->transformationHandler->transformSchema(
164
            $api['apiName'],
165
            $api['endpoint'],
166
            $schema,
167
            clone $schema
168
        );
169
        $response = new Response(json_encode($schema), 200);
170
        $response->headers->set('Content-Type', 'application/json');
171
172
        return $this->templating->renderResponse(
173
            'GravitonCoreBundle:Main:index.json.twig',
174
            array ('response' => $response->getContent()),
175
            $response
176
        );
177
    }
178
179
    /**
180
     * get API name and endpoint from the url (third party API)
181
     *
182
     * @param string $url the url
183
     *
184
     * @return array
185
     */
186
    protected function decideApiAndEndpoint($url)
187
    {
188
        $path = parse_url($url, PHP_URL_PATH);
189
190
        $pattern = array (
191
            "@schema\/@",
192
            "@\/3rdparty\/@",
193
            "@\/item$@",
194
        );
195
        $path = preg_replace($pattern, '', $path);
196
197
        //get api name and endpoint
198
        $apiName = substr($path, 0, strpos($path, '/'));
199
        $endpoint = str_replace($apiName, '', $path);
200
201
        return array (
202
            "apiName" => $apiName,
203
            "endpoint" => $endpoint,
204
        );
205
    }
206
207
    /**
208
     * Registers configured external services to be proxied.
209
     *
210
     * @return Void
211
     */
212
    private function registerProxySources()
213
    {
214
        if (array_key_exists('swagger', $this->proxySourceConfiguration)) {
215
            foreach ($this->proxySourceConfiguration['swagger'] as $config) {
216
                $this->apiLoader->setOption($config);
0 ignored issues
show
Bug introduced by
The method setOption() does not seem to exist on object<Symfony\Bridge\Ps...ctory\DiactorosFactory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
217
            }
218
        }
219
    }
220
221
    /**
222
     * get host, scheme and port
223
     */
224
    private function getHostWithScheme($url)
225
    {
226
        $components = parse_url($url);
227
        $host = $components['scheme'].'://'.$components['host'];
228
        if (!empty($components['port'])) {
229
            $host .= ':'.$components['port'];
230
        }
231
232
        return $host;
233
    }
234
}
235