EventuallyQueue.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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 _stringify = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/json/stringify"));
  9. var _splice = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/splice"));
  10. var _findIndex = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/find-index"));
  11. var _setInterval2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/set-interval"));
  12. var _find = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/find"));
  13. var _CoreManager = _interopRequireDefault(require("./CoreManager"));
  14. var _ParseError = _interopRequireDefault(require("./ParseError"));
  15. var _ParseObject = _interopRequireDefault(require("./ParseObject"));
  16. var _ParseQuery = _interopRequireDefault(require("./ParseQuery"));
  17. var _Storage = _interopRequireDefault(require("./Storage"));
  18. const QUEUE_KEY = 'Parse/Eventually/Queue';
  19. let queueCache = [];
  20. let dirtyCache = true;
  21. let polling = undefined;
  22. /**
  23. * Provides utility functions to queue objects that will be
  24. * saved to the server at a later date.
  25. *
  26. * @class Parse.EventuallyQueue
  27. * @static
  28. */
  29. const EventuallyQueue = {
  30. /**
  31. * Add object to queue with save operation.
  32. *
  33. * @function save
  34. * @name Parse.EventuallyQueue.save
  35. * @param {ParseObject} object Parse.Object to be saved eventually
  36. * @param {object} [serverOptions] See {@link https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Object.html#save Parse.Object.save} options.
  37. * @returns {Promise} A promise that is fulfilled if object is added to queue.
  38. * @static
  39. * @see Parse.Object#saveEventually
  40. */
  41. save(object) {
  42. let serverOptions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  43. return this.enqueue('save', object, serverOptions);
  44. },
  45. /**
  46. * Add object to queue with save operation.
  47. *
  48. * @function destroy
  49. * @name Parse.EventuallyQueue.destroy
  50. * @param {ParseObject} object Parse.Object to be destroyed eventually
  51. * @param {object} [serverOptions] See {@link https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Object.html#destroy Parse.Object.destroy} options
  52. * @returns {Promise} A promise that is fulfilled if object is added to queue.
  53. * @static
  54. * @see Parse.Object#destroyEventually
  55. */
  56. destroy(object) {
  57. let serverOptions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  58. return this.enqueue('destroy', object, serverOptions);
  59. },
  60. /**
  61. * Generate unique identifier to avoid duplicates and maintain previous state.
  62. *
  63. * @param {string} action save / destroy
  64. * @param {object} object Parse.Object to be queued
  65. * @returns {string}
  66. * @static
  67. * @ignore
  68. */
  69. generateQueueId(action, object) {
  70. object._getId();
  71. const {
  72. className,
  73. id,
  74. _localId
  75. } = object;
  76. const uniqueId = object.get('hash') || _localId;
  77. return [action, className, id, uniqueId].join('_');
  78. },
  79. /**
  80. * Build queue object and add to queue.
  81. *
  82. * @param {string} action save / destroy
  83. * @param {object} object Parse.Object to be queued
  84. * @param {object} [serverOptions]
  85. * @returns {Promise} A promise that is fulfilled if object is added to queue.
  86. * @static
  87. * @ignore
  88. */
  89. async enqueue(action, object, serverOptions) {
  90. const queueData = await this.getQueue();
  91. const queueId = this.generateQueueId(action, object);
  92. let index = this.queueItemExists(queueData, queueId);
  93. if (index > -1) {
  94. // Add cached values to new object if they don't exist
  95. for (const prop in queueData[index].object) {
  96. if (typeof object.get(prop) === 'undefined') {
  97. object.set(prop, queueData[index].object[prop]);
  98. }
  99. }
  100. } else {
  101. index = queueData.length;
  102. }
  103. queueData[index] = {
  104. queueId,
  105. action,
  106. object: object.toJSON(),
  107. serverOptions,
  108. id: object.id,
  109. className: object.className,
  110. hash: object.get('hash'),
  111. createdAt: new Date()
  112. };
  113. return this.setQueue(queueData);
  114. },
  115. store(data) {
  116. return _Storage.default.setItemAsync(QUEUE_KEY, (0, _stringify.default)(data));
  117. },
  118. load() {
  119. return _Storage.default.getItemAsync(QUEUE_KEY);
  120. },
  121. /**
  122. * Sets the in-memory queue from local storage and returns.
  123. *
  124. * @function getQueue
  125. * @name Parse.EventuallyQueue.getQueue
  126. * @returns {Promise<QueueObject[]>}
  127. * @static
  128. */
  129. async getQueue() {
  130. if (dirtyCache) {
  131. queueCache = JSON.parse((await this.load()) || '[]');
  132. dirtyCache = false;
  133. }
  134. return queueCache;
  135. },
  136. /**
  137. * Saves the queue to local storage
  138. *
  139. * @param {Queue} queue Queue containing Parse.Object data.
  140. * @returns {Promise} A promise that is fulfilled when queue is stored.
  141. * @static
  142. * @ignore
  143. */
  144. setQueue(queue) {
  145. queueCache = queue;
  146. return this.store(queueCache);
  147. },
  148. /**
  149. * Removes Parse.Object data from queue.
  150. *
  151. * @param {string} queueId Unique identifier for Parse.Object data.
  152. * @returns {Promise} A promise that is fulfilled when queue is stored.
  153. * @static
  154. * @ignore
  155. */
  156. async remove(queueId) {
  157. const queueData = await this.getQueue();
  158. const index = this.queueItemExists(queueData, queueId);
  159. if (index > -1) {
  160. (0, _splice.default)(queueData).call(queueData, index, 1);
  161. await this.setQueue(queueData);
  162. }
  163. },
  164. /**
  165. * Removes all objects from queue.
  166. *
  167. * @function clear
  168. * @name Parse.EventuallyQueue.clear
  169. * @returns {Promise} A promise that is fulfilled when queue is cleared.
  170. * @static
  171. */
  172. clear() {
  173. queueCache = [];
  174. return this.store([]);
  175. },
  176. /**
  177. * Return the index of a queueId in the queue. Returns -1 if not found.
  178. *
  179. * @param {Queue} queue Queue containing Parse.Object data.
  180. * @param {string} queueId Unique identifier for Parse.Object data.
  181. * @returns {number}
  182. * @static
  183. * @ignore
  184. */
  185. queueItemExists(queue, queueId) {
  186. return (0, _findIndex.default)(queue).call(queue, data => data.queueId === queueId);
  187. },
  188. /**
  189. * Return the number of objects in the queue.
  190. *
  191. * @function length
  192. * @name Parse.EventuallyQueue.length
  193. * @returns {Promise<number>}
  194. * @static
  195. */
  196. async length() {
  197. const queueData = await this.getQueue();
  198. return queueData.length;
  199. },
  200. /**
  201. * Sends the queue to the server.
  202. *
  203. * @function sendQueue
  204. * @name Parse.EventuallyQueue.sendQueue
  205. * @returns {Promise<boolean>} Returns true if queue was sent successfully.
  206. * @static
  207. */
  208. async sendQueue() {
  209. const queue = await this.getQueue();
  210. const queueData = [...queue];
  211. if (queueData.length === 0) {
  212. return false;
  213. }
  214. for (let i = 0; i < queueData.length; i += 1) {
  215. const queueObject = queueData[i];
  216. const {
  217. id,
  218. hash,
  219. className
  220. } = queueObject;
  221. const ObjectType = _ParseObject.default.extend(className);
  222. if (id) {
  223. await this.process.byId(ObjectType, queueObject);
  224. } else if (hash) {
  225. await this.process.byHash(ObjectType, queueObject);
  226. } else {
  227. await this.process.create(ObjectType, queueObject);
  228. }
  229. }
  230. return true;
  231. },
  232. /**
  233. * Build queue object and add to queue.
  234. *
  235. * @param {ParseObject} object Parse.Object to be processed
  236. * @param {QueueObject} queueObject Parse.Object data from the queue
  237. * @returns {Promise} A promise that is fulfilled when operation is performed.
  238. * @static
  239. * @ignore
  240. */
  241. async sendQueueCallback(object, queueObject) {
  242. if (!object) {
  243. return this.remove(queueObject.queueId);
  244. }
  245. switch (queueObject.action) {
  246. case 'save':
  247. // Queued update was overwritten by other request. Do not save
  248. if (typeof object.updatedAt !== 'undefined' && object.updatedAt > new Date(queueObject.object.createdAt)) {
  249. return this.remove(queueObject.queueId);
  250. }
  251. try {
  252. await object.save(queueObject.object, queueObject.serverOptions);
  253. await this.remove(queueObject.queueId);
  254. } catch (e) {
  255. if (e.code !== _ParseError.default.CONNECTION_FAILED) {
  256. await this.remove(queueObject.queueId);
  257. }
  258. }
  259. break;
  260. case 'destroy':
  261. try {
  262. await object.destroy(queueObject.serverOptions);
  263. await this.remove(queueObject.queueId);
  264. } catch (e) {
  265. if (e.code !== _ParseError.default.CONNECTION_FAILED) {
  266. await this.remove(queueObject.queueId);
  267. }
  268. }
  269. break;
  270. }
  271. },
  272. /**
  273. * Start polling server for network connection.
  274. * Will send queue if connection is established.
  275. *
  276. * @function poll
  277. * @name Parse.EventuallyQueue.poll
  278. * @param [ms] Milliseconds to ping the server. Default 2000ms
  279. * @static
  280. */
  281. poll() {
  282. let ms = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2000;
  283. if (polling) {
  284. return;
  285. }
  286. polling = (0, _setInterval2.default)(() => {
  287. const RESTController = _CoreManager.default.getRESTController();
  288. RESTController.request('GET', 'health').then(_ref => {
  289. let {
  290. status
  291. } = _ref;
  292. if (status === 'ok') {
  293. this.stopPoll();
  294. return this.sendQueue();
  295. }
  296. }).catch(e => e);
  297. }, ms);
  298. },
  299. /**
  300. * Turns off polling.
  301. *
  302. * @function stopPoll
  303. * @name Parse.EventuallyQueue.stopPoll
  304. * @static
  305. */
  306. stopPoll() {
  307. clearInterval(polling);
  308. polling = undefined;
  309. },
  310. /**
  311. * Return true if pinging the server.
  312. *
  313. * @function isPolling
  314. * @name Parse.EventuallyQueue.isPolling
  315. * @returns {boolean}
  316. * @static
  317. */
  318. isPolling() {
  319. return !!polling;
  320. },
  321. _setPolling(flag) {
  322. polling = flag;
  323. },
  324. process: {
  325. create(ObjectType, queueObject) {
  326. const object = new ObjectType();
  327. return EventuallyQueue.sendQueueCallback(object, queueObject);
  328. },
  329. async byId(ObjectType, queueObject) {
  330. const {
  331. sessionToken
  332. } = queueObject.serverOptions;
  333. const query = new _ParseQuery.default(ObjectType);
  334. query.equalTo('objectId', queueObject.id);
  335. const results = await (0, _find.default)(query).call(query, {
  336. sessionToken
  337. });
  338. return EventuallyQueue.sendQueueCallback(results[0], queueObject);
  339. },
  340. async byHash(ObjectType, queueObject) {
  341. const {
  342. sessionToken
  343. } = queueObject.serverOptions;
  344. const query = new _ParseQuery.default(ObjectType);
  345. query.equalTo('hash', queueObject.hash);
  346. const results = await (0, _find.default)(query).call(query, {
  347. sessionToken
  348. });
  349. if (results.length > 0) {
  350. return EventuallyQueue.sendQueueCallback(results[0], queueObject);
  351. }
  352. return EventuallyQueue.process.create(ObjectType, queueObject);
  353. }
  354. }
  355. };
  356. module.exports = EventuallyQueue;
  357. var _default = exports.default = EventuallyQueue;