EventuallyQueue.js 10.0 KB

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