box-node-sdk 1.26.1 → 1.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/lib/api-request.js +6 -1
- package/lib/managers/files.js +31 -0
- package/lib/managers/folders.js +31 -0
- package/lib/managers/search.js +2 -0
- package/lib/managers/users.js +21 -0
- package/lib/managers/web-links.js +108 -1
- package/lib/util/errors.js +41 -17
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.29.0 [2019-04-25]
|
|
4
|
+
|
|
5
|
+
- Added convenience methods for setting metadata on [files](./docs/metadata.md#set-metadata-on-a-file)
|
|
6
|
+
and [folders](./docs/metadata.md#set-metadata-on-a-folder) ([#376](https://github.com/box/box-node-sdk/pull/376))
|
|
7
|
+
|
|
8
|
+
## 1.28.0 [2019-03-28]
|
|
9
|
+
|
|
10
|
+
- Added methods for [moving](./docs/web-links.md#move-a-web-link) and [copying](./docs/web-links.md#move-a-web-link)
|
|
11
|
+
weblinks, as well as [adding or removing from a collection](./docs/web-links.md#add-web-link-to-a-collection)
|
|
12
|
+
|
|
13
|
+
## 1.27.0 [2019-02-28]
|
|
14
|
+
|
|
15
|
+
- Added the trace ID from API response headers to error messages for easier debugging
|
|
16
|
+
- Added more safety checks in the error flow to protect against throwing when handling a malformed request
|
|
17
|
+
- Added support for [retrieving a user's avatar image](./docs/users.md#get-user-avatar)
|
|
18
|
+
|
|
19
|
+
## 1.26.2 [2019-02-22]
|
|
20
|
+
|
|
21
|
+
- Fixed an error where under high request rates, code in the error handling logic could throw when handling a
|
|
22
|
+
malformed request
|
|
23
|
+
|
|
3
24
|
## 1.26.1 [2019-02-12]
|
|
4
25
|
|
|
5
26
|
- Fixed an error where some methods could throw an error when constructing an iterator
|
package/lib/api-request.js
CHANGED
|
@@ -339,7 +339,12 @@ APIRequest.prototype._retry = function(err) {
|
|
|
339
339
|
APIRequest.prototype._finish = function(err, response) {
|
|
340
340
|
var callback = this._callback;
|
|
341
341
|
process.nextTick(() => {
|
|
342
|
-
|
|
342
|
+
if (err) {
|
|
343
|
+
callback(err);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
callback(null, response);
|
|
343
348
|
});
|
|
344
349
|
};
|
|
345
350
|
|
package/lib/managers/files.js
CHANGED
|
@@ -743,6 +743,37 @@ Files.prototype.updateMetadata = function(fileID, scope, template, patch, callba
|
|
|
743
743
|
return this.client.wrapWithDefaultHandler(this.client.put)(apiPath, params, callback);
|
|
744
744
|
};
|
|
745
745
|
|
|
746
|
+
/**
|
|
747
|
+
* Sets metadata on a file, overwriting any metadata that exists for the provided keys.
|
|
748
|
+
*
|
|
749
|
+
* @param {string} fileID - The file to set metadata on
|
|
750
|
+
* @param {string} scope - The scope of the metadata template
|
|
751
|
+
* @param {string} template - The key of the metadata template
|
|
752
|
+
* @param {Object} metadata - The metadata to set
|
|
753
|
+
* @param {Function} [callback] - Called with updated metadata if successful
|
|
754
|
+
* @returns {Promise<Object>} A promise resolving to the updated metadata
|
|
755
|
+
*/
|
|
756
|
+
Files.prototype.setMetadata = function(fileID, scope, template, metadata, callback) {
|
|
757
|
+
|
|
758
|
+
return this.addMetadata(fileID, scope, template, metadata)
|
|
759
|
+
.catch(err => {
|
|
760
|
+
|
|
761
|
+
if (err.statusCode !== 409) {
|
|
762
|
+
throw err;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Metadata already exists on the file; update instead
|
|
766
|
+
var updates = Object.keys(metadata).map(key => ({
|
|
767
|
+
op: 'add',
|
|
768
|
+
path: `/${key}`,
|
|
769
|
+
value: metadata[key],
|
|
770
|
+
}));
|
|
771
|
+
|
|
772
|
+
return this.updateMetadata(fileID, scope, template, updates);
|
|
773
|
+
})
|
|
774
|
+
.asCallback(callback);
|
|
775
|
+
};
|
|
776
|
+
|
|
746
777
|
/**
|
|
747
778
|
* Deletes a metadata template from a file.
|
|
748
779
|
*
|
package/lib/managers/folders.js
CHANGED
|
@@ -368,6 +368,37 @@ Folders.prototype.updateMetadata = function(folderID, scope, template, patch, ca
|
|
|
368
368
|
return this.client.wrapWithDefaultHandler(this.client.put)(apiPath, params, callback);
|
|
369
369
|
};
|
|
370
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Sets metadata on a folder, overwriting any metadata that exists for the provided keys.
|
|
373
|
+
*
|
|
374
|
+
* @param {string} folderID - The folder to set metadata on
|
|
375
|
+
* @param {string} scope - The scope of the metadata template
|
|
376
|
+
* @param {string} template - The key of the metadata template
|
|
377
|
+
* @param {Object} metadata - The metadata to set
|
|
378
|
+
* @param {Function} [callback] - Called with updated metadata if successful
|
|
379
|
+
* @returns {Promise<Object>} A promise resolving to the updated metadata
|
|
380
|
+
*/
|
|
381
|
+
Folders.prototype.setMetadata = function(folderID, scope, template, metadata, callback) {
|
|
382
|
+
|
|
383
|
+
return this.addMetadata(folderID, scope, template, metadata)
|
|
384
|
+
.catch(err => {
|
|
385
|
+
|
|
386
|
+
if (err.statusCode !== 409) {
|
|
387
|
+
throw err;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Metadata already exists on the file; update instead
|
|
391
|
+
var updates = Object.keys(metadata).map(key => ({
|
|
392
|
+
op: 'add',
|
|
393
|
+
path: `/${key}`,
|
|
394
|
+
value: metadata[key],
|
|
395
|
+
}));
|
|
396
|
+
|
|
397
|
+
return this.updateMetadata(folderID, scope, template, updates);
|
|
398
|
+
})
|
|
399
|
+
.asCallback(callback);
|
|
400
|
+
};
|
|
401
|
+
|
|
371
402
|
/**
|
|
372
403
|
* Deletes a metadata template from a folder.
|
|
373
404
|
*
|
package/lib/managers/search.js
CHANGED
|
@@ -78,6 +78,8 @@ Search.prototype = {
|
|
|
78
78
|
* @param {SearchMetadataFilter[]} [options.mdfilters] - Searches for objects with a specific metadata object association. Searches with the this parameter do not require a query string
|
|
79
79
|
* @param {int} [options.limit=30] - The number of search results to return, max 200
|
|
80
80
|
* @param {int} [options.offset=0] - The search result at which to start the response, must be a multiple of limit
|
|
81
|
+
* @param {string} [options.sort] - The field on which the results should be sorted, e.g. "modified_at"
|
|
82
|
+
* @param {string} [options.direction] - The sort direction: "ASC" for ascending and "DESC" for descending
|
|
81
83
|
* @param {APIRequest~Callback} [callback] - passed the new comment data if it was posted successfully
|
|
82
84
|
* @returns {Promise<Object>} A promise resolving to the collection of search results
|
|
83
85
|
*/
|
package/lib/managers/users.js
CHANGED
|
@@ -189,6 +189,27 @@ Users.prototype.getGroupMemberships = function(userID, options, callback) {
|
|
|
189
189
|
return this.client.wrapWithDefaultHandler(this.client.get)(apiPath, params, callback);
|
|
190
190
|
};
|
|
191
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Retrieve the user's avatar image.
|
|
194
|
+
*
|
|
195
|
+
* API Endpoint: '/users/:userID/avatar'
|
|
196
|
+
* Method: GET
|
|
197
|
+
*
|
|
198
|
+
* @param {string} userID The ID of the user whose avatar should be retrieved
|
|
199
|
+
* @param {Function} [callback] Passed a stream over the bytes of the avatar image if successful
|
|
200
|
+
* @returns {Promise<Readable>} A promise resolving to the image stream
|
|
201
|
+
*/
|
|
202
|
+
Users.prototype.getAvatar = function(userID, callback) {
|
|
203
|
+
|
|
204
|
+
var apiPath = urlPath(BASE_PATH, userID, 'avatar'),
|
|
205
|
+
params = {
|
|
206
|
+
streaming: true
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
return this.client.get(apiPath, params)
|
|
210
|
+
.asCallback(callback);
|
|
211
|
+
};
|
|
212
|
+
|
|
192
213
|
// @NOTE(fschott) 2014-05-06: Still need to implement get, edit, create, etc.
|
|
193
214
|
// The problem is that they are only available to enterprise admins, so we'll
|
|
194
215
|
// first need to figure out how we want to handle access to those methods.
|
|
@@ -89,7 +89,7 @@ WebLinks.prototype.get = function(weblinkID, options, callback) {
|
|
|
89
89
|
* @param {Object} updates - Fields of the weblink to update
|
|
90
90
|
* @param {string} [updates.name] - Name for the web link. Will default to the URL if empty.
|
|
91
91
|
* @param {string} [updates.description] - Description of the web link. Will provide more context to users about the web link.
|
|
92
|
-
* @param {Function} [callback] - Passed the updated web
|
|
92
|
+
* @param {Function} [callback] - Passed the updated web link information if it was acquired successfully, error otherwise
|
|
93
93
|
* @returns {Promise<Object>} A promise resolving to the updated web link object
|
|
94
94
|
*/
|
|
95
95
|
WebLinks.prototype.update = function(weblinkID, updates, callback) {
|
|
@@ -117,4 +117,111 @@ WebLinks.prototype.delete = function(weblinkID, callback) {
|
|
|
117
117
|
return this.client.wrapWithDefaultHandler(this.client.del)(apiPath, null, callback);
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Move a web link into a new parent folder.
|
|
122
|
+
*
|
|
123
|
+
* API Endpoint: '/web_links/:webLinkID'
|
|
124
|
+
* Method: PUT
|
|
125
|
+
*
|
|
126
|
+
* @param {string} webLinkID - The Box ID of the web link being requested
|
|
127
|
+
* @param {string} newParentID - The Box ID for the new parent folder. '0' to move to All Files.
|
|
128
|
+
* @param {Function} [callback] - Passed the updated web link information if it was acquired successfully
|
|
129
|
+
* @returns {Promise<Object>} A promise resolving to the updated web link object
|
|
130
|
+
*/
|
|
131
|
+
WebLinks.prototype.move = function(webLinkID, newParentID, callback) {
|
|
132
|
+
var params = {
|
|
133
|
+
body: {
|
|
134
|
+
parent: {
|
|
135
|
+
id: newParentID
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
var apiPath = urlPath(BASE_PATH, webLinkID);
|
|
140
|
+
return this.client.wrapWithDefaultHandler(this.client.put)(apiPath, params, callback);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Copy a web link into a new, different folder
|
|
145
|
+
*
|
|
146
|
+
* API Endpoint: '/web_links/:webLinkID/copy
|
|
147
|
+
* Method: POST
|
|
148
|
+
*
|
|
149
|
+
* @param {string} webLinkID - The Box ID of the web link being requested
|
|
150
|
+
* @param {string} newParentID - The Box ID for the new parent folder. '0' to copy to All Files.
|
|
151
|
+
* @param {Object} [options] - Optional parameters for the copy operation, can be left null in most cases
|
|
152
|
+
* @param {string} [options.name] - A new name to use if there is an identically-named item in the new parent folder
|
|
153
|
+
* @param {Function} [callback] - passed the new web link info if call was successful
|
|
154
|
+
* @returns {Promise<Object>} A promise resolving to the new web link object
|
|
155
|
+
*/
|
|
156
|
+
WebLinks.prototype.copy = function(webLinkID, newParentID, options, callback) {
|
|
157
|
+
|
|
158
|
+
options = options || {};
|
|
159
|
+
|
|
160
|
+
options.parent = {
|
|
161
|
+
id: newParentID
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
var params = {
|
|
165
|
+
body: options
|
|
166
|
+
};
|
|
167
|
+
var apiPath = urlPath(BASE_PATH, webLinkID, '/copy');
|
|
168
|
+
return this.client.wrapWithDefaultHandler(this.client.post)(apiPath, params, callback);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Add a web link to a given collection
|
|
173
|
+
*
|
|
174
|
+
* API Endpoint: '/web_links/:webLinkID'
|
|
175
|
+
* Method: PUT
|
|
176
|
+
*
|
|
177
|
+
* @param {string} webLinkID - The web link to add to the collection
|
|
178
|
+
* @param {string} collectionID - The collection to add the web link to
|
|
179
|
+
* @param {Function} [callback] - Passed the updated web link if successful, error otherwise
|
|
180
|
+
* @returns {Promise<Object>} A promise resolving to the updated web link object
|
|
181
|
+
*/
|
|
182
|
+
WebLinks.prototype.addToCollection = function(webLinkID, collectionID, callback) {
|
|
183
|
+
|
|
184
|
+
return this.get(webLinkID, {fields: 'collections'})
|
|
185
|
+
.then(data => {
|
|
186
|
+
|
|
187
|
+
var collections = data.collections || [];
|
|
188
|
+
|
|
189
|
+
// Convert to correct format
|
|
190
|
+
collections = collections.map(c => ({id: c.id}));
|
|
191
|
+
|
|
192
|
+
if (!collections.find(c => c.id === collectionID)) {
|
|
193
|
+
|
|
194
|
+
collections.push({id: collectionID});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return this.update(webLinkID, {collections});
|
|
198
|
+
})
|
|
199
|
+
.asCallback(callback);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Remove a web link from a given collection
|
|
204
|
+
*
|
|
205
|
+
* API Endpoint: '/web_links/:webLinkID'
|
|
206
|
+
* Method: PUT
|
|
207
|
+
*
|
|
208
|
+
* @param {string} webLinkID - The web link to remove from the collection
|
|
209
|
+
* @param {string} collectionID - The collection to remove the web link from
|
|
210
|
+
* @param {Function} [callback] - Passed the updated web link if successful, error otherwise
|
|
211
|
+
* @returns {Promise<Object>} A promise resolving to the updated web link object
|
|
212
|
+
*/
|
|
213
|
+
WebLinks.prototype.removeFromCollection = function(webLinkID, collectionID, callback) {
|
|
214
|
+
|
|
215
|
+
return this.get(webLinkID, {fields: 'collections'})
|
|
216
|
+
.then(data => {
|
|
217
|
+
|
|
218
|
+
var collections = data.collections || [];
|
|
219
|
+
// Convert to correct object format and remove the specified collection
|
|
220
|
+
collections = collections.map(c => ({id: c.id})).filter(c => c.id !== collectionID);
|
|
221
|
+
|
|
222
|
+
return this.update(webLinkID, {collections});
|
|
223
|
+
})
|
|
224
|
+
.asCallback(callback);
|
|
225
|
+
};
|
|
226
|
+
|
|
120
227
|
module.exports = WebLinks;
|
package/lib/util/errors.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
var qs = require('querystring'),
|
|
11
11
|
httpStatusCodes = require('http-status');
|
|
12
12
|
|
|
13
|
+
const TRACE_ID_HEADER_NAME = 'box-request-id';
|
|
13
14
|
|
|
14
15
|
// ------------------------------------------------------------------------------
|
|
15
16
|
// Typedefs and Callbacks
|
|
@@ -37,14 +38,18 @@ var qs = require('querystring'),
|
|
|
37
38
|
*/
|
|
38
39
|
function Request(req) {
|
|
39
40
|
this.method = req.method;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
if (req.uri) {
|
|
42
|
+
this.url = {
|
|
43
|
+
protocol: req.uri.protocol,
|
|
44
|
+
host: req.uri.host,
|
|
45
|
+
path: req.uri.pathname,
|
|
46
|
+
query: qs.parse(req.uri.query),
|
|
47
|
+
fragment: req.uri.hash
|
|
48
|
+
};
|
|
49
|
+
} else {
|
|
50
|
+
this.url = null;
|
|
51
|
+
}
|
|
52
|
+
this.httpVersion = req.response ? req.response.httpVersion : null;
|
|
48
53
|
this.headers = req.headers;
|
|
49
54
|
this.body = req.body;
|
|
50
55
|
}
|
|
@@ -76,23 +81,42 @@ module.exports = {
|
|
|
76
81
|
message = message || 'API Response Error';
|
|
77
82
|
|
|
78
83
|
var statusCode = response.statusCode;
|
|
79
|
-
var
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
var statusMessage = httpStatusCodes[statusCode];
|
|
85
|
+
var debugID = ''; // Of the form <requestID>.<traceID>, both parts optional
|
|
86
|
+
var errorCode;
|
|
87
|
+
var errorDescription;
|
|
88
|
+
|
|
89
|
+
if (response.headers && response.headers[TRACE_ID_HEADER_NAME]) {
|
|
90
|
+
// Append trace ID with dot separator — if not present, the dot should be omitted
|
|
91
|
+
debugID += `.${response.headers[TRACE_ID_HEADER_NAME]}`;
|
|
82
92
|
}
|
|
83
93
|
|
|
84
|
-
var apiMessage = '';
|
|
85
94
|
|
|
86
95
|
if (response.body) {
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
97
|
+
if (response.body.request_id) {
|
|
98
|
+
// Prepend request ID
|
|
99
|
+
debugID = response.body.request_id + debugID;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
errorCode = response.body.code || response.body.error;
|
|
103
|
+
errorDescription = response.body.message || response.body.error_description;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
var errorMessage;
|
|
107
|
+
if (debugID) {
|
|
108
|
+
errorMessage = `${message} [${statusCode} ${statusMessage} | ${debugID}]`;
|
|
109
|
+
} else {
|
|
110
|
+
errorMessage = `${message} [${statusCode} ${statusMessage}]`;
|
|
111
|
+
}
|
|
90
112
|
|
|
91
|
-
|
|
92
|
-
|
|
113
|
+
if (errorCode) {
|
|
114
|
+
errorMessage += ` ${errorCode}`;
|
|
115
|
+
}
|
|
116
|
+
if (errorDescription) {
|
|
117
|
+
errorMessage += ` - ${errorDescription}`;
|
|
93
118
|
}
|
|
94
119
|
|
|
95
|
-
var errorMessage = `${message} [${statusCode} ${httpStatusCodes[statusCode]}${requestID}]${apiMessage}`;
|
|
96
120
|
var responseError = new Error(errorMessage);
|
|
97
121
|
|
|
98
122
|
responseError.statusCode = response.statusCode;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "box-node-sdk",
|
|
3
3
|
"author": "Box <oss@box.com>",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.29.0",
|
|
5
5
|
"description": "Official SDK for Box Plaform APIs",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"nyc": "^11.4.1",
|
|
59
59
|
"shelljs": "^0.8.1",
|
|
60
60
|
"shelljs-nodecli": "^0.1.1",
|
|
61
|
-
"sinon": "^
|
|
61
|
+
"sinon": "^7.2.4"
|
|
62
62
|
},
|
|
63
63
|
"files": [
|
|
64
64
|
"config",
|