Completed
Pull Request — develop (#273)
by Samuel
23:34 queued 09:33
created

ProxyController::proxyAction()   B

Complexity

Conditions 4
Paths 30

Size

Total Lines 49
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 5
Bugs 2 Features 2
Metric Value
dl 0
loc 49
ccs 0
cts 43
cp 0
rs 8.7972
c 5
b 2
f 2
cc 4
eloc 36
nc 30
nop 1
crap 20
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
            $response = $this->httpFoundationFactory->createResponse($psrResponse);
135
            $this->transformationHandler->transformResponse(
136
                $api['apiName'],
137
                $api['endpoint'],
138
                $response,
139
                clone $response
140
            );
141
        } catch (ClientException $e) {
142
            $response = $e->getResponse();
143
        } catch (ServerException $serverException) {
144
            $response = $serverException->getResponse();
145
        }
146
147
        return $response;
148
    }
149
150
    /**
151
     * get schema info
152
     *
153
     * @param Request $request request
154
     *
155
     * @return Response
156
     */
157
    public function schemaAction(Request $request)
158
    {
159
        $api = $this->decideApiAndEndpoint($request->getUri());
160
        $this->registerProxySources();
161
        $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...
162
        $schema = $this->transformationHandler->transformSchema(
163
            $api['apiName'],
164
            $api['endpoint'],
165
            $schema,
166
            clone $schema
167
        );
168
        $response = new Response(json_encode($schema), 200);
169
        $response->headers->set('Content-Type', 'application/json');
170
171
        return $this->templating->renderResponse(
172
            'GravitonCoreBundle:Main:index.json.twig',
173
            array ('response' => $response->getContent()),
174
            $response
175
        );
176
    }
177
178
    /**
179
     * get API name and endpoint from the url (third party API)
180
     *
181
     * @param string $url the url
182
     *
183
     * @return array
184
     */
185
    protected function decideApiAndEndpoint($url)
186
    {
187
        $path = parse_url($url, PHP_URL_PATH);
188
189
        $pattern = array (
190
            "@schema\/@",
191
            "@\/3rdparty\/@",
192
            "@\/item$@",
193
        );
194
        $path = preg_replace($pattern, '', $path);
195
196
        //get api name and endpoint
197
        $apiName = substr($path, 0, strpos($path, '/'));
198
        $endpoint = str_replace($apiName, '', $path);
199
200
        return array (
201
            "apiName" => $apiName,
202
            "endpoint" => $endpoint,
203
        );
204
    }
205
206
    /**
207
     * Registers configured external services to be proxied.
208
     *
209
     * @return Void
210
     */
211
    private function registerProxySources()
212
    {
213
        if (array_key_exists('swagger', $this->proxySourceConfiguration)) {
214
            foreach ($this->proxySourceConfiguration['swagger'] as $config) {
215
                $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...
216
            }
217
        }
218
    }
219
220
    /**
221
     * get host, scheme and port
222
     *
223
     * @param string $url the url
224
     *
225
     * @return string
226
     */
227
    private function getHostWithScheme($url)
228
    {
229
        $components = parse_url($url);
230
        $host = $components['scheme'].'://'.$components['host'];
231
        if (!empty($components['port'])) {
232
            $host .= ':'.$components['port'];
233
        }
234
235
        return $host;
236
    }
237
}
238