Passed
Push — master ( 5acd4d...aeba91 )
by Бабичев
06:53
created

Corundum::getCode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 0
cts 4
cp 0
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 0
crap 6
1
<?php
2
3
namespace Bavix\SDK;
4
5
use Bavix\Exceptions\Invalid;
6
use Bavix\Helpers\Arr;
7
use Bavix\Helpers\JSON;
8
use Bavix\Helpers\Str;
9
use Bavix\Slice\Slice;
10
use Carbon\Carbon;
11
use Bavix\SDK\Helpers\Curl;
12
use GuzzleHttp\Client;
13
use Psr\Http\Message\ResponseInterface;
14
15
class Corundum
16
{
17
18
    /**
19
     * @var array
20
     */
21
    protected $tokens = [];
22
23
    /**
24
     * @var string
25
     */
26
    protected $urlToken;
27
28
    /**
29
     * @var string
30
     */
31
    protected $basic;
32
33
    /**
34
     * @var Slice
35
     */
36
    protected $slice;
37
38
    /**
39
     * @var Slice
40
     */
41
    protected $fake;
42
43
    /**
44
     * @var ResponseInterface
45
     */
46
    protected $response;
47
48
    /**
49
     * @var Slice
50
     */
51
    protected $results;
52
53
    /**
54
     * Corundum constructor.
55
     *
56
     * @param Slice $slice
57
     */
58
    public function __construct(Slice $slice)
59
    {
60
        $clientId = $slice->getRequired('app.client_id');
61
        $secret   = $slice->getRequired('app.client_secret');
62
63
        $this->slice    = $slice;
64
        $this->basic    = \base64_encode($clientId . ':' . $secret);
65
        $this->urlToken = $slice->getRequired('app.url.token');
66
    }
67
68
    /**
69
     * @return array
70
     */
71
    public function getTokens(): array
72
    {
73
        return $this->tokens;
74
    }
75
76
    /**
77
     * @param array $tokens
78
     */
79
    public function setTokens(array $tokens)
80
    {
81
        $this->tokens = $tokens;
82
    }
83
84
    /**
85
     * @return Slice
86
     */
87
    protected function fake(): Slice
88
    {
89
        if (!$this->fake)
90
        {
91
            $this->fake = new Slice([]);
92
        }
93
94
        return $this->fake;
95
    }
96
97
    /**
98
     * @return string
99
     *
100
     * @throws \RuntimeException
101
     */
102
    public function getBody(): string
103
    {
104
        return $this->response->getBody()
105
            ->getContents();
106
    }
107
108
    /**
109
     * @return Slice|null
110
     */
111
    public function getResults()
112
    {
113
        return $this->results;
114
    }
115
116
    /**
117
     * @return int|null
118
     */
119
    public function getCode()
120
    {
121
        if ($this->response)
122
        {
123
            return $this->response->getStatusCode();
124
        }
125
126
        return null;
127
    }
128
129
    /**
130
     * @param mixed $value
131
     *
132
     * @return mixed
133
     */
134
    protected function value($value)
135
    {
136
        if (Str::first($value) === '@')
137
        {
138
            return fopen(Str::withoutFirst($value), 'rb');
139
        }
140
141
        return $value;
142
    }
143
144
    /**
145
     * @param array $params
146
     *
147
     * @return array
148
     */
149
    protected function multipart(array $params): array
150
    {
151
        $results = [];
152
153
        foreach ($params as $key => $value)
154
        {
155
            $content = [
156
                'name'     => $key,
157
                'contents' => $this->value($value)
158
            ];
159
160
            if (\is_resource($content['contents']))
161
            {
162
                $content['filename'] = basename($value);
163
            }
164
165
            $results[] = $content;
166
        }
167
168
        return $results;
169
    }
170
171
    /**
172
     * @param Slice $options
173
     *
174
     * @return Slice
175
     *
176
     * @throws Invalid
177
     */
178
    protected function apiRequest(Slice $options): Slice
179
    {
180
        $type    = $options->getData('token_type', 'Basic');
181
        $method  = $options->getData('method', 'POST');
182
        $token   = $options->getData('access_token', $this->basic);
183
        $url     = $options->getData('url', $this->urlToken);
184
        $params  = $options->getData('params', []);
185
        $headers = $options->getData('headers', []);
186
187
        // headers
188
        $headers['Authorization'] = $type . ' ' . $token;
189
190
        $client = new Client();
191
192
        $data = [
193
            'headers'   => $headers,
194
            'multipart' => $this->multipart($params),
195
        ];
196
197
        $this->response = $client->request(
198
            Str::upp($method),
199
            $url,
200
            $data
201
        );
202
203
        $this->results = null;
204
205
        $response = JSON::decode(
206
            $this->getBody()
207
        );
208
209
        if (JSON::errorNone())
210
        {
211
            $this->results = new Slice($response);
212
        }
213
214
        $code = $this->getCode();
215
216
        if ($code < 200 || $code > 299)
217
        {
218
            throw new Invalid(
219
                'Error: ' . $this->response->getReasonPhrase(),
220
                $code
221
            );
222
        }
223
224
        return $this->getResults();
225
    }
226
227
    /**
228
     * @param string $user
229
     * @param array  $params
230
     *
231
     * @return Slice
232
     *
233
     * @throws Invalid
234
     */
235
    protected function authorize(string $user, array $params = []): Slice
236
    {
237
        $grantType = $this->slice->getData('grant_type', 'password');
238
        $userData  = $this->slice->getSlice('users.' . $user);
239
        $defaults  = [
240
            'username'   => $userData->getRequired('username'),
241
            'password'   => $userData->getRequired('password'),
242
            'grant_type' => $grantType,
243
        ];
244
245
        $response = $this->apiRequest(new Slice([
246
            'params' => Arr::merge($defaults, $params)
247
        ]));
248
249
        $response->expires = Carbon::now()
250
            ->addSeconds($response->expires_in);
251
252
        return $response;
253
    }
254
255
    /**
256
     * @param string $user
257
     * @param string $scope
258
     *
259
     * @return Slice
260
     *
261
     * @throws Invalid
262
     */
263
    protected function getToken(string $user, string $scope = ''): Slice
264
    {
265
        if (!Arr::keyExists($this->tokens, $user))
266
        {
267
            $this->tokens[$user] = $this->authorize($user, [
268
                'scope' => $scope
269
            ]);
270
        }
271
272
        return $this->tokens[$user];
273
    }
274
275
    /**
276
     * @param string $user
277
     *
278
     * @return Slice
279
     *
280
     * @throws Invalid
281
     */
282
    protected function refreshToken(string $user): Slice
283
    {
284
        /**
285
         * @var $token Slice
286
         */
287
        $token = $this->tokens[$user];
288
289
        return $this->tokens[$user] = $this->authorize($user, [
290
            'grant_type'    => 'refresh_token',
291
            'refresh_token' => $token->getRequired('refresh_token'),
292
        ]);
293
    }
294
295
    /**
296
     * @param string $user
297
     * @param string $file
298
     * @param Slice  $options
299
     *
300
     * @return Slice
301
     *
302
     * @throws Invalid
303
     */
304
    public function upload(string $user, string $file, Slice $options = null): Slice
305
    {
306
        if (!$options)
307
        {
308
            $options = $this->fake();
309
        }
310
311
        $token = $this->getToken(
312
            $user,
313
            $options->getData('scope', '')
314
        );
315
316
        try
317
        {
318
            return $this->apiRequest(
319
                $this->uploadSlice($token, $options, $file)
320
            );
321
        }
322
        catch (\Throwable $throwable)
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
323
        {
324
            return $this->apiRequest(
325
                $this->uploadSlice(
326
                    $this->tokenUpdate($user, $options),
327
                    $options,
328
                    $file
329
                )
330
            );
331
        }
332
333
    }
334
335
    /**
336
     * @param Slice $token
337
     * @param Slice $options
338
     *
339
     * @return Slice
340
     */
341
    protected function uploadSlice(Slice $token, Slice $options, string $file): Slice
342
    {
343
        return new Slice([
344
            'token_type'   => $token->getRequired('token_type'),
345
            'access_token' => $token->getRequired('access_token'),
346
            'method'       => $options->getData('method', 'post'),
347
            'url'          => $this->slice->getRequired('app.url.upload'),
348
349
            'headers' => Arr::merge($options->getData('headers', []), [
350
                'Accept' => 'application/json'
351
            ]),
352
353
            'params' => Arr::merge($options->getData('params', []), [
354
                'file' => '@' . \ltrim($file, '@')
355
            ])
356
        ]);
357
    }
358
359
    /**
360
     * @param Slice $token
361
     *
362
     * @return Slice
363
     *
364
     * @throws Invalid
365
     */
366
    protected function verify(Slice $token): Slice
367
    {
368
        return $this->apiRequest(new Slice([
369
            'token_type'   => $token->getRequired('token_type'),
370
            'access_token' => $token->getRequired('access_token'),
371
            'url'          => $this->slice->getRequired('app.url.verify'),
372
            'headers'      => [
373
                'Accept' => 'application/json'
374
            ]
375
        ]));
376
    }
377
378
    /**
379
     * @param string $user
380
     * @param Slice  $slice
381
     *
382
     * @return Slice
383
     *
384
     * @throws Invalid
385
     */
386
    protected function tokenUpdate(string $user, Slice $slice): Slice
387
    {
388
        try
389
        {
390
            $token = $this->refreshToken($user);
391
392
            if (!$this->verify($token))
393
            {
394
                throw new Invalid('The token isn\'t verified');
395
            }
396
        }
397
        catch (Invalid $invalid)
398
        {
399
            $this->tokens[$user] = null;
400
401
            $token = $this->getToken(
402
                $user,
403
                $slice->getData('scope', '')
404
            );
405
        }
406
407
        return $token;
408
    }
409
410
}
411