Passed
Pull Request — master (#24)
by
unknown
23:13
created

AbstractCoreApiClient::unserializeResponse()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace MovingImage\Client\VMPro\ApiClient;
4
5
use GuzzleHttp\ClientInterface;
6
use GuzzleHttp\Psr7\Response;
7
use JMS\Serializer\Serializer;
8
use MovingImage\Client\VMPro\Exception;
9
use MovingImage\Client\VMPro\Util\Logging\Traits\LoggerAwareTrait;
10
use Psr\Cache\CacheItemPoolInterface;
11
use Psr\Http\Message\ResponseInterface;
12
use Psr\Log\LoggerAwareInterface;
13
use Symfony\Component\Cache\Adapter\NullAdapter;
14
15
/**
16
 * Class AbstractCoreApiClient.
17
 *
18
 * @author Ruben Knol <[email protected]>
19
 */
20
abstract class AbstractCoreApiClient implements LoggerAwareInterface
21
{
22
    use LoggerAwareTrait;
23
24
    /**
25
     * @const string
26
     */
27
    const OPT_VIDEO_MANAGER_ID = 'videoManagerId';
28
29
    /**
30
     * @var ClientInterface The Guzzle HTTP client
31
     */
32
    protected $httpClient;
33
34
    /**
35
     * @var Serializer The JMS Serializer instance
36
     */
37
    protected $serializer;
38
39
    /**
40
     * @var CacheItemPoolInterface PSR6 cache pool implementation
41
     */
42
    protected $cacheItemPool;
43
44
    /**
45
     * @var mixed time-to-live for cached responses
46
     * The type of this property might be integer, \DateInterval or null.
47
     * @see CacheItemInterface::expiresAfter()
48
     */
49
    protected $cacheTtl;
50
51
    /**
52
     * ApiClient constructor.
53
     *
54
     * @param ClientInterface        $httpClient
55
     * @param Serializer             $serializer
56
     * @param CacheItemPoolInterface $cacheItemPool
57
     * @param integer                $cacheTtl
58
     */
59
    public function __construct(
60
        ClientInterface $httpClient,
61
        Serializer $serializer,
62
        CacheItemPoolInterface $cacheItemPool = null,
63
        $cacheTtl = null
64
    ) {
65
        $this->httpClient = $httpClient;
66
        $this->serializer = $serializer;
67
        $this->cacheItemPool = $cacheItemPool ?: new NullAdapter();
68
        $this->cacheTtl = $cacheTtl;
69
    }
70
71
    /**
72
     * Perform the actual request in the implementation classes.
73
     *
74
     * @param string $method
75
     * @param string $uri
76
     * @param array  $options
77
     *
78
     * @return mixed
79
     */
80
    abstract protected function _doRequest($method, $uri, $options);
81
82
    /**
83
     * Make a request to the API and serialize the result according to our
84
     * serialization strategy.
85
     *
86
     * @param string $method
87
     * @param string $uri
88
     * @param array  $options
89
     *
90
     * @return object|ResponseInterface
91
     */
92
    protected function makeRequest($method, $uri, $options)
93
    {
94
        $logger = $this->getLogger();
95
96
        try {
97
            // Automagically pre-pend videoManagerId if the option is present in the
98
            // options for sending the request
99
            if (isset($options[self::OPT_VIDEO_MANAGER_ID])) {
100
                $uri = sprintf('%d/%s', $options[self::OPT_VIDEO_MANAGER_ID], $uri);
101
            }
102
103
            $cacheKey = $this->generateCacheKey($method, $uri, $options);
104
            $cacheItem = $this->cacheItemPool->getItem($cacheKey);
105
            if ($cacheItem->isHit()) {
106
                $logger->info(sprintf('Getting response from cache for %s request to %s', $method, $uri), [$uri]);
107
                return $this->unserializeResponse($cacheItem->get());
108
            }
109
110
            $logger->info(sprintf('Making API %s request to %s', $method, $uri), [$uri]);
111
112
            /** @var ResponseInterface $response */
113
            $response = $this->_doRequest($method, $uri, $options);
114
115
            $cacheItem->set($this->serializeResponse($response));
116
            $cacheItem->expiresAfter($this->cacheTtl);
117
            $this->cacheItemPool->save($cacheItem);
118
119
120
            $logger->debug('Response from HTTP call was status code:', [$response->getStatusCode()]);
121
            $logger->debug('Response JSON was:', [$response->getBody()]);
122
123
            return $response;
124
        } catch (\Exception $e) {
125
            throw $e; // Just rethrow for now
126
        }
127
    }
128
129
    /**
130
     * Deserialize a response into an instance of it's associated class.
131
     *
132
     * @param string $data
133
     * @param string $serialisationClass
134
     *
135
     * @return object
136
     */
137
    protected function deserialize($data, $serialisationClass)
138
    {
139
        return $this->serializer->deserialize($data, $serialisationClass, 'json');
140
    }
141
142
    /**
143
     * Helper method to build the JSON data array for making a request
144
     * with ::makeRequest(). Optional parameters with empty or null value will be
145
     * omitted from the return value.
146
     *
147
     * Examples:
148
     *
149
     * $this->buildJsonParameters(['title' => 'test'], ['description' => '', 'bla' => 'test'])
150
     *
151
     * Would result in:
152
     *
153
     * [
154
     *     'title' => 'test',
155
     *     'bla' => 'test',
156
     * ]
157
     *
158
     * @param array $required
159
     * @param array $optional
160
     *
161
     * @return array
162
     */
163
    protected function buildJsonParameters(array $required, array $optional)
164
    {
165
        foreach ($required as $key => $value) {
166
            if (empty($value)) {
167
                throw new Exception(sprintf('Required parameter \'%s\' is missing..', $key));
168
            }
169
        }
170
171
        $json = $required;
172
173
        foreach ($optional as $key => $value) {
174
            if (!empty($value) || $value === false) {
175
                $json[$key] = $value;
176
            }
177
        }
178
179
        return $json;
180
    }
181
182
    /**
183
     * Generates the cache key based on request method, uri and options.
184
     * @param string $method
185
     * @param string $uri
186
     * @param array $options
187
     * @return string
188
     */
189
    private function generateCacheKey($method, $uri, array $options = [])
190
    {
191
        return sha1(sprintf('%s.%s.%s', $method, $uri, json_encode($options)));
192
    }
193
194
    /**
195
     * Serializes the provided response to a string, suitable for caching.
196
     * @param ResponseInterface $response
197
     * @return string
198
     */
199
    private function serializeResponse(ResponseInterface $response)
200
    {
201
        $serialized = serialize([
202
            $response->getStatusCode(),
203
            $response->getHeaders(),
204
            $response->getBody()->getContents(),
205
        ]);
206
207
        //subsequent calls need to access the stream from the beginning
208
        $response->getBody()->rewind();
209
210
        return $serialized;
211
    }
212
213
    /**
214
     * Unserializes the serialized response into a ResponseInterface instance
215
     * @param string $serialized
216
     * @return ResponseInterface
217
     * @throws Exception
218
     */
219
    private function unserializeResponse($serialized)
220
    {
221
        $array = unserialize($serialized);
222
        if (!is_array($array) || count($array) !== 3) {
223
            throw new Exception(sprintf('Error unserializing response: %s', $serialized));
224
225
        }
226
227
        return new Response($array[0], $array[1], $array[2]);
228
    }
229
230
}
231