Completed
Pull Request — master (#15)
by Michal
28:17
created

ResponseTransformer::setWrapResponseData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
namespace MediaMonks\RestApiBundle\Response;
4
5
use MediaMonks\RestApiBundle\Model\ResponseModel;
6
use MediaMonks\RestApiBundle\Model\ResponseModelFactory;
7
use MediaMonks\RestApiBundle\Request\Format;
8
use MediaMonks\RestApiBundle\Serializer\SerializerInterface;
9
use Symfony\Component\HttpFoundation\Request;
10
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
11
use Symfony\Component\HttpFoundation\JsonResponse as SymfonyJsonResponse;
12
13
class ResponseTransformer implements ResponseTransformerInterface
14
{
15
    const WRAPPER_PADDING = 'padding';
16
    const WRAPPER_POST_MESSAGE = 'postMessage';
17
18
    const PARAMETER_CALLBACK = 'callback';
19
    const PARAMETER_WRAPPER = '_wrapper';
20
21
    /**
22
     * @var SerializerInterface
23
     */
24
    protected $serializer;
25
26
    /**
27
     * @var bool
28
     */
29
    protected $debug = false;
30
31
    /**
32
     * @var string
33
     */
34
    protected $postMessageOrigin;
35
36
    /**
37
     * @var ResponseModelFactory
38
     */
39
    protected $responseModelFactory;
40
41
    /**
42
     * @var bool
43
     */
44
    protected $wrapResponseData = true;
45
46
    /**
47
     * ResponseTransformer constructor.
48
     * @param SerializerInterface $serializer
49
     * @param array $options
50
     */
51 33
    public function __construct(SerializerInterface $serializer, $options = [])
52
    {
53 33
        $this->serializer = $serializer;
54 33
        $this->setOptions($options);
55 33
    }
56
57
    /**
58
     * @param array $options
59
     */
60 33
    public function setOptions(array $options)
61
    {
62 33
        if (isset($options['debug'])) {
63 20
            $this->setDebug($options['debug']);
64 20
        }
65 33
        if (isset($options['post_message_origin'])) {
66 2
            $this->setPostMessageOrigin($options['post_message_origin']);
67 2
        }
68 33
        if (isset($options['wrap_response_data'])) {
69 20
            $this->setWrapResponseData($options['wrap_response_data']);
70 20
        }
71 33
    }
72
73
    /**
74
     * @return boolean
75
     */
76 24
    public function isDebug()
77
    {
78 24
        return $this->debug;
79
    }
80
81
    /**
82
     * @param boolean $debug
83
     * @return ResponseTransformer
84
     */
85 20
    public function setDebug($debug)
86
    {
87 20
        $this->debug = $debug;
88
89 20
        return $this;
90
    }
91
92
    /**
93
     * @return string
94
     */
95 5
    public function getPostMessageOrigin()
96
    {
97 5
        return $this->postMessageOrigin;
98
    }
99
100
    /**
101
     * @param string $postMessageOrigin
102
     * @return ResponseTransformer
103
     */
104 3
    public function setPostMessageOrigin($postMessageOrigin)
105
    {
106 3
        $this->postMessageOrigin = $postMessageOrigin;
107
108 3
        return $this;
109
    }
110
111
    /**
112
     * @return bool
113
     */
114 24
    public function isWrapResponseData()
115
    {
116 24
        return $this->wrapResponseData;
117
    }
118
119
    /**
120
     * @param bool $wrapResponseData
121
     */
122 20
    public function setWrapResponseData($wrapResponseData)
123
    {
124 20
        $this->wrapResponseData = $wrapResponseData;
125 20
    }
126
127
    /**
128
     * @param Request $request
129
     * @param SymfonyResponse $response
130
     * @return SymfonyResponse
131
     */
132 24
    public function transformEarly(Request $request, SymfonyResponse $response)
133
    {
134 24
        $responseModel = $response->getContent();
135
136 24
        if (!$responseModel instanceof ResponseModel) {
137 5
            $responseModel = $this->getResponseModelFactory()->createFromContent($response);
138 5
        }
139
140 24
        $responseModel->setReturnStackTrace($this->isDebug());
141 24
        $response->setStatusCode($responseModel->getStatusCode());
142 24
        $this->forceStatusCodeHttpOK($request, $response, $responseModel);
143 24
        $response = $this->createSerializedResponse($request, $response, $responseModel);
144
145 24
        return $response;
146
    }
147
148
    /**
149
     * @param ResponseModelFactory $factory
150
     */
151 2
    public function setResponseModelFactory($factory)
152
    {
153 2
        $this->responseModelFactory = $factory;
154 2
    }
155
156
    /**
157
     * @return ResponseModelFactory
158
     */
159 7
    public function getResponseModelFactory()
160
    {
161 7
        if (!isset($this->responseModelFactory)) {
162 5
            $this->responseModelFactory = ResponseModelFactory::createFactory();
163 5
        }
164
165 7
        return $this->responseModelFactory;
166
    }
167
168
    /**
169
     * @param Request $request
170
     * @param SymfonyResponse $response
171
     */
172 23
    public function transformLate(Request $request, SymfonyResponse $response)
173
    {
174 23
        if ($request->getRequestFormat() === Format::FORMAT_JSON
175 23
            && $request->query->has(self::PARAMETER_CALLBACK)
176 23
            && $response instanceof JsonResponse
177 23
        ) {
178 2
            $this->wrapResponse($request, $response);
179 2
        }
180 23
    }
181
182
    /**
183
     * Check if we should put the status code in the output and force a 200 OK in the header
184
     *
185
     * @param Request $request
186
     * @param SymfonyResponse $response
187
     * @param ResponseModel $responseModel
188
     */
189 24
    protected function forceStatusCodeHttpOK(
190
        Request $request,
191
        SymfonyResponse $response,
192
        ResponseModel $responseModel
193
    ) {
194 24
        if ($request->headers->has('X-Force-Status-Code-200')
195 20
            || ($request->getRequestFormat() == Format::FORMAT_JSON && $request->query->has(self::PARAMETER_CALLBACK))
196 24
        ) {
197 4
            $responseModel->setReturnStatusCode(true);
198 4
            $response->setStatusCode(Response::HTTP_OK);
199 4
            $response->headers->set('X-Status-Code', Response::HTTP_OK);
200 4
        }
201 24
    }
202
203
    /**
204
     * @param Request $request
205
     * @param SymfonyResponse $response
206
     * @param ResponseModel $responseModel
207
     * @return SymfonyResponse
208
     */
209 24
    protected function createSerializedResponse(
210
        Request $request,
211
        SymfonyResponse $response,
212
        ResponseModel $responseModel
213
    ) {
214
        try {
215 24
            $response = $this->serialize($request, $response, $responseModel);
216 24
        } catch (\Exception $e) {
217 1
            $response = new SymfonyJsonResponse(
218
                [
219
                    'error' => [
220 1
                        'code'    => Error::CODE_SERIALIZE,
221 1
                        'message' => $e->getMessage(),
222 1
                    ],
223
                ]
224 1
            );
225
        }
226
227 24
        return $response;
228
    }
229
230
    /**
231
     * @param Request $request
232
     * @param SymfonyResponse $response
233
     * @param ResponseModel $responseModel
234
     * @return JsonResponse|SymfonyResponse
235
     */
236 24
    protected function serialize(Request $request, SymfonyResponse $response, ResponseModel $responseModel)
237
    {
238 24
        switch ($request->getRequestFormat()) {
239 24
            case Format::FORMAT_XML:
240 2
                $response->setContent($this->getSerializedContent($request, $responseModel));
241 2
                break;
242 22
            default:
243 22
                $headers = $response->headers;
244 22
                $response = new JsonResponse(
245 22
                    $this->getSerializedContent($request, $responseModel),
246 21
                    $response->getStatusCode()
247 21
                );
248 21
                $response->headers = $headers; // some headers might mess up if we pass it to the JsonResponse
249 21
                break;
250 24
        }
251
252 23
        return $response;
253
    }
254
255
    /**
256
     * @param Request $request
257
     * @param ResponseModel $responseModel
258
     * @return mixed|string
259
     */
260 24
    protected function getSerializedContent(Request $request, ResponseModel $responseModel)
261
    {
262 24
        return $this->serializer->serialize($responseModel->toArray($this->isWrapResponseData()), $request->getRequestFormat());
263
    }
264
265
    /**
266
     * @param Request $request
267
     * @param JsonResponse $response
268
     * @throws \Exception
269
     */
270 2
    protected function wrapResponse(Request $request, JsonResponse $response)
271
    {
272 2
        switch ($request->query->get(self::PARAMETER_WRAPPER)) {
273 2
            case self::WRAPPER_POST_MESSAGE:
274 1
                $response->setContent(
275 1
                    sprintf(
276 1
                        $this->getPostMessageTemplate(),
277 1
                        $response->getContent(),
278 1
                        $this->getCallbackFromRequest($request),
279 1
                        $this->getPostMessageOrigin()
280 1
                    )
281 1
                )->headers->set('Content-Type', 'text/html');
282 1
                break;
283 1
            default:
284 1
                $response->setCallback($request->query->get(self::PARAMETER_CALLBACK));
285 1
                break;
286 2
        }
287 2
    }
288
289
    /**
290
     * @param Request $request
291
     * @return string
292
     */
293 1
    protected function getCallbackFromRequest(Request $request)
294
    {
295 1
        $response = new JsonResponse('');
296 1
        $response->setCallback($request->query->get(self::PARAMETER_CALLBACK));
297
298 1
        return $response->getCallback();
299
    }
300
301
    /**
302
     * @return string
303
     */
304 1
    protected function getPostMessageTemplate()
305
    {
306
        return <<<EOD
307
<html>
308
<body>
309
<script>
310
    try {
311
        var data = %s;
312
    }
313
    catch (error) {
314
        var data = {"error": {"code": "error.parse.post_message", "message": "Post message parse error"}};
315
    }
316
317
    top.postMessage(JSON.stringify({
318
        name: '%s',
319
        result: data
320
    }), '%s');
321
</script>
322
</body>
323
</html>
324 1
EOD;
325
    }
326
}
327