comprodls-sdk 2.84.0 → 2.86.0-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,9 +26,7 @@
26
26
  var ERROR_TYPES = {
27
27
  "API_ERROR": "API_ERROR",
28
28
  "SDK_ERROR": "SDK_ERROR",
29
- "CHANNEL_SUBSCRIPTION": "CHANNEL_SUBSCRIPTION",
30
- "UNEXPECTED_ERROR": "UNEXPECTED_ERROR",
31
- "POLLING_INITIATION": "POLLING_INITIATION"
29
+ "CHANNEL_SUBSCRIPTION": "CHANNEL_SUBSCRIPTION"
32
30
  };
33
31
 
34
32
  var ERROR_CATEGORY = {
@@ -724,14 +724,17 @@ function getClassesOfAGradeformat(options) {
724
724
 
725
725
  /*
726
726
  options = {
727
- assignedpathid: "string", //Mandatory
727
+ assignedpathid: "string", // Mandatory
728
728
  classid: "string", // Mandatory
729
- body : [ // min:1 , max: 50
730
- {
731
- extUserId: "string", //Mandatory
732
- }.
733
- ....
734
- ]
729
+ body : {
730
+ "users":[ // min:1 , max: 50
731
+ {
732
+ extUserId: "string", // Mandatory
733
+ }
734
+ ....
735
+ ],
736
+ "context": {} // Optional
737
+ }
735
738
  }
736
739
  */
737
740
  function createUserAssignedPathEnrollments(options) {
@@ -79,8 +79,12 @@ function _connect(pubnubCW, options) {
79
79
  'pubnub': {
80
80
  'publishKey': options.publishKey,
81
81
  'subscribeKey': options.subscribeKey,
82
- 'ssl': true
83
- }
82
+ 'ssl': true,
83
+ 'suppressLeaveEvents': true
84
+ },
85
+ 'pollingEndpoint': options.pollingEndpoint,
86
+ 'pollingIterations': options.pollingIterations || 10, // Default polling iterations is 10
87
+ 'pollingInterval': options.pollingInterval, // Default polling interval is exponential backoff
84
88
  });
85
89
  }
86
90
 
@@ -1,4 +1,5 @@
1
1
  var pubNub = require("pubnub");
2
+ var request = require('superagent');
2
3
  var EventEmitter = require("events").EventEmitter;
3
4
  var helpers = require('../../helpers');
4
5
 
