@ndustrial/contxt-sdk 5.2.6 → 5.4.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.
@@ -344,4 +344,111 @@ describe('Bus/Channels', function() {
344
344
  });
345
345
  });
346
346
  });
347
+
348
+ describe('peek', function() {
349
+ context('the required fields are provided', function() {
350
+ let channelFromServerAfterFormat;
351
+ let channelFromServerBeforeFormat;
352
+ let expectedOrganizationId;
353
+ let expectedServiceId;
354
+ let expectedChannelId;
355
+ let expectedSubscription;
356
+ let promise;
357
+ let request;
358
+ let toCamelCase;
359
+
360
+ beforeEach(function() {
361
+ channelFromServerAfterFormat = fixture.build('channel');
362
+ expectedSubscription = "test";
363
+ expectedChannelId = channelFromServerAfterFormat.id;
364
+ expectedOrganizationId = channelFromServerAfterFormat.organizationId;
365
+ expectedServiceId = channelFromServerAfterFormat.serviceId;
366
+ channelFromServerBeforeFormat = fixture.build(
367
+ 'channel',
368
+ channelFromServerAfterFormat,
369
+ { fromServer: true }
370
+ );
371
+
372
+ request = {
373
+ ...baseRequest,
374
+ get: sinon.stub().resolves(channelFromServerBeforeFormat)
375
+ };
376
+ toCamelCase = sinon
377
+ .stub(objectUtils, 'toCamelCase')
378
+ .returns(channelFromServerAfterFormat);
379
+
380
+ const channels = new Channels(baseSdk, request);
381
+ channels._baseUrl = expectedHost;
382
+
383
+ promise = channels.peek(
384
+ expectedOrganizationId,
385
+ expectedServiceId,
386
+ expectedChannelId,
387
+ expectedSubscription
388
+ );
389
+ });
390
+
391
+ it('gets the channel from the server', function() {
392
+ expect(request.get).to.be.calledWith(
393
+ `${expectedHost}/organizations/${expectedOrganizationId}/services/${expectedServiceId}/channels/${expectedChannelId}/peek/${expectedSubscription}`
394
+ );
395
+ });
396
+
397
+ it('formats the channel object', function() {
398
+ return promise.then(() => {
399
+ expect(toCamelCase).to.be.calledWith(channelFromServerBeforeFormat);
400
+ });
401
+ });
402
+
403
+ it('returns the requested event', function() {
404
+ return expect(promise).to.be.fulfilled.and.to.eventually.deep.equal(
405
+ channelFromServerAfterFormat
406
+ );
407
+ });
408
+ });
409
+
410
+ context('the organizationId is not provided', function() {
411
+ it('throws an error', function() {
412
+ const channels = new Channels(baseSdk, baseRequest);
413
+ const promise = channels.peek();
414
+
415
+ return expect(promise).to.be.rejectedWith(
416
+ 'An organizationId is required to peek a message bus channel subscription.'
417
+ );
418
+ });
419
+ });
420
+
421
+ context('the serviceId is not provided', function() {
422
+ it('throws an error', function() {
423
+ const channels = new Channels(baseSdk, baseRequest);
424
+ const promise = channels.peek('1');
425
+
426
+ return expect(promise).to.be.rejectedWith(
427
+ 'A serviceId is required to peek a message bus channel subscription.'
428
+ );
429
+ });
430
+ });
431
+
432
+ context('the channelId is not provided', function() {
433
+ it('throws an error', function() {
434
+ const channels = new Channels(baseSdk, baseRequest);
435
+ const promise = channels.peek('1', '2');
436
+
437
+ return expect(promise).to.be.rejectedWith(
438
+ 'A channelId is required to peek a message bus channel subscription.'
439
+ );
440
+ });
441
+ });
442
+
443
+ context('the subscription is not provided', function() {
444
+ it('throws an error', function() {
445
+ const channels = new Channels(baseSdk, baseRequest);
446
+ const promise = channels.peek('1', '2', '3');
447
+
448
+ return expect(promise).to.be.rejectedWith(
449
+ 'A subscription name is required to peek a message bus channel subscription.'
450
+ );
451
+ });
452
+ });
453
+ });
347
454
  });
package/src/bus/index.js CHANGED
@@ -35,6 +35,20 @@ import WebSocketConnection from './webSocketConnection';
35
35
  * @property {WebSocket} _webSocket The raw WebSocket connection to the message bus
