RESTController.js 9.8 KB

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