BaseClient::retryMiddleware()   A
last analyzed

Complexity

Conditions 6
Paths 1

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 6
eloc 13
c 1
b 0
f 1
nc 1
nop 0
dl 0
loc 23
ccs 0
cts 9
cp 0
crap 42
rs 9.2222
1
<?php
2
3
namespace EasyIM\Kernel;
4
5
use EasyIM\Kernel\Contracts\AccessTokenInterface;
6
use EasyIM\Kernel\Http\Response;
7
use EasyIM\Kernel\Traits\HasHttpRequests;
8
use GuzzleHttp\MessageFormatter;
9
use GuzzleHttp\Middleware;
10
use Psr\Http\Message\RequestInterface;
11
use Psr\Http\Message\ResponseInterface;
12
use Psr\Log\LogLevel;
13
14
/**
15
 * Class BaseClient.
16
 */
17
class BaseClient
18
{
19
    use HasHttpRequests { request as performRequest; }
20
21
    /**
22
     * @var \EasyIM\Kernel\ServiceContainer
23
     */
24
    protected $app;
25
26
    /**
27
     * @var \EasyIM\Kernel\Contracts\AccessTokenInterface
28
     */
29
    protected $accessToken;
30
31
    /**
32
     * @var string
33
     */
34
    protected $baseUri;
35
36
    /**
37
     * BaseClient constructor.
38
     *
39
     * @param \EasyIM\Kernel\ServiceContainer                    $app
40
     * @param \EasyIM\Kernel\Contracts\AccessTokenInterface|null $accessToken
41
     */
42 12
    public function __construct(ServiceContainer $app, AccessTokenInterface $accessToken = null)
43
    {
44 12
        $this->app = $app;
45 12
        $this->accessToken = $accessToken ?? $this->app['access_token'];
46 12
    }
47
48
    /**
49
     * GET request.
50
     *
51
     * @param string $url
52
     * @param array  $query
53
     *
54
     * @return \Psr\Http\Message\ResponseInterface|\EasyIM\Kernel\Support\Collection|array|object|string
55
     *
56
     * @throws \EasyIM\Kernel\Exceptions\InvalidConfigException
57
     * @throws \GuzzleHttp\Exception\GuzzleException
58
     */
59 1
    public function httpGet(string $url, array $query = [])
60
    {
61 1
        return $this->request($url, 'GET', ['query' => $query]);
62
    }
63
64
    /**
65
     * POST request.
66
     *
67
     * @param string $url
68
     * @param array  $data
69
     *
70
     * @return \Psr\Http\Message\ResponseInterface|\EasyIM\Kernel\Support\Collection|array|object|string
71
     *
72
     * @throws \EasyIM\Kernel\Exceptions\InvalidConfigException
73
     * @throws \GuzzleHttp\Exception\GuzzleException
74
     */
75 1
    public function httpPost(string $url, array $data = [])
76
    {
77 1
        return $this->request($url, 'POST', ['form_params' => $data]);
78
    }
79
80
    /**
81
     * JSON request.
82
     *
83
     * @param string $url
84
     * @param array  $data
85
     * @param array  $query
86
     *
87
     * @return \Psr\Http\Message\ResponseInterface|\EasyIM\Kernel\Support\Collection|array|object|string
88
     *
89
     * @throws \EasyIM\Kernel\Exceptions\InvalidConfigException
90
     * @throws \GuzzleHttp\Exception\GuzzleException
91
     */
92 1
    public function httpPostJson(string $url, array $data = [], array $query = [])
93
    {
94 1
        return $this->request($url, 'POST', ['query' => $query, 'json' => $data]);
95
    }
96
97
    /**
98
     * Upload file.
99
     *
100
     * @param string $url
101
     * @param array  $files
102
     * @param array  $form
103
     * @param array  $query
104
     *
105
     * @return \Psr\Http\Message\ResponseInterface|\EasyIM\Kernel\Support\Collection|array|object|string
106
     *
107
     * @throws \EasyIM\Kernel\Exceptions\InvalidConfigException
108
     * @throws \GuzzleHttp\Exception\GuzzleException
109
     */
110 1
    public function httpUpload(string $url, array $files = [], array $form = [], array $query = [])
111
    {
112 1
        $multipart = [];
113
114 1
        foreach ($files as $name => $path) {
115 1
            $multipart[] = [
116 1
                'name' => $name,
117 1
                'contents' => fopen($path, 'r'),
118
            ];
119
        }
120
121 1
        foreach ($form as $name => $contents) {
122 1
            $multipart[] = compact('name', 'contents');
123
        }
124
125 1
        return $this->request($url, 'POST', ['query' => $query, 'multipart' => $multipart, 'connect_timeout' => 30, 'timeout' => 30, 'read_timeout' => 30]);
126
    }
127
128
    /**
129
     * @return AccessTokenInterface
130
     */
131 1
    public function getAccessToken(): AccessTokenInterface
132
    {
133 1
        return $this->accessToken;
134
    }
135
136
    /**
137
     * @param \EasyIM\Kernel\Contracts\AccessTokenInterface $accessToken
138
     *
139
     * @return $this
140
     */
141 1
    public function setAccessToken(AccessTokenInterface $accessToken)
142
    {
143 1
        $this->accessToken = $accessToken;
144
145 1
        return $this;
146
    }
147
148
    /**
149
     * @param string $url
150
     * @param string $method
151
     * @param array  $options
152
     * @param bool   $returnRaw
153
     *
154
     * @return \Psr\Http\Message\ResponseInterface|\EasyIM\Kernel\Support\Collection|array|object|string
155
     *
156
     * @throws \EasyIM\Kernel\Exceptions\InvalidConfigException
157
     * @throws \GuzzleHttp\Exception\GuzzleException
158
     */
159 1
    public function request(string $url, string $method = 'GET', array $options = [], $returnRaw = false)
160
    {
161 1
        if (empty($this->middlewares)) {
162 1
            $this->registerHttpMiddlewares();
163
        }
164
165
166 1
        $response = $this->performRequest($url, $method, $options);
167
168
169 1
        $this->app->events->dispatch(new Events\HttpResponseCreated($response));
170
171 1
        return $returnRaw ? $response : $this->castResponseToType($response, $this->app->config->get('response_type'));
172
    }
173
174
    /**
175
     * @param string $url
176
     * @param string $method
177
     * @param array  $options
178
     *
179
     * @return \EasyIM\Kernel\Http\Response
180
     *
181
     * @throws \EasyIM\Kernel\Exceptions\InvalidConfigException
182
     * @throws \GuzzleHttp\Exception\GuzzleException
183
     */
184 1
    public function requestRaw(string $url, string $method = 'GET', array $options = [])
185
    {
186 1
        return Response::buildFromPsrResponse($this->request($url, $method, $options, true));
187
    }
188
189
    /**
190
     * Register Guzzle middlewares.
191
     */
192
    protected function registerHttpMiddlewares()
193
    {
194
        // retry
195
        $this->pushMiddleware($this->retryMiddleware(), 'retry');
196
        // access token
197
        $this->pushMiddleware($this->accessTokenMiddleware(), 'access_token');
198
        // log
199
        $this->pushMiddleware($this->logMiddleware(), 'log');
200
    }
201
202
    /**
203
     * Attache access token to request query.
204
     *
205
     * @return \Closure
206
     */
207 1
    protected function accessTokenMiddleware()
208
    {
209
        return function (callable $handler) {
210
            return function (RequestInterface $request, array $options) use ($handler) {
211 1
                if ($this->accessToken) {
212 1
                    $request = $this->accessToken->applyToRequest($request, $options);
213
                }
214
215 1
                return $handler($request, $options);
216 1
            };
217 1
        };
218
    }
219
220
    /**
221
     * Log the request.
222
     *
223
     * @return \Closure
224
     */
225 1
    protected function logMiddleware()
226
    {
227 1
        $formatter = new MessageFormatter($this->app['config']['http.log_template'] ?? MessageFormatter::DEBUG);
228
229 1
        return Middleware::log($this->app['logger'], $formatter, LogLevel::DEBUG);
230
    }
231
232
    /**
233
     * Return retry middleware.
234
     *
235
     * @return \Closure
236
     */
237
    protected function retryMiddleware()
238
    {
239
        return Middleware::retry(function (
240
            $retries,
241
            RequestInterface $request,
242
            ResponseInterface $response = null
243
        ) {
244
            // Limit the number of retries to 2
245
            if ($retries < $this->app->config->get('http.max_retries', 1) && $response && $body = $response->getBody()) {
246
                // Retry on server errors
247
                $response = json_decode($body, true);
248
249
                if (!empty($response['ErrorCode']) && in_array(abs($response['ErrorCode']), [6206], true)) {
250
                    $this->accessToken->refresh();
251
                    $this->app['logger']->debug('Retrying with refreshed access token.');
252
253
                    return true;
254
                }
255
            }
256
257
            return false;
258
        }, function () {
259
            return abs($this->app->config->get('http.retry_delay', 500));
260
        });
261
    }
262
}
263