GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( dc930c...c6d3fd )
by Freek
15s
created

Client::createHandler()   A

Complexity

Conditions 5
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.5555
c 0
b 0
f 0
cc 5
nc 1
nop 0
1
<?php
2
3
namespace Spatie\Dropbox;
4
5
use Exception;
6
use GuzzleHttp\Psr7;
7
use GuzzleHttp\Middleware;
8
use GuzzleHttp\HandlerStack;
9
use GuzzleHttp\Psr7\PumpStream;
10
use GuzzleHttp\Psr7\StreamWrapper;
11
use Psr\Http\Message\StreamInterface;
12
use GuzzleHttp\Client as GuzzleClient;
13
use Psr\Http\Message\ResponseInterface;
14
use GuzzleHttp\Exception\ClientException;
15
use Spatie\Dropbox\Exceptions\BadRequest;
16
use GuzzleHttp\Exception\ConnectException;
17
use GuzzleHttp\Exception\RequestException;
18
19
class Client
20
{
21
    const THUMBNAIL_FORMAT_JPEG = 'jpeg';
22
    const THUMBNAIL_FORMAT_PNG = 'png';
23
24
    const THUMBNAIL_SIZE_XS = 'w32h32';
25
    const THUMBNAIL_SIZE_S = 'w64h64';
26
    const THUMBNAIL_SIZE_M = 'w128h128';
27
    const THUMBNAIL_SIZE_L = 'w640h480';
28
    const THUMBNAIL_SIZE_XL = 'w1024h768';
29
30
    const MAX_CHUNK_SIZE = 1024 * 1024 * 150;
31
32
    const RETRY_BACKOFF = 1000;
33
    const RETRY_CODES = [429];
34
35
    const UPLOAD_SESSION_START = 0;
36
    const UPLOAD_SESSION_APPEND = 1;
37
38
    /** @var string */
39
    protected $accessToken;
40
41
    /** @var \GuzzleHttp\Client */
42
    protected $client;
43
44
    /** @var int */
45
    protected $maxChunkSize;
46
47
    /** @var int */
48
    protected $maxUploadChunkRetries;
49
50
    /**
51
     * @param string $accessToken
52
     * @param GuzzleClient|null $client
53
     * @param int $maxChunkSize Set max chunk size per request (determines when to switch from "one shot upload" to upload session and defines chunk size for uploads via session).
54
     * @param int $maxUploadChunkRetries How many times to retry an upload session start or append after RequestException.
55
     */
56
    public function __construct(string $accessToken, GuzzleClient $client = null, int $maxChunkSize = self::MAX_CHUNK_SIZE, int $maxUploadChunkRetries = 0)
57
    {
58
        $this->accessToken = $accessToken;
59
60
        $this->client = $client ?? new GuzzleClient(['handler' => self::createHandler()]);
61
62
        $this->maxChunkSize = ($maxChunkSize < self::MAX_CHUNK_SIZE ? ($maxChunkSize > 1 ? $maxChunkSize : 1) : self::MAX_CHUNK_SIZE);
63
        $this->maxUploadChunkRetries = $maxUploadChunkRetries;
64
    }
65
66
    /**
67
     * Create a new guzzle handler stack.
68
     *
69
     * @return \GuzzleHttp\HandlerStack
70
     */
71
    protected static function createHandler()
72
    {
73
        $stack = HandlerStack::create();
74
75
        $stack->push(Middleware::retry(function ($retries, $request, $response = null, $exception = null) {
76
            return $retries < 3 && ($exception instanceof ConnectException || ($response && ($response->getStatusCode() >= 500 || in_array($response->getStatusCode(), self::RETRY_CODES, true))));
77
        }, function ($retries) {
78
            return (int) pow(2, $retries) * self::RETRY_BACKOFF;
79
        }));
80
81
        return $stack;
82
    }
83
84
    /**
85
     * Copy a file or folder to a different location in the user's Dropbox.
86
     *
87
     * If the source path is a folder all its contents will be copied.
88
     *
89
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-copy_v2
90
     */
91
    public function copy(string $fromPath, string $toPath): array
92
    {
93
        $parameters = [
94
            'from_path' => $this->normalizePath($fromPath),
95
            'to_path' => $this->normalizePath($toPath),
96
        ];
97
98
        return $this->rpcEndpointRequest('files/copy_v2', $parameters);
99
    }
100
101
    /**
102
     * Create a folder at a given path.
103
     *
104
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-create_folder
105
     */
106
    public function createFolder(string $path): array
107
    {
108
        $parameters = [
109
            'path' => $this->normalizePath($path),
110
        ];
111
112
        $object = $this->rpcEndpointRequest('files/create_folder', $parameters);
113
114
        $object['.tag'] = 'folder';
115
116
        return $object;
117
    }
118
119
    /**
120
     * Create a shared link with custom settings.
121
     *
122
     * If no settings are given then the default visibility is RequestedVisibility.public.
123
     * The resolved visibility, though, may depend on other aspects such as team and
124
     * shared folder settings). Only for paid users.
125
     *
126
     * @link https://www.dropbox.com/developers/documentation/http/documentation#sharing-create_shared_link_with_settings
127
     */
128
    public function createSharedLinkWithSettings(string $path, array $settings = [])
129
    {
130
        $parameters = [
131
            'path' => $this->normalizePath($path),
132
        ];
133
134
        if (count($settings)) {
135
            $parameters = array_merge(compact('settings'), $parameters);
136
        }
137
138
        return $this->rpcEndpointRequest('sharing/create_shared_link_with_settings', $parameters);
139
    }
140
141
    /**
142
     * List shared links.
143
     *
144
     * For empty path returns a list of all shared links. For non-empty path
145
     * returns a list of all shared links with access to the given path.
146
     *
147
     * If direct_only is set true, only direct links to the path will be returned, otherwise
148
     * it may return link to the path itself and parent folders as described on docs.
149
     *
150
     * @link https://www.dropbox.com/developers/documentation/http/documentation#sharing-list_shared_links
151
     */
152
    public function listSharedLinks(string $path = null, bool $direct_only = false, string $cursor = null): array
153
    {
154
        $parameters = [
155
            'path' => $path ? $this->normalizePath($path) : null,
156
            'cursor' => $cursor,
157
            'direct_only' => $direct_only,
158
        ];
159
160
        $body = $this->rpcEndpointRequest('sharing/list_shared_links', $parameters);
161
162
        return $body['links'];
163
    }
164
165
    /**
166
     * Delete the file or folder at a given path.
167
     *
168
     * If the path is a folder, all its contents will be deleted too.
169
     * A successful response indicates that the file or folder was deleted.
170
     *
171
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-delete
172
     */
173
    public function delete(string $path): array
174
    {
175
        $parameters = [
176
            'path' => $this->normalizePath($path),
177
        ];
178
179
        return $this->rpcEndpointRequest('files/delete', $parameters);
180
    }
181
182
    /**
183
     * Download a file from a user's Dropbox.
184
     *
185
     * @param string $path
186
     *
187
     * @return resource
188
     *
189
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-download
190
     */
191
    public function download(string $path)
192
    {
193
        $arguments = [
194
            'path' => $this->normalizePath($path),
195
        ];
196
197
        $response = $this->contentEndpointRequest('files/download', $arguments);
198
199
        return StreamWrapper::getResource($response->getBody());
200
    }
201
202
    /**
203
     * Returns the metadata for a file or folder.
204
     *
205
     * Note: Metadata for the root folder is unsupported.
206
     *
207
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_metadata
208
     */
209
    public function getMetadata(string $path): array
210
    {
211
        $parameters = [
212
            'path' => $this->normalizePath($path),
213
        ];
214
215
        return $this->rpcEndpointRequest('files/get_metadata', $parameters);
216
    }
217
218
    /**
219
     * Get a temporary link to stream content of a file.
220
     *
221
     * This link will expire in four hours and afterwards you will get 410 Gone.
222
     * Content-Type of the link is determined automatically by the file's mime type.
223
     *
224
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_temporary_link
225
     */
226
    public function getTemporaryLink(string $path): string
227
    {
228
        $parameters = [
229
            'path' => $this->normalizePath($path),
230
        ];
231
232
        $body = $this->rpcEndpointRequest('files/get_temporary_link', $parameters);
233
234
        return $body['link'];
235
    }
236
237
    /**
238
     * Get a thumbnail for an image.
239
     *
240
     * This method currently supports files with the following file extensions:
241
     * jpg, jpeg, png, tiff, tif, gif and bmp.
242
     *
243
     * Photos that are larger than 20MB in size won't be converted to a thumbnail.
244
     *
245
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_thumbnail
246
     */
247
    public function getThumbnail(string $path, string $format = 'jpeg', string $size = 'w64h64'): string
248
    {
249
        $arguments = [
250
            'path' => $this->normalizePath($path),
251
            'format' => $format,
252
            'size' => $size,
253
        ];
254
255
        $response = $this->contentEndpointRequest('files/get_thumbnail', $arguments);
256
257
        return (string) $response->getBody();
258
    }
259
260
    /**
261
     * Starts returning the contents of a folder.
262
     *
263
     * If the result's ListFolderResult.has_more field is true, call
264
     * list_folder/continue with the returned ListFolderResult.cursor to retrieve more entries.
265
     *
266
     * Note: auth.RateLimitError may be returned if multiple list_folder or list_folder/continue calls
267
     * with same parameters are made simultaneously by same API app for same user. If your app implements
268
     * retry logic, please hold off the retry until the previous request finishes.
269
     *
270
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder
271
     */
272
    public function listFolder(string $path = '', bool $recursive = false): array
273
    {
274
        $parameters = [
275
            'path' => $this->normalizePath($path),
276
            'recursive' => $recursive,
277
        ];
278
279
        return $this->rpcEndpointRequest('files/list_folder', $parameters);
280
    }
281
282
    /**
283
     * Once a cursor has been retrieved from list_folder, use this to paginate through all files and
284
     * retrieve updates to the folder, following the same rules as documented for list_folder.
285
     *
286
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-continue
287
     */
288
    public function listFolderContinue(string $cursor = ''): array
289
    {
290
        return $this->rpcEndpointRequest('files/list_folder/continue', compact('cursor'));
291
    }
292
293
    /**
294
     * Move a file or folder to a different location in the user's Dropbox.
295
     *
296
     * If the source path is a folder all its contents will be moved.
297
     *
298
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-move_v2
299
     */
300
    public function move(string $fromPath, string $toPath): array
301
    {
302
        $parameters = [
303
            'from_path' => $this->normalizePath($fromPath),
304
            'to_path' => $this->normalizePath($toPath),
305
        ];
306
307
        return $this->rpcEndpointRequest('files/move_v2', $parameters);
308
    }
309
310
    /**
311
     * The file should be uploaded in chunks if it size exceeds the 150 MB threshold
312
     * or if the resource size could not be determined (eg. a popen() stream).
313
     *
314
     * @param string|resource $contents
315
     *
316
     * @return bool
317
     */
318
    protected function shouldUploadChunked($contents): bool
319
    {
320
        $size = is_string($contents) ? strlen($contents) : fstat($contents)['size'];
321
322
        if ($this->isPipe($contents)) {
323
            return true;
324
        }
325
326
        if ($size === null) {
327
            return true;
328
        }
329
330
        return $size > $this->maxChunkSize;
331
    }
332
333
    /**
334
     * Check if the contents is a pipe stream (not seekable, no size defined).
335
     *
336
     * @param string|resource $contents
337
     *
338
     * @return bool
339
     */
340
    protected function isPipe($contents): bool
341
    {
342
        return is_resource($contents) ? (fstat($contents)['mode'] & 010000) != 0 : false;
343
    }
344
345
    /**
346
     * Create a new file with the contents provided in the request.
347
     *
348
     * Do not use this to upload a file larger than 150 MB. Instead, create an upload session with upload_session/start.
349
     *
350
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload
351
     *
352
     * @param string $path
353
     * @param string|resource $contents
354
     * @param string $mode
355
     *
356
     * @return array
357
     */
358
    public function upload(string $path, $contents, $mode = 'add'): array
359
    {
360
        if ($this->shouldUploadChunked($contents)) {
361
            return $this->uploadChunked($path, $contents, $mode);
362
        }
363
364
        $arguments = [
365
            'path' => $this->normalizePath($path),
366
            'mode' => $mode,
367
        ];
368
369
        $response = $this->contentEndpointRequest('files/upload', $arguments, $contents);
370
371
        $metadata = json_decode($response->getBody(), true);
372
373
        $metadata['.tag'] = 'file';
374
375
        return $metadata;
376
    }
377
378
    /**
379
     * Upload file split in chunks. This allows uploading large files, since
380
     * Dropbox API v2 limits the content size to 150MB.
381
     *
382
     * The chunk size will affect directly the memory usage, so be careful.
383
     * Large chunks tends to speed up the upload, while smaller optimizes memory usage.
384
     *
385
     * @param string $path
386
     * @param string|resource $contents
387
     * @param string $mode
388
     * @param int $chunkSize
389
     *
390
     * @return array
391
     */
392
    public function uploadChunked(string $path, $contents, $mode = 'add', $chunkSize = null): array
393
    {
394
        if ($chunkSize === null || $chunkSize > $this->maxChunkSize) {
395
            $chunkSize = $this->maxChunkSize;
396
        }
397
398
        $stream = $this->getStream($contents);
399
400
        $cursor = $this->uploadChunk(self::UPLOAD_SESSION_START, $stream, $chunkSize, null);
0 ignored issues
show
Bug introduced by
It seems like $stream defined by $this->getStream($contents) on line 398 can also be of type object<GuzzleHttp\Psr7\PumpStream>; however, Spatie\Dropbox\Client::uploadChunk() does only seem to accept object<GuzzleHttp\Psr7\Stream>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
401
402
        while (! $stream->eof()) {
403
            $cursor = $this->uploadChunk(self::UPLOAD_SESSION_APPEND, $stream, $chunkSize, $cursor);
404
        }
405
406
        return $this->uploadSessionFinish('', $cursor, $path, $mode);
407
    }
408
409
    /**
410
     * @param int $type
411
     * @param Psr7\Stream $stream
412
     * @param int $chunkSize
413
     * @param \Spatie\Dropbox\UploadSessionCursor|null $cursor
414
     * @return \Spatie\Dropbox\UploadSessionCursor
415
     * @throws Exception
416
     */
417
    protected function uploadChunk($type, &$stream, $chunkSize, $cursor = null): UploadSessionCursor
418
    {
419
        $maximumTries = $stream->isSeekable() ? $this->maxUploadChunkRetries : 0;
420
        $pos = $stream->tell();
421
422
        $tries = 0;
423
424
        tryUpload:
425
        try {
426
            $tries++;
427
428
            $chunkStream = new Psr7\LimitStream($stream, $chunkSize, $stream->tell());
429
430
            if ($type === self::UPLOAD_SESSION_START) {
431
                return $this->uploadSessionStart($chunkStream);
432
            }
433
434
            if ($type === self::UPLOAD_SESSION_APPEND && $cursor !== null) {
435
                return $this->uploadSessionAppend($chunkStream, $cursor);
436
            }
437
438
            throw new Exception('Invalid type');
439
        } catch (RequestException $exception) {
440
            if ($tries < $maximumTries) {
441
                // rewind
442
                $stream->seek($pos, SEEK_SET);
443
                goto tryUpload;
444
            }
445
            throw $exception;
446
        }
447
    }
448
449
    /**
450
     * Upload sessions allow you to upload a single file in one or more requests,
451
     * for example where the size of the file is greater than 150 MB.
452
     * This call starts a new upload session with the given data.
453
     *
454
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-start
455
     *
456
     * @param string|StreamInterface $contents
457
     * @param bool $close
458
     *
459
     * @return UploadSessionCursor
460
     */
461
    public function uploadSessionStart($contents, bool $close = false): UploadSessionCursor
462
    {
463
        $arguments = compact('close');
464
465
        $response = json_decode(
466
            $this->contentEndpointRequest('files/upload_session/start', $arguments, $contents)->getBody(),
467
            true
468
        );
469
470
        return new UploadSessionCursor($response['session_id'], ($contents instanceof StreamInterface ? $contents->tell() : strlen($contents)));
471
    }
472
473
    /**
474
     * Append more data to an upload session.
475
     * When the parameter close is set, this call will close the session.
476
     * A single request should not upload more than 150 MB.
477
     *
478
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-append_v2
479
     *
480
     * @param string|StreamInterface $contents
481
     * @param UploadSessionCursor $cursor
482
     * @param bool $close
483
     *
484
     * @return \Spatie\Dropbox\UploadSessionCursor
485
     */
486
    public function uploadSessionAppend($contents, UploadSessionCursor $cursor, bool $close = false): UploadSessionCursor
487
    {
488
        $arguments = compact('cursor', 'close');
489
490
        $pos = $contents instanceof StreamInterface ? $contents->tell() : 0;
491
        $this->contentEndpointRequest('files/upload_session/append_v2', $arguments, $contents);
492
493
        $cursor->offset += $contents instanceof StreamInterface ? ($contents->tell() - $pos) : strlen($contents);
494
495
        return $cursor;
496
    }
497
498
    /**
499
     * Finish an upload session and save the uploaded data to the given file path.
500
     * A single request should not upload more than 150 MB.
501
     *
502
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-finish
503
     *
504
     * @param string|StreamInterface $contents
505
     * @param \Spatie\Dropbox\UploadSessionCursor $cursor
506
     * @param string $path
507
     * @param string|array $mode
508
     * @param bool $autorename
509
     * @param bool $mute
510
     *
511
     * @return array
512
     */
513
    public function uploadSessionFinish($contents, UploadSessionCursor $cursor, string $path, $mode = 'add', $autorename = false, $mute = false): array
514
    {
515
        $arguments = compact('cursor');
516
        $arguments['commit'] = compact('path', 'mode', 'autorename', 'mute');
517
518
        $response = $this->contentEndpointRequest(
519
            'files/upload_session/finish',
520
            $arguments,
521
            ($contents == '') ? null : $contents
522
        );
523
524
        $metadata = json_decode($response->getBody(), true);
525
526
        $metadata['.tag'] = 'file';
527
528
        return $metadata;
529
    }
530
531
    /**
532
     * Get Account Info for current authenticated user.
533
     *
534
     * @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account
535
     *
536
     * @return array
537
     */
538
    public function getAccountInfo(): array
539
    {
540
        return $this->rpcEndpointRequest('users/get_current_account');
541
    }
542
543
    /**
544
     * Revoke current access token.
545
     *
546
     * @link https://www.dropbox.com/developers/documentation/http/documentation#auth-token-revoke
547
     */
548
    public function revokeToken()
549
    {
550
        $this->rpcEndpointRequest('auth/token/revoke');
551
    }
552
553
    protected function normalizePath(string $path): string
554
    {
555
        if (preg_match("/^id:.*|^rev:.*|^(ns:[0-9]+(\/.*)?)/", $path) === 1) {
556
            return $path;
557
        }
558
559
        $path = trim($path, '/');
560
561
        return ($path === '') ? '' : '/'.$path;
562
    }
563
564
    protected function getEndpointUrl(string $subdomain, string $endpoint): string
565
    {
566
        if (count($parts = explode('::', $endpoint)) === 2) {
567
            [$subdomain, $endpoint] = $parts;
568
        }
569
570
        return "https://{$subdomain}.dropboxapi.com/2/{$endpoint}";
571
    }
572
573
    /**
574
     * @param string $endpoint
575
     * @param array $arguments
576
     * @param string|resource|StreamInterface $body
577
     *
578
     * @return \Psr\Http\Message\ResponseInterface
579
     *
580
     * @throws \Exception
581
     */
582
    public function contentEndpointRequest(string $endpoint, array $arguments, $body = ''): ResponseInterface
583
    {
584
        $headers = ['Dropbox-API-Arg' => json_encode($arguments)];
585
586
        if ($body !== '') {
587
            $headers['Content-Type'] = 'application/octet-stream';
588
        }
589
590
        try {
591
            $response = $this->client->post($this->getEndpointUrl('content', $endpoint), [
592
                'headers' => $this->getHeaders($headers),
593
                'body' => $body,
594
            ]);
595
        } catch (ClientException $exception) {
596
            throw $this->determineException($exception);
597
        }
598
599
        return $response;
600
    }
601
602
    public function rpcEndpointRequest(string $endpoint, array $parameters = null): array
603
    {
604
        try {
605
            $options = ['headers' => $this->getHeaders()];
606
607
            if ($parameters) {
608
                $options['json'] = $parameters;
609
            }
610
611
            $response = $this->client->post($this->getEndpointUrl('api', $endpoint), $options);
612
        } catch (ClientException $exception) {
613
            throw $this->determineException($exception);
614
        }
615
616
        $response = json_decode($response->getBody(), true);
617
618
        return $response ?? [];
619
    }
620
621
    protected function determineException(ClientException $exception): Exception
622
    {
623
        if (in_array($exception->getResponse()->getStatusCode(), [400, 409])) {
624
            return new BadRequest($exception->getResponse());
0 ignored issues
show
Bug introduced by
It seems like $exception->getResponse() can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
625
        }
626
627
        return $exception;
628
    }
629
630
    /**
631
     * @param $contents
632
     *
633
     * @return \GuzzleHttp\Psr7\PumpStream|\GuzzleHttp\Psr7\Stream
634
     */
635
    protected function getStream($contents)
636
    {
637
        if ($this->isPipe($contents)) {
638
            /* @var resource $contents */
639
            return new PumpStream(function ($length) use ($contents) {
640
                $data = fread($contents, $length);
641
                if (strlen($data) === 0) {
642
                    return false;
643
                }
644
645
                return $data;
646
            });
647
        }
648
649
        return Psr7\stream_for($contents);
650
    }
651
652
    /**
653
     * Get the access token.
654
     */
655
    public function getAccessToken(): string
656
    {
657
        return $this->accessToken;
658
    }
659
660
    /**
661
     * Set the access token.
662
     */
663
    public function setAccessToken(string $accessToken): self
664
    {
665
        $this->accessToken = $accessToken;
666
667
        return $this;
668
    }
669
670
    /**
671
     * Get the HTTP headers.
672
     */
673
    protected function getHeaders(array $headers = []): array
674
    {
675
        return array_merge([
676
            'Authorization' => "Bearer {$this->accessToken}",
677
        ], $headers);
678
    }
679
}
680