LiveQueryClient.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
  7. var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
  8. var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
  9. var _CoreManager = _interopRequireDefault(require("./CoreManager"));
  10. var _ParseObject = _interopRequireDefault(require("./ParseObject"));
  11. var _LiveQuerySubscription = _interopRequireDefault(require("./LiveQuerySubscription"));
  12. var _promiseUtils = require("./promiseUtils");
  13. var _ParseError = _interopRequireDefault(require("./ParseError"));
  14. var CLIENT_STATE = {
  15. INITIALIZED: 'initialized',
  16. CONNECTING: 'connecting',
  17. CONNECTED: 'connected',
  18. CLOSED: 'closed',
  19. RECONNECTING: 'reconnecting',
  20. DISCONNECTED: 'disconnected'
  21. };
  22. var OP_TYPES = {
  23. CONNECT: 'connect',
  24. SUBSCRIBE: 'subscribe',
  25. UNSUBSCRIBE: 'unsubscribe',
  26. ERROR: 'error'
  27. };
  28. var OP_EVENTS = {
  29. CONNECTED: 'connected',
  30. SUBSCRIBED: 'subscribed',
  31. UNSUBSCRIBED: 'unsubscribed',
  32. ERROR: 'error',
  33. CREATE: 'create',
  34. UPDATE: 'update',
  35. ENTER: 'enter',
  36. LEAVE: 'leave',
  37. DELETE: 'delete'
  38. };
  39. var CLIENT_EMMITER_TYPES = {
  40. CLOSE: 'close',
  41. ERROR: 'error',
  42. OPEN: 'open'
  43. };
  44. var SUBSCRIPTION_EMMITER_TYPES = {
  45. OPEN: 'open',
  46. CLOSE: 'close',
  47. ERROR: 'error',
  48. CREATE: 'create',
  49. UPDATE: 'update',
  50. ENTER: 'enter',
  51. LEAVE: 'leave',
  52. DELETE: 'delete'
  53. };
  54. var generateInterval = function (k) {
  55. return Math.random() * Math.min(30, Math.pow(2, k) - 1) * 1000;
  56. };
  57. var LiveQueryClient = function () {
  58. function LiveQueryClient(_ref) {
  59. var _this = this;
  60. var applicationId = _ref.applicationId,
  61. serverURL = _ref.serverURL,
  62. javascriptKey = _ref.javascriptKey,
  63. masterKey = _ref.masterKey,
  64. sessionToken = _ref.sessionToken,
  65. installationId = _ref.installationId;
  66. (0, _classCallCheck2.default)(this, LiveQueryClient);
  67. if (!serverURL || serverURL.indexOf('ws') !== 0) {
  68. throw new Error('You need to set a proper Parse LiveQuery server url before using LiveQueryClient');
  69. }
  70. this.reconnectHandle = null;
  71. this.attempts = 1;
  72. this.id = 0;
  73. this.requestId = 1;
  74. this.serverURL = serverURL;
  75. this.applicationId = applicationId;
  76. this.javascriptKey = javascriptKey || undefined;
  77. this.masterKey = masterKey || undefined;
  78. this.sessionToken = sessionToken || undefined;
  79. this.installationId = installationId || undefined;
  80. this.additionalProperties = true;
  81. this.connectPromise = (0, _promiseUtils.resolvingPromise)();
  82. this.subscriptions = new Map();
  83. this.state = CLIENT_STATE.INITIALIZED;
  84. var EventEmitter = _CoreManager.default.getEventEmitter();
  85. this.emitter = new EventEmitter();
  86. this.on = function (eventName, listener) {
  87. return _this.emitter.on(eventName, listener);
  88. };
  89. this.emit = function (eventName) {
  90. var _this$emitter;
  91. for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  92. args[_key - 1] = arguments[_key];
  93. }
  94. return (_this$emitter = _this.emitter).emit.apply(_this$emitter, [eventName].concat(args));
  95. };
  96. this.on('error', function () {});
  97. }
  98. return (0, _createClass2.default)(LiveQueryClient, [{
  99. key: "shouldOpen",
  100. value: function () {
  101. return this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED;
  102. }
  103. }, {
  104. key: "subscribe",
  105. value: function (query, sessionToken) {
  106. var _queryJSON$keys,
  107. _queryJSON$watch,
  108. _this2 = this;
  109. if (!query) {
  110. return;
  111. }
  112. var className = query.className;
  113. var queryJSON = query.toJSON();
  114. var where = queryJSON.where;
  115. var keys = (_queryJSON$keys = queryJSON.keys) == null ? void 0 : _queryJSON$keys.split(',');
  116. var watch = (_queryJSON$watch = queryJSON.watch) == null ? void 0 : _queryJSON$watch.split(',');
  117. var subscribeRequest = {
  118. op: OP_TYPES.SUBSCRIBE,
  119. requestId: this.requestId,
  120. query: {
  121. className: className,
  122. where: where,
  123. keys: keys,
  124. watch: watch
  125. },
  126. sessionToken: undefined
  127. };
  128. if (sessionToken) {
  129. subscribeRequest.sessionToken = sessionToken;
  130. }
  131. var subscription = new _LiveQuerySubscription.default(this.requestId, query, sessionToken);
  132. this.subscriptions.set(this.requestId, subscription);
  133. this.requestId += 1;
  134. this.connectPromise.then(function () {
  135. _this2.socket.send(JSON.stringify(subscribeRequest));
  136. }).catch(function (error) {
  137. subscription.subscribePromise.reject(error);
  138. });
  139. return subscription;
  140. }
  141. }, {
  142. key: "unsubscribe",
  143. value: function () {
  144. var _unsubscribe = (0, _asyncToGenerator2.default)(function* (subscription) {
  145. var _this3 = this;
  146. if (!subscription) {
  147. return;
  148. }
  149. var unsubscribeRequest = {
  150. op: OP_TYPES.UNSUBSCRIBE,
  151. requestId: subscription.id
  152. };
  153. return this.connectPromise.then(function () {
  154. return _this3.socket.send(JSON.stringify(unsubscribeRequest));
  155. }).then(function () {
  156. return subscription.unsubscribePromise;
  157. });
  158. });
  159. function unsubscribe() {
  160. return _unsubscribe.apply(this, arguments);
  161. }
  162. return unsubscribe;
  163. }()
  164. }, {
  165. key: "open",
  166. value: function () {
  167. var _this4 = this;
  168. var WebSocketImplementation = _CoreManager.default.getWebSocketController();
  169. if (!WebSocketImplementation) {
  170. this.emit(CLIENT_EMMITER_TYPES.ERROR, 'Can not find WebSocket implementation');
  171. return;
  172. }
  173. if (this.state !== CLIENT_STATE.RECONNECTING) {
  174. this.state = CLIENT_STATE.CONNECTING;
  175. }
  176. this.socket = new WebSocketImplementation(this.serverURL);
  177. this.socket.closingPromise = (0, _promiseUtils.resolvingPromise)();
  178. this.socket.onopen = function () {
  179. _this4._handleWebSocketOpen();
  180. };
  181. this.socket.onmessage = function (event) {
  182. _this4._handleWebSocketMessage(event);
  183. };
  184. this.socket.onclose = function (event) {
  185. var _this4$socket$closing;
  186. (_this4$socket$closing = _this4.socket.closingPromise) == null ? void 0 : _this4$socket$closing.resolve(event);
  187. _this4._handleWebSocketClose();
  188. };
  189. this.socket.onerror = function (error) {
  190. _this4._handleWebSocketError(error);
  191. };
  192. }
  193. }, {
  194. key: "resubscribe",
  195. value: function () {
  196. var _this5 = this;
  197. this.subscriptions.forEach(function (subscription, requestId) {
  198. var _queryJSON$keys2, _queryJSON$watch2;
  199. var query = subscription.query;
  200. var queryJSON = query.toJSON();
  201. var where = queryJSON.where;
  202. var keys = (_queryJSON$keys2 = queryJSON.keys) == null ? void 0 : _queryJSON$keys2.split(',');
  203. var watch = (_queryJSON$watch2 = queryJSON.watch) == null ? void 0 : _queryJSON$watch2.split(',');
  204. var className = query.className;
  205. var sessionToken = subscription.sessionToken;
  206. var subscribeRequest = {
  207. op: OP_TYPES.SUBSCRIBE,
  208. requestId: requestId,
  209. query: {
  210. className: className,
  211. where: where,
  212. keys: keys,
  213. watch: watch
  214. },
  215. sessionToken: undefined
  216. };
  217. if (sessionToken) {
  218. subscribeRequest.sessionToken = sessionToken;
  219. }
  220. _this5.connectPromise.then(function () {
  221. _this5.socket.send(JSON.stringify(subscribeRequest));
  222. });
  223. });
  224. }
  225. }, {
  226. key: "close",
  227. value: function () {
  228. var _close = (0, _asyncToGenerator2.default)(function* () {
  229. var _this$socket, _this$socket2;
  230. if (this.state === CLIENT_STATE.INITIALIZED || this.state === CLIENT_STATE.DISCONNECTED) {
  231. return;
  232. }
  233. this.state = CLIENT_STATE.DISCONNECTED;
  234. (_this$socket = this.socket) == null ? void 0 : _this$socket.close();
  235. for (var subscription of this.subscriptions.values()) {
  236. subscription.subscribed = false;
  237. subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE);
  238. }
  239. this._handleReset();
  240. this.emit(CLIENT_EMMITER_TYPES.CLOSE);
  241. return (_this$socket2 = this.socket) == null ? void 0 : _this$socket2.closingPromise;
  242. });
  243. function close() {
  244. return _close.apply(this, arguments);
  245. }
  246. return close;
  247. }()
  248. }, {
  249. key: "_handleReset",
  250. value: function () {
  251. this.attempts = 1;
  252. this.id = 0;
  253. this.requestId = 1;
  254. this.connectPromise = (0, _promiseUtils.resolvingPromise)();
  255. this.subscriptions = new Map();
  256. }
  257. }, {
  258. key: "_handleWebSocketOpen",
  259. value: function () {
  260. var connectRequest = {
  261. op: OP_TYPES.CONNECT,
  262. applicationId: this.applicationId,
  263. javascriptKey: this.javascriptKey,
  264. masterKey: this.masterKey,
  265. sessionToken: this.sessionToken,
  266. installationId: undefined
  267. };
  268. if (this.additionalProperties) {
  269. connectRequest.installationId = this.installationId;
  270. }
  271. this.socket.send(JSON.stringify(connectRequest));
  272. }
  273. }, {
  274. key: "_handleWebSocketMessage",
  275. value: function (event) {
  276. var data = event.data;
  277. if (typeof data === 'string') {
  278. data = JSON.parse(data);
  279. }
  280. var subscription = null;
  281. if (data.requestId) {
  282. subscription = this.subscriptions.get(data.requestId) || null;
  283. }
  284. var response = {
  285. clientId: data.clientId,
  286. installationId: data.installationId
  287. };
  288. switch (data.op) {
  289. case OP_EVENTS.CONNECTED:
  290. if (this.state === CLIENT_STATE.RECONNECTING) {
  291. this.resubscribe();
  292. }
  293. this.emit(CLIENT_EMMITER_TYPES.OPEN);
  294. this.id = data.clientId;
  295. this.connectPromise.resolve();
  296. this.state = CLIENT_STATE.CONNECTED;
  297. break;
  298. case OP_EVENTS.SUBSCRIBED:
  299. if (subscription) {
  300. this.attempts = 1;
  301. subscription.subscribed = true;
  302. subscription.subscribePromise.resolve();
  303. setTimeout(function () {
  304. return subscription.emit(SUBSCRIPTION_EMMITER_TYPES.OPEN, response);
  305. }, 200);
  306. }
  307. break;
  308. case OP_EVENTS.ERROR:
  309. {
  310. var parseError = new _ParseError.default(data.code, data.error);
  311. if (!this.id) {
  312. this.connectPromise.reject(parseError);
  313. this.state = CLIENT_STATE.DISCONNECTED;
  314. }
  315. if (data.requestId) {
  316. if (subscription) {
  317. subscription.subscribePromise.reject(parseError);
  318. setTimeout(function () {
  319. return subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, data.error);
  320. }, 200);
  321. }
  322. } else {
  323. this.emit(CLIENT_EMMITER_TYPES.ERROR, data.error);
  324. }
  325. if (data.error === 'Additional properties not allowed') {
  326. this.additionalProperties = false;
  327. }
  328. if (data.reconnect) {
  329. this._handleReconnect();
  330. }
  331. break;
  332. }
  333. case OP_EVENTS.UNSUBSCRIBED:
  334. {
  335. if (subscription) {
  336. this.subscriptions.delete(data.requestId);
  337. subscription.subscribed = false;
  338. subscription.unsubscribePromise.resolve();
  339. }
  340. break;
  341. }
  342. default:
  343. {
  344. if (!subscription) {
  345. break;
  346. }
  347. var override = false;
  348. if (data.original) {
  349. override = true;
  350. delete data.original.__type;
  351. for (var field in data.original) {
  352. if (!(field in data.object)) {
  353. data.object[field] = undefined;
  354. }
  355. }
  356. data.original = _ParseObject.default.fromJSON(data.original, false);
  357. }
  358. delete data.object.__type;
  359. var parseObject = _ParseObject.default.fromJSON(data.object, !(subscription.query && subscription.query._select) ? override : false);
  360. if (data.original) {
  361. subscription.emit(data.op, parseObject, data.original, response);
  362. } else {
  363. subscription.emit(data.op, parseObject, response);
  364. }
  365. var localDatastore = _CoreManager.default.getLocalDatastore();
  366. if (override && localDatastore.isEnabled) {
  367. localDatastore._updateObjectIfPinned(parseObject).then(function () {});
  368. }
  369. }
  370. }
  371. }
  372. }, {
  373. key: "_handleWebSocketClose",
  374. value: function () {
  375. if (this.state === CLIENT_STATE.DISCONNECTED) {
  376. return;
  377. }
  378. this.state = CLIENT_STATE.CLOSED;
  379. this.emit(CLIENT_EMMITER_TYPES.CLOSE);
  380. for (var subscription of this.subscriptions.values()) {
  381. subscription.emit(SUBSCRIPTION_EMMITER_TYPES.CLOSE);
  382. }
  383. this._handleReconnect();
  384. }
  385. }, {
  386. key: "_handleWebSocketError",
  387. value: function (error) {
  388. this.emit(CLIENT_EMMITER_TYPES.ERROR, error);
  389. for (var subscription of this.subscriptions.values()) {
  390. subscription.emit(SUBSCRIPTION_EMMITER_TYPES.ERROR, error);
  391. }
  392. this._handleReconnect();
  393. }
  394. }, {
  395. key: "_handleReconnect",
  396. value: function () {
  397. var _this6 = this;
  398. if (this.state === CLIENT_STATE.DISCONNECTED) {
  399. return;
  400. }
  401. this.state = CLIENT_STATE.RECONNECTING;
  402. var time = generateInterval(this.attempts);
  403. if (this.reconnectHandle) {
  404. clearTimeout(this.reconnectHandle);
  405. }
  406. this.reconnectHandle = setTimeout(function () {
  407. _this6.attempts++;
  408. _this6.connectPromise = (0, _promiseUtils.resolvingPromise)();
  409. _this6.open();
  410. }.bind(this), time);
  411. }
  412. }]);
  413. }();
  414. var _default = exports.default = LiveQueryClient;