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
|
|
|
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
|
|
|
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
|
|
|
$verify = $this->verify($token); |
505
|
|
|
|
506
|
|
|
if (!$verify || !$verify->getRequired('verify')) |
|
|
|
|
507
|
|
|
{ |
508
|
|
|
throw new Invalid('The token isn\'t verified'); |
509
|
|
|
} |
510
|
|
|
} |
511
|
|
|
catch (Invalid $invalid) |
512
|
|
|
{ |
513
|
|
|
$this->tokens[$user] = null; |
514
|
|
|
|
515
|
|
|
$token = $this->getToken( |
516
|
|
|
$user, |
517
|
|
|
$slice->getData('scope', '') |
|
|
|
|
518
|
|
|
); |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
return $token; |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
} |
525
|
|
|
|