RestClient::getOAuthUserCredentialsData()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Yeelight\Services\Rest;
4
5
use GuzzleHttp\Client;
6
use Illuminate\Http\Response;
7
use Psr\Http\Message\ResponseInterface;
8
use RuntimeException;
9
10
/**
11
 * Class RestClient
12
 *
13
 * @category Yeelight
14
 *
15
 * @package Yeelight\Services\Rest
16
 *
17
 * @author Sheldon Lee <[email protected]>
18
 *
19
 * @license https://opensource.org/licenses/MIT MIT
20
 *
21
 * @link https://www.yeelight.com
22
 */
23
class RestClient
24
{
25
    /**
26
     * @var string
27
     */
28
    private $service_name;
29
30
    /**
31
     * @var string
32
     */
33
    protected $oauth_tokens_cache_key = 'yeelight-rest-api-client.oauth_tokens';
34
35
    /**
36
     * @var array
37
     */
38
    protected $service_config;
39
40
    /**
41
     * @var array
42
     */
43
    protected $shared_service_config;
44
45
    /**
46
     * @var \Psr\Http\Message\ResponseInterface
47
     */
48
    protected $guzzle_response;
49
50
    /**
51
     * @var Response
52
     */
53
    protected $response;
54
55
    /**
56
     * @var \GuzzleHttp\Client
57
     */
58
    protected $client;
59
60
    /**
61
     * @var array
62
     */
63
    protected $oauth_tokens = [];
64
65
    // Grant Types
66
    const GRANT_TYPE_CLIENT_CREDENTIALS = 'client_credentials';
67
    const GRANT_TYPE_AUTHORIZATION_CODE = 'authorization_code';
68
    const GRANT_TYPE_PASSWORD = 'password';
69
    const GRANT_TYPE_REFRESH_TOKEN = 'refresh_token';
70
71
    protected $use_oauth_token_grant_type = null;
72
73
    protected $oauth_user_credentials = null;
74
75
    protected $use_cache_token = null;
76
77
    protected $oauth_grant_request_data = [
78
        self::GRANT_TYPE_CLIENT_CREDENTIALS => [],
79
        self::GRANT_TYPE_AUTHORIZATION_CODE => [],
80
        self::GRANT_TYPE_PASSWORD           => [],
81
        self::GRANT_TYPE_REFRESH_TOKEN      => [],
82
    ];
83
84
    /**
85
     * @var bool
86
     */
87
    protected $debug_mode = false;
88
89
    /**
90
     * @var string
91
     */
92
    protected $environment;
93
94
    /**
95
     * Create a new RestClient Instance.
96
     *
97
     * @param $service_name
98
     * @param null $debug_mode
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $debug_mode is correct as it would always require null to be passed?
Loading history...
99
     */
100
    public function __construct($service_name = null, $debug_mode = null)
101
    {
102
        $this->environment = $this->getConfig('environment', 'production');
103
        $this->shared_service_config = $this->getConfig('shared_service_config');
104
        $this->debug_mode = $debug_mode !== null ? $debug_mode : $this->getConfig('debug_mode');
105
        $services = $this->getConfig('services');
106
107
        // use default service name
108
        if (empty($service_name)) {
109
            $service_name = $this->getConfig('default_service_name');
110
        }
111
112
        $this->service_name = $service_name;
113
114
        // choose service environment
115
        if (!isset($services[$this->environment])) {
116
            throw new RuntimeException("Rest Client Error: Service for environment [{$this->environment}] is not found in config.");
117
        }
118
        $services = $services[$this->environment];
119
120
        // check service configs
121
        if (!isset($services[$service_name])) {
122
            throw new RuntimeException("Rest Client Error: Service [$service_name] is not found in environment [{$this->environment}] config.");
123
        }
124
125
        $this->printLine('--------');
126
        $this->printLine('REST CLIENT SERVICE: '.$service_name.', ENVIRONMENT: '.$this->environment);
127
128
        // get cache
129
        $minutes = $this->getConfig('oauth_tokens_cache_minutes', 10);
130
        $this->use_cache_token = $minutes > 0;
131
        $this->useOAuthTokenFromCache();
132
133
        $this->setServiceConfig($services[$service_name]);
134
135
        $this->setUp();
136
    }
137
138
    /**
139
     * @param $key
140
     * @param null $default
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $default is correct as it would always require null to be passed?
Loading history...
141
     *
142
     * @return mixed
143
     */
144
    public function getConfig($key, $default = null)
145
    {
146
        return config("rest-client.$key", $default);
147
    }
148
149
    /**
150
     * @param $service_config
151
     */
152
    private function setServiceConfig($service_config)
153
    {
154
        $shared_service_config = $this->shared_service_config;
155
156
        $this->service_config = $this->mergeConfig($shared_service_config, $service_config);
157
    }
158
159
    /**
160
     * @param $service_config
161
     */
162
    public function addServiceConfig(array $service_config)
163
    {
164
        $this->service_config = $this->mergeConfig($this->service_config, $service_config);
165
    }
166
167
    /**
168
     * @param array $headers
169
     */
170
    public function addHeaders(array $headers)
171
    {
172
        $service_config['headers'] = $headers;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$service_config was never initialized. Although not strictly required by PHP, it is generally a good practice to add $service_config = array(); before regardless.
Loading history...
173
        $this->addServiceConfig($service_config);
174
    }
175
176
    /**
177
     * New Config will override Base Config if both present.
178
     *
179
     * @param array $baseConfig
180
     * @param array $newConfig
181
     *
182
     * @return array
183
     */
184
    private function mergeConfig($baseConfig = [], $newConfig = [])
185
    {
186
        $combined_service_config = $newConfig;
187
        foreach ($baseConfig as $key => $config) {
188
            if (is_array($config) && isset($combined_service_config[$key])) {
189
                $combined_service_config[$key] = array_merge($config, $combined_service_config[$key]);
190
            } elseif (!isset($combined_service_config[$key])) {
191
                $combined_service_config[$key] = $config;
192
            }
193
        }
194
195
        return $combined_service_config;
196
    }
197
198
    /**
199
     * @param $key
200
     *
201
     * @return mixed
202
     */
203
    public function getServiceConfig($key)
204
    {
205
        return $this->service_config[$key];
206
    }
207
208
    /**
209
     *  Set Up Client.
210
     */
211
    public function setUp()
212
    {
213
        $base_uri = $this->getServiceConfig('base_uri');
214
        $guzzle_client_config = $this->getConfig('guzzle_client_config', []);
215
        if (!ends_with($base_uri, '/')) {
216
            $base_uri .= '/';
217
        }
218
        $this->printLine('REST CLIENT BASE URI: '.$base_uri);
219
        $this->client = new Client(array_merge($guzzle_client_config, [
220
            'base_uri'   => $base_uri,
221
            'exceptions' => false,
222
        ]));
223
    }
224
225
    /**
226
     * @param bool $debug_mode
227
     */
228
    public function setDebugMode($debug_mode)
229
    {
230
        $this->debug_mode = $debug_mode;
231
    }
232
233
    /**
234
     * @return mixed
235
     */
236
    protected function getClientData()
237
    {
238
        return $this->getServiceConfig('oauth2_credentials');
239
    }
240
241
    /**
242
     * @deprecated Please use setOAuthGrantRequestData() instead
243
     *
244
     * @param $oauth_user_credentials
245
     */
246
    public function setOAuthUserCredentials($oauth_user_credentials)
247
    {
248
//        $oauth_user_credentials = [
249
//            'username' => '[email protected]',
250
//            'password' => '123456',
251
//        ];
252
        $this->oauth_user_credentials = $oauth_user_credentials;
253
        $this->useOAuthTokenFromCache();
254
    }
255
256
    /**
257
     * @deprecated Please use getOAuthGrantRequestData() instead
258
     *
259
     * @return null
260
     */
261
    protected function getOAuthUserCredentialsData()
262
    {
263
        if (empty($this->oauth_user_credentials)) {
264
            throw new RuntimeException('Please set "oauth_user_credentials" by calling setOAuthUserCredentialsData()!');
265
        }
266
267
        return $this->oauth_user_credentials;
268
    }
269
270
    /**
271
     * @param $grant_type
272
     * @param array $data
273
     */
274
    public function setOAuthGrantRequestData($grant_type, array $data)
275
    {
276
        $this->oauth_grant_request_data[$grant_type] = $data;
277
        $this->useOAuthTokenFromCache();
278
    }
279
280
    /**
281
     * @param $grant_type
282
     *
283
     * @return array
284
     */
285
    public function getOAuthGrantRequestData($grant_type)
286
    {
287
        if (!isset($this->oauth_grant_request_data[$grant_type])) {
288
            throw new RuntimeException('Request Data was not found for grant type ['.$grant_type.'] in "oauth_grant_request_data"');
289
        }
290
        $data = $this->oauth_grant_request_data[$grant_type];
291
292
        return array_merge($this->getClientData(), $data);
293
    }
294
295
    /**
296
     * @param $grant_type
297
     * @param $data
298
     *
299
     * @return $this;
300
     */
301
    protected function postRequestAccessToken($grant_type, $data)
302
    {
303
        $url = $this->getServiceConfig('oauth2_access_token_url');
304
305
        return $this->post($url, array_merge($data, [
306
            'grant_type' => $grant_type,
307
        ]), [], false);
308
    }
309
310
    /**
311
     * @param $options
312
     *
313
     * @return array
314
     */
315
    private function configureOptions($options)
316
    {
317
        $headers = $this->getServiceConfig('headers');
318
319
        // add client ip to header
320
        $request = request();
321
        $clientIp = $request->getClientIp();
322
        $headers['X-Client-Ip'] = $clientIp;
323
        $headers['X-Forwarded-For'] = $clientIp;
324
        $headers['Accept-Language'] = $request->header('Accept-Language', app()->getLocale());
325
326
        if ($this->use_oauth_token_grant_type) {
327
            $headers['Authorization'] = 'Bearer '.$this->getOAuthToken($this->use_oauth_token_grant_type);
328
        }
329
330
        if (isset($options['headers'])) {
331
            $headers = array_merge($headers, $options['headers']);
332
            unset($options['headers']);
333
        }
334
335
        return array_merge([
336
            'headers' => $headers,
337
        ], $options);
338
    }
339
340
    /**
341
     *  Use OAuth Tokens from Cache.
342
     */
343
    private function useOAuthTokenFromCache()
344
    {
345
        if (!$this->use_cache_token) {
346
            return;
347
        }
348
349
        $this->oauth_tokens = \Cache::get($this->getOauthTokensCacheKey(), []);
0 ignored issues
show
Unused Code introduced by
The call to Illuminate\Cache\CacheManager::get() has too many arguments starting with array(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

349
        /** @scrutinizer ignore-call */ 
350
        $this->oauth_tokens = \Cache::get($this->getOauthTokensCacheKey(), []);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
350
        if (!empty($this->oauth_tokens)) {
351
            $this->printLine('Using OAuth Tokens from cache:');
352
            $this->printArray($this->oauth_tokens);
353
        }
354
    }
355
356
    /**
357
     * @return string
358
     */
359
    private function getOauthTokensCacheKey()
360
    {
361
        $user_hash = '';
362
        if (!empty($this->oauth_user_credentials)) {
363
            $user_hash = '.'.sha1(serialize($this->oauth_user_credentials));
364
        }
365
        $cache_key = $this->oauth_tokens_cache_key.'.'.$this->service_name.'.'.$this->environment.$user_hash;
366
367
        return $cache_key;
368
    }
369
370
    /**
371
     * @param $grant_type
372
     *
373
     * @return mixed
374
     */
375
    private function getOAuthToken($grant_type)
376
    {
377
        $grant_types = $this->getServiceConfig('oauth2_grant_types');
0 ignored issues
show
Unused Code introduced by
The assignment to $grant_types is dead and can be removed.
Loading history...
378
        if (!isset($this->oauth_tokens[$grant_type])) {
379
            // request access token
380
            $this->postRequestAccessToken($grant_type, $this->getOAuthGrantRequestData($grant_type));
381
382
            // handle access token
383
            if ($this->getResponse()->getStatusCode() != 200) {
384
                throw new RuntimeException('Failed to get access token for grant type ['.$grant_type.']!');
385
            }
386
387
            $data = $this->getResponseData();
388
            if (!isset($data['access_token'])) {
389
                throw new RuntimeException('"access_token" is not exists in the response data!');
390
            }
391
            $access_token = $data['access_token'];
392
            $this->setOAuthToken($grant_type, $access_token);
393
        }
394
395
        return $this->oauth_tokens[$grant_type];
396
    }
397
398
    /**
399
     * @param $type
400
     * @param $access_token
401
     */
402
    public function setOAuthToken($type, $access_token)
403
    {
404
        if ($this->debug_mode) {
405
            echo "SET OAuthToken[$type]: $access_token\n\n";
406
        }
407
408
        if (empty($access_token)) {
409
            unset($this->oauth_tokens[$type]);
410
        } else {
411
            $this->oauth_tokens[$type] = $access_token;
412
        }
413
414
        // update to cache
415
        $minutes = $this->getConfig('oauth_tokens_cache_minutes', 10);
416
        \Cache::put($this->getOauthTokensCacheKey(), $this->oauth_tokens, $minutes);
417
    }
418
419
    /**
420
     * @param $grant_type
421
     * @param array|null $requestData
422
     *
423
     * @return $this
424
     */
425
    public function withOAuthToken($grant_type, $requestData = null)
426
    {
427
        if ($requestData !== null) {
428
            $this->setOAuthGrantRequestData($grant_type, $requestData);
429
        }
430
        $this->getOAuthToken($grant_type);
431
        $this->use_oauth_token_grant_type = $grant_type;
432
433
        return $this;
434
    }
435
436
    /**
437
     * @param array|null $requestData
438
     *
439
     * @return RestClient
440
     */
441
    public function withOAuthTokenTypePassword($requestData = null)
442
    {
443
        return $this->withOAuthToken(self::GRANT_TYPE_PASSWORD, $requestData);
444
    }
445
446
    /**
447
     * @param array|null $requestData
448
     *
449
     * @return RestClient
450
     */
451
    public function withOAuthTokenTypeClientCredentials($requestData = null)
452
    {
453
        return $this->withOAuthToken(self::GRANT_TYPE_CLIENT_CREDENTIALS, $requestData);
454
    }
455
456
    /**
457
     * @param array|null $requestData
458
     *
459
     * @return RestClient
460
     */
461
    public function withOAuthTokenTypeAuthorizationCode($requestData = null)
462
    {
463
        return $this->withOAuthToken(self::GRANT_TYPE_AUTHORIZATION_CODE, $requestData);
464
    }
465
466
    /**
467
     * @return $this
468
     */
469
    public function withoutOAuthToken()
470
    {
471
        $this->use_oauth_token_grant_type = null;
472
473
        return $this;
474
    }
475
476
    /**
477
     * @param string $uri
478
     * @param array  $query
479
     * @param array  $options
480
     * @param bool   $api
481
     *
482
     * @return $this ;
483
     */
484
    public function get($uri, array $query = [], array $options = [], $api = true)
485
    {
486
        $options = $this->configureOptions($options);
487
        $uri = $api ? $this->getServiceConfig('api_url').$uri : $uri;
488
        $this->printArray($options);
489
        $response = $this->client->get($uri, array_merge($options, [
490
            'query' => $query,
491
        ]));
492
        $this->setGuzzleResponse($response);
493
494
        return $this;
495
    }
496
497
    /**
498
     * @param string $uri
499
     * @param array  $data
500
     * @param array  $options
501
     * @param bool   $api
502
     *
503
     * @return $this;
504
     */
505
    public function post($uri, array $data = [], array $options = [], $api = true)
506
    {
507
        $options = $this->configureOptions($options);
508
        $uri = $api ? $this->getServiceConfig('api_url').$uri : $uri;
509
        $response = $this->client->post($uri, array_merge($options, [
510
            'form_params' => $data,
511
        ]));
512
        $this->setGuzzleResponse($response);
513
514
        return $this;
515
    }
516
517
    /**
518
     * @url http://docs.guzzlephp.org/en/latest/quickstart.html#sending-form-files
519
     *
520
     * @param $uri
521
     * @param array $multipart
522
     * @param array $options
523
     * @param bool  $api
524
     *
525
     * @return $this;
526
     */
527
    public function postMultipart($uri, array $multipart = [], array $options = [], $api = true)
528
    {
529
        $options = $this->configureOptions($options);
530
        $uri = $api ? $this->getServiceConfig('api_url').$uri : $uri;
531
        $response = $this->client->post($uri, array_merge($options, [
532
            'multipart' => $multipart,
533
        ]));
534
        $this->setGuzzleResponse($response);
535
536
        return $this;
537
    }
538
539
    /**
540
     * @param $uri
541
     * @param array $data
542
     * @param array $options
543
     * @param bool  $api
544
     *
545
     * @return $this;
546
     */
547
    public function postMultipartSimple($uri, array $data = [], array $options = [], $api = true)
548
    {
549
        $options = $this->configureOptions($options);
550
        $uri = $api ? $this->getServiceConfig('api_url').$uri : $uri;
551
        $multipart = [];
552
        foreach ($data as $key => $value) {
553
            $multipart[] = [
554
                'name'     => $key,
555
                'contents' => $value,
556
            ];
557
        }
558
        $response = $this->client->post($uri, array_merge($options, [
559
            'multipart' => $multipart,
560
        ]));
561
        $this->setGuzzleResponse($response);
562
563
        return $this;
564
    }
565
566
    /**
567
     * @param string $uri
568
     * @param array  $data
569
     * @param array  $options
570
     * @param bool   $api
571
     *
572
     * @return $this;
573
     */
574
    public function head($uri, array $data = [], array $options = [], $api = true)
575
    {
576
        $uri = $api ? $this->getServiceConfig('api_url').$uri : $uri;
577
        $response = $this->client->head($uri, array_merge($options, [
578
            'body' => $data,
579
        ]));
580
        $this->setGuzzleResponse($response);
581
582
        return $this;
583
    }
584
585
    /**
586
     * @param string $uri
587
     * @param array  $data
588
     * @param array  $options
589
     * @param bool   $api
590
     *
591
     * @return $this;
592
     */
593
    public function put($uri, array $data = [], array $options = [], $api = true)
594
    {
595
        $options = $this->configureOptions($options);
596
        $uri = $api ? $this->getServiceConfig('api_url').$uri : $uri;
597
        $response = $this->client->put($uri, array_merge($options, [
598
            'form_params' => $data,
599
        ]));
600
        $this->setGuzzleResponse($response);
601
602
        return $this;
603
    }
604
605
    /**
606
     * @param string $uri
607
     * @param array  $data
608
     * @param array  $options
609
     * @param bool   $api
610
     *
611
     * @return $this;
612
     */
613
    public function patch($uri, array $data = [], array $options = [], $api = true)
614
    {
615
        $options = $this->configureOptions($options);
616
        $uri = $api ? $this->getServiceConfig('api_url').$uri : $uri;
617
        $response = $this->client->patch($uri, array_merge($options, [
618
            'form_params' => $data,
619
        ]));
620
        $this->setGuzzleResponse($response);
621
622
        return $this;
623
    }
624
625
    /**
626
     * @param string $uri
627
     * @param array  $data
628
     * @param array  $options
629
     * @param bool   $api
630
     *
631
     * @return $this;
632
     */
633
    public function delete($uri, array $data = [], array $options = [], $api = true)
634
    {
635
        $options = $this->configureOptions($options);
636
        $uri = $api ? $this->getServiceConfig('api_url').$uri : $uri;
637
        $response = $this->client->delete($uri, array_merge($options, [
638
            'form_params' => $data,
639
        ]));
640
        $this->setGuzzleResponse($response);
641
642
        return $this;
643
    }
644
645
    /**
646
     * @return \Psr\Http\Message\ResponseInterface
647
     */
648
    public function getGuzzleResponse()
649
    {
650
        return $this->guzzle_response;
651
    }
652
653
    /**
654
     * @param ResponseInterface $response
655
     */
656
    public function setGuzzleResponse(ResponseInterface $response)
657
    {
658
        $this->guzzle_response = $response;
659
        $this->setResponse(new Response($response->getBody(), $response->getStatusCode(), $response->getHeaders()));
660
    }
661
662
    /**
663
     * @param Response $response
664
     */
665
    public function setResponse(Response $response)
666
    {
667
        $this->response = $response;
668
        $statusCode = $this->response->getStatusCode();
669
        if ($statusCode >= 300 && $this->debug_mode) {
670
            echo "\nResponse STATUS CODE is $statusCode:\n";
671
            $responseData = $this->getResponseData();
672
            if ($responseData) {
673
                $this->printArray($responseData);
674
            } else {
675
                $this->printLine($this->getResponse());
676
            }
677
        }
678
    }
679
680
    /**
681
     * Response is success if status code is < 300.
682
     *
683
     * @return bool
684
     */
685
    public function isResponseSuccess()
686
    {
687
        return $this->getResponse()->getStatusCode() < 300;
688
    }
689
690
    /**
691
     * @param $status_code
692
     *
693
     * @return bool
694
     */
695
    public function isResponseStatusCode($status_code)
696
    {
697
        return $this->getResponse()->getStatusCode() == $status_code;
698
    }
699
700
    /**
701
     * @return Response
702
     */
703
    public function getResponse()
704
    {
705
        return $this->response;
706
    }
707
708
    /**
709
     * @param bool $assoc
710
     *
711
     * @return mixed
712
     */
713
    public function getResponseAsJson($assoc = true)
714
    {
715
        return json_decode($this->getResponse()->getContent(), $assoc);
716
    }
717
718
    /**
719
     * @return mixed
720
     */
721
    public function getResponseData()
722
    {
723
        return $this->getResponseAsJson();
724
    }
725
726
    /**
727
     * @return array|mixed|null
728
     */
729
    public function getResponseErrors()
730
    {
731
        $responseData = $this->getResponseData();
732
        if (is_array($responseData) && isset($responseData['errors'])) {
733
            return $responseData['errors'];
734
        } else {
735
            return;
736
        }
737
    }
738
739
    /**
740
     * @return string|mixed|null
741
     */
742
    public function getResponseMessage()
743
    {
744
        $responseData = $this->getResponseData();
745
        if (is_array($responseData) && isset($responseData['message'])) {
746
            return $responseData['message'];
747
        } else {
748
            return;
749
        }
750
    }
751
752
    /**
753
     * @return $this
754
     */
755
    public function printResponseData()
756
    {
757
        print_r($this->getResponseData());
758
759
        return $this;
760
    }
761
762
    /**
763
     * @return $this
764
     */
765
    public function printResponseOriginContent()
766
    {
767
        print_r((string) $this->response->getOriginalContent());
768
769
        return $this;
770
    }
771
772
    /**
773
     * @param $string
774
     */
775
    protected function printLine($string)
776
    {
777
        if ($this->debug_mode) {
778
            echo $string."\n";
779
        }
780
    }
781
782
    /**
783
     * @param $array
784
     */
785
    protected function printArray($array)
786
    {
787
        if ($this->debug_mode) {
788
            print_r($array);
789
        }
790
    }
791
}
792