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 CHANGED
@@ -1 +1 @@
1
- 1.0.295
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 _axios = _interopRequireDefault(require("axios"));
15
- var _axiosRetry = _interopRequireDefault(require("axios-retry"));
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 _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(path, verb, options) {
38
- var isExternal, baseUrl, url, response;
39
- return _regenerator.default.wrap(function _callee$(_context) {
40
- while (1) switch (_context.prev = _context.next) {
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
- _context.next = 4;
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({}, options), {}, {
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
- Api._configureAutoRetry();
58
- _context.prev = 8;
59
- _context.next = 11;
60
- return (0, _axios.default)(_objectSpread({
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
- timeout: _Files.default.getNetworkTimeout() * 1000,
63
- url: url
117
+ timeoutSecs: _Files.default.getNetworkTimeout()
64
118
  }, options));
65
119
  case 11:
66
- response = _context.sent;
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(response.headers);
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
- return _context.abrupt("return", {
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: response.headers,
76
- data: response.data
77
- });
78
- case 17:
79
- _context.prev = 17;
80
- _context.t0 = _context["catch"](8);
81
- errors.handleErrorResponse(_context.t0);
82
- case 20:
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 _context.stop();
184
+ return _context2.stop();
85
185
  }
86
- }, _callee, null, [[8, 17]]);
186
+ }, _callee2, null, [[7, 41]]);
87
187
  }));
88
- return function (_x, _x2, _x3) {
89
- return _ref.apply(this, arguments);
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
- data: data
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 _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(path, verb, params, options, response, metadata) {
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, _ref3, autoPaginateCount, previousAutoPaginateData, nextPage, nextParams, nextMetadata;
106
- return _regenerator.default.wrap(function _callee2$(_context2) {
107
- while (1) switch (_context2.prev = _context2.next) {
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
- _context2.next = 12;
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
- _ref3 = metadata || {}, autoPaginateCount = _ref3.autoPaginateCount, previousAutoPaginateData = _ref3.previousAutoPaginateData;
214
+ _ref5 = metadata || {}, autoPaginateCount = _ref5.autoPaginateCount, previousAutoPaginateData = _ref5.previousAutoPaginateData;
115
215
  if (!nextCursor) {
116
- _context2.next = 10;
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 _context2.abrupt("return", Api.sendRequest(path, verb, nextParams, options, nextMetadata));
228
+ return _context3.abrupt("return", Api.sendRequest(path, verb, nextParams, options, nextMetadata));
129
229
  case 10:
130
230
  if (!previousAutoPaginateData) {
131
- _context2.next = 12;
231
+ _context3.next = 12;
132
232
  break;
133
233
  }
134
- return _context2.abrupt("return", _objectSpread(_objectSpread({}, response), {}, {
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 _context2.abrupt("return", response);
239
+ return _context3.abrupt("return", response);
140
240
  case 13:
141
241
  case "end":
142
- return _context2.stop();
242
+ return _context3.stop();
143
243
  }
144
- }, _callee2);
244
+ }, _callee3);
145
245
  }));
146
- return function (_x4, _x5, _x6, _x7, _x8, _x9) {
147
- return _ref2.apply(this, arguments);
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 _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(path, verb) {
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
- _args3 = arguments;
166
- return _regenerator.default.wrap(function _callee3$(_context3) {
167
- while (1) switch (_context3.prev = _context3.next) {
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 = _args3.length > 2 && _args3[2] !== undefined ? _args3[2] : null;
170
- options = _args3.length > 3 && _args3[3] !== undefined ? _args3[3] : {};
171
- metadata = _args3.length > 4 && _args3[4] !== undefined ? _args3[4] : null;
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
- _context3.next = 17;
279
+ _context4.next = 17;
180
280
  break;
181
281
  }
182
282
  sessionId = options.sessionId || _Files.default.getSessionId();
183
283
  if (!sessionId) {
184
- _context3.next = 11;
284
+ _context4.next = 11;
185
285
  break;
186
286
  }
187
287
  headers['X-FilesAPI-Auth'] = sessionId;
188
- _context3.next = 17;
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
- _context3.next = 17;
293
+ _context4.next = 17;
194
294
  break;
195
295
  }
196
296
  apiKey = options.apiKey || _Files.default.getApiKey();
197
297
  if (apiKey) {
198
- _context3.next = 16;
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 (_ref5) {
213
- var _ref6 = (0, _slicedToArray2.default)(_ref5, 2),
214
- key = _ref6[0],
215
- value = _ref6[1];
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.data = JSON.stringify(params);
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
- data: hasParams ? "payload keys: ".concat(Object.keys(params).join(', ')) : '(none)',
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
- _context3.next = 24;
334
+ _context4.next = 24;
235
335
  return Api._sendVerbatim(requestPath, verb, updatedOptions);
236
336
  case 24:
237
- response = _context3.sent;
238
- return _context3.abrupt("return", Api._autoPaginate(path, verb, params, updatedOptions, response, metadata));
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 _context3.stop();
341
+ return _context4.stop();
242
342
  }
243
- }, _callee3);
343
+ }, _callee4);
244
344
  }));