36
36
  */
37
37
 
38
+ /**
39
+ * Configuration object for the Bus
40
+ *
41
+ * @typedef {Object} BusConfig
42
+ * @property {boolean} autoAcknowledge
43
+ */
44
+
45
+ /**
46
+ * @type {BusConfig}
47
+ */
48
+ const defaultBusConfig = {
49
+ autoAcknowledge: true
50
+ };
51
+
38
52
  /**
39
53
  * Module that provides access to the message bus. This is for Node
40
54
  * environments. Documentation for browser environments is found under
@@ -46,8 +60,9 @@ class Bus {
46
60
  /**
47
61
  * @param {Object} sdk An instance of the SDK so the module can communicate with other modules
48
62
  * @param {Object} request An instance of the request module tied to this module's audience.
63
+ * @param {BusConfig} config A config object for the Bus instance
49
64
  */
50
- constructor(sdk, request) {
65
+ constructor(sdk, request, config) {
51
66
  const baseUrl = `${sdk.config.audiences.bus.host}`;
52
67
  const baseWebSocketUrl = `${sdk.config.audiences.bus.webSocket}`;
53
68
 
@@ -56,6 +71,7 @@ class Bus {
56
71
  this._request = request;
57
72
  this._sdk = sdk;
58
73
  this._webSockets = {};
74
+ this._config = Object.assign({}, defaultBusConfig, config);
59
75
 
60
76
  this.channels = new Channels(sdk, request, baseUrl);
61
77
  }
@@ -101,7 +117,8 @@ class Bus {
101
117
  ws.onopen = (event) => {
102
118
  this._webSockets[organizationId] = new WebSocketConnection(
103
119
  ws,
104
- organizationId
120
+ organizationId,
121
+ this._config.autoAcknowledge
105
122
  );
106
123
 
107
124
  resolve(this._webSockets[organizationId]);
@@ -290,6 +290,48 @@ describe('Bus', function() {
290
290
  );
291
291
  }
292
292
  );
293
+
294
+ context('when providing custom config', function() {
295
+ let bus;
296
+ let expectedApiToken;
297
+ let promise;
298
+ let sdk;
299
+ let server;
300
+ let busConfig
301
+
302
+ beforeEach(function() {
303
+ expectedApiToken = faker.internet.password();
304
+
305
+ sdk = {
306
+ ...baseSdk,
307
+ auth: {
308
+ ...baseSdk.auth,
309
+ getCurrentApiToken: sinon.stub().resolves(expectedApiToken)
310
+ }
311
+ };
312
+
313
+ server = new Server(
314
+ `${expectedHost}/organizations/${expectedOrganization.id}/stream`
315
+ );
316
+
317
+ busConfig = { autoAcknowledge: false };
318
+ bus = new Bus(sdk, baseRequest, busConfig);
319
+ bus._baseWebSocketUrl = expectedHost;
320
+
321
+ promise = bus.connect(expectedOrganization.id);
322
+ });
323
+
324
+ afterEach(function() {
325
+ server.stop();
326
+ });
327
+
328
+ it('passes the auto-acknowledge flag to the WebSocketConnection', function() {
329
+ return promise.then((resolvedWebSocket) => {
330
+ expect(resolvedWebSocket._autoAck).to.equal(busConfig.autoAcknowledge);
331
+ });
332
+ });
333
+
334
+ });
293
335
  });
294
336
 
295
337
  describe('getWebSocketConnection', function() {
@@ -29,11 +29,13 @@ class WebSocketConnection {
29
29
  /**
30
30
  * @param {WebSocket} webSocket A WebSocket connection to the message bus
31
31
  * @param {string} organizationId UUID corresponding with an organization
32
+ * @param {boolean} autoAcknowledge Whether the messages should be ACK'd explicitly or not
32
33
  */
33
- constructor(webSocket, organizationId) {
34
+ constructor(webSocket, organizationId, autoAcknowledge = true) {
34
35
  this._messageHandlers = {};
35
36
  this._organizationId = organizationId;
36
37
  this._webSocket = webSocket;
38
+ this._autoAck = autoAcknowledge;
37
39
 
38
40
  if (this._webSocket) {
39
41
  this._webSocket.onerror = this._onError;
@@ -282,20 +284,24 @@ class WebSocketConnection {
282
284
 
283
285
  if (error) {
284
286
  return resolve(errorHandler(error));
285
- } else {
286
- try {
287
- const ack = once(() => {
288
- return this._acknowledge(result.id);
289
- });
290
-
291
- return resolve(
292
- Promise.resolve(handler(result.body, ack)).then((res) => {
287
+ }
288
+
289
+ try {
290
+ const ack = once(() => {
291
+ return this._acknowledge(result.id);
292
+ });
293
+
294
+ return resolve(
295
+ Promise.resolve(handler(result.body, ack)).then((res) => {
296
+ if (this._autoAck) {
293
297
  return ack().then(() => res);
294
- })
295
- );
296
- } catch (throwable) {
297
- return reject(throwable);
298
- }
298
+ }
299
+
300
+ return res;
301
+ })
302
+ );
303
+ } catch (throwable) {
304
+ return reject(throwable);
299
305
  }
300
306
  });
301
307
  };
@@ -1755,6 +1755,51 @@ describe('Bus/WebSocketConnection', function() {
1755
1755
  });
1756
1756
  });
1757
1757
  });
1758
+
1759
+ context('and the client does not auto-ack', function() {
1760
+ let errorHandler;
1761
+
1762
+ beforeEach(function() {
1763
+ handler = sinon.stub().returns(null);
1764
+ errorHandler = sinon.stub().returns(null);
1765
+
1766
+ ws = new WebSocketConnection(
1767
+ expectedWebSocket,
1768
+ expectedOrganization.id,
1769
+ false
1770
+ );
1771
+
1772
+ promise = ws.subscribe(
1773
+ serviceId,
1774
+ channel,
1775
+ group,
1776
+ handler,
1777
+ errorHandler
1778
+ );
1779
+
1780
+ jsonRpcId = Object.keys(ws._messageHandlers)[0];
1781
+
1782
+ ws._messageHandlers[jsonRpcId]({
1783
+ result: {
1784
+ subscription
1785
+ }
1786
+ });
1787
+ });
1788
+
1789
+ it('doesn\'t call the ack function', function() {
1790
+ return promise
1791
+ .then(() => {
1792
+ return ws._messageHandlers[subscription]({
1793
+ result: {
1794
+ error: message
1795
+ }
1796
+ });
1797
+ })
1798
+ .catch(() => {
1799
+ expect(acknowledge).to.not.be.called;
1800
+ });
1801
+ });
1802
+ });
1758
1803
  });
