EventuallyQueue.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
  3. var _stringify = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/json/stringify"));
  4. var _splice = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/splice"));
  5. var _findIndex = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/find-index"));
  6. var _setInterval2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/set-interval"));
  7. var _find = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/find"));
  8. var _regenerator = _interopRequireDefault(require("@babel/runtime-corejs3/regenerator"));
  9. var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/toConsumableArray"));
  10. var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/asyncToGenerator"));
  11. var _CoreManager = _interopRequireDefault(require("./CoreManager"));
  12. var _ParseObject = _interopRequireDefault(require("./ParseObject"));
  13. var _ParseQuery = _interopRequireDefault(require("./ParseQuery"));
  14. var _Storage = _interopRequireDefault(require("./Storage"));
  15. /**
  16. * https://github.com/francimedia/parse-js-local-storage
  17. *
  18. * @flow
  19. */
  20. /*:: import type { SaveOptions } from './ParseObject';*/
  21. /*:: import type { RequestOptions } from './RESTController';*/
  22. /*:: type QueueObject = {
  23. queueId: string,
  24. action: string,
  25. object: ParseObject,
  26. serverOptions: SaveOptions | RequestOptions,
  27. id: string,
  28. className: string,
  29. hash: string,
  30. createdAt: Date,
  31. };*/
  32. /*:: type Queue = Array<QueueObject>;*/
  33. var QUEUE_KEY = 'Parse/Eventually/Queue';
  34. var queueCache = [];
  35. var dirtyCache = true;
  36. var polling = undefined;
  37. /**
  38. * Provides utility functions to queue objects that will be
  39. * saved to the server at a later date.
  40. *
  41. * @class Parse.EventuallyQueue
  42. * @static
  43. */
  44. var EventuallyQueue = {
  45. /**
  46. * Add object to queue with save operation.
  47. *
  48. * @function save
  49. * @name Parse.EventuallyQueue.save
  50. * @param {ParseObject} object Parse.Object to be saved eventually
  51. * @param {object} [serverOptions] See {@link https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Object.html#save Parse.Object.save} options.
  52. * @returns {Promise} A promise that is fulfilled if object is added to queue.
  53. * @static
  54. * @see Parse.Object#saveEventually
  55. */
  56. save: function (object /*: ParseObject*/) /*: Promise*/{
  57. var serverOptions /*: SaveOptions*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  58. return this.enqueue('save', object, serverOptions);
  59. },
  60. /**
  61. * Add object to queue with save operation.
  62. *
  63. * @function destroy
  64. * @name Parse.EventuallyQueue.destroy
  65. * @param {ParseObject} object Parse.Object to be destroyed eventually
  66. * @param {object} [serverOptions] See {@link https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Object.html#destroy Parse.Object.destroy} options
  67. * @returns {Promise} A promise that is fulfilled if object is added to queue.
  68. * @static
  69. * @see Parse.Object#destroyEventually
  70. */
  71. destroy: function (object /*: ParseObject*/) /*: Promise*/{
  72. var serverOptions /*: RequestOptions*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  73. return this.enqueue('destroy', object, serverOptions);
  74. },
  75. /**
  76. * Generate unique identifier to avoid duplicates and maintain previous state.
  77. *
  78. * @param {string} action save / destroy
  79. * @param {object} object Parse.Object to be queued
  80. * @returns {string}
  81. * @static
  82. * @ignore
  83. */
  84. generateQueueId: function (action /*: string*/, object /*: ParseObject*/) /*: string*/{
  85. object._getId();
  86. var className = object.className,
  87. id = object.id,
  88. _localId = object._localId;
  89. var uniqueId = object.get('hash') || _localId;
  90. return [action, className, id, uniqueId].join('_');
  91. },
  92. /**
  93. * Build queue object and add to queue.
  94. *
  95. * @param {string} action save / destroy
  96. * @param {object} object Parse.Object to be queued
  97. * @param {object} [serverOptions]
  98. * @returns {Promise} A promise that is fulfilled if object is added to queue.
  99. * @static
  100. * @ignore
  101. */
  102. enqueue: function (action /*: string*/, object /*: ParseObject*/, serverOptions /*: SaveOptions | RequestOptions*/) /*: Promise*/{
  103. var _this = this;
  104. return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
  105. var queueData, queueId, index, prop;
  106. return _regenerator.default.wrap(function (_context) {
  107. while (1) switch (_context.prev = _context.next) {
  108. case 0:
  109. _context.next = 2;
  110. return _this.getQueue();
  111. case 2:
  112. queueData = _context.sent;
  113. queueId = _this.generateQueueId(action, object);
  114. index = _this.queueItemExists(queueData, queueId);
  115. if (index > -1) {
  116. // Add cached values to new object if they don't exist
  117. for (prop in queueData[index].object) {
  118. if (typeof object.get(prop) === 'undefined') {
  119. object.set(prop, queueData[index].object[prop]);
  120. }
  121. }
  122. } else {
  123. index = queueData.length;
  124. }
  125. queueData[index] = {
  126. queueId: queueId,
  127. action: action,
  128. object: object.toJSON(),
  129. serverOptions: serverOptions,
  130. id: object.id,
  131. className: object.className,
  132. hash: object.get('hash'),
  133. createdAt: new Date()
  134. };
  135. return _context.abrupt("return", _this.setQueue(queueData));
  136. case 8:
  137. case "end":
  138. return _context.stop();
  139. }
  140. }, _callee);
  141. }))();
  142. },
  143. store: function (data) {
  144. return _Storage.default.setItemAsync(QUEUE_KEY, (0, _stringify.default)(data));
  145. },
  146. load: function () {
  147. return _Storage.default.getItemAsync(QUEUE_KEY);
  148. },
  149. /**
  150. * Sets the in-memory queue from local storage and returns.
  151. *
  152. * @function getQueue
  153. * @name Parse.EventuallyQueue.getQueue
  154. * @returns {Promise<Array>}
  155. * @static
  156. */
  157. getQueue: function () /*: Promise<Array>*/{
  158. var _this2 = this;
  159. return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() {
  160. return _regenerator.default.wrap(function (_context2) {
  161. while (1) switch (_context2.prev = _context2.next) {
  162. case 0:
  163. if (!dirtyCache) {
  164. _context2.next = 10;
  165. break;
  166. }
  167. _context2.t0 = JSON;
  168. _context2.next = 4;
  169. return _this2.load();
  170. case 4:
  171. _context2.t1 = _context2.sent;
  172. if (_context2.t1) {
  173. _context2.next = 7;
  174. break;
  175. }
  176. _context2.t1 = '[]';
  177. case 7:
  178. _context2.t2 = _context2.t1;
  179. queueCache = _context2.t0.parse.call(_context2.t0, _context2.t2);
  180. dirtyCache = false;
  181. case 10:
  182. return _context2.abrupt("return", queueCache);
  183. case 11:
  184. case "end":
  185. return _context2.stop();
  186. }
  187. }, _callee2);
  188. }))();
  189. },
  190. /**
  191. * Saves the queue to local storage
  192. *
  193. * @param {Queue} queue Queue containing Parse.Object data.
  194. * @returns {Promise} A promise that is fulfilled when queue is stored.
  195. * @static
  196. * @ignore
  197. */
  198. setQueue: function (queue /*: Queue*/) /*: Promise<void>*/{
  199. queueCache = queue;
  200. return this.store(queueCache);
  201. },
  202. /**
  203. * Removes Parse.Object data from queue.
  204. *
  205. * @param {string} queueId Unique identifier for Parse.Object data.
  206. * @returns {Promise} A promise that is fulfilled when queue is stored.
  207. * @static
  208. * @ignore
  209. */
  210. remove: function (queueId /*: string*/) /*: Promise<void>*/{
  211. var _this3 = this;
  212. return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3() {
  213. var queueData, index;
  214. return _regenerator.default.wrap(function (_context3) {
  215. while (1) switch (_context3.prev = _context3.next) {
  216. case 0:
  217. _context3.next = 2;
  218. return _this3.getQueue();
  219. case 2:
  220. queueData = _context3.sent;
  221. index = _this3.queueItemExists(queueData, queueId);
  222. if (!(index > -1)) {
  223. _context3.next = 8;
  224. break;
  225. }
  226. (0, _splice.default)(queueData).call(queueData, index, 1);
  227. _context3.next = 8;
  228. return _this3.setQueue(queueData);
  229. case 8:
  230. case "end":
  231. return _context3.stop();
  232. }
  233. }, _callee3);
  234. }))();
  235. },
  236. /**
  237. * Removes all objects from queue.
  238. *
  239. * @function clear
  240. * @name Parse.EventuallyQueue.clear
  241. * @returns {Promise} A promise that is fulfilled when queue is cleared.
  242. * @static
  243. */
  244. clear: function () /*: Promise*/{
  245. queueCache = [];
  246. return this.store([]);
  247. },
  248. /**
  249. * Return the index of a queueId in the queue. Returns -1 if not found.
  250. *
  251. * @param {Queue} queue Queue containing Parse.Object data.
  252. * @param {string} queueId Unique identifier for Parse.Object data.
  253. * @returns {number}
  254. * @static
  255. * @ignore
  256. */
  257. queueItemExists: function (queue /*: Queue*/, queueId /*: string*/) /*: number*/{
  258. return (0, _findIndex.default)(queue).call(queue, function (data) {
  259. return data.queueId === queueId;
  260. });
  261. },
  262. /**
  263. * Return the number of objects in the queue.
  264. *
  265. * @function length
  266. * @name Parse.EventuallyQueue.length
  267. * @returns {number}
  268. * @static
  269. */
  270. length: function () /*: number*/{
  271. var _this4 = this;
  272. return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4() {
  273. var queueData;
  274. return _regenerator.default.wrap(function (_context4) {
  275. while (1) switch (_context4.prev = _context4.next) {
  276. case 0:
  277. _context4.next = 2;
  278. return _this4.getQueue();
  279. case 2:
  280. queueData = _context4.sent;
  281. return _context4.abrupt("return", queueData.length);
  282. case 4:
  283. case "end":
  284. return _context4.stop();
  285. }
  286. }, _callee4);
  287. }))();
  288. },
  289. /**
  290. * Sends the queue to the server.
  291. *
  292. * @function sendQueue
  293. * @name Parse.EventuallyQueue.sendQueue
  294. * @returns {Promise<boolean>} Returns true if queue was sent successfully.
  295. * @static
  296. */
  297. sendQueue: function () /*: Promise<boolean>*/{
  298. var _this5 = this;
  299. return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5() {
  300. var queue, queueData, i, queueObject, id, hash, className, ObjectType;
  301. return _regenerator.default.wrap(function (_context5) {
  302. while (1) switch (_context5.prev = _context5.next) {
  303. case 0:
  304. _context5.next = 2;
  305. return _this5.getQueue();
  306. case 2:
  307. queue = _context5.sent;
  308. queueData = (0, _toConsumableArray2.default)(queue);
  309. if (!(queueData.length === 0)) {
  310. _context5.next = 6;
  311. break;
  312. }
  313. return _context5.abrupt("return", false);
  314. case 6:
  315. i = 0;
  316. case 7:
  317. if (!(i < queueData.length)) {
  318. _context5.next = 26;
  319. break;
  320. }
  321. queueObject = queueData[i];
  322. id = queueObject.id, hash = queueObject.hash, className = queueObject.className;
  323. ObjectType = _ParseObject.default.extend(className);
  324. if (!id) {
  325. _context5.next = 16;
  326. break;
  327. }
  328. _context5.next = 14;
  329. return _this5.process.byId(ObjectType, queueObject);
  330. case 14:
  331. _context5.next = 23;
  332. break;
  333. case 16:
  334. if (!hash) {
  335. _context5.next = 21;
  336. break;
  337. }
  338. _context5.next = 19;
  339. return _this5.process.byHash(ObjectType, queueObject);
  340. case 19:
  341. _context5.next = 23;
  342. break;
  343. case 21:
  344. _context5.next = 23;
  345. return _this5.process.create(ObjectType, queueObject);
  346. case 23:
  347. i += 1;
  348. _context5.next = 7;
  349. break;
  350. case 26:
  351. return _context5.abrupt("return", true);
  352. case 27:
  353. case "end":
  354. return _context5.stop();
  355. }
  356. }, _callee5);
  357. }))();
  358. },
  359. /**
  360. * Build queue object and add to queue.
  361. *
  362. * @param {ParseObject} object Parse.Object to be processed
  363. * @param {QueueObject} queueObject Parse.Object data from the queue
  364. * @returns {Promise} A promise that is fulfilled when operation is performed.
  365. * @static
  366. * @ignore
  367. */
  368. sendQueueCallback: function (object /*: ParseObject*/, queueObject /*: QueueObject*/) /*: Promise<void>*/{
  369. var _this6 = this;
  370. return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6() {
  371. return _regenerator.default.wrap(function (_context6) {
  372. while (1) switch (_context6.prev = _context6.next) {
  373. case 0:
  374. if (object) {
  375. _context6.next = 2;
  376. break;
  377. }
  378. return _context6.abrupt("return", _this6.remove(queueObject.queueId));
  379. case 2:
  380. _context6.t0 = queueObject.action;
  381. _context6.next = _context6.t0 === 'save' ? 5 : _context6.t0 === 'destroy' ? 20 : 33;
  382. break;
  383. case 5:
  384. if (!(typeof object.updatedAt !== 'undefined' && object.updatedAt > new Date(queueObject.object.createdAt))) {
  385. _context6.next = 7;
  386. break;
  387. }
  388. return _context6.abrupt("return", _this6.remove(queueObject.queueId));
  389. case 7:
  390. _context6.prev = 7;
  391. _context6.next = 10;
  392. return object.save(queueObject.object, queueObject.serverOptions);
  393. case 10:
  394. _context6.next = 12;
  395. return _this6.remove(queueObject.queueId);
  396. case 12:
  397. _context6.next = 19;
  398. break;
  399. case 14:
  400. _context6.prev = 14;
  401. _context6.t1 = _context6["catch"](7);
  402. if (!(_context6.t1.message !== 'XMLHttpRequest failed: "Unable to connect to the Parse API"')) {
  403. _context6.next = 19;
  404. break;
  405. }
  406. _context6.next = 19;
  407. return _this6.remove(queueObject.queueId);
  408. case 19:
  409. return _context6.abrupt("break", 33);
  410. case 20:
  411. _context6.prev = 20;
  412. _context6.next = 23;
  413. return object.destroy(queueObject.serverOptions);
  414. case 23:
  415. _context6.next = 25;
  416. return _this6.remove(queueObject.queueId);
  417. case 25:
  418. _context6.next = 32;
  419. break;
  420. case 27:
  421. _context6.prev = 27;
  422. _context6.t2 = _context6["catch"](20);
  423. if (!(_context6.t2.message !== 'XMLHttpRequest failed: "Unable to connect to the Parse API"')) {
  424. _context6.next = 32;
  425. break;
  426. }
  427. _context6.next = 32;
  428. return _this6.remove(queueObject.queueId);
  429. case 32:
  430. return _context6.abrupt("break", 33);
  431. case 33:
  432. case "end":
  433. return _context6.stop();
  434. }
  435. }, _callee6, null, [[7, 14], [20, 27]]);
  436. }))();
  437. },
  438. /**
  439. * Start polling server for network connection.
  440. * Will send queue if connection is established.
  441. *
  442. * @function poll
  443. * @name Parse.EventuallyQueue.poll
  444. * @param [ms] Milliseconds to ping the server. Default 2000ms
  445. * @static
  446. */
  447. poll: function () {
  448. var _this7 = this;
  449. var ms /*: number*/ = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2000;
  450. if (polling) {
  451. return;
  452. }
  453. polling = (0, _setInterval2.default)(function () {
  454. var RESTController = _CoreManager.default.getRESTController();
  455. RESTController.request('GET', 'health').then(function (_ref) {
  456. var status = _ref.status;
  457. if (status === 'ok') {
  458. _this7.stopPoll();
  459. return _this7.sendQueue();
  460. }
  461. }).catch(function (e) {
  462. return e;
  463. });
  464. }, ms);
  465. },
  466. /**
  467. * Turns off polling.
  468. *
  469. * @function stopPoll
  470. * @name Parse.EventuallyQueue.stopPoll
  471. * @static
  472. */
  473. stopPoll: function () {
  474. clearInterval(polling);
  475. polling = undefined;
  476. },
  477. /**
  478. * Return true if pinging the server.
  479. *
  480. * @function isPolling
  481. * @name Parse.EventuallyQueue.isPolling
  482. * @returns {boolean}
  483. * @static
  484. */
  485. isPolling: function () /*: boolean*/{
  486. return !!polling;
  487. },
  488. _setPolling: function (flag /*: boolean*/) {
  489. polling = flag;
  490. },
  491. process: {
  492. create: function (ObjectType, queueObject) {
  493. var object = new ObjectType();
  494. return EventuallyQueue.sendQueueCallback(object, queueObject);
  495. },
  496. byId: function (ObjectType, queueObject) {
  497. return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee7() {
  498. var sessionToken, query, results;
  499. return _regenerator.default.wrap(function (_context7) {
  500. while (1) switch (_context7.prev = _context7.next) {
  501. case 0:
  502. sessionToken = queueObject.serverOptions.sessionToken;
  503. query = new _ParseQuery.default(ObjectType);
  504. query.equalTo('objectId', queueObject.id);
  505. _context7.next = 5;
  506. return (0, _find.default)(query).call(query, {
  507. sessionToken: sessionToken
  508. });
  509. case 5:
  510. results = _context7.sent;
  511. return _context7.abrupt("return", EventuallyQueue.sendQueueCallback(results[0], queueObject));
  512. case 7:
  513. case "end":
  514. return _context7.stop();
  515. }
  516. }, _callee7);
  517. }))();
  518. },
  519. byHash: function (ObjectType, queueObject) {
  520. return (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8() {
  521. var sessionToken, query, results;
  522. return _regenerator.default.wrap(function (_context8) {
  523. while (1) switch (_context8.prev = _context8.next) {
  524. case 0:
  525. sessionToken = queueObject.serverOptions.sessionToken;
  526. query = new _ParseQuery.default(ObjectType);
  527. query.equalTo('hash', queueObject.hash);
  528. _context8.next = 5;
  529. return (0, _find.default)(query).call(query, {
  530. sessionToken: sessionToken
  531. });
  532. case 5:
  533. results = _context8.sent;
  534. if (!(results.length > 0)) {
  535. _context8.next = 8;
  536. break;
  537. }
  538. return _context8.abrupt("return", EventuallyQueue.sendQueueCallback(results[0], queueObject));
  539. case 8:
  540. return _context8.abrupt("return", EventuallyQueue.process.create(ObjectType, queueObject));
  541. case 9:
  542. case "end":
  543. return _context8.stop();
  544. }
  545. }, _callee8);
  546. }))();
  547. }
  548. }
  549. };
  550. module.exports = EventuallyQueue;