1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Bavix\SDK; |
4
|
|
|
|
5
|
|
|
use Bavix\Exceptions\Invalid; |
6
|
|
|
use Bavix\Helpers\Arr; |
7
|
|
|
use Bavix\Helpers\File; |
8
|
|
|
use Bavix\Helpers\JSON; |
9
|
|
|
use Bavix\Helpers\Str; |
10
|
|
|
use Bavix\Slice\Slice; |
11
|
|
|
use Carbon\Carbon; |
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|null |
175
|
|
|
* |
176
|
|
|
* @throws Invalid |
177
|
|
|
*/ |
178
|
|
|
protected function apiRequest(Slice $options) |
179
|
|
|
{ |
180
|
|
|
$allow404 = $options->getData('allow404', false); |
181
|
|
|
$type = $options->getData('token_type', 'Basic'); |
182
|
|
|
$method = $options->getData('method', 'POST'); |
183
|
|
|
$token = $options->getData('access_token', $this->basic); |
184
|
|
|
$url = $options->getData('url', $this->urlToken); |
185
|
|
|
$params = $options->getData('params', []); |
186
|
|
|
$headers = $options->getData('headers', []); |
187
|
|
|
|
188
|
|
|
// headers |
189
|
|
|
$headers['Authorization'] = $type . ' ' . $token; |
190
|
|
|
|
191
|
|
|
$client = new Client([ |
192
|
|
|
'debug' => $this->slice->getData('debug', false), |
193
|
|
|
'http_errors' => false |
194
|
|
|
]); |
195
|
|
|
|
196
|
|
|
$data = [ |
197
|
|
|
'headers' => $headers, |
198
|
|
|
'multipart' => $this->multipart($params), |
199
|
|
|
]; |
200
|
|
|
|
201
|
|
|
$this->response = $client->request( |
202
|
|
|
Str::upp($method), |
203
|
|
|
$url, |
204
|
|
|
$data |
205
|
|
|
); |
206
|
|
|
|
207
|
|
|
$this->results = null; |
208
|
|
|
|
209
|
|
|
$response = JSON::decode( |
210
|
|
|
$this->getBody() |
211
|
|
|
); |
212
|
|
|
|
213
|
|
|
if (JSON::errorNone()) |
214
|
|
|
{ |
215
|
|
|
$this->results = new Slice($response); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
$code = $this->getCode(); |
219
|
|
|
|
220
|
|
|
if ($allow404 && $code === 404) |
221
|
|
|
{ |
222
|
|
|
return $this->getResults(); |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
if ($code > 199 && $code < 300) |
226
|
|
|
{ |
227
|
|
|
return $this->getResults(); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
throw new Invalid( |
231
|
|
|
'Error: ' . $this->response->getReasonPhrase(), |
232
|
|
|
$code |
233
|
|
|
); |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* @param string $user |
238
|
|
|
* @param array $params |
239
|
|
|
* |
240
|
|
|
* @return Slice |
241
|
|
|
* |
242
|
|
|
* @throws Invalid |
243
|
|
|
*/ |
244
|
|
|
protected function authorize(string $user, array $params = []): Slice |
245
|
|
|
{ |
246
|
|
|
$grantType = $this->slice->getData('grant_type', 'password'); |
247
|
|
|
$userData = $this->slice->getSlice('users.' . $user); |
248
|
|
|
$defaults = [ |
249
|
|
|
'username' => $userData->getRequired('username'), |
250
|
|
|
'password' => $userData->getRequired('password'), |
251
|
|
|
'grant_type' => $grantType, |
252
|
|
|
]; |
253
|
|
|
|
254
|
|
|
$response = $this->apiRequest(new Slice([ |
255
|
|
|
'params' => Arr::merge($defaults, $params) |
256
|
|
|
])); |
257
|
|
|
|
258
|
|
|
if ($response) |
259
|
|
|
{ |
260
|
|
|
$response->expires = Carbon::now() |
261
|
|
|
->addSeconds($response->expires_in); |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
return $response; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* @param string $user |
269
|
|
|
* @param string $scope |
270
|
|
|
* |
271
|
|
|
* @return Slice |
272
|
|
|
* |
273
|
|
|
* @throws Invalid |
274
|
|
|
*/ |
275
|
|
|
protected function getToken(string $user, string $scope = ''): Slice |
276
|
|
|
{ |
277
|
|
|
if (!isset($this->tokens[$user])) |
278
|
|
|
{ |
279
|
|
|
$this->tokens[$user] = $this->authorize($user, [ |
280
|
|
|
'scope' => $scope |
281
|
|
|
]); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
return $this->tokens[$user]; |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* @param string $user |
289
|
|
|
* |
290
|
|
|
* @return Slice |
291
|
|
|
* |
292
|
|
|
* @throws Invalid |
293
|
|
|
*/ |
294
|
|
|
protected function refreshToken(string $user): Slice |
295
|
|
|
{ |
296
|
|
|
/** |
297
|
|
|
* @var $token Slice |
298
|
|
|
*/ |
299
|
|
|
$token = $this->tokens[$user]; |
300
|
|
|
|
301
|
|
|
return $this->tokens[$user] = $this->authorize($user, [ |
302
|
|
|
'grant_type' => 'refresh_token', |
303
|
|
|
'refresh_token' => $token->getRequired('refresh_token'), |
304
|
|
|
]); |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* @param string $user |
309
|
|
|
* @param string $file |
310
|
|
|
* @param Slice $options |
311
|
|
|
* |
312
|
|
|
* @return Slice |
313
|
|
|
* |
314
|
|
|
* @throws Invalid |
315
|
|
|
*/ |
316
|
|
|
public function upload(string $user, string $file, Slice $options = null): Slice |
317
|
|
|
{ |
318
|
|
|
if (!File::isFile($file)) |
319
|
|
|
{ |
320
|
|
|
throw new \Bavix\Exceptions\NotFound\Path('File not found!'); |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
if (!$options) |
324
|
|
|
{ |
325
|
|
|
$options = $this->fake(); |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
$token = $this->getToken( |
329
|
|
|
$user, |
330
|
|
|
$options->getData('scope', '') |
331
|
|
|
); |
332
|
|
|
|
333
|
|
|
return $this->apiSend( |
334
|
|
|
$this->uploadSlice($token, $options, $file), |
335
|
|
|
function () use ($user, $options, $file) { |
336
|
|
|
return $this->uploadSlice( |
337
|
|
|
$this->tokenUpdate($user, $options), |
338
|
|
|
$options, |
339
|
|
|
$file |
340
|
|
|
); |
341
|
|
|
} |
342
|
|
|
); |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* @param string $user |
347
|
|
|
* @param string $name |
348
|
|
|
* @param Slice|null $options |
349
|
|
|
* |
350
|
|
|
* @return Slice |
351
|
|
|
*/ |
352
|
|
View Code Duplication |
public function update(string $user, string $name, Slice $options = null): Slice |
|
|
|
|
353
|
|
|
{ |
354
|
|
|
if (!$options) |
355
|
|
|
{ |
356
|
|
|
$options = $this->fake(); |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
$token = $this->getToken( |
360
|
|
|
$user, |
361
|
|
|
$options->getData('scope', '') |
362
|
|
|
); |
363
|
|
|
|
364
|
|
|
$options->allow404 = true; |
365
|
|
|
|
366
|
|
|
$urlUpload = $this->slice->getRequired('app.url.upload'); |
367
|
|
|
$options->url = Path::slash($urlUpload) . $name; |
368
|
|
|
|
369
|
|
|
return $this->apiSend( |
370
|
|
|
$this->uploadSlice($token, $options), |
371
|
|
|
function () use ($user, $options) { |
372
|
|
|
return $this->uploadSlice( |
373
|
|
|
$this->tokenUpdate($user, $options), |
374
|
|
|
$options |
375
|
|
|
); |
376
|
|
|
} |
377
|
|
|
); |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* @param string $user |
382
|
|
|
* @param string $name |
383
|
|
|
* @param Slice|null $options |
384
|
|
|
* |
385
|
|
|
* @return Slice|null |
386
|
|
|
*/ |
387
|
|
View Code Duplication |
public function delete(string $user, string $name, Slice $options = null) |
|
|
|
|
388
|
|
|
{ |
389
|
|
|
if (!$options) |
390
|
|
|
{ |
391
|
|
|
$options = $this->fake(); |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
$token = $this->getToken( |
395
|
|
|
$user, |
396
|
|
|
$options->getData('scope', '') |
397
|
|
|
); |
398
|
|
|
|
399
|
|
|
$options->allow404 = true; |
400
|
|
|
|
401
|
|
|
$urlUpload = $this->slice->getRequired('app.url.upload'); |
402
|
|
|
$options->url = Path::slash($urlUpload) . $name; |
403
|
|
|
|
404
|
|
|
$options->method = 'delete'; |
405
|
|
|
|
406
|
|
|
return $this->apiSend( |
407
|
|
|
$this->uploadSlice($token, $options), |
408
|
|
|
function () use ($user, $options) { |
409
|
|
|
return $this->uploadSlice( |
410
|
|
|
$this->tokenUpdate($user, $options), |
411
|
|
|
$options |
412
|
|
|
); |
413
|
|
|
} |
414
|
|
|
); |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
/** |
418
|
|
|
* @param Slice $slice |
419
|
|
|
* @param callable $callback |
420
|
|
|
* |
421
|
|
|
* @return Slice|null |
422
|
|
|
*/ |
423
|
|
|
protected function apiSend(Slice $slice, callable $callback) |
424
|
|
|
{ |
425
|
|
|
try |
426
|
|
|
{ |
427
|
|
|
return $this->apiRequest($slice); |
428
|
|
|
} |
429
|
|
|
catch (\Throwable $throwable) |
|
|
|
|
430
|
|
|
{ |
431
|
|
|
return $this->apiRequest($callback()); |
432
|
|
|
} |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
/** |
436
|
|
|
* @param Slice $token |
437
|
|
|
* @param Slice $options |
438
|
|
|
* @param string $file |
439
|
|
|
* |
440
|
|
|
* @return Slice |
441
|
|
|
*/ |
442
|
|
|
protected function uploadSlice(Slice $token, Slice $options, string $file = null): Slice |
443
|
|
|
{ |
444
|
|
|
$params = $options->getData('params', []); |
445
|
|
|
|
446
|
|
|
if ($file) |
|
|
|
|
447
|
|
|
{ |
448
|
|
|
$params = Arr::merge($params, [ |
449
|
|
|
'file' => '@' . \ltrim($file, '@') |
450
|
|
|
]); |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
return new Slice([ |
454
|
|
|
'token_type' => $token->getRequired('token_type'), |
455
|
|
|
'access_token' => $token->getRequired('access_token'), |
456
|
|
|
'method' => $options->getData('method', 'post'), |
457
|
|
|
'url' => $options->getData( |
458
|
|
|
'url', |
459
|
|
|
$this->slice->getRequired('app.url.upload') |
460
|
|
|
), |
461
|
|
|
|
462
|
|
|
'allow404' => $options->getData('allow404', false), |
463
|
|
|
|
464
|
|
|
'headers' => Arr::merge($options->getData('headers', []), [ |
465
|
|
|
'Accept' => 'application/json' |
466
|
|
|
]), |
467
|
|
|
|
468
|
|
|
'params' => $params |
469
|
|
|
]); |
470
|
|
|
} |
471
|
|
|
|
472
|
|
|
/** |
473
|
|
|
* @param Slice $token |
474
|
|
|
* |
475
|
|
|
* @return Slice |
476
|
|
|
* |
477
|
|
|
* @throws Invalid |
478
|
|
|
*/ |
479
|
|
|
protected function verify(Slice $token): Slice |
480
|
|
|
{ |
481
|
|
|
return $this->apiRequest(new Slice([ |
482
|
|
|
'token_type' => $token->getRequired('token_type'), |
483
|
|
|
'access_token' => $token->getRequired('access_token'), |
484
|
|
|
'url' => $this->slice->getRequired('app.url.verify'), |
485
|
|
|
'headers' => [ |
486
|
|
|
'Accept' => 'application/json' |
487
|
|
|
] |
488
|
|
|
])); |
489
|
|
|
} |
490
|
|
|
|
491
|
|
|
/** |
492
|
|
|
* @param string $user |
493
|
|
|
* @param Slice $slice |
494
|
|
|
* |
495
|
|
|
* @return Slice |
496
|
|
|
* |
497
|
|
|
* @throws Invalid |
498
|
|
|
*/ |
499
|
|
|
protected function tokenUpdate(string $user, Slice $slice): Slice |
500
|
|
|
{ |
501
|
|
|
try |
502
|
|
|
{ |
503
|
|
|
$token = $this->refreshToken($user); |
504
|
|
|
|
505
|
|
|
if (!$this->verify($token)) |
506
|
|
|
{ |
507
|
|
|
throw new Invalid('The token isn\'t verified'); |
508
|
|
|
} |
509
|
|
|
} |
510
|
|
|
catch (Invalid $invalid) |
511
|
|
|
{ |
512
|
|
|
$this->tokens[$user] = null; |
513
|
|
|
|
514
|
|
|
$token = $this->getToken( |
515
|
|
|
$user, |
516
|
|
|
$slice->getData('scope', '') |
517
|
|
|
); |
518
|
|
|
} |
519
|
|
|
|
520
|
|
|
return $token; |
521
|
|
|
} |
522
|
|
|
|
523
|
|
|
} |
524
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.