Completed
Push — master ( 7bd75b...613007 )
by Robert
05:22
created

ResponseTransformer::getPostMessageTemplate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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