@wazo/sdk 0.37.30 → 0.37.34

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.
Files changed (80) hide show
  1. package/dist/wazo-sdk.js +4 -4
  2. package/esm/__tests__/api-client.test.js +30 -0
  3. package/esm/__tests__/web-rtc-client.test.js +57 -6
  4. package/esm/api/calld.js +30 -0
  5. package/esm/api-client.js +30 -0
  6. package/esm/checker/Checker.js +57 -6
  7. package/esm/checker/checks/aor.js +30 -0
  8. package/esm/checker/checks/api.js +30 -0
  9. package/esm/checker/checks/index.js +57 -6
  10. package/esm/checker/checks/webrtc-transport.js +57 -6
  11. package/esm/checker/checks/webrtc.js +57 -6
  12. package/esm/domain/AdHocAPIConference.js +30 -0
  13. package/esm/domain/CallSession.js +9 -1
  14. package/esm/domain/Features.js +30 -0
  15. package/esm/domain/MeetingStatus.js +29 -0
  16. package/esm/domain/Phone/CTIPhone.js +39 -1
  17. package/esm/domain/Phone/WebRTCPhone.js +43 -8
  18. package/esm/domain/__tests__/CallSession.test.js +41 -5
  19. package/esm/domain/__tests__/IndirectTransfer.test.js +9 -1
  20. package/esm/domain/__tests__/Room.test.js +9 -1
  21. package/esm/index.js +100 -14
  22. package/esm/service/CallApi.js +30 -0
  23. package/esm/service/getApiClient.js +30 -0
  24. package/esm/simple/Auth.js +100 -14
  25. package/esm/simple/Configuration.js +100 -14
  26. package/esm/simple/Directory.js +30 -0
  27. package/esm/simple/Phone.js +100 -14
  28. package/esm/simple/index.js +100 -14
  29. package/esm/simple/room/LocalParticipant.js +100 -14
  30. package/esm/simple/room/Participant.js +100 -14
  31. package/esm/simple/room/RemoteParticipant.js +100 -14
  32. package/esm/simple/room/Room.js +100 -14
  33. package/esm/simple/room/SipRoom.js +100 -14
  34. package/esm/simple/room/Stream.js +100 -14
  35. package/esm/simple/room/__tests__/RemoteParticipant.test.js +100 -14
  36. package/esm/simple/utils.js +100 -14
  37. package/esm/web-rtc-client.js +57 -6
  38. package/lib/__tests__/api-client.test.js +30 -0
  39. package/lib/__tests__/web-rtc-client.test.js +57 -6
  40. package/lib/api/calld.js +30 -0
  41. package/lib/api-client.js +30 -0
  42. package/lib/checker/Checker.js +57 -6
  43. package/lib/checker/checks/aor.js +30 -0
  44. package/lib/checker/checks/api.js +30 -0
  45. package/lib/checker/checks/index.js +57 -6
  46. package/lib/checker/checks/webrtc-transport.js +57 -6
  47. package/lib/checker/checks/webrtc.js +57 -6
  48. package/lib/domain/AdHocAPIConference.js +30 -0
  49. package/lib/domain/CallSession.js +9 -1
  50. package/lib/domain/Features.js +30 -0
  51. package/lib/domain/MeetingStatus.js +31 -0
  52. package/lib/domain/Phone/CTIPhone.js +39 -1
  53. package/lib/domain/Phone/WebRTCPhone.js +43 -8
  54. package/lib/domain/__tests__/CallSession.test.js +41 -5
  55. package/lib/domain/__tests__/IndirectTransfer.test.js +9 -1
  56. package/lib/domain/__tests__/Room.test.js +9 -1
  57. package/lib/index.js +100 -14
  58. package/lib/service/CallApi.js +30 -0
  59. package/lib/service/getApiClient.js +30 -0
  60. package/lib/simple/Auth.js +100 -14
  61. package/lib/simple/Configuration.js +100 -14
  62. package/lib/simple/Directory.js +30 -0
  63. package/lib/simple/Phone.js +100 -14
  64. package/lib/simple/index.js +100 -14
  65. package/lib/simple/room/LocalParticipant.js +100 -14
  66. package/lib/simple/room/Participant.js +100 -14
  67. package/lib/simple/room/RemoteParticipant.js +100 -14
  68. package/lib/simple/room/Room.js +100 -14
  69. package/lib/simple/room/SipRoom.js +100 -14
  70. package/lib/simple/room/Stream.js +100 -14
  71. package/lib/simple/room/__tests__/RemoteParticipant.test.js +100 -14
  72. package/lib/simple/utils.js +100 -14
  73. package/lib/web-rtc-client.js +57 -6
  74. package/package.json +1 -1
  75. package/src/api/calld.js +3 -0
  76. package/src/domain/CallSession.js +9 -1
  77. package/src/domain/MeetingStatus.js +27 -0
  78. package/src/domain/Phone/WebRTCPhone.js +34 -7
  79. package/src/domain/__tests__/CallSession.test.js +32 -4
  80. package/src/web-rtc-client.js +27 -6
