RESTController.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. "use strict";
  2. var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");
  3. var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
  4. _Object$defineProperty(exports, "__esModule", {
  5. value: true
  6. });
  7. exports.default = void 0;
  8. var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));
  9. var _stringify = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/json/stringify"));
  10. var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));
  11. var _forEach = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/for-each"));
  12. var _indexOf = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/index-of"));
  13. var _setTimeout2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/set-timeout"));
  14. var _uuid = _interopRequireDefault(require("./uuid"));
  15. var _CoreManager = _interopRequireDefault(require("./CoreManager"));
  16. var _ParseError = _interopRequireDefault(require("./ParseError"));
  17. var _promiseUtils = require("./promiseUtils");
  18. var _Xhr = _interopRequireDefault(require("./Xhr.weapp"));
  19. /* global XMLHttpRequest, XDomainRequest */
  20. let XHR = null;
  21. if (typeof XMLHttpRequest !== 'undefined') {
  22. XHR = XMLHttpRequest;
  23. }
  24. let useXDomainRequest = false;
  25. // @ts-ignore
  26. if (typeof XDomainRequest !== 'undefined' && !('withCredentials' in new XMLHttpRequest())) {
  27. useXDomainRequest = true;
  28. }
  29. function ajaxIE9(method, url, data, headers, options) {
  30. return new _promise.default((resolve, reject) => {
  31. // @ts-ignore
  32. const xdr = new XDomainRequest();
  33. xdr.onload = function () {
  34. let response;
  35. try {
  36. response = JSON.parse(xdr.responseText);
  37. } catch (e) {
  38. reject(e);
  39. }
  40. if (response) {
  41. resolve({
  42. response
  43. });
  44. }
  45. };
  46. xdr.onerror = xdr.ontimeout = function () {
  47. // Let's fake a real error message.
  48. const fakeResponse = {
  49. responseText: (0, _stringify.default)({
  50. code: _ParseError.default.X_DOMAIN_REQUEST,
  51. error: "IE's XDomainRequest does not supply error info."
  52. })
  53. };
  54. reject(fakeResponse);
  55. };
  56. xdr.onprogress = function () {
  57. if (options && typeof options.progress === 'function') {
  58. options.progress(xdr.responseText);
  59. }
  60. };
  61. xdr.open(method, url);
  62. xdr.send(data);
  63. // @ts-ignore
  64. if (options && typeof options.requestTask === 'function') {
  65. // @ts-ignore
  66. options.requestTask(xdr);
  67. }
  68. });
  69. }
  70. const RESTController = {
  71. ajax(method, url, data, headers, options) {
  72. var _context;
  73. if (useXDomainRequest) {
  74. return ajaxIE9(method, url, data, headers, options);
  75. }
  76. const promise = (0, _promiseUtils.resolvingPromise)();
  77. const isIdempotent = _CoreManager.default.get('IDEMPOTENCY') && (0, _includes.default)(_context = ['POST', 'PUT']).call(_context, method);
  78. const requestId = isIdempotent ? (0, _uuid.default)() : '';
  79. let attempts = 0;
  80. const dispatch = function () {
  81. if (XHR == null) {
  82. throw new Error('Cannot make a request: No definition of XMLHttpRequest was found.');
  83. }
  84. let handled = false;
  85. const xhr = new XHR();
  86. xhr.onreadystatechange = function () {
  87. if (xhr.readyState !== 4 || handled || xhr._aborted) {
  88. return;
  89. }
  90. handled = true;
  91. if (xhr.status >= 200 && xhr.status < 300) {
  92. let response;
  93. try {
  94. response = JSON.parse(xhr.responseText);
  95. const availableHeaders = typeof xhr.getAllResponseHeaders === 'function' ? xhr.getAllResponseHeaders() : '';
  96. headers = {};
  97. if (typeof xhr.getResponseHeader === 'function' && availableHeaders?.indexOf('access-control-expose-headers') >= 0) {
  98. const responseHeaders = xhr.getResponseHeader('access-control-expose-headers').split(', ');
  99. (0, _forEach.default)(responseHeaders).call(responseHeaders, header => {
  100. if ((0, _indexOf.default)(availableHeaders).call(availableHeaders, header.toLowerCase()) >= 0) {
  101. headers[header] = xhr.getResponseHeader(header.toLowerCase());
  102. }
  103. });
  104. }
  105. } catch (e) {
  106. promise.reject(e.toString());
  107. }
  108. if (response) {
  109. promise.resolve({
  110. response,
  111. headers,
  112. status: xhr.status,
  113. xhr
  114. });
  115. }
  116. } else if (xhr.status >= 500 || xhr.status === 0) {
  117. // retry on 5XX or node-xmlhttprequest error
  118. if (++attempts < _CoreManager.default.get('REQUEST_ATTEMPT_LIMIT')) {
  119. // Exponentially-growing random delay
  120. const delay = Math.round(Math.random() * 125 * Math.pow(2, attempts));
  121. (0, _setTimeout2.default)(dispatch, delay);
  122. } else if (xhr.status === 0) {
  123. promise.reject('Unable to connect to the Parse API');
  124. } else {
  125. // After the retry limit is reached, fail
  126. promise.reject(xhr);
  127. }
  128. } else {
  129. promise.reject(xhr);
  130. }
  131. };
  132. headers = headers || {};
  133. if (typeof headers['Content-Type'] !== 'string') {
  134. headers['Content-Type'] = 'text/plain'; // Avoid pre-flight
  135. }
  136. if (_CoreManager.default.get('IS_NODE')) {
  137. headers['User-Agent'] = 'Parse/' + _CoreManager.default.get('VERSION') + ' (NodeJS ' + process.versions.node + ')';
  138. }
  139. if (isIdempotent) {
  140. headers['X-Parse-Request-Id'] = requestId;
  141. }
  142. if (_CoreManager.default.get('SERVER_AUTH_TYPE') && _CoreManager.default.get('SERVER_AUTH_TOKEN')) {
  143. headers['Authorization'] = _CoreManager.default.get('SERVER_AUTH_TYPE') + ' ' + _CoreManager.default.get('SERVER_AUTH_TOKEN');
  144. }
  145. const customHeaders = _CoreManager.default.get('REQUEST_HEADERS');
  146. for (const key in customHeaders) {
  147. headers[key] = customHeaders[key];
  148. }
  149. if (options && typeof options.progress === 'function') {
  150. const handleProgress = function (type, event) {
  151. if (event.lengthComputable) {
  152. options.progress(event.loaded / event.total, event.loaded, event.total, {
  153. type
  154. });
  155. } else {
  156. options.progress(null, null, null, {
  157. type
  158. });
  159. }
  160. };
  161. xhr.onprogress = event => {
  162. handleProgress('download', event);
  163. };
  164. if (xhr.upload) {
  165. xhr.upload.onprogress = event => {
  166. handleProgress('upload', event);
  167. };
  168. }
  169. }
  170. xhr.open(method, url, true);
  171. for (const h in headers) {
  172. xhr.setRequestHeader(h, headers[h]);
  173. }
  174. xhr.onabort = function () {
  175. promise.resolve({
  176. response: {
  177. results: []
  178. },
  179. status: 0,
  180. xhr
  181. });
  182. };
  183. xhr.send(data);
  184. // @ts-ignore
  185. if (options && typeof options.requestTask === 'function') {
  186. // @ts-ignore
  187. options.requestTask(xhr);
  188. }
  189. };
  190. dispatch();
  191. return promise;
  192. },
  193. request(method, path, data, options) {
  194. options = options || {};
  195. let url = _CoreManager.default.get('SERVER_URL');
  196. if (url[url.length - 1] !== '/') {
  197. url += '/';
  198. }
  199. url += path;
  200. const payload = {};
  201. if (data && typeof data === 'object') {
  202. for (const k in data) {
  203. payload[k] = data[k];
  204. }
  205. }
  206. // Add context
  207. const context = options.context;
  208. if (context !== undefined) {
  209. payload._context = context;
  210. }
  211. if (method !== 'POST') {
  212. payload._method = method;
  213. method = 'POST';
  214. }
  215. payload._ApplicationId = _CoreManager.default.get('APPLICATION_ID');
  216. const jsKey = _CoreManager.default.get('JAVASCRIPT_KEY');
  217. if (jsKey) {
  218. payload._JavaScriptKey = jsKey;
  219. }
  220. payload._ClientVersion = _CoreManager.default.get('VERSION');
  221. let useMasterKey = options.useMasterKey;
  222. if (typeof useMasterKey === 'undefined') {
  223. useMasterKey = _CoreManager.default.get('USE_MASTER_KEY');
  224. }
  225. if (useMasterKey) {
  226. if (_CoreManager.default.get('MASTER_KEY')) {
  227. delete payload._JavaScriptKey;
  228. payload._MasterKey = _CoreManager.default.get('MASTER_KEY');
  229. } else {
  230. throw new Error('Cannot use the Master Key, it has not been provided.');
  231. }
  232. }
  233. if (_CoreManager.default.get('FORCE_REVOCABLE_SESSION')) {
  234. payload._RevocableSession = '1';
  235. }
  236. const installationId = options.installationId;
  237. let installationIdPromise;
  238. if (installationId && typeof installationId === 'string') {
  239. installationIdPromise = _promise.default.resolve(installationId);
  240. } else {
  241. const installationController = _CoreManager.default.getInstallationController();
  242. installationIdPromise = installationController.currentInstallationId();
  243. }
  244. return installationIdPromise.then(iid => {
  245. payload._InstallationId = iid;
  246. const userController = _CoreManager.default.getUserController();
  247. if (options && typeof options.sessionToken === 'string') {
  248. return _promise.default.resolve(options.sessionToken);
  249. } else if (userController) {
  250. return userController.currentUserAsync().then(user => {
  251. if (user) {
  252. return _promise.default.resolve(user.getSessionToken());
  253. }
  254. return _promise.default.resolve(null);
  255. });
  256. }
  257. return _promise.default.resolve(null);
  258. }).then(token => {
  259. if (token) {
  260. payload._SessionToken = token;
  261. }
  262. const payloadString = (0, _stringify.default)(payload);
  263. return RESTController.ajax(method, url, payloadString, {}, options).then(_ref => {
  264. let {
  265. response,
  266. status,
  267. headers,
  268. xhr
  269. } = _ref;
  270. if (options.returnStatus) {
  271. return {
  272. ...response,
  273. _status: status,
  274. _headers: headers,
  275. _xhr: xhr
  276. };
  277. } else {
  278. return response;
  279. }
  280. });
  281. }).catch(RESTController.handleError);
  282. },
  283. handleError(response) {
  284. // Transform the error into an instance of ParseError by trying to parse
  285. // the error string as JSON
  286. let error;
  287. if (response && response.responseText) {
  288. try {
  289. const errorJSON = JSON.parse(response.responseText);
  290. error = new _ParseError.default(errorJSON.code, errorJSON.error);
  291. } catch (e) {
  292. // If we fail to parse the error text, that's okay.
  293. error = new _ParseError.default(_ParseError.default.INVALID_JSON, 'Received an error with invalid JSON from Parse: ' + response.responseText);
  294. }
  295. } else {
  296. const message = response.message ? response.message : response;
  297. error = new _ParseError.default(_ParseError.default.CONNECTION_FAILED, 'XMLHttpRequest failed: ' + (0, _stringify.default)(message));
  298. }
  299. return _promise.default.reject(error);
  300. },
  301. _setXHR(xhr) {
  302. XHR = xhr;
  303. },
  304. _getXHR() {
  305. return XHR;
  306. }
  307. };
  308. module.exports = RESTController;
  309. var _default = exports.default = RESTController;