@@ -21,6 +22,13 @@ module.exports = function () {
21
22
  var _globalSubscription = [];
22
23
  var _globalSubscriptionStatus = {};
23
24
  var bStatusSubscribed = false;
25
+ /** ID of the setTimeout - used for Polling events when PubNub fails */
26
+ var _setTimeoutIDForPolling;
27
+ /** Timestamp from when the events need to be fetched */
28
+ var _startTimestampForPolling;
29
+ /** Counter for polling */
30
+ var _pollingCounter;
31
+ var bPollingInitiated = false;
24
32
 
25
33
  /** ###### END OF MODULE GLOBALS */
26
34
 
@@ -36,6 +44,9 @@ module.exports = function () {
36
44
  //Returning the adaptor (Plain Javascript object)
37
45
  return {
38
46
  "on": function (channelObj, handler) {
47
+ // If PubNub fails, we will fetch the events from this timestamp
48
+ _startTimestampForPolling = Date.now();
49
+
39
50
  var pubNubChannel;
40
51
  var channelContext = [];
41
52
 
@@ -64,6 +75,9 @@ module.exports = function () {
64
75
  _eventEmitter.on(channelObj.channel, handler);
65
76
  bStatusSubscribed = true;
66
77
  }
78
+
79
+ // Clean up the old events
80
+ _checkOldBubbledEvents();
67
81
  },
68
82
  "getMySubscriptionStatus": __getMySubscriptionStatus
69
83
  };
@@ -77,6 +91,10 @@ module.exports = function () {
77
91
 
78
92
  var _translatePubnubStatus = function(status, options) {
79
93
  var channels = [], error, successObj;
94
+
95
+ // If polling has been initiated, ignore all punnub status
96
+ if (bPollingInitiated) return;
97
+
80
98
  switch (status.category) {
81
99
  case "PNConnectedCategory":
82
100
  if(status.operation === "PNSubscribeOperation") {
@@ -138,11 +156,10 @@ module.exports = function () {
138
156
  break;
139
157
  case "PNNetworkIssuesCategory":
140
158
  if(status.operation === 'PNSubscribeOperation') {
141
- if(!options.accountId) {
159
+ if(!options.accountId || !_userOptions.pollingEndpoint) {
142
160
  error = {
143
- message: "Missing mandatory parameters to initiate polling operation - accountId",
144
- status: status.statusCode,
145
- type: helpers.errors.ERROR_TYPES.POLLING_INITIATION,
161
+ message: "Polling initiation error: Missing mandatory parameters to initiate polling operation - [accountId, pollingEndpoint]",
162
+ status: status.statusCode
146
163
  };
147
164
  error = new PUSHXError(helpers.errors.ERROR_CATEGORY.PUSHX, error);
148
165
  _eventEmitter.emit('pushx_status', error);
@@ -156,6 +173,9 @@ module.exports = function () {
156
173
  * The wrapper supports multiple channels, but the APP currently uses a single channel only.
157
174
  * Polling will also limited to a single channel.
158
175
  */
176
+ _pollingCounter = 0;
177
+ _pollForEvents();
178
+ bPollingInitiated = true;
159
179
  }
160
180
  }
161
181
  else {
@@ -168,9 +188,8 @@ module.exports = function () {
168
188
  default:
169
189
  // Emit error for other status-category received from pubnub
170
190
  error = {
171
- message: "PushX Error",
191
+ message: "PushX Error: unexpected error",
172
192
  status: status.statusCode,
173
- type: helpers.errors.ERROR_TYPES.UNEXPECTED_ERROR,
174
193
  pushXError: status
175
194
  };
176
195
  error = new PUSHXError(helpers.errors.ERROR_CATEGORY.PUSHX, error);
@@ -207,6 +226,7 @@ module.exports = function () {
207
226
  * @returns PROMISE.
208
227
  */
209
228
  var __setup = function (userOptions) {
229
+
210
230
  var pubnubConfig = userOptions.pubnub;
211
231
  pubnubConfig.uuid = userOptions.userid;
212
232
  var accountId = userOptions.accountid;
@@ -245,8 +265,169 @@ module.exports = function () {
245
265
  _globalSubscription = [];
246
266
  bStatusSubscribed = false;
247
267
  _globalSubscriptionStatus = {};
268
+ bPollingInitiated = false;
248
269
  }
249
270
  _pubnubClient = undefined;
271
+
272
+ // Clear the Polling
273
+ if (_setTimeoutIDForPolling) {
274
+ clearTimeout(_setTimeoutIDForPolling);
275
+ // Setting the start time to now to clear the old events
276
+ _startTimestampForPolling = Date.now();
277
+ _checkOldBubbledEvents();
278
+ _setTimeoutIDForPolling = undefined;
279
+ _pollingCounter = undefined;
280
+ _startTimestampForPolling = undefined;
281
+ }
282
+ };
283
+
284
+ /**
285
+ * Polling function
286
+ */
287
+ var _pollForEvents = function () {
288
+ // Setup request params
289
+ var params = {
290
+ accountid: _userOptions.accountid,
291
+ channelname: _globalSubscription[0],
292
+ starttime: _startTimestampForPolling,
293
+ endtime: Date.now()
294
+ };
295
+
296
+ // Safe check to avoid poll for events if the channel name or start time is not set
297
+ // This handles an edge condition where the clearTimeout doesn't work as expected
298
+ if (!params.channelname || !params.starttime) {
299
+ return;
300
+ }
301
+
302
+ var requestAPI = request.get(_userOptions.pollingEndpoint).query(params);
303
+
304
+ requestAPI
305
+ .end(function(error, response) {
306
+
307
+ if (!error) {
308
+ // Bubble the connected status
309
+ if (_pollingCounter === 0) {
310
+ // Set the status of the channel to subscribed
311
+ if(_globalSubscriptionStatus[_globalSubscription[0]]) {
312
+ _globalSubscriptionStatus[_globalSubscription[0]].status = 'subscribed';
313
+ }
314
+
315
+ var successObj = {
316
+ category: 'PUSHX',
317
+ type: 'CHANNEL_SUBSCRIPTION',
318
+ status: 'SUCCESS',
319
+ message: 'Success: Subscribed successfully.',
320
+ httpcode: 200,
321
+ data: {
322
+ payload: {
323
+ channels: _globalSubscription
324
+ },
325
+ message: 'Success: Subscribed successfully.'
326
+ }
327
+ };
328
+ _eventEmitter.emit('pushx_status', successObj);
329
+ }
330
+
331
+ // Bubble the received events
332
+ _bubblePolledEvents(response.body);
333
+
334
+ // Increase the counter
335
+ _pollingCounter++;
336
+ }
337
+
338
+ // Polling will be done only for specific number of times
339
+ if (_pollingCounter > _userOptions.pollingIterations) {
340
+ var error = {
341
+ message: "Polling error: Polling limit exceeded",
342
+ status: 429
343
+ };
344
+ error = new PUSHXError(helpers.errors.ERROR_CATEGORY.PUSHX, error);
345
+ _eventEmitter.emit('pushx_status', error);
346
+ } else {
347
+ var timeoutDelay;
348
+
349
+ // If the polling interval is provided, use that
350
+ if (_userOptions.pollingInterval) {
351
+ timeoutDelay = _userOptions.pollingInterval * 60 * 1000;
352
+ } else {
353
+ // Else, use exponential backoff
354
+ timeoutDelay = parseInt(Math.pow(1.3, _pollingCounter) * 10 * 1000, 10);
355
+ }
356
+
357
+ // Set timeout for next poll
358
+ if (timeoutDelay) {
359
+ _setTimeoutIDForPolling = setTimeout(_pollForEvents, timeoutDelay);
360
+ }
361
+ }
362
+ });
363
+ };
364
+
365
+ /**
366
+ * Bubble the polled events to the FE APP
367
+ * @param {*} events - Events recieved from the polling endpoint
368
+ */
369
+ var _bubblePolledEvents = function (events) {
370
+ // Load the old events from the session storage
371
+ var oldEventData = _checkOldBubbledEvents();
372
+
373
+ var oldEvents = oldEventData.events;
374
+ var sessionStorageKey = oldEventData.key;
375
+
376
+ for (var i = 0; i < events.entities.length; i++) {
377
+ var event = events.entities[i];
378
+ var eventId = event.pk + '_' + event.sk;
379
+
380
+ // Check if the event is already emitted
381
+ if (!oldEvents[eventId]) {
382
+ oldEvents[eventId] = event.context.start_time;
383
+
384
+ // Emit the event
385
+ _eventEmitter.emit(_globalSubscription[0], event);
386
+ }
387
+ }
388
+
389
+ // Save the bubbled events to session storage
390
+ sessionStorage.setItem(sessionStorageKey, JSON.stringify(oldEvents));
391
+ };
392
+
393
+ /**
394
+ * Check the old bubbled events and remove the events that are older than the start timestamp
395
+ * @returns {object} - Object containing the key and the events
396
+ */
397
+ var _checkOldBubbledEvents = function () {
398
+ var sessionStorageKey = 'comprodls.old_pushx_events.' + _userOptions.userid + '.' + _globalSubscription[0];
399
+ var oldEvents = sessionStorage.getItem(sessionStorageKey);
400
+
401
+ if (oldEvents) {
402
+ try {
403
+ oldEvents = JSON.parse(oldEvents);
404
+ } catch (e) {
405
+ oldEvents = {};
406
+ }
407
+
408
+ var oldEventIds = Object.keys(oldEvents);
409
+
410
+ // Remove the events that are older than the start timestamp
411
+ for (var i = 0; i < oldEventIds.length; i++) {
412
+ var oldEventId = oldEventIds[i];
413
+
414
+ if (oldEvents[oldEventId] < _startTimestampForPolling) {
415
+ delete oldEvents[oldEventId];
416
+ }
417
+ }
418
+
419
+ // If the number of events has changed, save the events
420
+ if (oldEventIds.length !== Object.keys(oldEvents).length) {
421
+ sessionStorage.setItem(sessionStorageKey, JSON.stringify(oldEvents));
422
+ }
423
+ } else {
424
+ oldEvents = {};
425
+ }
426
+
427
+ return {
428
+ key: sessionStorageKey,
429
+ events: oldEvents
430
+ };
250
431
  };
251
432
 
252
433
  return { // Return public methods for the wrapper
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "comprodls-sdk",
3
3
  "description": "comproDLS SDK for JavaScript",
4
- "version": "2.84.0",
4
+ "version": "2.86.0-alpha",
5
5
  "author": {
6
6
  "name": "Compro Technologies Private Limited",
7
7
  "url": "http://www.comprotechnologies.com/"