files.com 1.0.295 → 1.0.296
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/_VERSION +1 -1
- package/lib/Api.js +180 -80
- package/lib/Errors.js +2 -2
- package/lib/isomorphic/File.node.js +5 -3
- package/package.json +2 -3
- package/src/Api.js +68 -30
- package/src/Errors.js +2 -2
- package/src/isomorphic/File.node.js +5 -2
- package/test/package.json +2 -1
- package/test/src/index.js +54 -40
package/_VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.296
|
package/lib/Api.js
CHANGED
@@ -7,92 +7,192 @@ exports.default = void 0;
|
|
7
7
|
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
8
8
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
9
9
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
10
|
-
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
11
10
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
12
11
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
13
12
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
14
|
-
var
|
15
|
-
var
|
13
|
+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
14
|
+
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
|
15
|
+
var _crossFetch = _interopRequireDefault(require("cross-fetch"));
|
16
16
|
var _Files = _interopRequireDefault(require("./Files"));
|
17
17
|
var errors = _interopRequireWildcard(require("./Errors"));
|
18
18
|
var _Logger = _interopRequireDefault(require("./Logger"));
|
19
19
|
var _utils = require("./utils");
|
20
|
+
var _excluded = ["timeoutSecs"];
|
20
21
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
21
22
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
22
23
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
23
24
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
25
|
+
var _fetchWithTimeout = function _fetchWithTimeout(url) {
|
26
|
+
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
27
|
+
timeoutSecs = _ref.timeoutSecs,
|
28
|
+
options = (0, _objectWithoutProperties2.default)(_ref, _excluded);
|
29
|
+
return timeoutSecs <= 0 ? (0, _crossFetch.default)(url, options) : Promise.race([(0, _crossFetch.default)(url, options), new Promise(function (_, reject) {
|
30
|
+
setTimeout(function () {
|
31
|
+
return reject(new errors.FilesError('Request timed out'));
|
32
|
+
}, timeoutSecs * 1000);
|
33
|
+
})]);
|
34
|
+
};
|
35
|
+
var fetchWithRetry = /*#__PURE__*/function () {
|
36
|
+
var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(url, options) {
|
37
|
+
var retries,
|
38
|
+
maxRetries,
|
39
|
+
minRetryDelaySecs,
|
40
|
+
maxRetryDelaySecs,
|
41
|
+
nextRetries,
|
42
|
+
delaySecs,
|
43
|
+
_args = arguments;
|
44
|
+
return _regenerator.default.wrap(function _callee$(_context) {
|
45
|
+
while (1) switch (_context.prev = _context.next) {
|
46
|
+
case 0:
|
47
|
+
retries = _args.length > 2 && _args[2] !== undefined ? _args[2] : 0;
|
48
|
+
maxRetries = _Files.default.getMaxNetworkRetries();
|
49
|
+
minRetryDelaySecs = _Files.default.getMinNetworkRetryDelay();
|
50
|
+
maxRetryDelaySecs = _Files.default.getMaxNetworkRetryDelay();
|
51
|
+
_context.prev = 4;
|
52
|
+
_context.next = 7;
|
53
|
+
return _fetchWithTimeout(url, options);
|
54
|
+
case 7:
|
55
|
+
return _context.abrupt("return", _context.sent);
|
56
|
+
case 10:
|
57
|
+
_context.prev = 10;
|
58
|
+
_context.t0 = _context["catch"](4);
|
59
|
+
_Logger.default.info("Request #".concat(retries + 1, " failed: ").concat(_context.t0.message));
|
60
|
+
if (!(retries >= maxRetries)) {
|
61
|
+
_context.next = 17;
|
62
|
+
break;
|
63
|
+
}
|
64
|
+
throw _context.t0;
|
65
|
+
case 17:
|
66
|
+
nextRetries = retries + 1;
|
67
|
+
_Logger.default.info("Retrying request (retry ".concat(nextRetries, " of ").concat(maxRetries, ")"));
|
68
|
+
delaySecs = Math.min(minRetryDelaySecs * Math.pow(2, retries), maxRetryDelaySecs); // exponential backoff
|
69
|
+
_context.next = 22;
|
70
|
+
return new Promise(function (resolve) {
|
71
|
+
return setTimeout(resolve, delaySecs * 1000);
|
72
|
+
});
|
73
|
+
case 22:
|
74
|
+
return _context.abrupt("return", fetchWithRetry(url, options, nextRetries));
|
75
|
+
case 23:
|
76
|
+
case "end":
|
77
|
+
return _context.stop();
|
78
|
+
}
|
79
|
+
}, _callee, null, [[4, 10]]);
|
80
|
+
}));
|
81
|
+
return function fetchWithRetry(_x, _x2) {
|
82
|
+
return _ref2.apply(this, arguments);
|
83
|
+
};
|
84
|
+
}();
|
24
85
|
var Api = /*#__PURE__*/(0, _createClass2.default)(function Api() {
|
25
86
|
(0, _classCallCheck2.default)(this, Api);
|
26
87
|
});
|
27
|
-
(0, _defineProperty2.default)(Api, "_configureAutoRetry", function () {
|
28
|
-
(0, _axiosRetry.default)(_axios.default, {
|
29
|
-
retries: _Files.default.getMaxNetworkRetries(),
|
30
|
-
retryDelay: function retryDelay(retries) {
|
31
|
-
_Logger.default.info("Retrying request (retry ".concat(retries, " of ").concat(_Files.default.getMaxNetworkRetries(), ")"));
|
32
|
-
return Math.min(retries * _Files.default.getMinNetworkRetryDelay() * 1000, _Files.default.getMaxNetworkRetryDelay() * 1000);
|
33
|
-
}
|
34
|
-
});
|
35
|
-
});
|
36
88
|
(0, _defineProperty2.default)(Api, "_sendVerbatim", /*#__PURE__*/function () {
|
37
|
-
var
|
38
|
-
var isExternal, baseUrl, url, response;
|
39
|
-
return _regenerator.default.wrap(function
|
40
|
-
while (1) switch (
|
89
|
+
var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(path, verb, options) {
|
90
|
+
var isExternal, baseUrl, url, agent, response, headers, contentType, data, normalizedResponse;
|
91
|
+
return _regenerator.default.wrap(function _callee2$(_context2) {
|
92
|
+
while (1) switch (_context2.prev = _context2.next) {
|
41
93
|
case 0:
|
42
94
|
isExternal = /^[a-zA-Z]+:\/\//.test(path);
|
43
95
|
baseUrl = _Files.default.getBaseUrl();
|
44
96
|
if (!(!isExternal && !baseUrl)) {
|
45
|
-
|
97
|
+
_context2.next = 4;
|
46
98
|
break;
|
47
99
|
}
|
48
100
|
throw new errors.ConfigurationError('Base URL has not been set - use Files.setBaseUrl() to set it');
|
49
101
|
case 4:
|
50
102
|
url = isExternal ? path : "".concat(baseUrl).concat(_Files.default.getEndpointPrefix()).concat(path);
|
51
103
|
_Logger.default.debug("Sending request: ".concat(verb, " ").concat(url));
|
52
|
-
_Logger.default.debug('Sending options:', _objectSpread(_objectSpread({
|
104
|
+
_Logger.default.debug('Sending options:', _objectSpread(_objectSpread({
|
105
|
+
method: verb
|
106
|
+
}, options), {}, {
|
53
107
|
headers: _objectSpread(_objectSpread({}, options.headers), {}, {
|
54
108
|
'X-FilesAPI-Key': '<redacted>'
|
55
109
|
})
|
56
110
|
}));
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
return (
|
111
|
+
_context2.prev = 7;
|
112
|
+
agent = (options === null || options === void 0 ? void 0 : options.agent) || (options === null || options === void 0 ? void 0 : options.httpsAgent) || (options === null || options === void 0 ? void 0 : options.httpAgent);
|
113
|
+
_context2.next = 11;
|
114
|
+
return fetchWithRetry(url, _objectSpread({
|
115
|
+
agent: agent,
|
61
116
|
method: verb,
|
62
|
-
|
63
|
-
url: url
|
117
|
+
timeoutSecs: _Files.default.getNetworkTimeout()
|
64
118
|
}, options));
|
65
119
|
case 11:
|
66
|
-
response =
|
120
|
+
response = _context2.sent;
|
121
|
+
headers = Object.fromEntries(response.headers.entries());
|
67
122
|
_Logger.default.debug("Status: ".concat(response.status, " ").concat(response.statusText));
|
68
123
|
if (_Files.default.shouldDebugResponseHeaders()) {
|
69
124
|
_Logger.default.debug('Response Headers: ');
|
70
|
-
_Logger.default.debug(
|
125
|
+
_Logger.default.debug(headers);
|
126
|
+
}
|
127
|
+
contentType = headers['content-type'] || '';
|
128
|
+
if (!contentType.includes('application/json')) {
|
129
|
+
_context2.next = 22;
|
130
|
+
break;
|
131
|
+
}
|
132
|
+
_context2.next = 19;
|
133
|
+
return response.json();
|
134
|
+
case 19:
|
135
|
+
data = _context2.sent;
|
136
|
+
_context2.next = 35;
|
137
|
+
break;
|
138
|
+
case 22:
|
139
|
+
if (!contentType.includes('text/')) {
|
140
|
+
_context2.next = 28;
|
141
|
+
break;
|
71
142
|
}
|
72
|
-
|
143
|
+
_context2.next = 25;
|
144
|
+
return response.text();
|
145
|
+
case 25:
|
146
|
+
data = _context2.sent;
|
147
|
+
_context2.next = 35;
|
148
|
+
break;
|
149
|
+
case 28:
|
150
|
+
if (!contentType.includes('multipart/form-data')) {
|
151
|
+
_context2.next = 34;
|
152
|
+
break;
|
153
|
+
}
|
154
|
+
_context2.next = 31;
|
155
|
+
return response.formData();
|
156
|
+
case 31:
|
157
|
+
data = _context2.sent;
|
158
|
+
_context2.next = 35;
|
159
|
+
break;
|
160
|
+
case 34:
|
161
|
+
data = response.body;
|
162
|
+
case 35:
|
163
|
+
normalizedResponse = {
|
73
164
|
status: response.status,
|
74
165
|
reason: response.statusText,
|
75
|
-
headers:
|
76
|
-
data:
|
77
|
-
}
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
166
|
+
headers: headers,
|
167
|
+
data: data
|
168
|
+
};
|
169
|
+
if (response.ok) {
|
170
|
+
_context2.next = 38;
|
171
|
+
break;
|
172
|
+
}
|
173
|
+
throw {
|
174
|
+
response: normalizedResponse
|
175
|
+
};
|
176
|
+
case 38:
|
177
|
+
return _context2.abrupt("return", normalizedResponse);
|
178
|
+
case 41:
|
179
|
+
_context2.prev = 41;
|
180
|
+
_context2.t0 = _context2["catch"](7);
|
181
|
+
errors.handleErrorResponse(_context2.t0);
|
182
|
+
case 44:
|
83
183
|
case "end":
|
84
|
-
return
|
184
|
+
return _context2.stop();
|
85
185
|
}
|
86
|
-
},
|
186
|
+
}, _callee2, null, [[7, 41]]);
|
87
187
|
}));
|
88
|
-
return function (
|
89
|
-
return
|
188
|
+
return function (_x3, _x4, _x5) {
|
189
|
+
return _ref3.apply(this, arguments);
|
90
190
|
};
|
91
191
|
}());
|
92
192
|
(0, _defineProperty2.default)(Api, "sendFilePart", function (externalUrl, verb, data) {
|
93
193
|
var headers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
94
194
|
var params = {
|
95
|
-
|
195
|
+
body: data
|
96
196
|
};
|
97
197
|
if (headers) {
|
98
198
|
params.headers = headers;
|
@@ -100,20 +200,20 @@ var Api = /*#__PURE__*/(0, _createClass2.default)(function Api() {
|
|
100
200
|
return Api._sendVerbatim(externalUrl, verb, params);
|
101
201
|
});
|
102
202
|
(0, _defineProperty2.default)(Api, "_autoPaginate", /*#__PURE__*/function () {
|
103
|
-
var
|
203
|
+
var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(path, verb, params, options, response, metadata) {
|
104
204
|
var _options$autoPaginate;
|
105
|
-
var _response$headers, nextCursor,
|
106
|
-
return _regenerator.default.wrap(function
|
107
|
-
while (1) switch (
|
205
|
+
var _response$headers, nextCursor, _ref5, autoPaginateCount, previousAutoPaginateData, nextPage, nextParams, nextMetadata;
|
206
|
+
return _regenerator.default.wrap(function _callee3$(_context3) {
|
207
|
+
while (1) switch (_context3.prev = _context3.next) {
|
108
208
|
case 0:
|
109
209
|
if (!((_options$autoPaginate = options.autoPaginate) !== null && _options$autoPaginate !== void 0 ? _options$autoPaginate : _Files.default.getAutoPaginate())) {
|
110
|
-
|
210
|
+
_context3.next = 12;
|
111
211
|
break;
|
112
212
|
}
|
113
213
|
nextCursor = response === null || response === void 0 || (_response$headers = response.headers) === null || _response$headers === void 0 ? void 0 : _response$headers['x-files-cursor'];
|
114
|
-
|
214
|
+
_ref5 = metadata || {}, autoPaginateCount = _ref5.autoPaginateCount, previousAutoPaginateData = _ref5.previousAutoPaginateData;
|
115
215
|
if (!nextCursor) {
|
116
|
-
|
216
|
+
_context3.next = 10;
|
117
217
|
break;
|
118
218
|
}
|
119
219
|
nextPage = (Number(params === null || params === void 0 ? void 0 : params.page) || 1) + 1;
|
@@ -125,30 +225,30 @@ var Api = /*#__PURE__*/(0, _createClass2.default)(function Api() {
|
|
125
225
|
autoPaginateCount: (autoPaginateCount || 1) + 1,
|
126
226
|
previousAutoPaginateData: [].concat((0, _toConsumableArray2.default)(previousAutoPaginateData || []), (0, _toConsumableArray2.default)((response === null || response === void 0 ? void 0 : response.data) || []))
|
127
227
|
};
|
128
|
-
return
|
228
|
+
return _context3.abrupt("return", Api.sendRequest(path, verb, nextParams, options, nextMetadata));
|
129
229
|
case 10:
|
130
230
|
if (!previousAutoPaginateData) {
|
131
|
-
|
231
|
+
_context3.next = 12;
|
132
232
|
break;
|
133
233
|
}
|
134
|
-
return
|
234
|
+
return _context3.abrupt("return", _objectSpread(_objectSpread({}, response), {}, {
|
135
235
|
autoPaginateRequests: autoPaginateCount,
|
136
236
|
data: [].concat((0, _toConsumableArray2.default)(previousAutoPaginateData), (0, _toConsumableArray2.default)((response === null || response === void 0 ? void 0 : response.data) || []))
|
137
237
|
}));
|
138
238
|
case 12:
|
139
|
-
return
|
239
|
+
return _context3.abrupt("return", response);
|
140
240
|
case 13:
|
141
241
|
case "end":
|
142
|
-
return
|
242
|
+
return _context3.stop();
|
143
243
|
}
|
144
|
-
},
|
244
|
+
}, _callee3);
|
145
245
|
}));
|
146
|
-
return function (
|
147
|
-
return
|
246
|
+
return function (_x6, _x7, _x8, _x9, _x10, _x11) {
|
247
|
+
return _ref4.apply(this, arguments);
|
148
248
|
};
|
149
249
|
}());
|
150
250
|
(0, _defineProperty2.default)(Api, "sendRequest", /*#__PURE__*/function () {
|
151
|
-
var
|
251
|
+
var _ref6 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(path, verb) {
|
152
252
|
var params,
|
153
253
|
options,
|
154
254
|
metadata,
|
@@ -162,13 +262,13 @@ var Api = /*#__PURE__*/(0, _createClass2.default)(function Api() {
|
|
162
262
|
hasParams,
|
163
263
|
pairs,
|
164
264
|
response,
|
165
|
-
|
166
|
-
return _regenerator.default.wrap(function
|
167
|
-
while (1) switch (
|
265
|
+
_args4 = arguments;
|
266
|
+
return _regenerator.default.wrap(function _callee4$(_context4) {
|
267
|
+
while (1) switch (_context4.prev = _context4.next) {
|
168
268
|
case 0:
|
169
|
-
params =
|
170
|
-
options =
|
171
|
-
metadata =
|
269
|
+
params = _args4.length > 2 && _args4[2] !== undefined ? _args4[2] : null;
|
270
|
+
options = _args4.length > 3 && _args4[3] !== undefined ? _args4[3] : {};
|
271
|
+
metadata = _args4.length > 4 && _args4[4] !== undefined ? _args4[4] : null;
|
172
272
|
headers = _objectSpread(_objectSpread({
|
173
273
|
Accept: 'application/json'
|
174
274
|
}, options.headers), {}, {
|
@@ -176,26 +276,26 @@ var Api = /*#__PURE__*/(0, _createClass2.default)(function Api() {
|
|
176
276
|
});
|
177
277
|
isExternal = /^[a-zA-Z]+:\/\//.test(path);
|
178
278
|
if (isExternal) {
|
179
|
-
|
279
|
+
_context4.next = 17;
|
180
280
|
break;
|
181
281
|
}
|
182
282
|
sessionId = options.sessionId || _Files.default.getSessionId();
|
183
283
|
if (!sessionId) {
|
184
|
-
|
284
|
+
_context4.next = 11;
|
185
285
|
break;
|
186
286
|
}
|
187
287
|
headers['X-FilesAPI-Auth'] = sessionId;
|
188
|
-
|
288
|
+
_context4.next = 17;
|
189
289
|
break;
|
190
290
|
case 11:
|
191
291
|
isCreatingSession = path === '/sessions' && verb.toUpperCase() === 'POST'; // api key cannot be used when creating a session
|
192
292
|
if (isCreatingSession) {
|
193
|
-
|
293
|
+
_context4.next = 17;
|
194
294
|
break;
|
195
295
|
}
|
196
296
|
apiKey = options.apiKey || _Files.default.getApiKey();
|
197
297
|
if (apiKey) {
|
198
|
-
|
298
|
+
_context4.next = 16;
|
199
299
|
break;
|
200
300
|
}
|
201
301
|
throw new errors.ConfigurationError('API key has not been set - use Files.setApiKey() to set it');
|
@@ -209,41 +309,41 @@ var Api = /*#__PURE__*/(0, _createClass2.default)(function Api() {
|
|
209
309
|
hasParams = (0, _utils.isObject)(params) && !(0, _utils.isEmpty)(params);
|
210
310
|
if (hasParams) {
|
211
311
|
if (verb.toUpperCase() === 'GET') {
|
212
|
-
pairs = Object.entries(params).map(function (
|
213
|
-
var
|
214
|
-
key =
|
215
|
-
value =
|
312
|
+
pairs = Object.entries(params).map(function (_ref7) {
|
313
|
+
var _ref8 = (0, _slicedToArray2.default)(_ref7, 2),
|
314
|
+
key = _ref8[0],
|
315
|
+
value = _ref8[1];
|
216
316
|
return "".concat(encodeURIComponent(key), "=").concat(encodeURIComponent(value));
|
217
317
|
});
|
218
318
|
requestPath += path.includes('?') ? '&' : '?';
|
219
319
|
requestPath += pairs.join('&');
|
220
320
|
} else {
|
221
|
-
updatedOptions.
|
321
|
+
updatedOptions.body = JSON.stringify(params);
|
222
322
|
headers['Content-Type'] = 'application/json';
|
223
323
|
}
|
224
324
|
}
|
225
325
|
if (_Files.default.shouldDebugRequest()) {
|
226
326
|
_Logger.default.debug('Request Options:');
|
227
327
|
_Logger.default.debug(_objectSpread(_objectSpread({}, updatedOptions), {}, {
|
228
|
-
|
328
|
+
body: hasParams ? "payload keys: ".concat(Object.keys(params).join(', ')) : '(none)',
|
229
329
|
headers: _objectSpread(_objectSpread({}, headers), {}, {
|
230
330
|
'X-FilesAPI-Key': '<redacted>'
|
231
331
|
})
|
232
332
|
}));
|
233
333
|
}
|
234
|
-
|
334
|
+
_context4.next = 24;
|
235
335
|
return Api._sendVerbatim(requestPath, verb, updatedOptions);
|
236
336
|
case 24:
|
237
|
-
response =
|
238
|
-
return
|
337
|
+
response = _context4.sent;
|
338
|
+
return _context4.abrupt("return", Api._autoPaginate(path, verb, params, updatedOptions, response, metadata));
|
239
339
|
case 26:
|
240
340
|
case "end":
|
241
|
-
return
|
341
|
+
return _context4.stop();
|
242
342
|
}
|
243
|
-
},
|
343
|
+
}, _callee4);
|
244
344
|
}));
|
245
|
-
return function (
|
246
|
-
return
|
345
|
+
return function (_x12, _x13) {
|
346
|
+
return _ref6.apply(this, arguments);
|
247
347
|
};
|
248
348
|
}());
|
249
349
|
var _default = Api;
|
package/lib/Errors.js
CHANGED
@@ -56,14 +56,14 @@ var handleErrorResponse = function handleErrorResponse(error) {
|
|
56
56
|
var message = ((_errorData = errorData) === null || _errorData === void 0 ? void 0 : _errorData.error) || (response === null || response === void 0 ? void 0 : response.statusText) || error.message;
|
57
57
|
var code = (response === null || response === void 0 ? void 0 : response.status) || ((_errorData2 = errorData) === null || _errorData2 === void 0 ? void 0 : _errorData2['http-code']) || 0;
|
58
58
|
if (!errorData) {
|
59
|
-
_Logger.default.error('FilesApiError Exception
|
59
|
+
_Logger.default.error('FilesApiError Exception:', code, message);
|
60
60
|
throw new FilesApiError(message, code);
|
61
61
|
}
|
62
62
|
if (Array.isArray(errorData)) {
|
63
63
|
errorData = errorData[0];
|
64
64
|
}
|
65
65
|
if (!errorData.type) {
|
66
|
-
_Logger.default.error('FilesApiError Exception
|
66
|
+
_Logger.default.error('FilesApiError Exception:', code, message);
|
67
67
|
throw new FilesApiError(message, code);
|
68
68
|
}
|
69
69
|
var parts = errorData.type.split('/');
|
@@ -24,8 +24,12 @@ var saveUrlToStream = /*#__PURE__*/function () {
|
|
24
24
|
var https = require('https');
|
25
25
|
https.get(url, function (response) {
|
26
26
|
response.pipe(stream);
|
27
|
-
stream.on('finish',
|
27
|
+
stream.on('finish', function () {
|
28
|
+
stream.close();
|
29
|
+
resolve();
|
30
|
+
});
|
28
31
|
}).on('error', function (error) {
|
32
|
+
stream.close();
|
29
33
|
reject(error);
|
30
34
|
});
|
31
35
|
}));
|
@@ -80,8 +84,6 @@ var saveUrlToFile = /*#__PURE__*/function () {
|
|
80
84
|
_context3.next = 3;
|
81
85
|
return saveUrlToStream(url, stream);
|
82
86
|
case 3:
|
83
|
-
stream.close();
|
84
|
-
case 4:
|
85
87
|
case "end":
|
86
88
|
return _context3.stop();
|
87
89
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "files.com",
|
3
|
-
"version": "1.0.
|
3
|
+
"version": "1.0.296",
|
4
4
|
"description": "Files.com SDK for JavaScript",
|
5
5
|
"keywords": [
|
6
6
|
"files.com",
|
@@ -20,8 +20,7 @@
|
|
20
20
|
"main": "lib/files.js",
|
21
21
|
"dependencies": {
|
22
22
|
"@babel/runtime": "^7.10.3",
|
23
|
-
"
|
24
|
-
"axios-retry": "^3.5.0",
|
23
|
+
"cross-fetch": "^4.0.0",
|
25
24
|
"readable-stream": "^4.4.0",
|
26
25
|
"safe-buffer": "^5.2.1"
|
27
26
|
},
|
package/src/Api.js
CHANGED
@@ -1,29 +1,45 @@
|
|
1
|
-
import
|
2
|
-
import axiosRetry from 'axios-retry'
|
1
|
+
import fetch from 'cross-fetch'
|
3
2
|
|
4
3
|
import Files from './Files'
|
5
4
|
import * as errors from './Errors'
|
6
5
|
import Logger from './Logger'
|
7
6
|
import { isEmpty, isObject } from './utils'
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
8
|
+
const _fetchWithTimeout = (url, { timeoutSecs, ...options } = {}) =>
|
9
|
+
timeoutSecs <= 0
|
10
|
+
? fetch(url, options)
|
11
|
+
: Promise.race([
|
12
|
+
fetch(url, options),
|
13
|
+
new Promise((_, reject) => {
|
14
|
+
setTimeout(() => reject(new errors.FilesError('Request timed out')), timeoutSecs * 1000)
|
15
|
+
})
|
16
|
+
])
|
17
|
+
|
18
|
+
const fetchWithRetry = async (url, options, retries = 0) => {
|
19
|
+
const maxRetries = Files.getMaxNetworkRetries()
|
20
|
+
const minRetryDelaySecs = Files.getMinNetworkRetryDelay()
|
21
|
+
const maxRetryDelaySecs = Files.getMaxNetworkRetryDelay()
|
22
|
+
|
23
|
+
try {
|
24
|
+
return await _fetchWithTimeout(url, options)
|
25
|
+
} catch (error) {
|
26
|
+
Logger.info(`Request #${retries + 1} failed: ${error.message}`)
|
27
|
+
|
28
|
+
if (retries >= maxRetries) {
|
29
|
+
throw error
|
30
|
+
} else {
|
31
|
+
const nextRetries = retries + 1
|
32
|
+
Logger.info(`Retrying request (retry ${nextRetries} of ${maxRetries})`)
|
33
|
+
|
34
|
+
const delaySecs = Math.min(minRetryDelaySecs * 2 ** retries, maxRetryDelaySecs) // exponential backoff
|
35
|
+
await new Promise(resolve => setTimeout(resolve, delaySecs * 1000))
|
36
|
+
|
37
|
+
return fetchWithRetry(url, options, nextRetries)
|
38
|
+
}
|
25
39
|
}
|
40
|
+
}
|
26
41
|
|
42
|
+
class Api {
|
27
43
|
static _sendVerbatim = async (path, verb, options) => {
|
28
44
|
const isExternal = /^[a-zA-Z]+:\/\//.test(path)
|
29
45
|
const baseUrl = Files.getBaseUrl()
|
@@ -39,6 +55,7 @@ class Api {
|
|
39
55
|
Logger.debug(`Sending request: ${verb} ${url}`)
|
40
56
|
|
41
57
|
Logger.debug('Sending options:', {
|
58
|
+
method: verb,
|
42
59
|
...options,
|
43
60
|
headers: {
|
44
61
|
...options.headers,
|
@@ -46,36 +63,57 @@ class Api {
|
|
46
63
|
},
|
47
64
|
})
|
48
65
|
|
49
|
-
Api._configureAutoRetry()
|
50
|
-
|
51
66
|
try {
|
52
|
-
const
|
67
|
+
const agent = options?.agent || options?.httpsAgent || options?.httpAgent
|
68
|
+
|
69
|
+
const response = await fetchWithRetry(url, {
|
70
|
+
agent,
|
53
71
|
method: verb,
|
54
|
-
|
55
|
-
url,
|
72
|
+
timeoutSecs: Files.getNetworkTimeout(),
|
56
73
|
...options,
|
57
74
|
})
|
58
75
|
|
76
|
+
const headers = Object.fromEntries(response.headers.entries())
|
77
|
+
|
59
78
|
Logger.debug(`Status: ${response.status} ${response.statusText}`)
|
60
79
|
|
61
80
|
if (Files.shouldDebugResponseHeaders()) {
|
62
81
|
Logger.debug('Response Headers: ')
|
63
|
-
Logger.debug(
|
82
|
+
Logger.debug(headers)
|
83
|
+
}
|
84
|
+
|
85
|
+
const contentType = headers['content-type'] || ''
|
86
|
+
let data
|
87
|
+
|
88
|
+
if (contentType.includes('application/json')) {
|
89
|
+
data = await response.json()
|
90
|
+
} else if (contentType.includes('text/')) {
|
91
|
+
data = await response.text()
|
92
|
+
} else if (contentType.includes('multipart/form-data')) {
|
93
|
+
data = await response.formData()
|
94
|
+
} else {
|
95
|
+
data = response.body
|
64
96
|
}
|
65
97
|
|
66
|
-
|
98
|
+
const normalizedResponse = {
|
67
99
|
status: response.status,
|
68
100
|
reason: response.statusText,
|
69
|
-
headers
|
70
|
-
data
|
101
|
+
headers,
|
102
|
+
data,
|
103
|
+
}
|
104
|
+
|
105
|
+
if (!response.ok) {
|
106
|
+
throw { response: normalizedResponse }
|
71
107
|
}
|
108
|
+
|
109
|
+
return normalizedResponse
|
72
110
|
} catch (error) {
|
73
111
|
errors.handleErrorResponse(error)
|
74
112
|
}
|
75
113
|
}
|
76
114
|
|
77
115
|
static sendFilePart = (externalUrl, verb, data, headers = {}) => {
|
78
|
-
const params = { data }
|
116
|
+
const params = { body: data }
|
79
117
|
|
80
118
|
if (headers) {
|
81
119
|
params.headers = headers
|
@@ -166,7 +204,7 @@ class Api {
|
|
166
204
|
requestPath += path.includes('?') ? '&' : '?'
|
167
205
|
requestPath += pairs.join('&')
|
168
206
|
} else {
|
169
|
-
updatedOptions.
|
207
|
+
updatedOptions.body = JSON.stringify(params)
|
170
208
|
headers['Content-Type'] = 'application/json'
|
171
209
|
}
|
172
210
|
}
|
@@ -175,7 +213,7 @@ class Api {
|
|
175
213
|
Logger.debug('Request Options:')
|
176
214
|
Logger.debug({
|
177
215
|
...updatedOptions,
|
178
|
-
|
216
|
+
body: hasParams
|
179
217
|
? `payload keys: ${Object.keys(params).join(', ')}`
|
180
218
|
: '(none)',
|
181
219
|
headers: {
|
package/src/Errors.js
CHANGED
@@ -32,7 +32,7 @@ export const handleErrorResponse = error => {
|
|
32
32
|
const code = response?.status || errorData?.['http-code'] || 0
|
33
33
|
|
34
34
|
if (!errorData) {
|
35
|
-
Logger.error('FilesApiError Exception
|
35
|
+
Logger.error('FilesApiError Exception:', code, message)
|
36
36
|
throw new FilesApiError(message, code)
|
37
37
|
}
|
38
38
|
|
@@ -41,7 +41,7 @@ export const handleErrorResponse = error => {
|
|
41
41
|
}
|
42
42
|
|
43
43
|
if (!errorData.type) {
|
44
|
-
Logger.error('FilesApiError Exception
|
44
|
+
Logger.error('FilesApiError Exception:', code, message)
|
45
45
|
throw new FilesApiError(message, code)
|
46
46
|
}
|
47
47
|
|
@@ -14,9 +14,13 @@ const saveUrlToStream = async (url, stream) => new Promise((resolve, reject) =>
|
|
14
14
|
https.get(url, response => {
|
15
15
|
response.pipe(stream)
|
16
16
|
|
17
|
-
stream.on('finish',
|
17
|
+
stream.on('finish', () => {
|
18
|
+
stream.close()
|
19
|
+
resolve()
|
20
|
+
})
|
18
21
|
})
|
19
22
|
.on('error', error => {
|
23
|
+
stream.close()
|
20
24
|
reject(error)
|
21
25
|
})
|
22
26
|
})
|
@@ -37,7 +41,6 @@ const saveUrlToString = async url => new Promise((resolve, reject) => {
|
|
37
41
|
const saveUrlToFile = async (url, destinationPath) => {
|
38
42
|
const stream = openDiskFileWriteStream(destinationPath)
|
39
43
|
await saveUrlToStream(url, stream)
|
40
|
-
stream.close()
|
41
44
|
}
|
42
45
|
|
43
46
|
export {
|
package/test/package.json
CHANGED
package/test/src/index.js
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
import assert from 'assert'
|
2
|
-
// import * as errors from 'files.com/lib/Errors'
|
3
|
-
const errors = require('files.com/lib/Errors')
|
4
1
|
import Files from 'files.com/lib/Files'
|
5
2
|
import Logger, { LogLevel } from 'files.com/lib/Logger'
|
6
3
|
import ApiKey from 'files.com/lib/models/ApiKey'
|
@@ -9,6 +6,9 @@ import Folder from 'files.com/lib/models/Folder'
|
|
9
6
|
import Session from 'files.com/lib/models/Session'
|
10
7
|
import User from 'files.com/lib/models/User'
|
11
8
|
import { isBrowser } from 'files.com/lib/utils'
|
9
|
+
import invariant from 'tiny-invariant'
|
10
|
+
|
11
|
+
const errors = require('files.com/lib/Errors')
|
12
12
|
|
13
13
|
// name of an existing folder in your root to create/delete test files and folders
|
14
14
|
const SDK_TEST_ROOT_FOLDER = 'sdk-test'
|
@@ -32,11 +32,17 @@ if (apiDomain.substr(-10) === 'staging.av') {
|
|
32
32
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0
|
33
33
|
}
|
34
34
|
|
35
|
-
|
35
|
+
const DEBUG_MODE = false
|
36
|
+
|
37
|
+
Files.setLogLevel(DEBUG_MODE ? LogLevel.DEBUG : LogLevel.INFO)
|
36
38
|
|
37
39
|
Files.configureDebugging({
|
38
|
-
debugRequest:
|
39
|
-
debugResponseHeaders:
|
40
|
+
debugRequest: DEBUG_MODE,
|
41
|
+
debugResponseHeaders: DEBUG_MODE,
|
42
|
+
})
|
43
|
+
|
44
|
+
Files.configureNetwork({
|
45
|
+
networkTimeout: DEBUG_MODE ? 10 : 30,
|
40
46
|
})
|
41
47
|
|
42
48
|
const testSuite = async () => {
|
@@ -49,7 +55,7 @@ const testSuite = async () => {
|
|
49
55
|
|
50
56
|
const firstPageItems = await Folder.listFor('/', { per_page: 1 })
|
51
57
|
|
52
|
-
|
58
|
+
invariant(firstPageItems.length === 1, 'First page should have 1 item')
|
53
59
|
|
54
60
|
Files.configureNetwork({
|
55
61
|
autoPaginate: true,
|
@@ -59,7 +65,7 @@ const testSuite = async () => {
|
|
59
65
|
const allPageItems = await Folder.listFor('/', { per_page: 1 })
|
60
66
|
|
61
67
|
// if auto-pagination executed, we'll have found more than just the 1 we requested
|
62
|
-
|
68
|
+
invariant(allPageItems.length > 1, 'Auto-pagination should have found more than 1 item across all pages')
|
63
69
|
|
64
70
|
Logger.info('***** testFolderListAutoPagination() succeeded! *****')
|
65
71
|
}
|
@@ -72,18 +78,20 @@ const testSuite = async () => {
|
|
72
78
|
|
73
79
|
const file = await File.uploadFile(destinationPath, sourceFilePath)
|
74
80
|
|
75
|
-
|
76
|
-
|
81
|
+
invariant(!!file.path, 'Uploaded file response object should have a path')
|
82
|
+
invariant(file.display_name === displayName, 'Uploaded file response object should have the same display_name as the file we uploaded')
|
77
83
|
|
78
84
|
const foundFile = await File.find(destinationPath)
|
79
85
|
|
80
|
-
|
81
|
-
|
82
|
-
|
86
|
+
invariant(foundFile.path === destinationPath, 'Found file should have the same path as the file we uploaded')
|
87
|
+
invariant(foundFile.display_name === displayName, 'Found file should have the same display_name as the file we uploaded')
|
88
|
+
invariant(typeof foundFile.getDownloadUri() === 'undefined', 'Found file should not have a download uri yet')
|
83
89
|
|
84
90
|
if (!isBrowser()) {
|
85
91
|
const downloadableFile = await foundFile.download()
|
86
|
-
|
92
|
+
|
93
|
+
invariant(downloadableFile.size > 0, 'Uploaded file should not be empty')
|
94
|
+
invariant(typeof downloadableFile.getDownloadUri() !== 'undefined', 'Downloadable file should have a download uri')
|
87
95
|
|
88
96
|
const downloadPath = `./${displayName}`
|
89
97
|
await downloadableFile.downloadToFile(downloadPath)
|
@@ -92,7 +100,8 @@ const testSuite = async () => {
|
|
92
100
|
const originalBuffer = fs.readFileSync(sourceFilePath)
|
93
101
|
const downloadedBuffer = fs.readFileSync(downloadPath)
|
94
102
|
|
95
|
-
|
103
|
+
invariant(originalBuffer.length === downloadedBuffer.length, 'Source file length should match downloaded file length')
|
104
|
+
invariant(originalBuffer.equals(downloadedBuffer), 'Source file contents should match downloaded file contents')
|
96
105
|
|
97
106
|
fs.unlinkSync(downloadPath)
|
98
107
|
}
|
@@ -109,22 +118,22 @@ const testSuite = async () => {
|
|
109
118
|
const sourceFileContents = 'The quick brown fox jumped over the lazy dogs.'
|
110
119
|
const file = await File.uploadData(destinationPath, sourceFileContents)
|
111
120
|
|
112
|
-
|
113
|
-
|
121
|
+
invariant(!!file.path, 'Uploaded file response object should have a path')
|
122
|
+
invariant(file.display_name === displayName, 'Uploaded file response object should have the same display_name as the file we uploaded')
|
114
123
|
|
115
124
|
const foundFile = await File.find(destinationPath)
|
116
125
|
|
117
|
-
|
118
|
-
|
119
|
-
|
126
|
+
invariant(foundFile.path === destinationPath, 'Found file should have the same path as the file we uploaded')
|
127
|
+
invariant(foundFile.display_name === displayName, 'Found file should have the same display_name as the file we uploaded')
|
128
|
+
invariant(typeof foundFile.getDownloadUri() === 'undefined', 'Found file should not have a download uri yet')
|
120
129
|
|
121
130
|
if (!isBrowser()) {
|
122
131
|
const downloadableFile = await foundFile.download()
|
123
|
-
|
132
|
+
invariant(typeof downloadableFile.getDownloadUri() !== 'undefined', 'Downloadable file should have a download uri')
|
124
133
|
|
125
134
|
const downloadedFileContents = await downloadableFile.downloadToString()
|
126
135
|
|
127
|
-
|
136
|
+
invariant(sourceFileContents === downloadedFileContents, 'Source file contents should match downloaded file contents')
|
128
137
|
}
|
129
138
|
|
130
139
|
await file.delete()
|
@@ -141,14 +150,14 @@ const testSuite = async () => {
|
|
141
150
|
|
142
151
|
const file = await File.uploadFile(destinationPath, sourceFilePath)
|
143
152
|
|
144
|
-
|
145
|
-
|
153
|
+
invariant(!!file.path, 'Uploaded file response object should have a path')
|
154
|
+
invariant(file.display_name === displayName, 'Uploaded file response object should have the same display_name as the file we uploaded')
|
146
155
|
|
147
156
|
const foundFile = await File.find(destinationPath)
|
148
157
|
|
149
|
-
|
150
|
-
|
151
|
-
|
158
|
+
invariant(foundFile.path === destinationPath, 'Found file should have the same path as the file we uploaded')
|
159
|
+
invariant(foundFile.display_name === displayName, 'Found file should have the same display_name as the file we uploaded')
|
160
|
+
invariant(typeof foundFile.getDownloadUri() === 'undefined', 'Found file should not have a download uri yet')
|
152
161
|
|
153
162
|
await file.delete()
|
154
163
|
|
@@ -167,7 +176,7 @@ const testSuite = async () => {
|
|
167
176
|
|
168
177
|
const session = await Session.create({ username, password })
|
169
178
|
|
170
|
-
|
179
|
+
invariant(!!session.id, 'Session should have an id')
|
171
180
|
|
172
181
|
const manual = await ApiKey.list({ user_id: 0 }, { sessionId: session.id })
|
173
182
|
|
@@ -175,7 +184,7 @@ const testSuite = async () => {
|
|
175
184
|
|
176
185
|
const auto = await ApiKey.list({ user_id: 0 })
|
177
186
|
|
178
|
-
|
187
|
+
invariant(JSON.stringify(manual.attributes) === JSON.stringify(auto.attributes), 'Manual and auto session API key lists should match')
|
179
188
|
|
180
189
|
await Session.destroy()
|
181
190
|
|
@@ -197,7 +206,7 @@ const testSuite = async () => {
|
|
197
206
|
caughtInvalidCredentialsError = error instanceof errors.NotAuthenticated_InvalidCredentialsError
|
198
207
|
}
|
199
208
|
|
200
|
-
|
209
|
+
invariant(caughtInvalidCredentialsError === true, 'should have caught an invalid credentials error')
|
201
210
|
|
202
211
|
// restore valid api key
|
203
212
|
Files.setApiKey(apiKey)
|
@@ -218,9 +227,9 @@ const testSuite = async () => {
|
|
218
227
|
|
219
228
|
const updatedUser = await User.find(firstUser.id)
|
220
229
|
|
221
|
-
|
222
|
-
|
223
|
-
|
230
|
+
invariant(updatedUser.isLoaded(), 'updated user should be loaded')
|
231
|
+
invariant(oldName !== newName, 'old name should not equal new name')
|
232
|
+
invariant(updatedUser.name === newName, 'updated user name should match new name')
|
224
233
|
|
225
234
|
Logger.info('***** testUserListAndUpdate() succeeded! *****');
|
226
235
|
}
|
@@ -229,13 +238,18 @@ const testSuite = async () => {
|
|
229
238
|
// execute all tests
|
230
239
|
//
|
231
240
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
241
|
+
try {
|
242
|
+
await testFolderListAutoPagination()
|
243
|
+
await testUploadAndDownloadToFile()
|
244
|
+
await testUploadAndDownloadToString()
|
245
|
+
// await testUploadHugeFile() // to run this test, put a file (or symlink) at huge-file.ext
|
246
|
+
await testSession()
|
247
|
+
await testFailure()
|
248
|
+
await testUserListAndUpdate()
|
249
|
+
} catch (error) {
|
250
|
+
console.log('*** TEST SUITE FAILED ***')
|
251
|
+
console.error(error)
|
252
|
+
}
|
239
253
|
}
|
240
254
|
|
241
255
|
export default testSuite
|