crisp-api 5.3.0 → 6.0.0

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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,21 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ ## v6.0.0
5
+
6
+ ### Breaking Changes
7
+
8
+ * Support for NodeJS 6 has been removed. The minimum version is now NodeJS 8.
9
+ * The `CrispClient.on` method now returns a `Promise`. Please update your code accordingly. We do recommend that you add error catchers.
10
+
11
+ ### New Features
12
+
13
+ * The RTM API URL is now dynamically pulled from the REST API, based on the authentication tier. This allows for (much) more efficient message routing at Crisp scale, and offers performance and stability benefits to your integration.
14
+
15
+ ### Bug Fixes
16
+
17
+ * Fixed an issue where the library would not reconnect to the RTM API when it lost connection with the server.
18
+
4
19
  ## v5.3.0
5
20
 
6
21
  ### New Features
package/README.md CHANGED
@@ -79,7 +79,13 @@ CrispClient.on("message:send", function(message) {
79
79
  .catch(function(error) {
80
80
  console.error("Error sending message:", error);
81
81
  });
82
- });
82
+ })
83
+ .then(function() {
84
+ console.error("Requested to listen to sent messages");
85
+ })
86
+ .catch(function(error) {
87
+ console.error("Failed listening to sent messages:", error);
88
+ });
83
89
  ```
84
90
 
85
91
  ## Resource Methods
@@ -2338,7 +2344,7 @@ _👉 Notice: The `peopleID` argument can be an email or the `peopleID`._
2338
2344
 
2339
2345
  You can bind to realtime events from Crisp, in order to get notified of incoming messages and updates in websites.
2340
2346
 
2341
- You won't receive any event if you don't explicitly subscribe to realtime events using `CrispClient.on()`, as the library doesn't connect to the realtime backend automatically.
2347
+ You won't receive any event if you don't explicitly subscribe to realtime events using `CrispClient.on()`, as the library doesn't connect to the realtime backend automatically. This method returns a `Promise` object.
2342
2348
 
2343
2349
  Available events are listed below:
2344
2350
 
package/lib/crisp.js CHANGED
@@ -10,15 +10,12 @@
10
10
 
11
11
 
12
12
  // Imports
13
- var pkg = require('../package.json');
13
+ var pkg = require("../package.json");
14
14
  var got = require("got");
15
15
 
16
+ var URL = require("url").URL;
16
17
  var EventEmitter = require("fbemitter").EventEmitter;
17
18
 
18
- var __Promise = (
19
- (typeof Promise !== "undefined") ? Promise : require("q").Promise
20
- );
21
-
22
19
 
23
20
  // Base configuration
24
21
  Crisp.DEFAULT_REQUEST_TIMEOUT = 10000;
@@ -36,8 +33,6 @@ Crisp.DEFAULT_REST_BASE_PATH = "/v1/";
36
33
 
37
34
 
38
35
  // RTM API defaults
39
- Crisp.DEFAULT_RTM_HOST = "https://app.relay.crisp.chat";
40
-
41
36
  Crisp.DEFAULT_RTM_EVENTS = [
42
37
  // Session Events
43
38
  "session:update_availability",
@@ -165,7 +160,7 @@ function Crisp() {
165
160
 
166
161
  /** @private */
167
162
  this._rtm = {
168
- host : Crisp.DEFAULT_RTM_HOST
163
+ host : null
169
164
  };
170
165
 
171
166
  /** @private */
@@ -270,7 +265,7 @@ Crisp.prototype = {
270
265
  head : function(resource, query, body) {
271
266
  var self = this;
272
267
 
273
- return new __Promise(function(resolve, reject) {
268
+ return new Promise(function(resolve, reject) {
274
269
  self._request(
275
270
  resource, "head", (query || {}), null, resolve, reject
276
271
  );
@@ -288,7 +283,7 @@ Crisp.prototype = {
288
283
  get : function(resource, query) {
289
284
  var self = this;
290
285
 
291
- return new __Promise(function(resolve, reject) {
286
+ return new Promise(function(resolve, reject) {
292
287
  self._request(
293
288
  resource, "get", (query || {}), null, resolve, reject
294
289
  );
@@ -306,7 +301,7 @@ Crisp.prototype = {
306
301
  post : function(resource, query, body) {
307
302
  var self = this;
308
303
 
309
- return new __Promise(function(resolve, reject) {
304
+ return new Promise(function(resolve, reject) {
310
305
  self._request(
311
306
  resource, "post", (query || {}), (body || {}), resolve, reject
312
307
  );
@@ -324,7 +319,7 @@ Crisp.prototype = {
324
319
  patch : function(resource, query, body) {
325
320
  var self = this;
326
321
 
327
- return new __Promise(function(resolve, reject) {
322
+ return new Promise(function(resolve, reject) {
328
323
  self._request(
329
324
  resource, "patch", (query || {}), (body || {}), resolve, reject
330
325
  );
@@ -342,7 +337,7 @@ Crisp.prototype = {
342
337
  put : function(resource, query, body) {
343
338
  var self = this;
344
339
 
345
- return new __Promise(function(resolve, reject) {
340
+ return new Promise(function(resolve, reject) {
346
341
  self._request(
347
342
  resource, "put", (query || {}), (body || {}), resolve, reject
348
343
  );
@@ -360,7 +355,7 @@ Crisp.prototype = {
360
355
  delete : function(resource, query, body) {
361
356
  var self = this;
362
357
 
363
- return new __Promise(function(resolve, reject) {
358
+ return new Promise(function(resolve, reject) {
364
359
  self._request(
365
360
  resource, "delete", (query || {}), (body || null), resolve, reject
366
361
  );
@@ -409,7 +404,7 @@ Crisp.prototype = {
409
404
  this._boundEvents.push(event);
410
405
 
411
406
  // Socket not connected? Connect now.
412
- this._prepareSocket(
407
+ return this._prepareSocket(
413
408
  function(socket, emitter) {
414
409
  // Listen for event (once socket is bound)
415
410
  socket.on(event, function(data) {
@@ -418,6 +413,8 @@ Crisp.prototype = {
418
413
  }
419
414
  );
420
415
  }
416
+
417
+ return Promise.resolve();
421
418
  },
422
419
 
423
420
  /**
@@ -455,7 +452,10 @@ Crisp.prototype = {
455
452
  // No resources defined in service?
456
453
  if (!serviceInstance._resources ||
457
454
  serviceInstance._resources.length === 0) {
458
- throw new Error("Service '" + name + "' has no resources defined");
455
+ throw new Error(
456
+ "[Crisp] prepareServices: service '" + name + "' has no resources " +
457
+ "defined"
458
+ );
459
459
  }
460
460
 
461
461
  // Prepare all resources (for service)
@@ -471,9 +471,9 @@ Crisp.prototype = {
471
471
  * Binds resources to the service object
472
472
  * @memberof Crisp
473
473
  * @private
474
+ * @method _prepareResources
474
475
  * @param {object} serviceInstance
475
476
  * @param {Array} resources
476
- * @method _prepareResources
477
477
  */
478
478
  _prepareResources : function(serviceInstance, resources) {
479
479
  for (var i = 0; i < resources.length; i++) {
@@ -494,41 +494,116 @@ Crisp.prototype = {
494
494
  _prepareSocket : function(fnBindHook) {
495
495
  var self = this;
496
496
 
497
- var rtmHost = this._rtm.host;
497
+ return new Promise(function(resolve, reject) {
498
+ var rtmHostOverride = self._rtm.host;
499
+
500
+ // Append bind hook to pending stack
501
+ self._socketBindHooks.push(fnBindHook);
498
502
 
499
- // Append bind hook to pending stack
500
- this._socketBindHooks.push(fnBindHook);
503
+ // Make sure to prepare socket once? (defer socket binding, waiting that \
504
+ // all listeners have been bound, that way we submit the list of \
505
+ // filtered events to the RTM API once, and never again in the future)
506
+ if (self._socketScheduler === null) {
507
+ // Socket is already set? We should not even have entered there.
508
+ if (self._socket) {
509
+ throw new Error(
510
+ "[Crisp] prepareSocket: illegal call to prepare socket (tie break)"
511
+ );
512
+ }
501
513
 
502
- // Make sure to prepare socket once? (defer socket binding, waiting that \
503
- // all listeners have been bound, that way we submit the list of \
504
- // filtered events to the RTM API once, and never again in the future)
505
- if (this._socketScheduler === null) {
506
- // Socket is already set? We should not even have entered there.
507
- if (this._socket) {
508
- throw new Error("[Crisp] illegal call to prepare socket (tie break)");
514
+ self._socketScheduler = setTimeout(function() {
515
+ // Connect to socket now
516
+ self._connectSocket(rtmHostOverride)
517
+ .then(resolve)
518
+ .catch(reject);
519
+ }, Crisp.DEFAULT_SOCKET_SCHEDULE);
520
+ } else {
521
+ // Pass-through
522
+ resolve();
509
523
  }
524
+ });
525
+ },
526
+
527
+ /**
528
+ * Connects socket, using preferred RTM API host
529
+ * @memberof Crisp
530
+ * @private
531
+ * @method _connectSocket
532
+ * @param {string} rtmHostOverride
533
+ */
534
+ _connectSocket : function(rtmHostOverride) {
535
+ var self = this;
536
+
537
+ return Promise.resolve()
538
+ .then(function() {
539
+ // Any override RTM API host?
540
+ if (rtmHostOverride) {
541
+ return Promise.resolve({
542
+ socket : {
543
+ app : rtmHostOverride
544
+ }
545
+ });
546
+ }
547
+
548
+ // Acquire RTM API URL from remote
549
+ var restUrlSegments;
550
+
551
+ switch (self._tier) {
552
+ case "plugin": {
553
+ restUrlSegments = ["plugin", "connect", "endpoints"];
510
554
 
511
- this._socketScheduler = setTimeout(function() {
512
- self._socket = require('socket.io-client')(rtmHost, {
555
+ break;
556
+ }
557
+
558
+ default: {
559
+ restUrlSegments = ["user", "connect", "endpoints"];
560
+ }
561
+ }
562
+
563
+ return self.get(
564
+ self._prepareRestUrl(restUrlSegments)
565
+ )
566
+ .catch(function() {
567
+ // Void error (consider as empty response)
568
+ return Promise.resolve({});
569
+ });
570
+ })
571
+ .then(function(endpoints) {
572
+ var rtmHostAffinity = ((endpoints.socket || {}).app || null);
573
+
574
+ // No RTM API host acquired?
575
+ if (rtmHostAffinity === null) {
576
+ throw new Error(
577
+ "[Crisp] connectSocket: could not acquire target host to " +
578
+ "connect to, is your session valid for tier?"
579
+ );
580
+ }
581
+
582
+ // Parse target RTM API host as an URL object
583
+ var rtmHostUrl = new URL(rtmHostAffinity);
584
+
585
+ // Connect to socket
586
+ self._socket = require("socket.io-client")(rtmHostUrl.origin, {
587
+ path : (rtmHostUrl.pathname || "/"),
513
588
  transports : ["websocket"],
514
589
  timeout : Crisp.DEFAULT_SOCKET_TIMEOUT,
515
590
  reconnection : true,
516
591
  reconnectionDelay : Crisp.DEFAULT_SOCKET_RECONNECT_DELAY,
517
592
  reconnectionDelayMax : Crisp.DEFAULT_SOCKET_RECONNECT_DELAY_MAX,
518
593
  randomizationFactor : Crisp.DEFAULT_SOCKET_RECONNECT_FACTOR
519
-
520
594
  });
521
595
 
522
596
  self._emitAuthenticate();
523
597
 
524
598
  // Setup base socket event listeners
525
- self._socket.on("reconnect", function() {
599
+ self._socket.io.on("reconnect", function() {
526
600
  self._emitAuthenticate();
527
601
  });
528
602
 
529
603
  self._socket.on("unauthorized", function() {
530
604
  throw new Error(
531
- "[Crisp] cannot listen for events as authentication is invalid"
605
+ "[Crisp] connectSocket: cannot listen for events as " +
606
+ "authentication is invalid"
532
607
  );
533
608
  });
534
609
 
@@ -538,8 +613,7 @@ Crisp.prototype = {
538
613
  self._socket, self._emitter
539
614
  );
540
615
  }
541
- }, Crisp.DEFAULT_SOCKET_SCHEDULE);
542
- }
616
+ });
543
617
  },
544
618
 
545
619
  /**
@@ -555,7 +629,7 @@ Crisp.prototype = {
555
629
  * @param {function} reject
556
630
  */
557
631
  _request : function(resource, method, query, body, resolve, reject) {
558
- var _parameters = {
632
+ var requestParameters = {
559
633
  json : true,
560
634
  timeout : Crisp.DEFAULT_REQUEST_TIMEOUT,
561
635
 
@@ -567,21 +641,21 @@ Crisp.prototype = {
567
641
 
568
642
  // Add authorization?
569
643
  if (this.auth.token) {
570
- _parameters.headers.Authorization = ("Basic " + this.auth.token);
644
+ requestParameters.headers.Authorization = ("Basic " + this.auth.token);
571
645
  }
572
646
 
573
647
  // Add body?
574
648
  if (body) {
575
- _parameters.body = body;
649
+ requestParameters.body = body;
576
650
  }
577
651
 
578
652
  // Add query?
579
653
  if (query) {
580
- _parameters.query = query;
654
+ requestParameters.query = query;
581
655
  }
582
656
 
583
657
  // Proceed request
584
- got[method](resource, _parameters)
658
+ got[method](resource, requestParameters)
585
659
  .catch(function(error) {
586
660
  return Promise.resolve(error);
587
661
  })
@@ -644,17 +718,20 @@ Crisp.prototype = {
644
718
 
645
719
  if (!this._socket) {
646
720
  throw new Error(
647
- "[Crisp] cannot listen for events as socket is not yet bound"
721
+ "[Crisp] emitAuthenticate: cannot listen for events as socket is " +
722
+ "not yet bound"
648
723
  );
649
724
  }
650
725
  if (!auth.identifier || !auth.key) {
651
726
  throw new Error(
652
- "[Crisp] cannot listen for events as you did not authenticate"
727
+ "[Crisp] emitAuthenticate: cannot listen for events as you did not " +
728
+ "authenticate"
653
729
  );
654
730
  }
655
731
  if (this._boundEvents.length === 0) {
656
732
  throw new Error(
657
- "[Crisp] cannot listen for events as no event is being listened to"
733
+ "[Crisp] emitAuthenticate: cannot listen for events as no event is " +
734
+ "being listened to"
658
735
  );
659
736
  }
660
737
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "crisp-api",
3
3
  "description": "Crisp API wrapper for Node - official, maintained by Crisp",
4
- "version": "5.3.0",
4
+ "version": "6.0.0",
5
5
  "homepage": "https://github.com/crisp-im/node-crisp-api",
6
6
  "license": "MIT",
7
7
  "author": {
@@ -25,7 +25,7 @@
25
25
  "main": "./lib/crisp",
26
26
  "types": "./types/crisp.d.ts",
27
27
  "engines": {
28
- "node": ">= 6.0.0"
28
+ "node": ">= 8.0.0"
29
29
  },
30
30
  "scripts": {
31
31
  "test": "check-build",
@@ -38,8 +38,7 @@
38
38
  "dependencies": {
39
39
  "socket.io-client": "4.4.1",
40
40
  "fbemitter": "3.0.0",
41
- "got": "9.6.0",
42
- "q": "2.0.3"
41
+ "got": "9.6.0"
43
42
  },
44
43
  "keywords": [
45
44
  "crisp",
package/types/crisp.d.ts CHANGED
@@ -48,7 +48,7 @@ declare class Crisp {
48
48
  _emitAuthenticate: () => void;
49
49
  }
50
50
  declare namespace Crisp {
51
- export { DEFAULT_REQUEST_TIMEOUT, DEFAULT_SOCKET_TIMEOUT, DEFAULT_SOCKET_RECONNECT_DELAY, DEFAULT_SOCKET_RECONNECT_DELAY_MAX, DEFAULT_SOCKET_RECONNECT_FACTOR, DEFAULT_SOCKET_SCHEDULE, DEFAULT_USERAGENT_PREFIX, DEFAULT_REST_HOST, DEFAULT_REST_BASE_PATH, DEFAULT_RTM_HOST, DEFAULT_RTM_EVENTS, Crisp };
51
+ export { DEFAULT_REQUEST_TIMEOUT, DEFAULT_SOCKET_TIMEOUT, DEFAULT_SOCKET_RECONNECT_DELAY, DEFAULT_SOCKET_RECONNECT_DELAY_MAX, DEFAULT_SOCKET_RECONNECT_FACTOR, DEFAULT_SOCKET_SCHEDULE, DEFAULT_USERAGENT_PREFIX, DEFAULT_REST_HOST, DEFAULT_REST_BASE_PATH, DEFAULT_RTM_EVENTS, Crisp };
52
52
  }
53
53
  declare var DEFAULT_REQUEST_TIMEOUT: number;
54
54
  declare var DEFAULT_SOCKET_TIMEOUT: number;
@@ -59,5 +59,4 @@ declare var DEFAULT_SOCKET_SCHEDULE: number;
59
59
  declare var DEFAULT_USERAGENT_PREFIX: string;
60
60
  declare var DEFAULT_REST_HOST: string;
61
61
  declare var DEFAULT_REST_BASE_PATH: string;
62
- declare var DEFAULT_RTM_HOST: string;
63
62
  declare var DEFAULT_RTM_EVENTS: string[];