RESTController.js 13 KB

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