245
- return function (_x10, _x11) {
246
- return _ref4.apply(this, arguments);
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 >', code, message);
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 >', code, message);
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', resolve);
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.295",
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
- "axios": "^1.4.0",
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 axios from 'axios'
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
- class Api {
10
- static _configureAutoRetry = () => {
11
- axiosRetry(
12
- axios,
13
- {
14
- retries: Files.getMaxNetworkRetries(),
15
- retryDelay: retries => {
16
- Logger.info(`Retrying request (retry ${retries} of ${Files.getMaxNetworkRetries()})`)
17
-
18
- return Math.min(
19
- retries * Files.getMinNetworkRetryDelay() * 1000,
20
- Files.getMaxNetworkRetryDelay() * 1000
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 response = await axios({
67
+ const agent = options?.agent || options?.httpsAgent || options?.httpAgent
68
+
69
+ const response = await fetchWithRetry(url, {
70
+ agent,
53
71
  method: verb,
54
- timeout: Files.getNetworkTimeout() * 1000,
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(response.headers)
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
- return {
98
+ const normalizedResponse = {
67
99
  status: response.status,
68
100
  reason: response.statusText,
69
- headers: response.headers,
70
- data: response.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.data = JSON.stringify(params)
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
- data: hasParams
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 >', code, message)
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 >', code, message)
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', resolve)
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
@@ -8,7 +8,8 @@
8
8
  "dependencies": {
9
9
  "@babel/runtime": "^7.10.3",
10
10
  "files.com": "file:../",
11
- "readable-stream": "^3.6.0"
11
+ "readable-stream": "^3.6.0",
12
+ "tiny-invariant": "^1.3.1"
12
13
  },
13
14
  "devDependencies": {
14
15
  "@babel/cli": "^7.10.3",
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
- Files.setLogLevel(LogLevel.INFO)
35
+ const DEBUG_MODE = false
36
+
37
+ Files.setLogLevel(DEBUG_MODE ? LogLevel.DEBUG : LogLevel.INFO)
36
38
 
37
39
  Files.configureDebugging({
38
- debugRequest: false,
39
- debugResponseHeaders: false,
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
- assert(firstPageItems.length === 1)
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
- assert(allPageItems.length > 1)
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
- assert(!!file.path)
76
- assert(file.display_name === displayName)
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
- assert(foundFile.path === destinationPath)
81
- assert(foundFile.display_name === displayName)
82
- assert(typeof foundFile.getDownloadUri() === 'undefined')
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
- assert(typeof downloadableFile.getDownloadUri() !== 'undefined')
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
- assert(originalBuffer.equals(downloadedBuffer))
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
- assert(!!file.path)
113
- assert(file.display_name === displayName)
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
- assert(foundFile.path === destinationPath)
118
- assert(foundFile.display_name === displayName)
119
- assert(typeof foundFile.getDownloadUri() === 'undefined')
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
- assert(typeof downloadableFile.getDownloadUri() !== 'undefined')
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
- assert(sourceFileContents === downloadedFileContents)
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
- assert(!!file.path)
145
- assert(file.display_name === displayName)
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
- assert(foundFile.path === destinationPath)
150
- assert(foundFile.display_name === displayName)
151
- assert(typeof foundFile.getDownloadUri() === 'undefined')
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
- assert(!!session.id)
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
- assert(JSON.stringify(manual.attributes) === JSON.stringify(auto.attributes))
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
- assert(caughtInvalidCredentialsError === true)
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
- assert(updatedUser.isLoaded())
222
- assert(oldName !== newName)
223
- assert(updatedUser.name === newName)
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
- await testFolderListAutoPagination()
233
- await testUploadAndDownloadToFile()
234
- await testUploadAndDownloadToString()
235
- // await testUploadHugeFile() // to run this test, put a file (or symlink) at huge-file.ext
236
- await testSession()
237
- await testFailure()
238
- await testUserListAndUpdate()
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