1759
1804
 
1760
1805
  context('without a group', function() {
@@ -7,5 +7,8 @@ export default {
7
7
  interceptors: {
8
8
  request: [],
9
9
  response: []
10
+ },
11
+ bus: {
12
+ autoAcknowledge: true
10
13
  }
11
14
  };
@@ -116,6 +116,11 @@ class Config {
116
116
  ...defaultConfigs.interceptors,
117
117
  ...userConfig.interceptors
118
118
  };
119
+
120
+ this.bus = {
121
+ ...defaultConfigs.bus,
122
+ ...userConfig.bus
123
+ };
119
124
  }
120
125
 
121
126
  addDynamicAudience(audienceName, { clientId, host }) {
package/src/index.js CHANGED
@@ -78,7 +78,7 @@ class ContxtSdk {
78
78
  this.config = new Config(config, externalModules);
79
79
 
80
80
  this.auth = this._createAuthSession(sessionType);
81
- this.bus = new Bus(this, this._createRequest('bus'));
81
+ this.bus = new Bus(this, this._createRequest('bus'), this.config.bus);
82
82
  this.coordinator = new Coordinator(
83
83
  this,
84
84
  this._createRequest('coordinator')
package/src/index.spec.js CHANGED
@@ -76,6 +76,15 @@ describe('ContxtSdk', function() {
76
76
  expect(contxtSdk.bus).to.be.an.instanceof(Bus);
77
77
  });
78
78
 
79
+ it('passes bus config to the bus constructor', function() {
80
+ contxtSdk = new ContxtSdk({
81
+ config: {...baseConfig, bus: {autoAcknowledge: false}},
82
+ externalModules: expectedExternalModules,
83
+ sessionType: expectedAuthSessionType
84
+ });
85
+ expect(contxtSdk.bus._config).to.deep.equal({autoAcknowledge: false});
86
+ });
87
+
79
88
  it('sets an instance of Config', function() {
80
89
  expect(contxtSdk.config).to.be.an.instanceof(Config);
81
90
  });