package/src/api/calld.js CHANGED
@@ -7,6 +7,7 @@ import ChatMessage from '../domain/ChatMessage';
7
7
  import Voicemail from '../domain/Voicemail';
8
8
  import Call from '../domain/Call';
9
9
  import IndirectTransfer from '../domain/IndirectTransfer';
10
+ import MeetingStatus from '../domain/MeetingStatus';
10
11
 
11
12
  type CallQuery = {
12
13
  from_mobile: boolean,
@@ -200,4 +201,6 @@ export default (client: ApiRequester, baseUrl: string) => ({
200
201
  client.put(`${baseUrl}/users/me/calls/${callId}/record/resume`, null, null,
201
202
  ApiRequester.successResponseParser),
202
203
 
204
+ guestGetMeetingStatus: (meetingUuid: string): Promise<MeetingStatus> =>
205
+ client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}/status`).then(MeetingStatus.parse),
203
206
  });
@@ -344,13 +344,21 @@ export default class CallSession {
344
344
 
345
345
  // Retro-compatibility: `answered` was a boolean before. We can reproduce the behaviour with a getter/setter
346
346
  set answered(value: boolean) {
347
- this.answerTime = value ? new Date() : null;
347
+ this.answerTime = value ? this.answerTime || new Date() : null;
348
348
  }
349
349
 
350
350
  get answered(): boolean {
351
351
  return !!this.answerTime;
352
352
  }
353
353
 
354
+ toJSON() {
355
+ const jsonObj = { ...this };
356
+ // $FlowFixMe
357
+ jsonObj.answered = this.answered;
358
+
359
+ return jsonObj;
360
+ }
361
+
354
362
  static parseCall(call: Call): CallSession {
355
363
  return new CallSession({
356
364
  callId: call.id,
@@ -0,0 +1,27 @@
1
+ // @flow
2
+
3
+ export type MeetingStatusResponse = {
4
+ full: boolean
5
+ };
6
+
7
+ export default class MeetingStatus {
8
+ type: string;
9
+
10
+ full: boolean;
11
+
12
+ constructor({
13
+ full,
14
+ }: Object = {}) {
15
+ this.full = full;
16
+
17
+ // Useful to compare instead of instanceof with minified code
18
+ this.type = 'MeetingStatus';
19
+ }
20
+
21
+ static parse(plain: MeetingStatusResponse): MeetingStatus {
22
+ return new MeetingStatus({
23
+ full: plain.full,
24
+ });
25
+ }
26
+
27
+ }
@@ -631,6 +631,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
631
631
  }
632
632
 
633
633
  callSession.answerTime = new Date();
634
+ this._updateCallSession(callSession);
634
635
  this.eventEmitter.emit(ON_CALL_ACCEPTED, callSession, cameraEnabled);
635
636
 
636
637
  return callSession;
@@ -840,6 +841,12 @@ export default class WebRTCPhone extends Emitter implements Phone {
840
841
  return Promise.resolve(null);
841
842
  }
842
843
 
844
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
845
+ if (sipSessionId && !(sipSessionId in this.callSessions)) {
846
+ logger.warn('Call session already ended or not found, can\'t accept it.', { sipSessionId });
847
+ return Promise.resolve(null);
848
+ }
849
+
843
850
  if (callSession.getId() in this.acceptedSessions) {
844
851
  logger.warn('CallSession already accepted.', { id: callSession ? callSession.getId() : 'n/a' });
845
852
  return Promise.resolve(callSession.sipCallId);
@@ -847,6 +854,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
847
854
 
848
855
  this.shouldSendReinvite = false;
849
856
  callSession.answerTime = new Date();
857
+ this._updateCallSession(callSession);
850
858
  this.acceptedSessions[callSession.getId()] = true;
851
859
 
852
860
  this.eventEmitter.emit(ON_CALL_ANSWERED, callSession);
@@ -946,7 +954,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
946
954
  // Hold current session if different from the current one (we don't want 2 sessions active at the same time).
947
955
  if (this.currentSipSession && this.currentSipSession.id !== sipSession.id) {
948
956
  logger.info('WebRTC hold call after resume', { id: this.currentSipSession.id });
949
- this.holdSipSession(this.currentSipSession, callSession);
957
+ this.holdSipSession(this.currentSipSession, this.currentCallSession);
950
958
  }
951
959
 
952
960
  const promise = this.unholdSipSession(sipSession, callSession, withEvent, isConference);
@@ -1157,6 +1165,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1157
1165
  }
1158
1166
  const callSession = this._createOutgoingCallSession(sipSession, cameraEnabled || false);
1159
1167
  callSession.creationTime = new Date();
1168
+ this._updateCallSession(callSession);
1160
1169
 
1161
1170
  this.eventEmitter.emit(ON_PLAY_PROGRESS_SOUND, this.audioOutputDeviceId, this.audioOutputVolume);
1162
1171
 
@@ -1298,7 +1307,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1298
1307
  return this.client && this.client.isConference(callSession.sipCallId);
1299
1308
  }
1300
1309
 
1301
- async close(): Promise<void> {
1310
+ async close(force: boolean = false): Promise<void> {
1302
1311
  logger.info('WebRTC close');
1303
1312
 
1304
1313
  try {
@@ -1310,7 +1319,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1310
1319
  logger.error('WebRTC close, unregister error', e);
1311
1320
  }
1312
1321
 
1313
- this.client.close();
1322
+ this.client.close(force);
1314
1323
  this.unbind();
1315
1324
 
1316
1325
  this.incomingSessions = [];
@@ -1436,6 +1445,8 @@ export default class WebRTCPhone extends Emitter implements Phone {
1436
1445
  }
1437
1446
 
1438
1447
  callSession.creationTime = new Date();
1448
+ this._updateCallSession(callSession);
1449
+
1439
1450
  this.eventEmitter.emit(ON_CALL_INCOMING, callSession, wantsToDoVideo);
1440
1451
  });
1441
1452
 
@@ -1460,9 +1471,17 @@ export default class WebRTCPhone extends Emitter implements Phone {
1460
1471
  });
1461
1472
 
1462
1473
  this.client.on(this.client.ACCEPTED, (sipSession: Session) => {
1463
- logger.info('WebRTC call accepted', { sipId: sipSession.id });
1474
+ const sessionId = this.getSipSessionId(sipSession);
1475
+ const hasSession = sessionId in this.callSessions;
1476
+
1477
+ logger.info('WebRTC call accepted', { sipId: sessionId, hasSession });
1478
+
1479
+ if (!hasSession) {
1480
+ logger.warn('Call accepted ignored, session is no longer present in the WebRtcPhone', { sessionId });
1481
+ return;
1482
+ }
1464
1483
 
1465
- this._onCallAccepted(sipSession, this.client.hasVideo(this.getSipSessionId(sipSession)));
1484
+ this._onCallAccepted(sipSession, this.client.hasVideo(sessionId));
1466
1485
 
1467
1486
  if (this.audioOutputDeviceId) {
1468
1487
  this.client.changeAudioOutputDevice(this.audioOutputDeviceId);
@@ -1479,6 +1498,8 @@ export default class WebRTCPhone extends Emitter implements Phone {
1479
1498
  logger.info('WebRTC call rejected', session.id);
1480
1499
  const callSession = this._createCallSession(session);
1481
1500
  callSession.endTime = new Date();
1501
+ this._updateCallSession(callSession);
1502
+
1482
1503
  this.eventEmitter.emit(ON_CALL_REJECTED, callSession);
1483
1504
  });
1484
1505
 
@@ -1596,9 +1617,14 @@ export default class WebRTCPhone extends Emitter implements Phone {
1596
1617
  return this.client.getSipSessionId(sipSession);
1597
1618
  }
1598
1619
 
1599
- _updateCallSession(callSession: CallSession) {
1620
+ getSipSessionIdFromCallSession(callSession: CallSession) {
1600
1621
  const sipSession = this.findSipSession(callSession);
1601
- const sipSessionId = sipSession ? this.getSipSessionId(sipSession) : null;
1622
+
1623
+ return sipSession ? this.getSipSessionId(sipSession) : null;
1624
+ }
1625
+
1626
+ _updateCallSession(callSession: CallSession) {
1627
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
1602
1628
 
1603
1629
  if (sipSessionId) {
1604
1630
  this.callSessions[sipSessionId] = callSession;
@@ -1705,6 +1731,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1705
1731
  displayName: identity ? identity.displayName || number : number,
1706
1732
  startTime: fromSession ? fromSession.startTime : new Date(),
1707
1733
  creationTime: fromSession ? fromSession.creationTime : null,
1734
+ answerTime: fromSession ? fromSession.answerTime : null,
1708
1735
  endTime: fromSession ? fromSession.endTime : null,
1709
1736
  answered: state === SessionState.Established,
1710
1737
  paused: this.client.isCallHeld(sipSession),
@@ -1,5 +1,7 @@
1
1
  import CallSession from '../CallSession';
2
2
 
3
+ const stringify = cs => JSON.parse(JSON.stringify(cs));
4
+
3
5
  describe('CallSession domain', () => {
4
6
  it('should update from another CallSession without data loss', () => {
5
7
  const callSession = new CallSession({
@@ -38,7 +40,7 @@ describe('CallSession domain', () => {
38
40
  expect(cs2.is(cs1)).toBeTruthy();
39
41
  });
40
42
 
41
- it('should set answerTime when setting answered', () => {
43
+ it('should set answerTime when setting answered to true', () => {
42
44
  const cs1 = new CallSession({});
43
45
  cs1.answered = true;
44
46
 
@@ -47,11 +49,37 @@ describe('CallSession domain', () => {
47
49
 
48
50
  const cs3 = new CallSession({ answered: true });
49
51
 
50
- expect(cs1.answered).toBeTruthy();
52
+ expect(cs1.answered).toEqual(true);
51
53
  expect(cs1.answerTime).not.toBeNull();
52
- expect(cs2.answered).toBeTruthy();
54
+ expect(cs2.answered).toEqual(true);
53
55
  expect(cs2.answerTime).not.toBeNull();
54
- expect(cs3.answered).toBeTruthy();
56
+ expect(cs3.answered).toEqual(true);
55
57
  expect(cs3.answerTime).not.toBeNull();
58
+
59
+ const cs4 = stringify(cs1);
60
+ const cs5 = stringify(cs2);
61
+ const cs6 = stringify(cs3);
62
+
63
+ expect(cs4.answered).toEqual(true);
64
+ expect(cs5.answered).toEqual(true);
65
+ expect(cs6.answered).toEqual(true);
66
+ });
67
+
68
+ it('should reset answerTime when setting answered to false', () => {
69
+ const cs1 = new CallSession({});
70
+ cs1.answered = false;
71
+
72
+ const cs2 = new CallSession({ answered: false });
73
+
74
+ expect(cs1.answered).toEqual(false);
75
+ expect(cs1.answerTime).toBeNull();
76
+ expect(cs2.answered).toEqual(false);
77
+ expect(cs2.answerTime).toBeNull();
78
+
79
+ const cs4 = stringify(cs1);
80
+ const cs5 = stringify(cs2);
81
+
82
+ expect(cs4.answered).toEqual(false);
83
+ expect(cs5.answered).toEqual(false);
56
84
  });
57
85
  });
@@ -75,7 +75,7 @@ export const events = [REGISTERED, UNREGISTERED, REGISTRATION_FAILED, INVITE];
75
75
  export const transportEvents = [CONNECTED, DISCONNECTED, TRANSPORT_ERROR, MESSAGE];
76
76
  export class CanceledCallError extends Error {}
77
77
 
78
- const MAX_REGISTER_TRIES = 10;
78
+ const MAX_REGISTER_TRIES = 5;
79
79
 
80
80
  type MediaConfig = {
81
81
  audio: Object & boolean,
@@ -129,6 +129,7 @@ export default class WebRTCClient extends Emitter {
129
129
  skipRegister: boolean;
130
130
  networkMonitoringInterval: { [string]: IntervalID };
131
131
  sessionNetworkStats: { [string]: Object };
132
+ forceClosed: boolean;
132
133
 
133
134
  // sugar
134
135
  ON_USER_AGENT: string;
@@ -191,6 +192,7 @@ export default class WebRTCClient extends Emitter {
191
192
  this.conferences = {};
192
193
  this.networkMonitoringInterval = {};
193
194
  this.sessionNetworkStats = {};
195
+ this.forceClosed = false;
194
196
 
195
197
  this._boundOnHeartbeat = this._onHeartbeat.bind(this);
196
198
  this.heartbeat = new Heartbeat(config.heartbeatDelay, config.heartbeatTimeout, config.maxHeartbeats);
@@ -312,6 +314,8 @@ export default class WebRTCClient extends Emitter {
312
314
  skipRegister: this.skipRegister,
313
315
  };
314
316
 
317
+ this.forceClosed = false;
318
+
315
319
  if (this.skipRegister) {
316
320
  logger.info('sdk webrtc skip register...', logInfo);
317
321
  return Promise.resolve();
@@ -336,7 +340,15 @@ export default class WebRTCClient extends Emitter {
336
340
  const registerOptions = this._isWeb() ? {} : { extraContactHeaderParams: ['mobility=mobile'] };
337
341
 
338
342
  const onRegisterFailed = () => {
339
- logger.info('sdk webrtc registering failed', { tries, registerer: !!this.registerer });
343
+ logger.info('sdk webrtc registering failed', {
344
+ tries,
345
+ registerer: !!this.registerer,
346
+ forceClosed: this.forceClosed,
347
+ });
348
+ if (this.forceClosed) {
349
+ return;
350
+ }
351
+
340
352
  this.connectionPromise = null;
341
353
  if (this.registerer && this.registerer.waiting) {
342
354
  this.registerer.waitingToggle(false);
@@ -614,8 +626,9 @@ export default class WebRTCClient extends Emitter {
614
626
  }
615
627
  }
616
628
 
617
- async close() {
618
- logger.info('sdk webrtc closing client', { userAgent: !!this.userAgent });
629
+ async close(force: boolean = false) {
630
+ logger.info('sdk webrtc closing client', { userAgent: !!this.userAgent, force });
631
+ this.forceClosed = force;
619
632
  this._cleanupMedia();
620
633
  this.connectionPromise = null;
621
634
 
@@ -634,7 +647,7 @@ export default class WebRTCClient extends Emitter {
634
647
  this.userAgent.delegate = null;
635
648
  this.userAgent.stateChange.removeAllListeners();
636
649
 
637
- await this._disconnectTransport();
650
+ await this._disconnectTransport(force);
638
651
 
639
652
  this._cleanupRegister();
640
653
 
@@ -2070,7 +2083,15 @@ export default class WebRTCClient extends Emitter {
2070
2083
  return UserAgent.makeURI(`sip:${target}@${this.config.host}`);
2071
2084
  }
2072
2085
 
2073
- async _disconnectTransport() {
2086
+ async _disconnectTransport(force: boolean = false) {
2087
+ if (force) {
2088
+ // Bypass sip.js state machine that prevent to close WS with the state `Connecting`
2089
+ this.userAgent.transport.disconnectResolve = () => {};
2090
+ // eslint-disable-next-line
2091
+ this.userAgent.transport._ws.close(1000);
2092
+ return;
2093
+ }
2094
+
2074
2095
  // Check if `disconnectPromise` is not present to avoid `Disconnect promise must not be defined` errors.
2075
2096
  if (this.userAgent && this.userAgent.transport && !this.userAgent.transport.disconnectPromise) {
2076
2097
  try {