Skip to content

Commit fad0267

Browse files
committed
Add ResponseSerializer
1 parent e051d63 commit fad0267

2 files changed

Lines changed: 218 additions & 0 deletions

File tree

src/ResponseSerializer.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
namespace TraderInteractive\Api;
4+
5+
use GuzzleHttp\Psr7;
6+
use Psr\Http\Message\ResponseInterface;
7+
use SubjectivePHP\Psr\SimpleCache\Serializer\SerializerInterface;
8+
9+
final class ResponseSerializer implements SerializerInterface
10+
{
11+
/**
12+
* @var array
13+
*/
14+
const REQUIRED_CACHE_KEYS = [
15+
'statusCode',
16+
'headers',
17+
'body',
18+
];
19+
20+
/**
21+
* Unserializes cached data into the original psr response.
22+
*
23+
* @param mixed $data The data to unserialize.
24+
*
25+
* @return Psr7\Response
26+
*
27+
* @throws SerializerException Thrown if the given value cannot be unserialized.
28+
*/
29+
public function unserialize($data)
30+
{
31+
$this->validateCachedData($data);
32+
$statusCode = $data['statusCode'];
33+
$headers = $data['headers'];
34+
$body = $data['body'];
35+
if ($body !== '') {
36+
$body = Psr7\stream_for($body);
37+
}
38+
39+
return new Psr7\Response($statusCode, $headers, $body);
40+
}
41+
42+
/**
43+
* Serializes the given psr response for storage in caching.
44+
*
45+
* @param Psr7\Response $response The http response message to serialize for caching.
46+
*
47+
* @return mixed The result of serializing the given $data.
48+
*
49+
* @throws SerializerException Thrown if the given value cannot be serialized for caching.
50+
*/
51+
public function serialize($response)
52+
{
53+
if (!($response instanceof ResponseInterface)) {
54+
$type = is_object($response) ? get_class($response) : gettype($response);
55+
throw new SerializerException("Cannot serialize value of type '{$type}'");
56+
}
57+
58+
return [
59+
'statusCode' => $response->getStatusCode(),
60+
'headers' => $response->getHeaders(),
61+
'body' => $response->getBody()->getContents(),
62+
];
63+
}
64+
65+
private function validateCachedData($data)
66+
{
67+
if (!is_array($data)) {
68+
throw new SerializerException('Serialized data is not an array');
69+
}
70+
71+
foreach (self::REQUIRED_CACHE_KEYS as $key) {
72+
if (!array_key_exists($key, $data)) {
73+
throw new SerializerException("Data is missing '{$key}' value");
74+
}
75+
}
76+
}
77+
}

tests/ResponseSerializerTest.php

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<?php
2+
3+
namespace TraderInteractive\Api;
4+
5+
use Fig\Http\Message\StatusCodeInterface as StatusCodes;
6+
use GuzzleHttp\Psr7;
7+
use PHPUnit\Framework\TestCase;
8+
use Psr\Http\Message\ResponseInterface;
9+
10+
/**
11+
* @coversDefaultClass \TraderInteractive\Api\ResponseSerializer
12+
* @covers ::<private>
13+
*/
14+
final class ResponseSerializerTest extends TestCase
15+
{
16+
/**
17+
* @param ResponseInterface $response The PSR response to serialize.
18+
* @param array $expectedData The expected serialized data.
19+
* @test
20+
* @covers ::serialize
21+
*
22+
* @dataProvider provideValidSerializationData
23+
*/
24+
public function serializeRepsonse(ResponseInterface $response, array $expectedData)
25+
{
26+
$serializer = new ResponseSerializer();
27+
$actualData = $serializer->serialize($response);
28+
$this->assertSame($expectedData, $actualData);
29+
}
30+
31+
/**
32+
* @param ResponseInterface $expectedResponse The expected PSR response after unserializing.
33+
* @param array $serailizedData The data to unserialize.
34+
*
35+
* @test
36+
* @covers ::unserialize
37+
* @dataProvider provideValidSerializationData
38+
*/
39+
public function unserializeResponse(ResponseInterface $expectedResponse, array $serailizedData)
40+
{
41+
$serializer = new ResponseSerializer();
42+
$actualResponse = $serializer->unserialize($serailizedData);
43+
$this->assertSame($expectedResponse->getStatusCode(), $actualResponse->getStatusCode());
44+
$this->assertSame($expectedResponse->getHeaders(), $actualResponse->getHeaders());
45+
$this->assertSame($expectedResponse->getBody()->getContents(), $actualResponse->getBody()->getContents());
46+
}
47+
48+
/**
49+
* @return array
50+
*/
51+
public function provideValidSerializationData() : array
52+
{
53+
return [
54+
[
55+
'response' => new Psr7\Response(
56+
StatusCodes::STATUS_OK,
57+
['Content-Type' => ['application/json']],
58+
Psr7\stream_for('{"success": true}')
59+
),
60+
'data' => [
61+
'statusCode' => StatusCodes::STATUS_OK,
62+
'headers' => ['Content-Type' => ['application/json']],
63+
'body' => '{"success": true}',
64+
],
65+
],
66+
[
67+
'response' => new Psr7\Response(
68+
StatusCodes::STATUS_NO_CONTENT,
69+
['X-PHPUnit' => ['testing']]
70+
),
71+
'data' => [
72+
'statusCode' => StatusCodes::STATUS_NO_CONTENT,
73+
'headers' => ['X-PHPUnit' => ['testing']],
74+
'body' => '',
75+
],
76+
],
77+
];
78+
}
79+
80+
/**
81+
* @test
82+
* @covers ::serialize
83+
* @expectedException \TraderInteractive\Api\SerializerException
84+
* @expectedExceptionMessage Cannot serialize value of type 'array'
85+
*/
86+
public function serializeAcceptsOnlyResponses()
87+
{
88+
$serializer = new ResponseSerializer();
89+
$serializer->serialize(['foo']);
90+
}
91+
92+
/**
93+
* @param mixed $serializedData The data to be unserialized
94+
* @param string $expectedExceptionMessage The expected message for the SerializerException
95+
*
96+
* @test
97+
* @covers ::unserialize
98+
* @dataProvider provideInvalidSerializedData
99+
*/
100+
public function unserializeEnforcesKeyRequirements($serializedData, string $expectedExceptionMessage)
101+
{
102+
$this->expectException(SerializerException::class);
103+
$this->expectExceptionMessage($expectedExceptionMessage);
104+
$serializer = new ResponseSerializer();
105+
$serializer->unserialize($serializedData);
106+
}
107+
108+
/**
109+
* @return array
110+
*/
111+
public function provideInvalidSerializedData() : array
112+
{
113+
return [
114+
'missing statusCode' => [
115+
'data' => [
116+
'headers' => ['X-PHPUnit' => ['testing']],
117+
'body' => '',
118+
],
119+
'message' => "Data is missing 'statusCode' value",
120+
],
121+
'missing headers' => [
122+
'data' => [
123+
'statusCode' => StatusCodes::STATUS_NO_CONTENT,
124+
'body' => '',
125+
],
126+
'message' => "Data is missing 'headers' value",
127+
],
128+
'missing body' => [
129+
'data' => [
130+
'statusCode' => StatusCodes::STATUS_NO_CONTENT,
131+
'headers' => ['X-PHPUnit' => ['testing']],
132+
],
133+
'message' => "Data is missing 'body' value",
134+
],
135+
'data is not an array' => [
136+
'data' => '{"foo": "bar"}',
137+
'message' => 'Serialized data is not an array',
138+
],
139+
];
140+
}
141+
}

0 commit comments

Comments
 (0)