Completed
Push — master ( f5d814...39a656 )
by Wojciech
19s queued 10s
created

AbstractApiClient::processHeaders()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 6
rs 10
cc 2
nc 2
nop 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
    use PsrLoggerTrait;
30
31
    private $client;
32
    private $logger;
33
    private $headers;
34
35
    public function __construct(array $config = [], LoggerInterface $logger = null, array $headers = [])
36
    {
37
        $this->setClient(new Client($config));
38
        $this->logger = $logger;
39
        $this->headers = $headers;
40
    }
41
42
    public static function make(string $baseUrl): self
43
    {
44
        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
    public function get(string $uri, array $query = [], array $headers = [])
58
    {
59
        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
    public function post(string $uri, array $body = [], array $query = [], array $headers = [])
74
    {
75
        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
    public function put(string $uri, array $body = [], array $query = [], array $headers = [])
90
    {
91
        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
    public function patch(string $uri, array $body = [], array $query = [], array $headers = [])
106
    {
107
        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
    public function delete(string $uri, array $query = [], array $headers = [])
121
    {
122
        return $this->send((new Request('DELETE', $uri)), [], $query, $headers);
123
    }
124
125
    public function getClient(): ClientInterface
126
    {
127
        return $this->client;
128
    }
129
130
    protected function setClient(ClientInterface $client): self
131
    {
132
        $this->client = $client;
133
134
        return $this;
135
    }
136
137
    protected function getLogger(): ?LoggerInterface
138
    {
139
        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
    private function send(RequestInterface $request, array $body = [], array $query = [], array $headers = [])
154
    {
155
        $options = $this->processRequestBody($body);
156
        $this->processQuery($query, $options);
157
        $this->processHeaders($headers, $options);
158
159
        try {
160
            $response = $this->getClient()->send($request, $options);
161
162
            return $this->processResponse($response, $request);
163
        } catch (GuzzleException $e) {
164
            $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
    protected function handleException(GuzzleException $e, RequestInterface $request)
176
    {
177
        if ($e instanceof Exception\ClientException) { // 4xx
178
            $this->processErrorResponse($e->getResponse(), $request);
179
            $this->logError(
180
                'Api unhandled [' . $e->getResponse()->getStatusCode() . '] Error Response from [' . $request->getUri() . ']',
181
                $this->formatBadResponseException($e)
182
            );
183
184
            throw new BadResponseException($e->getMessage(), $e->getCode(), $e);
185
        } elseif ($e instanceof Exception\BadResponseException) { // 4xx & 5xx
186
            $this->logError(
187
                'Api Bad Response from [' . $request->getUri() . '] Failed[' . $e->getResponse()->getStatusCode() . ']',
188
                $this->formatBadResponseException($e)
189
            );
190
191
            throw new BadResponseException($e->getMessage(), $e->getCode(), $e);
192
        } elseif ($e instanceof Exception\RequestException) {
193
            $this->logError(
194
                'Api problem with request to [' . $request->getUri() . ']',
195
                $this->formatRequestException($e)
196
            );
197
        }
198
199
        throw new TransportException($e->getMessage(), $e->getCode(), $e);
200
    }
201
202
    private function processQuery(array $query, array &$options): void
203
    {
204
        if (count($query) > 0) {
205
            $options['query'] = $query;
206
        }
207
    }
208
209
    private function processHeaders(array $headers, array &$options): void
210
    {
211
        $headers = array_merge($this->headers, $headers);
212
213
        if (count($headers) > 0) {
214
            $options['headers'] = $headers;
215
        }
216
    }
217
218
    private function formatBadResponseException(Exception\BadResponseException $e): array
219
    {
220
        return [
221
            'message' => $e->getMessage(),
222
            'request' => [
223
                'headers'   => $e->getRequest()->getHeaders(),
224
                'body'      => $e->getRequest()->getBody()->getContents(),
225
                'method'    => $e->getRequest()->getMethod(),
226
                'uri'       => $e->getRequest()->getUri(),
227
            ],
228
            'response' => [
229
                'body'      => ($e->getResponse())?$e->getResponse()->getBody()->getContents():'[EMPTY]',
230
                'headers'   => ($e->getResponse())?$e->getResponse()->getHeaders():'[EMPTY]',
231
            ],
232
        ];
233
    }
234
235
    private function formatRequestException(Exception\RequestException $e): array
236
    {
237
        return [
238
            'message' => $e->getMessage(),
239
            'request' => [
240
                'headers'   => $e->getRequest()->getHeaders(),
241
                'body'      => $e->getRequest()->getBody()->getContents(),
242
                'method'    => $e->getRequest()->getMethod(),
243
                'uri'       => $e->getRequest()->getUri(),
244
            ],
245
        ];
246
    }
247
248
    abstract protected function processRequestBody(array $body): array;
249
250
    /**
251
     * @param ResponseInterface $response
252
     * @param RequestInterface $request
253
     * @return array
254
     * @throws WrongResponseException
255
     */
256
    abstract protected function processResponse(ResponseInterface $response, RequestInterface $request): array;
257
258
    /**
259
     * @param ResponseInterface $response
260
     * @param RequestInterface $request
261
     * @throws ErrorResponseException
262
     */
263
    abstract protected function processErrorResponse(ResponseInterface $response, RequestInterface $request): void;
264
}
265