Skip to content

Commit 8ec6a83

Browse files
committed
Re-implement Dropbox#getItemURL
1 parent 30a80b2 commit 8ec6a83

File tree

4 files changed

+89
-17
lines changed

4 files changed

+89
-17
lines changed

doc/getting-started/dropbox-and-google-drive.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Known issues
4949
* Listing and deleting folders with more than 10000 files will cause problems
5050
* Content-Type is not fully supported due to limitations of the Dropbox API
5151
* Dropbox preserves cases but is not case-sensitive
52-
* ``getItemURL`` is not implemented yet (see issue :issue:`1052`)
52+
* ``getItemURL`` works only for files which exist and are public
5353

5454
Google Drive
5555
------------

doc/js-api/base-client.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,9 @@ Other functions
430430
:short-name:
431431

432432
.. WARNING::
433-
This method currently only works for remoteStorage
434-
backends. The issues for implementing it for Dropbox and Google
435-
Drive can be found at :issue:`1052` and :issue:`1054`.
433+
This method currently only works for remoteStorage and Dropbox backends.
434+
The issue for implementing it for Google Drive can be found at
435+
:issue:`1054`.
436436

437437
.. autofunction:: BaseClient#scope(path)
438438
:short-name:

src/dropbox.js

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ var getDropboxPath = function (path) {
5959
return cleanPath(PATH_PREFIX + '/' + path).replace(/\/$/, '');
6060
};
6161

62+
const isPublicPath = path => path.match(/^\/public\/.*[^/]$/);
63+
6264
var compareApiError = function (response, expect) {
6365
return new RegExp('^' + expect.join('\\/') + '(\\/|$)').test(response.error_summary);
6466
};
@@ -575,26 +577,52 @@ Dropbox.prototype = {
575577
return this._deleteSimple(path);
576578
},
577579

580+
/**
581+
* Retrieve full, absolute URL of an item. Items which are non-public or do
582+
* not exist always resolve to undefined.
583+
*
584+
* @returns {Promise} - resolves to an absolute URL of the item
585+
*
586+
* @protected
587+
*/
588+
getItemURL: function (path) {
589+
if (!isPublicPath(path)) {
590+
return Promise.resolve(undefined);
591+
}
592+
593+
let url = this._itemRefs[path];
594+
if (url !== undefined) {
595+
return Promise.resolve(url);
596+
}
597+
598+
return this._getSharedLink(path).then((link) => {
599+
if (link !== undefined) {
600+
return link;
601+
}
602+
return this._share(path);
603+
});
604+
},
605+
578606
/**
579607
* Calls share, if the provided path resides in a public folder.
580608
*
581609
* @private
582610
*/
583611
_shareIfNeeded: function (path) {
584-
if (path.match(/^\/public\/.*[^/]$/) && this._itemRefs[path] === undefined) {
585-
this.share(path);
612+
if (isPublicPath(path) && this._itemRefs[path] === undefined) {
613+
this._share(path);
586614
}
587615
},
588616

589617
/**
590618
* Gets a publicly-accessible URL for the path from Dropbox and stores it
591-
* in ``_itemRefs``.
619+
* in ``_itemRefs``. Resolves to undefined if the path does not exist.
592620
*
593621
* @return {Promise} a promise for the URL
594622
*
595623
* @private
596624
*/
597-
share: function (path) {
625+
_share: function (path) {
598626
var url = 'https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings';
599627
var options = {
600628
body: {path: getDropboxPath(path)}
@@ -617,6 +645,9 @@ Dropbox.prototype = {
617645
if (compareApiError(body, ['shared_link_already_exists'])) {
618646
return this._getSharedLink(path);
619647
}
648+
if (compareApiError(body, ['path', 'not_found'])) {
649+
return Promise.resolve(undefined);
650+
}
620651

621652
return Promise.reject(new Error('API error: ' + body.error_summary));
622653
}
@@ -980,7 +1011,8 @@ Dropbox.prototype = {
9801011
},
9811012

9821013
/**
983-
* Requests the link for an already-shared file or folder.
1014+
* Requests the link for a shared file or folder. Resolves to undefined if
1015+
* the requested file or folder has not bee shared.
9841016
*
9851017
* @param {string} path - path to the file or folder
9861018
*
@@ -1002,7 +1034,8 @@ Dropbox.prototype = {
10021034
return Promise.reject(new Error('Invalid response status: ' + response.status));
10031035
}
10041036

1005-
var body;
1037+
let body;
1038+
let link;
10061039

10071040
try {
10081041
body = JSON.parse(response.responseText);
@@ -1011,14 +1044,16 @@ Dropbox.prototype = {
10111044
}
10121045

10131046
if (response.status === 409) {
1047+
if (compareApiError(body, ['path', 'not_found'])) {
1048+
return Promise.resolve(undefined);
1049+
}
10141050
return Promise.reject(new Error('API error: ' + response.error_summary));
10151051
}
10161052

1017-
if (!body.links.length) {
1018-
return Promise.reject(new Error('No links returned'));
1053+
if (body.links.length) {
1054+
link = body.links[0].url;
10191055
}
1020-
1021-
return Promise.resolve(body.links[0].url);
1056+
return Promise.resolve(link);
10221057
}, (error) => {
10231058
error.message = 'Could not get link to a shared file or folder ("' + path + '"): ' + error.message;
10241059
return Promise.reject(error);
@@ -1066,9 +1101,7 @@ function unHookSync(rs) {
10661101
function hookGetItemURL (rs) {
10671102
if (rs._origBaseClientGetItemURL) { return; }
10681103
rs._origBaseClientGetItemURL = BaseClient.prototype.getItemURL;
1069-
BaseClient.prototype.getItemURL = function (/*path*/) {
1070-
throw new Error('getItemURL is not implemented for Dropbox yet');
1071-
};
1104+
BaseClient.prototype.getItemURL = rs.dropbox.getItemURL.bind(rs.dropbox);
10721105
}
10731106

10741107
/**

test/unit/dropbox-suite.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,45 @@ define(['require', './src/util', './src/dropbox', './src/wireclient',
815815
}
816816
},
817817

818+
{
819+
desc: "#getItemURL returns from cache",
820+
run: function (env, test) {
821+
env.connectedClient._itemRefs['/public/foo'] = 'http://example.com/public/foo';
822+
env.connectedClient.getItemURL('/public/foo').then((itemURL) => {
823+
test.assert(itemURL, 'http://example.com/public/foo');
824+
});
825+
}
826+
},
827+
828+
{
829+
desc: "#getItemURL creates shared link if it does not exist",
830+
run: function (env, test) {
831+
env.connectedClient.getItemURL('/public/foo').then((itemURL) => {
832+
test.assert(itemURL, 'http://example.com/public/foo');
833+
});
834+
835+
setTimeout(() => {
836+
let req = XMLHttpRequest.instances.shift();
837+
test.assertFail(req, undefined);
838+
req.status = 200;
839+
req.responseText = JSON.stringify({ links: [] });
840+
req._onload();
841+
}, 10);
842+
843+
setTimeout(() => {
844+
req = XMLHttpRequest.instances.shift();
845+
test.assertFail(req, undefined);
846+
test.assertAnd(req.url, 'https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings');
847+
req.status = 200;
848+
req.responseText = JSON.stringify({
849+
'.tag': 'file',
850+
url: 'http://example.com/public/foo',
851+
});
852+
req._onload();
853+
}, 20);
854+
}
855+
},
856+
818857
{
819858
desc: "requests are aborted if they aren't responded after the configured timeout",
820859
timeout: 2000,

0 commit comments

Comments
 (0)