Completed
Pull Request — master (#97)
by
unknown
03:44
created

Dropbox   F

Complexity

Total Complexity 80

Size/Duplication

Total Lines 1155
Duplicated Lines 4.42 %

Coupling/Cohesion

Components 1
Dependencies 21

Test Coverage

Coverage 92.18%

Importance

Changes 0
Metric Value
wmc 80
lcom 1
cbo 21
dl 51
loc 1155
ccs 224
cts 243
cp 0.9218
rs 0.6314
c 0
b 0
f 0

44 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 27 1
A getAuthHelper() 0 8 1
A getOAuth2Client() 0 12 2
A getApp() 0 4 1
A getClient() 0 4 1
A getRandomStringGenerator() 0 4 1
A getPersistentDataStore() 0 4 1
A getMetadata() 0 16 3
A postToAPI() 0 4 1
A sendRequest() 0 15 3
A getAccessToken() 0 4 1
A setAccessToken() 0 6 1
A makeModelFromResponse() 0 12 2
A listFolder() 17 17 2
A listFolderContinue() 0 7 1
B listFolderLatestCursor() 0 26 4
B listRevisions() 0 24 3
A search() 18 18 2
A createFolder() 0 11 1
A delete() 0 8 1
A move() 8 8 1
A copy() 8 8 1
A restore() 0 11 1
A getCopyReference() 0 9 1
A saveCopyReference() 0 14 3
A getTemporaryLink() 0 8 1
A saveUrl() 0 13 2
A checkJobStatus() 0 17 2
A upload() 0 14 2
B makeDropboxFile() 0 23 5
B uploadChunked() 0 49 5
B startUploadSession() 0 24 3
A postToContent() 0 4 1
B appendUploadSession() 0 27 2
B finishUploadSession() 0 25 1
A simpleUpload() 0 16 1
A getThumbnail() 0 22 2
A getThumbnailSize() 0 12 2
B getMetadataFromResponseHeaders() 0 28 4
A download() 0 17 3
A getCurrentAccount() 0 9 1
A getAccount() 0 9 1
A getAccounts() 0 9 1
A getSpaceUsage() 0 9 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Dropbox often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Dropbox, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Kunnu\Dropbox;
4
5
use Kunnu\Dropbox\Models\File;
6
use Kunnu\Dropbox\Models\Account;
7
use Kunnu\Dropbox\Models\Thumbnail;
8
use Kunnu\Dropbox\Models\AccountList;
9
use Kunnu\Dropbox\Models\ModelFactory;
10
use Kunnu\Dropbox\Models\FileMetadata;
11
use Kunnu\Dropbox\Models\CopyReference;
12
use Kunnu\Dropbox\Models\FolderMetadata;
13
use Kunnu\Dropbox\Models\ModelCollection;
14
use Kunnu\Dropbox\Authentication\OAuth2Client;
15
use Kunnu\Dropbox\Store\PersistentDataStoreFactory;
16
use Kunnu\Dropbox\Authentication\DropboxAuthHelper;
17
use Kunnu\Dropbox\Exceptions\DropboxClientException;
18
use Kunnu\Dropbox\Security\RandomStringGeneratorFactory;
19
use Kunnu\Dropbox\Http\Clients\DropboxHttpClientFactory;
20
21
/**
22
 * Dropbox
23
 */
24
class Dropbox
25
{
26
    /**
27
     * Uploading a file with the 'uploadFile' method, with the file's
28
     * size less than this value (~8 MB), the simple `upload` method will be
29
     * used, if the file size exceed this value (~8 MB), the `startUploadSession`,
30
     * `appendUploadSession` & `finishUploadSession` methods will be used
31
     * to upload the file in chunks.
32
     *
33
     * @const int
34
     */
35
    const AUTO_CHUNKED_UPLOAD_THRESHOLD = 8000000;
36
37
    /**
38
     * The Chunk Size the file will be
39
     * split into and uploaded (~4 MB)
40
     *
41
     * @const int
42
     */
43
    const DEFAULT_CHUNK_SIZE = 4000000;
44
45
    /**
46
     * Response header containing file metadata
47
     *
48
     * @const string
49
     */
50
    const METADATA_HEADER = 'dropbox-api-result';
51
52
    /**
53
     * The Dropbox App
54
     *
55
     * @var \Kunnu\Dropbox\DropboxApp
56
     */
57
    protected $app;
58
59
    /**
60
     * OAuth2 Access Token
61
     *
62
     * @var string
63
     */
64
    protected $accessToken;
65
66
    /**
67
     * Dropbox Client
68
     *
69
     * @var \Kunnu\Dropbox\DropboxClient
70
     */
71
    protected $client;
72
73
    /**
74
     * OAuth2 Client
75
     *
76
     * @var \Kunnu\Dropbox\Authentication\OAuth2Client
77
     */
78
    protected $oAuth2Client;
79
80
    /**
81
     * Random String Generator
82
     *
83
     * @var \Kunnu\Dropbox\Security\RandomStringGeneratorInterface
84
     */
85
    protected $randomStringGenerator;
86
87
    /**
88
     * Persistent Data Store
89
     *
90
     * @var \Kunnu\Dropbox\Store\PersistentDataStoreInterface
91
     */
92
    protected $persistentDataStore;
93
94
    /**
95
     * Create a new Dropbox instance
96
     *
97
     * @param \Kunnu\Dropbox\DropboxApp
98
     * @param array $config Configuration Array
99
     */
100 58
    public function __construct(DropboxApp $app, array $config = [])
101
    {
102
        //Configuration
103 58
        $config = array_merge([
104 58
            'http_client_handler' => null,
105
            'random_string_generator' => null,
106
            'persistent_data_store' => null
107 58
        ], $config);
108
109
        //Set the app
110 58
        $this->app = $app;
111
112
        //Set the access token
113 58
        $this->setAccessToken($app->getAccessToken());
114
115
        //Make the HTTP Client
116 58
        $httpClient = DropboxHttpClientFactory::make($config['http_client_handler']);
117
118
        //Make and Set the DropboxClient
119 58
        $this->client = new DropboxClient($httpClient);
120
121
        //Make and Set the Random String Generator
122 58
        $this->randomStringGenerator = RandomStringGeneratorFactory::makeRandomStringGenerator($config['random_string_generator']);
123
124
        //Make and Set the Persistent Data Store
125 58
        $this->persistentDataStore = PersistentDataStoreFactory::makePersistentDataStore($config['persistent_data_store']);
126 58
    }
127
128
    /**
129
     * Get Dropbox Auth Helper
130
     *
131
     * @return \Kunnu\Dropbox\Authentication\DropboxAuthHelper
132
     */
133
    public function getAuthHelper()
134
    {
135
        return new DropboxAuthHelper(
136
            $this->getOAuth2Client(),
137
            $this->getRandomStringGenerator(),
138
            $this->getPersistentDataStore()
139
        );
140
    }
141
142
    /**
143
     * Get OAuth2Client
144
     *
145
     * @return \Kunnu\Dropbox\Authentication\OAuth2Client
146
     */
147
    public function getOAuth2Client()
148
    {
149
        if (!$this->oAuth2Client instanceof OAuth2Client) {
150
            return new OAuth2Client(
151
                $this->getApp(),
152
                $this->getClient(),
153
                $this->getRandomStringGenerator()
154
            );
155
        }
156
157
        return $this->oAuth2Client;
158
    }
159
160
    /**
161
     * Get the Dropbox App.
162
     *
163
     * @return \Kunnu\Dropbox\DropboxApp Dropbox App
164
     */
165
    public function getApp()
166
    {
167
        return $this->app;
168
    }
169
170
    /**
171
     * Get the Client
172
     *
173
     * @return \Kunnu\Dropbox\DropboxClient
174
     */
175 49
    public function getClient()
176
    {
177 49
        return $this->client;
178
    }
179
180
    /**
181
     * Get the Random String Generator
182
     *
183
     * @return \Kunnu\Dropbox\Security\RandomStringGeneratorInterface
184
     */
185
    public function getRandomStringGenerator()
186
    {
187
        return $this->randomStringGenerator;
188
    }
189
190
    /**
191
     * Get Persistent Data Store
192
     *
193
     * @return \Kunnu\Dropbox\Store\PersistentDataStoreInterface
194
     */
195
    public function getPersistentDataStore()
196
    {
197
        return $this->persistentDataStore;
198
    }
199
200
    /**
201
     * Get the Metadata for a file or folder
202
     *
203
     * @param  string $path   Path of the file or folder
204
     * @param  array  $params Additional Params
205
     *
206
     * @return \Kunnu\Dropbox\Models\FileMetadata | \Kunnu\Dropbox\Models\FolderMetadata | \Kunnu\Dropbox\Models\DeletedMetadata
207
     * @throws \Kunnu\Dropbox\Exceptions\DropboxClientException
208
     *
209
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_metadata
210
     *
211
     */
212 6
    public function getMetadata(string $path = '', array $params = [])
213
    {
214
        //Root folder is unsupported
215 6
        if ('/' === $path || '' === $path) {
216 2
            throw new DropboxClientException("Metadata for the root folder is unsupported.");
217
        }
218
219
        //Set the path
220 4
        $params['path'] = $path;
221
222
        //Get File Metadata
223 4
        $response = $this->postToAPI('/files/get_metadata', $params);
224
225
        //Make and Return the Model
226 3
        return $this->makeModelFromResponse($response);
227
    }
228
229
    /**
230
     * Make a HTTP POST Request to the API endpoint type
231
     *
232
     * @param  string $endpoint    API Endpoint to send Request to
233
     * @param  array  $params      Request Query Params
234
     * @param  string $accessToken Access Token to send with the Request
235
     *
236
     * @return \Kunnu\Dropbox\DropboxResponse
237
     */
238 39
    public function postToAPI($endpoint, array $params = [], $accessToken = null)
239
    {
240 39
        return $this->sendRequest("POST", $endpoint, 'api', $params, $accessToken);
241
    }
242
243
    /**
244
     * Make Request to the API
245
     *
246
     * @param  string      $method       HTTP Request Method
247
     * @param  string      $endpoint     API Endpoint to send Request to
248
     * @param  string      $endpointType Endpoint type ['api'|'content']
249
     * @param  array       $params       Request Query Params
250
     * @param  string      $accessToken  Access Token to send with the Request
251
     * @param  DropboxFile $responseFile Save response to the file
252
     *
253
     * @return \Kunnu\Dropbox\DropboxResponse
254
     *
255
     * @throws \Kunnu\Dropbox\Exceptions\DropboxClientException
256
     */
257 49
    public function sendRequest($method, $endpoint, $endpointType = 'api', array $params = [], $accessToken = null, DropboxFile $responseFile = null)
258
    {
259
        //Access Token
260 49
        $accessToken = $this->getAccessToken() ? $this->getAccessToken() : $accessToken;
261
262
        //Make a DropboxRequest object
263 49
        $request = new DropboxRequest($method, $endpoint, $accessToken, $endpointType, $params);
264
265
        //Make a DropboxResponse object if a response should be saved to the file
266 49
        $response = $responseFile ? new DropboxResponseToFile($request, $responseFile) : null;
267
268
        //Send Request through the DropboxClient
269
        //Fetch and return the Response
270 49
        return $this->getClient()->sendRequest($request, $response);
271
    }
272
273
    /**
274
     * Get the Access Token.
275
     *
276
     * @return string Access Token
277
     */
278 49
    public function getAccessToken()
279
    {
280 49
        return $this->accessToken;
281
    }
282
283
    /**
284
     * Set the Access Token.
285
     *
286
     * @param string $accessToken Access Token
287
     *
288
     * @return \Kunnu\Dropbox\Dropbox Dropbox Client
289
     */
290 58
    public function setAccessToken($accessToken)
291
    {
292 58
        $this->accessToken = $accessToken;
293
294 58
        return $this;
295
    }
296
297
    /**
298
     * Make Model from DropboxResponse
299
     *
300
     * @param  DropboxResponse $response
301
     *
302
     * @return \Kunnu\Dropbox\Models\ModelInterface
303
     */
304 18
    public function makeModelFromResponse(DropboxResponse $response)
305
    {
306
        //Get the Decoded Body
307 18
        $body = $response->getDecodedBody();
308
309 18
        if (is_null($body)) {
310 1
            $body = [];
311
        }
312
313
        //Make and Return the Model
314 18
        return ModelFactory::make($body);
315
    }
316
317
    /**
318
     * Get the contents of a Folder
319
     *
320
     * @param  string $path   Path to the folder. Defaults to root.
321
     * @param  array  $params Additional Params
322
     *
323
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder
324
     *
325
     * @return \Kunnu\Dropbox\Models\MetadataCollection
326
     */
327 3 View Code Duplication
    public function listFolder(string $path = '', array $params = [])
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...
328
    {
329
        //Specify the root folder as an
330
        //empty string rather than as "/"
331 3
        if ($path === '/') {
332 1
            $path = "";
333
        }
334
335
        //Set the path
336 3
        $params['path'] = $path;
337
338
        //Get File Metadata
339 3
        $response = $this->postToAPI('/files/list_folder', $params);
340
341
        //Make and Return the Model
342 3
        return $this->makeModelFromResponse($response);
343
    }
344
345
    /**
346
     * Paginate through all files and retrieve updates to the folder,
347
     * using the cursor retrieved from listFolder or listFolderContinue
348
     *
349
     * @param  string $cursor The cursor returned by your
350
     *                        last call to listFolder or listFolderContinue
351
     *
352
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-continue
353
     *
354
     * @return \Kunnu\Dropbox\Models\MetadataCollection
355
     */
356 1
    public function listFolderContinue(string $cursor)
357
    {
358 1
        $response = $this->postToAPI('/files/list_folder/continue', ['cursor' => $cursor]);
359
360
        //Make and Return the Model
361 1
        return $this->makeModelFromResponse($response);
362
    }
363
364
    /**
365
     * Get a cursor for the folder's state.
366
     *
367
     * @param  string $path   Path to the folder. Defaults to root.
368
     * @param  array  $params Additional Params
369
     *
370
     * @return string The Cursor for the folder's state
371
     *
372
     * @throws \Kunnu\Dropbox\Exceptions\DropboxClientException
373
     *
374
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-get_latest_cursor
375
     *
376
     */
377 4
    public function listFolderLatestCursor(string $path = '', array $params = [])
378
    {
379
        //Specify the root folder as an
380
        //empty string rather than as "/"
381 4
        if ($path === '/') {
382 1
            $path = "";
383
        }
384
385
        //Set the path
386 4
        $params['path'] = $path;
387
388
        //Fetch the cursor
389 4
        $response = $this->postToAPI('/files/list_folder/get_latest_cursor', $params);
390
391
        //Retrieve the cursor
392 4
        $body = $response->getDecodedBody();
393 4
        $cursor = isset($body['cursor']) ? $body['cursor'] : false;
394
395
        //No cursor returned
396 4
        if (!$cursor) {
397 1
            throw new DropboxClientException("Could not retrieve cursor. Something went wrong.");
398
        }
399
400
        //Return the cursor
401 3
        return $cursor;
402
    }
403
404
    /**
405
     * Get Revisions of a File
406
     *
407
     * @param  string $path   Path to the file
408
     * @param  array  $params Additional Params
409
     *
410
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_revisions
411
     *
412
     * @return \Kunnu\Dropbox\Models\ModelCollection
413
     */
414 1
    public function listRevisions(string $path, array $params = [])
415
    {
416
        //Set the Path
417 1
        $params['path'] = $path;
418
419
        //Fetch the Revisions
420 1
        $response = $this->postToAPI('/files/list_revisions', $params);
421
422
        //The file metadata of the entries, returned by this
423
        //endpoint doesn't include a '.tag' attribute, which
424
        //is used by the ModelFactory to resolve the correct
425
        //model. But since we know that revisions returned
426
        //are file metadata objects, we can explicitly cast
427
        //them as \Kunnu\Dropbox\Models\FileMetadata manually.
428 1
        $body = $response->getDecodedBody();
429 1
        $entries = isset($body['entries']) ? $body['entries'] : [];
430 1
        $processedEntries = [];
431
432 1
        foreach ($entries as $entry) {
433 1
            $processedEntries[] = new FileMetadata($entry);
434
        }
435
436 1
        return new ModelCollection($processedEntries);
437
    }
438
439
    /**
440
     * Search a folder for files/folders
441
     *
442
     * @param  string $path   Path to search
443
     * @param  string $query  Search Query
444
     * @param  array  $params Additional Params
445
     *
446
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-search
447
     *
448
     * @return \Kunnu\Dropbox\Models\SearchResults
449
     */
450 3 View Code Duplication
    public function search(string $path = '', $query, array $params = [])
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...
451
    {
452
        //Specify the root folder as an
453
        //empty string rather than as "/"
454 3
        if ($path === '/') {
455 1
            $path = "";
456
        }
457
458
        //Set the path and query
459 3
        $params['path'] = $path;
460 3
        $params['query'] = $query;
461
462
        //Fetch Search Results
463 3
        $response = $this->postToAPI('/files/search', $params);
464
465
        //Make and Return the Model
466 3
        return $this->makeModelFromResponse($response);
467
    }
468
469
    /**
470
     * Create a folder at the given path
471
     *
472
     * @param  string  $path       Path to create
473
     * @param  boolean $autorename Auto Rename File
474
     *
475
     * @return \Kunnu\Dropbox\Models\FolderMetadata
476
     *
477
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-create_folder
478
     *
479
     */
480 1
    public function createFolder(string $path, $autorename = false)
481
    {
482
        //Create Folder
483 1
        $response = $this->postToAPI('/files/create_folder', ['path' => $path, 'autorename' => $autorename]);
484
485
        //Fetch the Metadata
486 1
        $body = $response->getDecodedBody();
487
488
        //Make and Return the Model
489 1
        return new FolderMetadata($body['metadata']);
490
    }
491
492
    /**
493
     * Delete a file or folder at the given path
494
     *
495
     * @param  string $path Path to file/folder to delete
496
     *
497
     * @return \Kunnu\Dropbox\Models\FileMetadata | \Kunnu\Dropbox\Models\FolderMetadata
498
     *
499
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-delete_v2
500
     *
501
     */
502 2
    public function delete(string $path)
503
    {
504
        //Delete
505 2
        $response = $this->postToAPI('/files/delete_v2', ['path' => $path]);
506
507
        //Make and Return the Model
508 2
        return $this->makeModelFromResponse($response);
509
    }
510
511
    /**
512
     * Move a file or folder to a different location
513
     *
514
     * @param  string $fromPath Path to be moved
515
     * @param  string $toPath   Path to be moved to
516
     *
517
     * @return \Kunnu\Dropbox\Models\FileMetadata | \Kunnu\Dropbox\Models\FolderMetadata
518
     *
519
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-move
520
     *
521
     */
522 2 View Code Duplication
    public function move(string $fromPath, string $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...
523
    {
524
        //Response
525 2
        $response = $this->postToAPI('/files/move', ['from_path' => $fromPath, 'to_path' => $toPath]);
526
527
        //Make and Return the Model
528 2
        return $this->makeModelFromResponse($response);
529
    }
530
531
    /**
532
     * Copy a file or folder to a different location
533
     *
534
     * @param  string $fromPath Path to be copied
535
     * @param  string $toPath   Path to be copied to
536
     *
537
     * @return \Kunnu\Dropbox\Models\FileMetadata | \Kunnu\Dropbox\Models\FolderMetadata
538
     *
539
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-copy
540
     *
541
     */
542 2 View Code Duplication
    public function copy(string $fromPath, string $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...
543
    {
544
        //Response
545 2
        $response = $this->postToAPI('/files/copy', ['from_path' => $fromPath, 'to_path' => $toPath]);
546
547
        //Make and Return the Model
548 2
        return $this->makeModelFromResponse($response);
549
    }
550
551
    /**
552
     * Restore a file to the specific version
553
     *
554
     * @param  string $path Path to the file to restore
555
     * @param  string $rev  Revision to store for the file
556
     *
557
     * @return \Kunnu\Dropbox\Models\FileMetadata
558
     *
559
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-restore
560
     *
561
     */
562 1
    public function restore(string $path, string $rev)
563
    {
564
        //Response
565 1
        $response = $this->postToAPI('/files/restore', ['path' => $path, 'rev' => $rev]);
566
567
        //Fetch the Metadata
568 1
        $body = $response->getDecodedBody();
569
570
        //Make and Return the Model
571 1
        return new FileMetadata($body);
572
    }
573
574
    /**
575
     * Get Copy Reference
576
     *
577
     * @param  string $path Path to the file or folder to get a copy reference to
578
     *
579
     * @return \Kunnu\Dropbox\Models\CopyReference
580
     *
581
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-copy_reference-get
582
     *
583
     */
584 2
    public function getCopyReference(string $path)
585
    {
586
        //Get Copy Reference
587 2
        $response = $this->postToAPI('/files/copy_reference/get', ['path' => $path]);
588 2
        $body = $response->getDecodedBody();
589
590
        //Make and Return the Model
591 2
        return new CopyReference($body);
592
    }
593
594
    /**
595
     * Save Copy Reference
596
     *
597
     * @param  string $path          Path to the file or folder to get a copy reference to
598
     * @param  string $copyReference Copy reference returned by getCopyReference
599
     *
600
     * @return \Kunnu\Dropbox\Models\FileMetadata|\Kunnu\Dropbox\Models\FolderMetadata
601
     *
602
     * @throws \Kunnu\Dropbox\Exceptions\DropboxClientException
603
     *
604
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-copy_reference-save
605
     *
606
     */
607 4
    public function saveCopyReference(string $path, string $copyReference)
608
    {
609
        //Save Copy Reference
610 4
        $response = $this->postToAPI('/files/copy_reference/save', ['path' => $path, 'copy_reference' => $copyReference]);
611 4
        $body = $response->getDecodedBody();
612
613
        //Response doesn't have Metadata
614 4
        if (!isset($body['metadata']) || !is_array($body['metadata'])) {
615 2
            throw new DropboxClientException("Invalid Response.");
616
        }
617
618
        //Make and return the Model
619 2
        return ModelFactory::make($body['metadata']);
620
    }
621
622
    /**
623
     * Get a temporary link to stream contents of a file
624
     *
625
     * @param  string $path Path to the file you want a temporary link to
626
     *
627
     * https://www.dropbox.com/developers/documentation/http/documentation#files-get_temporary_link
628
     *
629
     * @return \Kunnu\Dropbox\Models\TemporaryLink
630
     */
631 1
    public function getTemporaryLink(string $path)
632
    {
633
        //Get Temporary Link
634 1
        $response = $this->postToAPI('/files/get_temporary_link', ['path' => $path]);
635
636
        //Make and Return the Model
637 1
        return $this->makeModelFromResponse($response);
638
    }
639
640
    /**
641
     * Save a specified URL into a file in user's Dropbox
642
     *
643
     * @param  string $path Path where the URL will be saved
644
     * @param  string $url  URL to be saved
645
     *
646
     * @return string Async Job ID
647
     *
648
     * @throws \Kunnu\Dropbox\Exceptions\DropboxClientException
649
     *
650
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-save_url
651
     *
652
     */
653 2
    public function saveUrl(string $path, string $url)
654
    {
655
        //Save URL
656 2
        $response = $this->postToAPI('/files/save_url', ['path' => $path, 'url' => $url]);
657 2
        $body = $response->getDecodedBody();
658
659 2
        if (!isset($body['async_job_id'])) {
660 1
            throw new DropboxClientException("Could not retrieve Async Job ID.");
661
        }
662
663
        //Return the Async Job ID
664 1
        return $body['async_job_id'];
665
    }
666
667
    /**
668
     * Save a specified URL into a file in user's Dropbox
669
     *
670
     * @param $asyncJobId
671
     *
672
     * @return \Kunnu\Dropbox\Models\FileMetadata|string Status (failed|in_progress) or FileMetadata (if complete)
673
     *
674
     * @link     https://www.dropbox.com/developers/documentation/http/documentation#files-save_url-check_job_status
675
     *
676
     */
677 2
    public function checkJobStatus(string $asyncJobId)
678
    {
679
        //Get Job Status
680 2
        $response = $this->postToAPI('/files/save_url/check_job_status', ['async_job_id' => $asyncJobId]);
681 2
        $body = $response->getDecodedBody();
682
683
        //Status
684 2
        $status = $body['.tag'] ?? '';
685
686
        //If status is complete
687 2
        if ($status === 'complete') {
688 1
            return new FileMetadata($body);
689
        }
690
691
        //Return the status
692 1
        return $status;
693
    }
694
695
    /**
696
     * Upload a File to Dropbox
697
     *
698
     * @param  string|DropboxFile $dropboxFile DropboxFile object or Path to file
699
     * @param  string             $path        Path to upload the file to
700
     * @param  array              $params      Additional Params
701
     *
702
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload
703
     *
704
     * @return \Kunnu\Dropbox\Models\FileMetadata
705
     */
706 3
    public function upload($dropboxFile, string $path, array $params = [])
707
    {
708
        //Make Dropbox File
709 3
        $dropboxFile = $this->makeDropboxFile($dropboxFile);
710
711
        //If the file is larger than the Chunked Upload Threshold
712 3
        if ($dropboxFile->getSize() > static::AUTO_CHUNKED_UPLOAD_THRESHOLD) {
713
            //Upload the file in sessions/chunks
714 1
            return $this->uploadChunked($dropboxFile, $path, null, null, $params);
715
        }
716
717
        //Simple file upload
718 2
        return $this->simpleUpload($dropboxFile, $path, $params);
719
    }
720
721
    /**
722
     * Make DropboxFile Object
723
     *
724
     * @param  string|DropboxFile $dropboxFile DropboxFile object or Path to file
725
     * @param  int                $maxLength   Max Bytes to read from the file
726
     * @param  int                $offset      Seek to specified offset before reading
727
     * @param  string             $mode        The type of access
728
     *
729
     * @return \Kunnu\Dropbox\DropboxFile
730
     */
731 13
    public function makeDropboxFile($dropboxFile, $maxLength = null, $offset = null, $mode = DropboxFile::MODE_READ)
732
    {
733
        //Uploading file by file path
734 13
        if (!$dropboxFile instanceof DropboxFile) {
735
            //Create a DropboxFile Object
736 10
            $dropboxFile = new DropboxFile($dropboxFile, $mode);
737 6
        } elseif ($mode !== $dropboxFile->getMode()) {
738
            //Reopen the file with expected mode
739 2
            $dropboxFile->close();
740 2
            $dropboxFile = new DropboxFile($dropboxFile->getFilePath(), $mode);
741
        }
742
743 13
        if (!is_null($offset)) {
744 4
            $dropboxFile->setOffset($offset);
745
        }
746
747 13
        if (!is_null($maxLength)) {
748 6
            $dropboxFile->setMaxLength($maxLength);
749
        }
750
751
        //Return the DropboxFile Object
752 13
        return $dropboxFile;
753
    }
754
755
    /**
756
     * Upload file in sessions/chunks
757
     *
758
     * @param  string|DropboxFile $dropboxFile DropboxFile object or Path to file
759
     * @param  string             $path        Path to save the file to, on Dropbox
760
     * @param  int                $fileSize    The size of the file
761
     * @param  int                $chunkSize   The amount of data to upload in each chunk
762
     * @param  array              $params      Additional Params
763
     *
764
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-start
765
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-finish
766
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-append_v2
767
     *
768
     * @return \Kunnu\Dropbox\Models\FileMetadata
769
     */
770 1
    public function uploadChunked($dropboxFile, $path, $fileSize = null, $chunkSize = null, array $params = array())
771
    {
772
        //Make Dropbox File
773 1
        $dropboxFile = $this->makeDropboxFile($dropboxFile);
774
775
        //No file size specified explicitly
776 1
        if (is_null($fileSize)) {
777 1
            $fileSize = $dropboxFile->getSize();
778
        }
779
780
        //No chunk size specified, use default size
781 1
        if (is_null($chunkSize)) {
782 1
            $chunkSize = static::DEFAULT_CHUNK_SIZE;
783
        }
784
785
        //If the fileSize is smaller
786
        //than the chunk size, we'll
787
        //make the chunk size relatively
788
        //smaller than the file size
789 1
        if ($fileSize <= $chunkSize) {
790
            $chunkSize = intval($fileSize / 2);
791
        }
792
793
        //Start the Upload Session with the file path
794
        //since the DropboxFile object will be created
795
        //again using the new chunk size.
796 1
        $sessionId = $this->startUploadSession($dropboxFile->getFilePath(), $chunkSize);
797
798
        //Uploaded
799 1
        $uploaded = $chunkSize;
800
801
        //Remaining
802 1
        $remaining = $fileSize - $chunkSize;
803
804
        //While the remaining bytes are
805
        //more than the chunk size, append
806
        //the chunk to the upload session.
807 1
        while ($remaining > $chunkSize) {
808
            //Append the next chunk to the Upload session
809 1
            $sessionId = $this->appendUploadSession($dropboxFile, $sessionId, $uploaded, $chunkSize);
810
811
            //Update remaining and uploaded
812 1
            $uploaded = $uploaded + $chunkSize;
813 1
            $remaining = $remaining - $chunkSize;
814
        }
815
816
        //Finish the Upload Session and return the Uploaded File Metadata
817 1
        return $this->finishUploadSession($dropboxFile, $sessionId, $uploaded, $remaining, $path, $params);
818
    }
819
820
    /**
821
     * Start an Upload Session
822
     *
823
     * @param  string|DropboxFile $dropboxFile DropboxFile object or Path to file
824
     * @param  int                $chunkSize   Size of file chunk to upload
825
     * @param  boolean            $close       Closes the session for "appendUploadSession"
826
     *
827
     * @return string Unique identifier for the upload session
828
     *
829
     * @throws \Kunnu\Dropbox\Exceptions\DropboxClientException
830
     *
831
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-start
832
     *
833
     */
834 3
    public function startUploadSession($dropboxFile, $chunkSize = -1, $close = false)
835
    {
836
        //Make Dropbox File with the given chunk size
837 3
        $dropboxFile = $this->makeDropboxFile($dropboxFile, $chunkSize);
838
839
        //Set the close param
840
        $params = [
841 3
            'close' => $close ? true : false,
842 3
            'file' => $dropboxFile
843
        ];
844
845
        //Upload File
846 3
        $file = $this->postToContent('/files/upload_session/start', $params);
847
848 3
        $body = $file->getDecodedBody();
849
850
        //Cannot retrieve Session ID
851 3
        if (!isset($body['session_id'])) {
852 1
            throw new DropboxClientException("Could not retrieve Session ID.");
853
        }
854
855
        //Return the Session ID
856 2
        return $body['session_id'];
857
    }
858
859
    /**
860
     * Make a HTTP POST Request to the Content endpoint type
861
     *
862
     * @param  string      $endpoint     Content Endpoint to send Request to
863
     * @param  array       $params       Request Query Params
864
     * @param  string      $accessToken  Access Token to send with the Request
865
     * @param  DropboxFile $responseFile Save response to the file
866
     *
867
     * @return \Kunnu\Dropbox\DropboxResponse
868
     */
869 10
    public function postToContent($endpoint, array $params = [], $accessToken = null, DropboxFile $responseFile = null)
870
    {
871 10
        return $this->sendRequest("POST", $endpoint, 'content', $params, $accessToken, $responseFile);
872
    }
873
874
    /**
875
     * Append more data to an Upload Session
876
     *
877
     * @param  string|DropboxFile $dropboxFile DropboxFile object or Path to file
878
     * @param  string             $sessionId   Session ID returned by `startUploadSession`
879
     * @param  int                $offset      The amount of data that has been uploaded so far
880
     * @param  int                $chunkSize   The amount of data to upload
881
     * @param  boolean            $close       Closes the session for futher "appendUploadSession" calls
882
     *
883
     * @return string Unique identifier for the upload session
884
     *
885
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-append_v2
886
     *
887
     */
888 2
    public function appendUploadSession($dropboxFile, string $sessionId, int $offset, int $chunkSize, bool $close = false)
889
    {
890
        //Make Dropbox File
891 2
        $dropboxFile = $this->makeDropboxFile($dropboxFile, $chunkSize, $offset);
892
893 2
        $params = [];
894
895
        //Set the File
896 2
        $params['file'] = $dropboxFile;
897
898
        //Set the Cursor: Session ID and Offset
899 2
        $params['cursor'] = ['session_id' => $sessionId, 'offset' => $offset];
900
901
        //Set the close param
902 2
        $params['close'] = $close ? true : false;
903
904
        //Since this endpoint doesn't have
905
        //any return values, we'll disable the
906
        //response validation for this request.
907 2
        $params['validateResponse'] = false;
908
909
        //Upload File
910 2
        $this->postToContent('/files/upload_session/append_v2', $params);
911
912
        //Make and Return the Model
913 2
        return $sessionId;
914
    }
915
916
    /**
917
     * Finish an upload session and save the uploaded data to the given file path
918
     *
919
     * @param  string|DropboxFile $dropboxFile DropboxFile object or Path to file
920
     * @param  string             $sessionId   Session ID returned by `startUploadSession`
921
     * @param  int                $offset      The amount of data that has been uploaded so far
922
     * @param  int                $remaining   The amount of data that is remaining
923
     * @param  string             $path        Path to save the file to, on Dropbox
924
     * @param  array              $params      Additional Params
925
     *
926
     * @return \Kunnu\Dropbox\Models\FileMetadata
927
     *
928
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-finish
929
     *
930
     */
931 2
    public function finishUploadSession($dropboxFile, string $sessionId, int $offset, int $remaining, string $path, array $params = [])
932
    {
933
        //Make Dropbox File
934 2
        $dropboxFile = $this->makeDropboxFile($dropboxFile, $remaining, $offset);
935
936 2
        $queryParams = [];
937
938
        //Set the File
939 2
        $queryParams['file'] = $dropboxFile;
940
941
        //Set the Cursor: Session ID and Offset
942 2
        $queryParams['cursor'] = ['session_id' => $sessionId, 'offset' => $offset];
943
944
        //Set the path
945 2
        $params['path'] = $path;
946
        //Set the Commit
947 2
        $queryParams['commit'] = $params;
948
949
        //Upload File
950 2
        $file = $this->postToContent('/files/upload_session/finish', $queryParams);
951 2
        $body = $file->getDecodedBody();
952
953
        //Make and Return the Model
954 2
        return new FileMetadata($body);
955
    }
956
957
    /**
958
     * Upload a File to Dropbox in a single request
959
     *
960
     * @param  string|DropboxFile $dropboxFile DropboxFile object or Path to file
961
     * @param  string             $path        Path to upload the file to
962
     * @param  array              $params      Additional Params
963
     *
964
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload
965
     *
966
     * @return \Kunnu\Dropbox\Models\FileMetadata
967
     */
968 2
    public function simpleUpload($dropboxFile, $path, array $params = [])
969
    {
970
        //Make Dropbox File
971 2
        $dropboxFile = $this->makeDropboxFile($dropboxFile);
972
973
        //Set the path and file
974 2
        $params['path'] = $path;
975 2
        $params['file'] = $dropboxFile;
976
977
        //Upload File
978 2
        $file = $this->postToContent('/files/upload', $params);
979 2
        $body = $file->getDecodedBody();
980
981
        //Make and Return the Model
982 2
        return new FileMetadata($body);
983
    }
984
985
    /**
986
     * Get a thumbnail for an image
987
     *
988
     * @param  string $path   Path to the file you want a thumbnail to
989
     * @param  string $size   Size for the thumbnail image ['thumb','small','medium','large','huge']
990
     * @param  string $format Format for the thumbnail image ['jpeg'|'png']
991
     *
992
     * @return \Kunnu\Dropbox\Models\Thumbnail
993
     *
994
     * @throws \Kunnu\Dropbox\Exceptions\DropboxClientException
995
     *
996
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_thumbnail
997
     *
998
     */
999 2
    public function getThumbnail(string $path, $size = 'small', $format = 'jpeg')
1000
    {
1001
        //Invalid Format
1002 2
        if (!in_array($format, ['jpeg', 'png'])) {
1003 1
            throw new DropboxClientException("Invalid format. Must either be 'jpeg' or 'png'.");
1004
        }
1005
1006
        //Thumbnail size
1007 1
        $size = $this->getThumbnailSize($size);
1008
1009
        //Get Thumbnail
1010 1
        $response = $this->postToContent('/files/get_thumbnail', ['path' => $path, 'format' => $format, 'size' => $size]);
1011
1012
        //Get file metadata from response headers
1013 1
        $metadata = $this->getMetadataFromResponseHeaders($response);
1014
1015
        //File Contents
1016 1
        $contents = $response->getBody();
1017
1018
        //Make and return a Thumbnail model
1019 1
        return new Thumbnail($metadata, $contents);
1020
    }
1021
1022
    /**
1023
     * Get thumbnail size
1024
     *
1025
     * @param  string $size Thumbnail Size
1026
     *
1027
     * @return string
1028
     */
1029 1
    protected function getThumbnailSize($size)
1030
    {
1031
        $thumbnailSizes = [
1032 1
            'thumb' => 'w32h32',
1033
            'small' => 'w64h64',
1034
            'medium' => 'w128h128',
1035
            'large' => 'w640h480',
1036
            'huge' => 'w1024h768'
1037
        ];
1038
1039 1
        return isset($thumbnailSizes[$size]) ? $thumbnailSizes[$size] : $thumbnailSizes['small'];
1040
    }
1041
1042
    /**
1043
     * Get metadata from response headers
1044
     *
1045
     * @param  DropboxResponse $response
1046
     *
1047
     * @return array
1048
     */
1049 3
    protected function getMetadataFromResponseHeaders(DropboxResponse $response)
1050
    {
1051
        //Response Headers
1052 3
        $headers = $response->getHeaders();
1053
1054
        //Empty metadata for when
1055
        //metadata isn't returned
1056 3
        $metadata = [];
1057
1058
        //If metadata is available
1059 3
        if (isset($headers[static::METADATA_HEADER])) {
1060
            //File Metadata
1061 3
            $data = $headers[static::METADATA_HEADER];
1062
1063
            //The metadata is present in the first index
1064
            //of the metadata response header array
1065 3
            if (is_array($data) && isset($data[0])) {
1066 3
                $data = $data[0];
1067
            }
1068
1069
            //Since the metadata is returned as a json string
1070
            //it needs to be decoded into an associative array
1071 3
            $metadata = json_decode((string)$data, true);
1072
        }
1073
1074
        //Return the metadata
1075 3
        return $metadata;
1076
    }
1077
1078
    /**
1079
     * Download a File
1080
     *
1081
     * @param  string                  $path        Path to the file you want to download
1082
     * @param  null|string|DropboxFile $dropboxFile DropboxFile object or Path to target file
1083
     *
1084
     * @return \Kunnu\Dropbox\Models\File
1085
     *
1086
     * @link https://www.dropbox.com/developers/documentation/http/documentation#files-download
1087
     *
1088
     */
1089 2
    public function download(string $path, $dropboxFile = null)
1090
    {
1091
        //Make Dropbox File if target is specified
1092 2
        $dropboxFile = $dropboxFile ? $this->makeDropboxFile($dropboxFile, null, null, DropboxFile::MODE_WRITE) : null;
1093
1094
        //Download File
1095 2
        $response = $this->postToContent('/files/download', ['path' => $path], null, $dropboxFile);
1096
1097
        //Get file metadata from response headers
1098 2
        $metadata = $this->getMetadataFromResponseHeaders($response);
1099
1100
        //File Contents
1101 2
        $contents = $dropboxFile ? $this->makeDropboxFile($dropboxFile) : $response->getBody();
1102
1103
        //Make and return a File model
1104 2
        return new File($metadata, $contents);
1105
    }
1106
1107
    /**
1108
     * Get Current Account
1109
     *
1110
     * @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account
1111
     *
1112
     * @return \Kunnu\Dropbox\Models\Account
1113
     */
1114 1
    public function getCurrentAccount()
1115
    {
1116
        //Get current account
1117 1
        $response = $this->postToAPI('/users/get_current_account', []);
1118 1
        $body = $response->getDecodedBody();
1119
1120
        //Make and return the model
1121 1
        return new Account($body);
1122
    }
1123
1124
    /**
1125
     * Get Account
1126
     *
1127
     * @param string $account_id Account ID of the account to get details for
1128
     *
1129
     * @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_account
1130
     *
1131
     * @return \Kunnu\Dropbox\Models\Account
1132
     */
1133 1
    public function getAccount($account_id)
1134
    {
1135
        //Get account
1136 1
        $response = $this->postToAPI('/users/get_account', ['account_id' => $account_id]);
1137 1
        $body = $response->getDecodedBody();
1138
1139
        //Make and return the model
1140 1
        return new Account($body);
1141
    }
1142
1143
    /**
1144
     * Get Multiple Accounts in one call
1145
     *
1146
     * @param array $account_ids IDs of the accounts to get details for
1147
     *
1148
     * @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_account_batch
1149
     *
1150
     * @return \Kunnu\Dropbox\Models\AccountList
1151
     */
1152 1
    public function getAccounts(array $account_ids = [])
1153
    {
1154
        //Get account
1155 1
        $response = $this->postToAPI('/users/get_account_batch', ['account_ids' => $account_ids]);
1156 1
        $body = $response->getDecodedBody();
1157
1158
        //Make and return the model
1159 1
        return new AccountList($body);
1160
    }
1161
1162
    /**
1163
     * Get Space Usage for the current user's account
1164
     *
1165
     * @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_space_usage
1166
     *
1167
     * @return array
1168
     */
1169 1
    public function getSpaceUsage()
1170
    {
1171
        //Get space usage
1172 1
        $response = $this->postToAPI('/users/get_space_usage', []);
1173 1
        $body = $response->getDecodedBody();
1174
1175
        //Return the decoded body
1176 1
        return $body;
1177
    }
1178
}
1179