Completed
Push — master ( 2d1f23...ea4020 )
by Raza
01:51
created

DropboxClient::performContentApiRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 1
1
<?php
2
3
namespace Srmklive\Dropbox\Client;
4
5
use GuzzleHttp\Client as HttpClient;
6
use GuzzleHttp\Exception\ClientException as HttpClientException;
7
use GuzzleHttp\Psr7\StreamWrapper;
8
use Srmklive\Dropbox\Exceptions\BadRequest;
9
use Srmklive\Dropbox\UploadContent;
10
11
class DropboxClient
12
{
13
    use UploadContent;
14
15
    const THUMBNAIL_FORMAT_JPEG = 'jpeg';
16
    const THUMBNAIL_FORMAT_PNG = 'png';
17
18
    const THUMBNAIL_SIZE_XS = 'w32h32';
19
    const THUMBNAIL_SIZE_S = 'w64h64';
20
    const THUMBNAIL_SIZE_M = 'w128h128';
21
    const THUMBNAIL_SIZE_L = 'w640h480';
22
    const THUMBNAIL_SIZE_XL = 'w1024h768';
23
24
    const MAX_CHUNK_SIZE = 157286400;
25
26
    /** @var \GuzzleHttp\Client */
27
    protected $client;
28
29
    /**
30
     * Dropbox OAuth access token.
31
     *
32
     * @var string
33
     */
34
    protected $accessToken;
35
36
    /**
37
     * Dropbox API v2 Url.
38
     *
39
     * @var string
40
     */
41
    protected $apiUrl;
42
43
    /**
44
     * Dropbox content API v2 url for uploading content.
45
     *
46
     * @var string
47
     */
48
    protected $apiContentUrl;
49
50
    /**
51
     * Dropbox API v2 endpoint.
52
     *
53
     * @var string
54
     */
55
    protected $apiEndpoint;
56
57
    /**
58
     * @var mixed
59
     */
60
    protected $content;
61
62
    /**
63
     * Dropbox API request data.
64
     *
65
     * @var array
66
     */
67
    protected $request;
68
69
    /**
70
     * @var int
71
     */
72
    protected $maxChunkSize;
73
74
    /**
75
     * DropboxClient constructor.
76
     *
77
     * @param string             $token
78
     * @param \GuzzleHttp\Client $client
79
     * @param int                $maxChunkSize
80
     */
81 24
    public function __construct($token, HttpClient $client = null, $maxChunkSize = self::MAX_CHUNK_SIZE)
82
    {
83 24
        $this->setAccessToken($token);
84
85 24
        $this->setClient($client);
86
87 24
        $this->apiUrl = 'https://api.dropboxapi.com/2/';
88 24
        $this->apiContentUrl = 'https://content.dropboxapi.com/2/';
89 24
        $this->maxChunkSize = ($maxChunkSize < self::MAX_CHUNK_SIZE ?
90 24
            ($maxChunkSize > 1 ? $maxChunkSize : 1) : self::MAX_CHUNK_SIZE);
91 24
    }
92
93
    /**
94
     * Set Http Client.
95
     *
96
     * @param \GuzzleHttp\Client $client
97
     */
98 24
    protected function setClient(HttpClient $client = null)
99
    {
100 24
        if ($client instanceof HttpClient) {
101 19
            $this->client = $client;
102 19
        } else {
103 5
            $this->client = new HttpClient([
104
                'headers' => [
105 5
                    'Authorization' => "Bearer {$this->accessToken}",
106 5
                ],
107 5
            ]);
108
        }
109 24
    }
110
111
    /**
112
     * Set Dropbox OAuth access token.
113
     *
114
     * @param string $token
115
     */
116 24
    protected function setAccessToken($token)
117
    {
118 24
        $this->accessToken = $token;
119 24
    }
120
121
    /**
122
     * Copy a file or folder to a different location in the user's Dropbox.
123
     *
124
     * If the source path is a folder all its contents will be copied.
125
     *
126
     * @param string $fromPath
127
     * @param string $toPath
128
     *
129
     * @throws \Exception
130
     *
131
     * @return \Psr\Http\Message\ResponseInterface
132
     *
133
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-copy
134
     */
135 1 View Code Duplication
    public function copy($fromPath, $toPath)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
136
    {
137 1
        $this->setupRequest([
138 1
            'from_path' => $this->normalizePath($fromPath),
139 1
            'to_path'   => $this->normalizePath($toPath),
140 1
        ]);
141
142 1
        $this->apiEndpoint = 'files/copy';
143
144 1
        return $this->doDropboxApiRequest();
145
    }
146
147
    /**
148
     * Create a folder at a given path.
149
     *
150
     * @param string $path
151
     *
152
     * @throws \Exception
153
     *
154
     * @return \Psr\Http\Message\ResponseInterface
155
     *
156
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-create_folder
157
     */
158 1
    public function createFolder($path)
159
    {
160 1
        $this->setupRequest([
161 1
            'path' => $this->normalizePath($path),
162 1
        ]);
163
164 1
        $this->apiEndpoint = 'files/create_folder';
165
166 1
        $response = $this->doDropboxApiRequest();
167 1
        $response['.tag'] = 'folder';
168
169 1
        return $response;
170
    }
171
172
    /**
173
     * Delete the file or folder at a given path.
174
     *
175
     * If the path is a folder, all its contents will be deleted too.
176
     * A successful response indicates that the file or folder was deleted.
177
     *
178
     * @param string $path
179
     *
180
     * @throws \Exception
181
     *
182
     * @return \Psr\Http\Message\ResponseInterface
183
     *
184
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-delete
185
     */
186 1
    public function delete($path)
187
    {
188 1
        $this->setupRequest([
189 1
            'path' => $this->normalizePath($path),
190 1
        ]);
191
192 1
        $this->apiEndpoint = 'files/delete';
193
194 1
        return $this->doDropboxApiRequest();
195
    }
196
197
    /**
198
     * Download a file from a user's Dropbox.
199
     *
200
     * @param string $path
201
     *
202
     * @throws \Exception
203
     *
204
     * @return resource
205
     *
206
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-download
207
     */
208 1
    public function download($path)
209
    {
210 1
        $this->setupRequest([
211 1
            'path' => $this->normalizePath($path),
212 1
        ]);
213
214 1
        $this->apiEndpoint = 'files/download';
215
216 1
        $this->content = null;
217
218 1
        $response = $this->doDropboxApiContentRequest();
219
220 1
        return StreamWrapper::getResource($response->getBody());
221
    }
222
223
    /**
224
     * Returns the metadata for a file or folder.
225
     *
226
     * Note: Metadata for the root folder is unsupported.
227
     *
228
     * @param string $path
229
     *
230
     * @throws \Exception
231
     *
232
     * @return \Psr\Http\Message\ResponseInterface
233
     *
234
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_metadata
235
     */
236 1
    public function getMetaData($path)
237
    {
238 1
        $this->setupRequest([
239 1
            'path' => $this->normalizePath($path),
240 1
        ]);
241
242 1
        $this->apiEndpoint = 'files/get_metadata';
243
244 1
        return $this->doDropboxApiRequest();
245
    }
246
247
    /**
248
     * Get a temporary link to stream content of a file.
249
     *
250
     * This link will expire in four hours and afterwards you will get 410 Gone.
251
     * Content-Type of the link is determined automatically by the file's mime type.
252
     *
253
     * @param string $path
254
     *
255
     * @throws \Exception
256
     *
257
     * @return string
258
     *
259
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_temporary_link
260
     */
261 1 View Code Duplication
    public function getTemporaryLink($path)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
262
    {
263 1
        $this->setupRequest([
264 1
            'path' => $this->normalizePath($path),
265 1
        ]);
266
267 1
        $this->apiEndpoint = 'files/get_temporary_link';
268
269 1
        $response = $this->doDropboxApiRequest();
270
271 1
        return $response['link'];
272
    }
273
274
    /**
275
     * Get a thumbnail for an image.
276
     *
277
     * This method currently supports files with the following file extensions:
278
     * jpg, jpeg, png, tiff, tif, gif and bmp.
279
     *
280
     * Photos that are larger than 20MB in size won't be converted to a thumbnail.
281
     *
282
     * @param string $path
283
     * @param string $format
284
     * @param string $size
285
     *
286
     * @throws \Exception
287
     *
288
     * @return string
289
     */
290 1
    public function getThumbnail($path, $format = 'jpeg', $size = 'w64h64')
291
    {
292 1
        $this->setupRequest([
293 1
            'path'   => $this->normalizePath($path),
294 1
            'format' => $format,
295 1
            'size'   => $size,
296 1
        ]);
297
298 1
        $this->apiEndpoint = 'files/get_thumbnail';
299
300 1
        $this->content = null;
301
302 1
        $response = $this->doDropboxApiContentRequest();
303
304 1
        return (string) $response->getBody();
305
    }
306
307
    /**
308
     * Starts returning the contents of a folder.
309
     *
310
     * If the result's ListFolderResult.has_more field is true, call
311
     * list_folder/continue with the returned ListFolderResult.cursor to retrieve more entries.
312
     *
313
     * Note: auth.RateLimitError may be returned if multiple list_folder or list_folder/continue calls
314
     * with same parameters are made simultaneously by same API app for same user. If your app implements
315
     * retry logic, please hold off the retry until the previous request finishes.
316
     *
317
     * @param string $path
318
     * @param bool   $recursive
319
     *
320
     * @throws \Exception
321
     *
322
     * @return \Psr\Http\Message\ResponseInterface
323
     *
324
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder
325
     */
326 1 View Code Duplication
    public function listFolder($path = '', $recursive = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
327
    {
328 1
        $this->setupRequest([
329 1
            'path'      => $this->normalizePath($path),
330 1
            'recursive' => $recursive,
331 1
        ]);
332
333 1
        $this->apiEndpoint = 'files/list_folder';
334
335 1
        return $this->doDropboxApiRequest();
336
    }
337
338
    /**
339
     * Once a cursor has been retrieved from list_folder, use this to paginate through all files and
340
     * retrieve updates to the folder, following the same rules as documented for list_folder.
341
     *
342
     * @param string $cursor
343
     *
344
     * @throws \Exception
345
     *
346
     * @return \Psr\Http\Message\ResponseInterface
347
     *
348
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-continue
349
     */
350 1
    public function listFolderContinue($cursor = '')
351
    {
352 1
        $this->setupRequest([
353 1
            'cursor' => $cursor,
354 1
        ]);
355
356 1
        $this->apiEndpoint = 'files/list_folder/continue';
357
358 1
        return $this->doDropboxApiRequest();
359
    }
360
361
    /**
362
     * Move a file or folder to a different location in the user's Dropbox.
363
     *
364
     * If the source path is a folder all its contents will be moved.
365
     *
366
     * @param string $fromPath
367
     * @param string $toPath
368
     *
369
     * @throws \Exception
370
     *
371
     * @return \Psr\Http\Message\ResponseInterface
372
     *
373
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-move
374
     */
375 1 View Code Duplication
    public function move($fromPath, $toPath)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
376
    {
377 1
        $this->setupRequest([
378 1
            'from_path' => $this->normalizePath($fromPath),
379 1
            'to_path'   => $this->normalizePath($toPath),
380 1
        ]);
381
382 1
        $this->apiEndpoint = 'files/move_v2';
383
384 1
        return $this->doDropboxApiRequest();
385
    }
386
387
    /**
388
     * Create a new file with the contents provided in the request.
389
     *
390
     * Do not use this to upload a file larger than 150 MB. Instead, create an upload session with upload_session/start.
391
     *
392
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload
393
     *
394
     * @param string          $path
395
     * @param string|resource $contents
396
     * @param string|array    $mode
397
     *
398
     * @throws \Exception
399
     *
400
     * @return array
401
     */
402 1
    public function upload($path, $contents, $mode = 'add')
403
    {
404 1
        if ($this->shouldUploadChunk($contents)) {
405
            return $this->uploadChunk($path, $contents, $mode);
0 ignored issues
show
Bug introduced by
It seems like $mode defined by parameter $mode on line 402 can also be of type array; however, Srmklive\Dropbox\UploadContent::uploadChunk() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
406
        }
407
408 1
        $this->setupRequest([
409 1
            'path' => $this->normalizePath($path),
410 1
            'mode' => $mode,
411 1
        ]);
412
413 1
        $this->content = $contents;
414
415 1
        $this->apiEndpoint = 'files/upload';
416
417 1
        $response = $this->doDropboxApiContentRequest();
418
419 1
        $metadata = json_decode($response->getBody(), true);
420 1
        $metadata['.tag'] = 'file';
421
422 1
        return $metadata;
423
    }
424
425
    /**
426
     * Get Account Info for current authenticated user.
427
     *
428
     * @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account
429
     *
430
     * @throws \Exception
431
     *
432
     * @return \Psr\Http\Message\ResponseInterface
433
     */
434 1
    public function getAccountInfo()
435
    {
436 1
        $this->apiEndpoint = 'users/get_current_account';
437
438 1
        return $this->doDropboxApiRequest();
439
    }
440
441
    /**
442
     * Revoke current access token.
443
     *
444
     * @link https://www.dropbox.com/developers/documentation/http/documentation#auth-token-revoke
445
     *
446
     * @throws \Exception
447
     *
448
     * @return \Psr\Http\Message\ResponseInterface
449
     */
450 1
    public function revokeToken()
451
    {
452 1
        $this->apiEndpoint = 'auth/token/revoke';
453
454 1
        return $this->doDropboxApiRequest();
455
    }
456
457
    /**
458
     * Set Dropbox API request data.
459
     *
460
     * @param array $request
461
     */
462 17
    protected function setupRequest($request)
463
    {
464 17
        $this->request = $request;
465 17
    }
466
467
    /**
468
     * Perform Dropbox API v2 request.
469
     *
470
     * @param $endpoint
471
     * @param $payload
472
     *
473
     * @throws \Exception
474
     *
475
     * @return \Psr\Http\Message\ResponseInterface
476
     */
477 2
    public function performApiRequest($endpoint, $payload)
478
    {
479 2
        $this->setupRequest($payload);
480 2
        $this->apiEndpoint = $endpoint;
481
482 2
        return $this->doDropboxApiRequest();
483
    }
484
485
    /**
486
     * Perform Dropbox API v2 content request.
487
     *
488
     * @param $endpoint
489
     * @param $payload
490
     *
491
     * @throws \Exception
492
     *
493
     * @return \Psr\Http\Message\ResponseInterface
494
     */
495 1
    public function performContentApiRequest($endpoint, $payload)
496
    {
497 1
        $this->setupRequest($payload);
498 1
        $this->apiEndpoint = $endpoint;
499
500 1
        return $this->doDropboxApiContentRequest();
501
    }
502
503
    /**
504
     * Perform Dropbox API request.
505
     *
506
     * @throws \Exception
507
     *
508
     * @return \Psr\Http\Message\ResponseInterface
509
     */
510 12
    protected function doDropboxApiRequest()
511
    {
512 12
        $request = empty($this->request) ? [] : ['json' => $this->request];
513
514
        try {
515 12
            $response = $this->client->post("{$this->apiUrl}{$this->apiEndpoint}", $request);
516 12
        } catch (HttpClientException $exception) {
517 2
            throw $this->determineException($exception);
518
        }
519
520 10
        return json_decode($response->getBody(), true);
521
    }
522
523
    /**
524
     * Setup headers for Dropbox API request.
525
     *
526
     * @return array
527
     */
528 7
    protected function setupDropboxHeaders()
529
    {
530
        $headers = [
531 7
            'Dropbox-API-Arg' => json_encode(
532 7
                $this->request
533 7
            ),
534 7
        ];
535 7
        if (!empty($this->content) ||
536 7
               $this->apiEndpoint == 'files/upload_session/finish') {
537
            // The upload_session/finish API requires a Content-Type, always
538 4
            $headers['Content-Type'] = 'application/octet-stream';
539 4
        }
540
541 7
        return $headers;
542
    }
543
544
    /**
545
     * Perform Dropbox API request.
546
     *
547
     * @throws \Exception
548
     *
549
     * @return \Psr\Http\Message\ResponseInterface
550
     */
551 7
    protected function doDropboxApiContentRequest()
552
    {
553
        try {
554 7
            $response = $this->client->post("{$this->apiContentUrl}{$this->apiEndpoint}", [
555 7
                'headers' => $this->setupDropboxHeaders(),
556 7
                'body'    => !empty($this->content) ? $this->content : '',
557 7
            ]);
558 7
        } catch (HttpClientException $exception) {
559 1
            throw $this->determineException($exception);
560
        }
561
562 6
        return $response;
563
    }
564
565
    /**
566
     * Normalize path.
567
     *
568
     * @param string $path
569
     *
570
     * @return string
571
     */
572 11
    protected function normalizePath($path)
573
    {
574 11
        if (preg_match("/^id:.*|^rev:.*|^(ns:[0-9]+(\/.*)?)/", $path) === 1) {
575 1
            return $path;
576
        }
577
578 11
        $path = (trim($path, '/') === '') ? '' : '/'.$path;
579
580 11
        return str_replace('//', '/', $path);
581
    }
582
583
    /**
584
     * Catch Dropbox API request exception.
585
     *
586
     * @param HttpClientException $exception
587
     *
588
     * @return \Exception
589
     */
590 3
    protected function determineException(HttpClientException $exception)
591
    {
592 3
        if (!empty($exception->getResponse()) && in_array($exception->getResponse()->getStatusCode(), [400, 409])) {
593 2
            return new BadRequest($exception->getResponse());
594
        }
595
596 1
        return $exception;
597
    }
598
}
599