AbstractApiClient   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 238
Duplicated Lines 0 %

Test Coverage

Coverage 98.67%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 24
eloc 65
c 2
b 0
f 0
dl 0
loc 238
ccs 74
cts 75
cp 0.9867
rs 10

16 Methods

Rating   Name   Duplication   Size   Complexity  
A get() 0 3 1
A post() 0 3 1
A make() 0 3 1
A __construct() 0 5 1
A delete() 0 3 1
A getClient() 0 3 1
A setClient() 0 5 1
A put() 0 3 1
A getLogger() 0 3 1
A patch() 0 3 1
A send() 0 12 2
A formatBadResponseException() 0 13 3
A handleException() 0 26 4
A processQuery() 0 4 2
A formatRequestException() 0 9 1
A processHeaders() 0 6 2
1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of the Bushido\ApiClient package.
4
 *
5
 * (c) Wojciech Nowicki <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE.md
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Bushido\ApiClient;
12
13
use Bushido\ApiClient\Exceptions\BadResponseException;
14
use Bushido\ApiClient\Exceptions\ErrorResponseException;
15
use Bushido\ApiClient\Exceptions\TransportException;
16
use Bushido\ApiClient\Exceptions\WrongResponseException;
17
use Bushido\Foundation\Helpers\PsrLoggerTrait;
18
use GuzzleHttp\Client;
19
use GuzzleHttp\ClientInterface;
20
use GuzzleHttp\Exception;
21
use GuzzleHttp\Exception\GuzzleException;
22
use GuzzleHttp\Psr7\Request;
23
use Psr\Http\Message\RequestInterface;
24
use Psr\Http\Message\ResponseInterface;
25
use Psr\Log\LoggerInterface;
26
27
abstract class AbstractApiClient
28
{
29 1
    use PsrLoggerTrait;
30
31
    private $client;
32
    private $logger;
33
    private $headers;
34
35 36
    public function __construct(array $config = [], LoggerInterface $logger = null, array $headers = [])
36
    {
37 36
        $this->setClient(new Client($config));
38 36
        $this->logger = $logger;
39 36
        $this->headers = $headers;
40 36
    }
41
42 36
    public static function make(string $baseUrl): self
43
    {
44 36
        return new static(['base_uri' => $baseUrl]);
45
    }
46
47
    /**
48
     * @param string $uri
49
     * @param array $query
50
     * @param array $headers
51
     * @return array
52
     * @throws BadResponseException For 5xx and unhandled 4xx
53
     * @throws ErrorResponseException For handled 4xx
54
     * @throws TransportException For errors on transport layer
55
     * @throws WrongResponseException Response in unexpected format or structure
56
     */
57 18
    public function get(string $uri, array $query = [], array $headers = []): array
58
    {
59 18
        return $this->send((new Request('GET', $uri)), [], $query, $headers);
60
    }
61
62
    /**
63
     * @param string $uri
64
     * @param array $body
65
     * @param array $query
66
     * @param array $headers
67
     * @return array
68
     * @throws BadResponseException For 5xx and unhandled 4xx
69
     * @throws ErrorResponseException For handled 4xx
70
     * @throws TransportException For errors on transport layer
71
     * @throws WrongResponseException Response in unexpected format or structure
72
     */
73 6
    public function post(string $uri, array $body = [], array $query = [], array $headers = []): array
74
    {
75 6
        return $this->send((new Request('POST', $uri)), $body, $query, $headers);
76
    }
77
78
    /**
79
     * @param string $uri
80
     * @param array $body
81
     * @param array $query
82
     * @param array $headers
83
     * @return array
84
     * @throws BadResponseException For 5xx and unhandled 4xx
85
     * @throws ErrorResponseException For handled 4xx
86
     * @throws TransportException For errors on transport layer
87
     * @throws WrongResponseException Response in unexpected format or structure
88
     */
89 3
    public function put(string $uri, array $body = [], array $query = [], array $headers = []): array
90
    {
91 3
        return $this->send((new Request('PUT', $uri)), $body, $query, $headers);
92
    }
93
94
    /**
95
     * @param string $uri
96
     * @param array $body
97
     * @param array $query
98
     * @param array $headers
99
     * @return array
100
     * @throws BadResponseException For 5xx and unhandled 4xx
101
     * @throws ErrorResponseException For handled 4xx
102
     * @throws TransportException For errors on transport layer
103
     * @throws WrongResponseException Response in unexpected format or structure
104
     */
105 3
    public function patch(string $uri, array $body = [], array $query = [], array $headers = []): array
106
    {
107 3
        return $this->send((new Request('PATCH', $uri)), $body, $query, $headers);
108
    }
109
110
    /**
111
     * @param string $uri
112
     * @param array $query
113
     * @param array $headers
114
     * @return array
115
     * @throws BadResponseException For 5xx and unhandled 4xx
116
     * @throws ErrorResponseException For handled 4xx
117
     * @throws TransportException For errors on transport layer
118
     * @throws WrongResponseException Response in unexpected format or structure
119
     */
120 3
    public function delete(string $uri, array $query = [], array $headers = []): array
121
    {
122 3
        return $this->send((new Request('DELETE', $uri)), [], $query, $headers);
123
    }
124
125 33
    public function getClient(): ClientInterface
126
    {
127 33
        return $this->client;
128
    }
129
130 36
    protected function setClient(ClientInterface $client): self
131
    {
132 36
        $this->client = $client;
133
134 36
        return $this;
135
    }
136
137 9
    protected function getLogger(): ?LoggerInterface
138
    {
139 9
        return $this->logger;
140
    }
141
142
    /**
143
     * @param RequestInterface $request
144
     * @param array $body
145
     * @param array $query
146
     * @param array $headers
147
     * @return array
148
     * @throws BadResponseException
149
     * @throws TransportException
150
     * @throws WrongResponseException
151
     * @throws ErrorResponseException
152
     */
153 33
    private function send(RequestInterface $request, array $body = [], array $query = [], array $headers = []): array
154
    {
155 33
        $options = $this->processRequestBody($body);
156 33
        $this->processQuery($query, $options);
157 33
        $this->processHeaders($headers, $options);
158
159
        try {
160 33
            $response = $this->getClient()->send($request, $options);
161
162 24
            return $this->processResponse($response, $request);
163 12
        } catch (GuzzleException $e) {
164 9
            $this->handleException($e, $request);
165
        }
166
    }
167
168
    /**
169
     * @param GuzzleException $e
170
     * @param RequestInterface $request
171
     * @throws BadResponseException
172
     * @throws ErrorResponseException
173
     * @throws TransportException
174
     */
175 9
    protected function handleException(GuzzleException $e, RequestInterface $request): void
176
    {
177 9
        if ($e instanceof Exception\ClientException) { // 4xx
178 3
            $this->processErrorResponse($e->getResponse(), $request);
179 3
            $this->logError(
180 3
                'Api unhandled [' . $e->getResponse()->getStatusCode() .
181 3
                '] Error Response from [' . $request->getUri() . ']',
182 3
                $this->formatBadResponseException($e)
183
            );
184
185 3
            throw new BadResponseException($e->getMessage(), $e->getCode(), $e);
186 6
        } elseif ($e instanceof Exception\BadResponseException) { // 4xx & 5xx
187 3
            $this->logError(
188 3
                'Api Bad Response from [' . $request->getUri() . '] Failed[' . $e->getResponse()->getStatusCode() . ']',
189 3
                $this->formatBadResponseException($e)
190
            );
191
192 3
            throw new BadResponseException($e->getMessage(), $e->getCode(), $e);
193 3
        } elseif ($e instanceof Exception\RequestException) {
194 3
            $this->logError(
195 3
                'Api problem with request to [' . $request->getUri() . ']',
196 3
                $this->formatRequestException($e)
197
            );
198
        }
199
200 3
        throw new TransportException($e->getMessage(), $e->getCode(), $e);
201
    }
202
203 33
    private function processQuery(array $query, array &$options): void
204
    {
205 33
        if (count($query) > 0) {
206 3
            $options['query'] = $query;
207
        }
208 33
    }
209
210 33
    private function processHeaders(array $headers, array &$options): void
211
    {
212 33
        $headers = array_merge($this->headers, $headers);
213
214 33
        if (count($headers) > 0) {
215 3
            $options['headers'] = $headers;
216
        }
217 33
    }
218
219 6
    private function formatBadResponseException(Exception\BadResponseException $e): array
220
    {
221
        return [
222 6
            'message' => $e->getMessage(),
223
            'request' => [
224 6
                'headers'   => $e->getRequest()->getHeaders(),
225 6
                'body'      => $e->getRequest()->getBody()->getContents(),
226 6
                'method'    => $e->getRequest()->getMethod(),
227 6
                'uri'       => $e->getRequest()->getUri(),
228
            ],
229
            'response' => [
230 6
                'body'      => ($e->getResponse())?$e->getResponse()->getBody()->getContents():'[EMPTY]',
231 6
                'headers'   => ($e->getResponse())?$e->getResponse()->getHeaders():'[EMPTY]',
232
            ],
233
        ];
234
    }
235
236 3
    private function formatRequestException(Exception\RequestException $e): array
237
    {
238
        return [
239 3
            'message' => $e->getMessage(),
240
            'request' => [
241 3
                'headers'   => $e->getRequest()->getHeaders(),
242 3
                'body'      => $e->getRequest()->getBody()->getContents(),
243 3
                'method'    => $e->getRequest()->getMethod(),
244 3
                'uri'       => $e->getRequest()->getUri(),
245
            ],
246
        ];
247
    }
248
249
    abstract protected function processRequestBody(array $body): array;
250
251
    /**
252
     * @param ResponseInterface $response
253
     * @param RequestInterface $request
254
     * @return array
255
     * @throws WrongResponseException
256
     */
257
    abstract protected function processResponse(ResponseInterface $response, RequestInterface $request): array;
258
259
    /**
260
     * @param ResponseInterface $response
261
     * @param RequestInterface $request
262
     * @throws ErrorResponseException
263
     */
264
    abstract protected function processErrorResponse(ResponseInterface $response, RequestInterface $request): void;
265
}
266