@wazo/sdk 1.0.0-alpha.2 → 1.0.0-alpha.20

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 (158) hide show
  1. package/README.md +16 -7
  2. package/dist/wazo-sdk.js +2 -2
  3. package/esm/__tests__/__voice__/call.test.js +52 -3
  4. package/esm/api/auth.js +3 -0
  5. package/esm/checker/checks/webrtc.js +2 -2
  6. package/esm/domain/CallLog.js +2 -3
  7. package/esm/domain/Phone/CTIPhone.js +2 -2
  8. package/esm/domain/Phone/WebRTCPhone.js +59 -57
  9. package/esm/index.js +3 -1
  10. package/esm/service/CallApi.js +4 -4
  11. package/esm/simple/Auth.js +13 -3
  12. package/esm/simple/Phone.js +5 -5
  13. package/esm/simple/index.js +2 -1
  14. package/esm/simple/room/Room.js +2 -2
  15. package/esm/simple/room/SipRoom.js +2 -2
  16. package/esm/state-machine/call-state-machine.js +34 -19
  17. package/esm/state-machine/softphone-state-machine.js +1 -2
  18. package/esm/state-machine/utils.js +10 -4
  19. package/esm/types/api/auth.d.ts +3 -0
  20. package/esm/types/api/auth.d.ts.map +1 -1
  21. package/esm/types/checker/checks/index.d.ts +1 -1
  22. package/esm/types/checker/checks/webrtc-transport.d.ts +2 -2
  23. package/esm/types/checker/checks/webrtc-transport.d.ts.map +1 -1
  24. package/esm/types/domain/CallLog.d.ts.map +1 -1
  25. package/esm/types/domain/CallSession.d.ts +3 -3
  26. package/esm/types/domain/CallSession.d.ts.map +1 -1
  27. package/esm/types/domain/Phone/CTIPhone.d.ts.map +1 -1
  28. package/esm/types/domain/Phone/WebRTCPhone.d.ts +22 -22
  29. package/esm/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  30. package/esm/types/domain/types.d.ts +0 -4
  31. package/esm/types/domain/types.d.ts.map +1 -1
  32. package/esm/types/index.d.ts +3 -1
  33. package/esm/types/index.d.ts.map +1 -1
  34. package/esm/types/lib/WazoSessionDescriptionHandler.d.ts +3 -3
  35. package/esm/types/lib/WazoSessionDescriptionHandler.d.ts.map +1 -1
  36. package/esm/types/service/CallApi.d.ts +5 -5
  37. package/esm/types/service/CallApi.d.ts.map +1 -1
  38. package/esm/types/simple/Auth.d.ts +6 -1
  39. package/esm/types/simple/Auth.d.ts.map +1 -1
  40. package/esm/types/simple/Phone.d.ts +8 -8
  41. package/esm/types/simple/Phone.d.ts.map +1 -1
  42. package/esm/types/simple/index.d.ts +3 -1
  43. package/esm/types/simple/index.d.ts.map +1 -1
  44. package/esm/types/state-machine/call-state-machine.d.ts +12 -1
  45. package/esm/types/state-machine/call-state-machine.d.ts.map +1 -1
  46. package/esm/types/state-machine/softphone-state-machine.d.ts +1 -2
  47. package/esm/types/state-machine/softphone-state-machine.d.ts.map +1 -1
  48. package/esm/types/state-machine/utils.d.ts +1 -1
  49. package/esm/types/state-machine/utils.d.ts.map +1 -1
  50. package/esm/types/utils/sdp.d.ts +1 -2
  51. package/esm/types/utils/sdp.d.ts.map +1 -1
  52. package/esm/types/utils/update-from.d.ts +1 -1
  53. package/esm/types/utils/update-from.d.ts.map +1 -1
  54. package/esm/types/utils/webrtc.d.ts.map +1 -1
  55. package/esm/types/voice/call.d.ts +15 -3
  56. package/esm/types/voice/call.d.ts.map +1 -1
  57. package/esm/types/voice/softphone.d.ts +7 -0
  58. package/esm/types/voice/softphone.d.ts.map +1 -1
  59. package/esm/types/web-rtc-client.d.ts +39 -38
  60. package/esm/types/web-rtc-client.d.ts.map +1 -1
  61. package/esm/utils/sdp.js +16 -9
  62. package/esm/utils/update-from.js +2 -2
  63. package/esm/utils/webrtc.js +10 -4
  64. package/esm/voice/call.js +86 -28
  65. package/esm/voice/softphone.js +58 -17
  66. package/esm/web-rtc-client.js +67 -58
  67. package/lib/__tests__/__voice__/call.test.js +51 -2
  68. package/lib/api/auth.js +3 -0
  69. package/lib/checker/checks/webrtc.js +1 -1
  70. package/lib/domain/CallLog.js +2 -3
  71. package/lib/domain/Phone/CTIPhone.js +2 -2
  72. package/lib/domain/Phone/WebRTCPhone.js +58 -56
  73. package/lib/index.js +7 -3
  74. package/lib/service/CallApi.js +2 -2
  75. package/lib/simple/Auth.js +15 -3
  76. package/lib/simple/Phone.js +2 -2
  77. package/lib/simple/index.js +2 -1
  78. package/lib/simple/room/Room.js +1 -1
  79. package/lib/simple/room/SipRoom.js +1 -1
  80. package/lib/state-machine/call-state-machine.js +35 -20
  81. package/lib/state-machine/softphone-state-machine.js +1 -2
  82. package/lib/state-machine/utils.js +10 -4
  83. package/lib/types/api/auth.d.ts +3 -0
  84. package/lib/types/api/auth.d.ts.map +1 -1
  85. package/lib/types/checker/checks/index.d.ts +1 -1
  86. package/lib/types/checker/checks/webrtc-transport.d.ts +2 -2
  87. package/lib/types/checker/checks/webrtc-transport.d.ts.map +1 -1
  88. package/lib/types/domain/CallLog.d.ts.map +1 -1
  89. package/lib/types/domain/CallSession.d.ts +3 -3
  90. package/lib/types/domain/CallSession.d.ts.map +1 -1
  91. package/lib/types/domain/Phone/CTIPhone.d.ts.map +1 -1
  92. package/lib/types/domain/Phone/WebRTCPhone.d.ts +22 -22
  93. package/lib/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  94. package/lib/types/domain/types.d.ts +0 -4
  95. package/lib/types/domain/types.d.ts.map +1 -1
  96. package/lib/types/index.d.ts +3 -1
  97. package/lib/types/index.d.ts.map +1 -1
  98. package/lib/types/lib/WazoSessionDescriptionHandler.d.ts +3 -3
  99. package/lib/types/lib/WazoSessionDescriptionHandler.d.ts.map +1 -1
  100. package/lib/types/service/CallApi.d.ts +5 -5
  101. package/lib/types/service/CallApi.d.ts.map +1 -1
  102. package/lib/types/simple/Auth.d.ts +6 -1
  103. package/lib/types/simple/Auth.d.ts.map +1 -1
  104. package/lib/types/simple/Phone.d.ts +8 -8
  105. package/lib/types/simple/Phone.d.ts.map +1 -1
  106. package/lib/types/simple/index.d.ts +3 -1
  107. package/lib/types/simple/index.d.ts.map +1 -1
  108. package/lib/types/state-machine/call-state-machine.d.ts +12 -1
  109. package/lib/types/state-machine/call-state-machine.d.ts.map +1 -1
  110. package/lib/types/state-machine/softphone-state-machine.d.ts +1 -2
  111. package/lib/types/state-machine/softphone-state-machine.d.ts.map +1 -1
  112. package/lib/types/state-machine/utils.d.ts +1 -1
  113. package/lib/types/state-machine/utils.d.ts.map +1 -1
  114. package/lib/types/utils/sdp.d.ts +1 -2
  115. package/lib/types/utils/sdp.d.ts.map +1 -1
  116. package/lib/types/utils/update-from.d.ts +1 -1
  117. package/lib/types/utils/update-from.d.ts.map +1 -1
  118. package/lib/types/utils/webrtc.d.ts.map +1 -1
  119. package/lib/types/voice/call.d.ts +15 -3
  120. package/lib/types/voice/call.d.ts.map +1 -1
  121. package/lib/types/voice/softphone.d.ts +7 -0
  122. package/lib/types/voice/softphone.d.ts.map +1 -1
  123. package/lib/types/web-rtc-client.d.ts +39 -38
  124. package/lib/types/web-rtc-client.d.ts.map +1 -1
  125. package/lib/utils/sdp.js +17 -10
  126. package/lib/utils/update-from.js +2 -2
  127. package/lib/utils/webrtc.js +11 -4
  128. package/lib/voice/call.js +89 -27
  129. package/lib/voice/softphone.js +62 -17
  130. package/lib/web-rtc-client.js +66 -57
  131. package/package.json +1 -1
  132. package/src/__tests__/__voice__/call.test.ts +61 -3
  133. package/src/api/auth.ts +5 -0
  134. package/src/checker/checks/webrtc-transport.ts +2 -2
  135. package/src/checker/checks/webrtc.ts +2 -2
  136. package/src/domain/CallLog.ts +2 -3
  137. package/src/domain/CallSession.ts +3 -3
  138. package/src/domain/Phone/CTIPhone.ts +3 -2
  139. package/src/domain/Phone/WebRTCPhone.ts +87 -85
  140. package/src/domain/__tests__/CallSession.test.ts +4 -0
  141. package/src/domain/types.ts +0 -5
  142. package/src/index.ts +4 -1
  143. package/src/lib/WazoSessionDescriptionHandler.ts +3 -3
  144. package/src/service/CallApi.ts +7 -7
  145. package/src/simple/Auth.ts +15 -3
  146. package/src/simple/Phone.ts +11 -11
  147. package/src/simple/index.ts +2 -1
  148. package/src/simple/room/Room.ts +3 -3
  149. package/src/simple/room/SipRoom.ts +3 -3
  150. package/src/state-machine/call-state-machine.ts +38 -19
  151. package/src/state-machine/softphone-state-machine.ts +1 -2
  152. package/src/state-machine/utils.ts +12 -4
  153. package/src/utils/sdp.ts +18 -11
  154. package/src/utils/update-from.ts +2 -2
  155. package/src/utils/webrtc.ts +11 -4
  156. package/src/voice/call.ts +109 -36
  157. package/src/voice/softphone.ts +75 -18
  158. package/src/web-rtc-client.ts +112 -98
@@ -17,7 +17,7 @@ import Emitter from './utils/Emitter';
17
17
  import ApiClient from './api-client';
18
18
  import IssueReporter from './service/IssueReporter';
19
19
  import Heartbeat from './utils/Heartbeat';
20
- import { getSipSessionId, makeURI, replaceLocalIpModifier, SIP_ID_LENGTH } from './utils/sdp';
20
+ import { getSipCallId, makeURI, replaceLocalIpModifier } from './utils/sdp';
21
21
  import { cleanupStream, createUaOptions, fetchNetworkStats, getAudioConstraints, getLocalStream, getLocalTracks, getLocalVideoStream, getMediaConfiguration, getPeerConnection, getRemoteStream, getRemoteTracks, getRemoteVideoStream, getRemoteVideoStreamFromPc, getVideoConstraints, hasALocalVideoTrack, hasARemoteVideoTrack, hasAVideoTrack, hasLocalVideo, hasRemoteVideo, hasVideoTrack, isAudioOnly, isVideoRemotelyHeld, isWeb, sessionWantsToDoVideo, setLocalMediaStream, toggleAudio, toggleVideo, updateRemoteStream } from './utils/webrtc';
22
22
  const SEND_STATS_DELAY = 5000;
23
23
  const states = ['STATUS_NULL', 'STATUS_NEW', 'STATUS_CONNECTING', 'STATUS_CONNECTED', 'STATUS_COMPLETED'];
@@ -25,6 +25,7 @@ const logger = IssueReporter ? IssueReporter.loggerFor('webrtc-client') : consol
25
25
  const statsLogger = IssueReporter ? IssueReporter.loggerFor('webrtc-stats') : console;
26
26
  // events
27
27
  const REGISTERED = 'registered';
28
+ const REGISTERING = 'registering';
28
29
  const UNREGISTERED = 'unregistered';
29
30
  const REGISTRATION_FAILED = 'registrationFailed';
30
31
  const INVITE = 'invite';
@@ -42,7 +43,7 @@ const ON_ERROR = 'onError';
42
43
  const ON_SCREEN_SHARING_REINVITE = 'onScreenSharingReinvite';
43
44
  const ON_NETWORK_STATS = 'onNetworkStats';
44
45
  const ON_DISCONNECTED = 'onDisconnected';
45
- export const events = [REGISTERED, UNREGISTERED, REGISTRATION_FAILED, INVITE];
46
+ export const events = [REGISTERED, REGISTERING, UNREGISTERED, REGISTRATION_FAILED, INVITE];
46
47
  export const transportEvents = [CONNECTED, DISCONNECTED, TRANSPORT_ERROR, MESSAGE];
47
48
  export class CanceledCallError extends Error {
48
49
  }
@@ -76,6 +77,7 @@ export default class WebRTCClient extends Emitter {
76
77
  // sugar
77
78
  ON_USER_AGENT;
78
79
  REGISTERED;
80
+ REGISTERING;
79
81
  UNREGISTERED;
80
82
  REGISTRATION_FAILED;
81
83
  INVITE;
@@ -103,7 +105,9 @@ export default class WebRTCClient extends Emitter {
103
105
  this.skipRegister = config.skipRegister;
104
106
  this._buildConfig(config, session).then((newConfig) => {
105
107
  this.config = newConfig;
106
- this.userAgent = this.createUserAgent(uaConfigOverrides);
108
+ if (!this.userAgent) {
109
+ this.userAgent = this.createUserAgent(uaConfigOverrides);
110
+ }
107
111
  });
108
112
  this.audioOutputDeviceId = config.audioDeviceOutput;
109
113
  this.audioOutputVolume = config.audioOutputVolume || 1;
@@ -128,6 +132,7 @@ export default class WebRTCClient extends Emitter {
128
132
  this.heartbeat.setOnHeartbeatTimeout(this._onHeartbeatTimeout.bind(this));
129
133
  // sugar
130
134
  this.REGISTERED = REGISTERED;
135
+ this.REGISTERING = REGISTERING;
131
136
  this.UNREGISTERED = UNREGISTERED;
132
137
  this.REGISTRATION_FAILED = REGISTRATION_FAILED;
133
138
  this.INVITE = INVITE;
@@ -168,7 +173,7 @@ export default class WebRTCClient extends Emitter {
168
173
  logger.info('sdk webrtc on invite', {
169
174
  method: 'delegate.onInvite',
170
175
  clientId: this.clientId,
171
- id: invitation.id,
176
+ id: getSipCallId(invitation),
172
177
  remoteURI: invitation.remoteURI,
173
178
  });
174
179
  this._setupSession(invitation);
@@ -230,7 +235,7 @@ export default class WebRTCClient extends Emitter {
230
235
  // @ts-ignore: private
231
236
  this.registerer.waitingToggle(false);
232
237
  }
233
- this.eventEmitter.emit(UNREGISTERED);
238
+ this.eventEmitter.emit(UNREGISTERED, error);
234
239
  }
235
240
  }
236
241
  async register(tries = 0) {
@@ -251,6 +256,7 @@ export default class WebRTCClient extends Emitter {
251
256
  return Promise.resolve();
252
257
  }
253
258
  logger.info('sdk webrtc registering...', logInfo);
259
+ this.eventEmitter.emit(REGISTERING);
254
260
  if (!this.userAgent) {
255
261
  logger.info('sdk webrtc recreating User Agent');
256
262
  this.userAgent = this.createUserAgent(this.uaConfigOverrides);
@@ -358,6 +364,12 @@ export default class WebRTCClient extends Emitter {
358
364
  userAgent: !!this.userAgent,
359
365
  registerer: !!this.registerer,
360
366
  });
367
+ // @ts-ignore: private
368
+ if (this.registerer?.waiting) {
369
+ logger.info('sdk webrtc unregistering, waiting for register to finish...');
370
+ // We have to wait for the register phase to finish
371
+ await this.waitForRegister();
372
+ }
361
373
  try {
362
374
  return new Promise((resolve, reject) => {
363
375
  if (!this.registerer) {
@@ -435,7 +447,7 @@ export default class WebRTCClient extends Emitter {
435
447
  this._setupSession(session);
436
448
  }
437
449
  if (conference) {
438
- this.conferences[getSipSessionId(session)] = true;
450
+ this.conferences[getSipCallId(session)] = true;
439
451
  }
440
452
  const inviteOptions = {
441
453
  requestDelegate: {
@@ -450,7 +462,7 @@ export default class WebRTCClient extends Emitter {
450
462
  },
451
463
  onReject: (response) => {
452
464
  logger.info('on call rejected', {
453
- id: session?.id,
465
+ id: getSipCallId(session),
454
466
  // @ts-ignore: fromTag does not exist
455
467
  fromTag: session?.fromTag,
456
468
  });
@@ -480,7 +492,7 @@ export default class WebRTCClient extends Emitter {
480
492
  answer(session, enableVideo) {
481
493
  logger.info('sdk webrtc answer call', {
482
494
  clientId: this.clientId,
483
- id: session.id,
495
+ id: getSipCallId(session),
484
496
  enableVideo,
485
497
  });
486
498
  if (!session || !session.accept) {
@@ -496,7 +508,7 @@ export default class WebRTCClient extends Emitter {
496
508
  if (session.isCanceled) {
497
509
  const message = 'accepted a canceled session (or was canceled during the accept phase).';
498
510
  logger.error(message, {
499
- id: session.id,
511
+ id: getSipCallId(session),
500
512
  });
501
513
  this.onCallEnded(session);
502
514
  throw new CanceledCallError(message);
@@ -504,7 +516,7 @@ export default class WebRTCClient extends Emitter {
504
516
  logger.info('sdk webrtc answer, accepted.');
505
517
  this._onAccepted(session);
506
518
  }).catch(e => {
507
- logger.error(`answer call error for ${session ? session.id : 'n/a'}`, e);
519
+ logger.error(`answer call error for ${getSipCallId(session)}`, e);
508
520
  throw e;
509
521
  });
510
522
  }
@@ -514,7 +526,7 @@ export default class WebRTCClient extends Emitter {
514
526
  try {
515
527
  this._stopSendingStats(session);
516
528
  this._cleanupMedia(session);
517
- delete this.sipSessions[getSipSessionId(session)];
529
+ delete this.sipSessions[getSipCallId(session)];
518
530
  // Check if Invitation or Inviter (Invitation = incoming call)
519
531
  const isInviter = session instanceof Inviter;
520
532
  // @see github.com/onsip/SIP.js/blob/f11dfd584bc9788ccfc94e03034020672b738975/src/platform/web/simple-user/simple-user.ts#L1004
@@ -536,7 +548,7 @@ export default class WebRTCClient extends Emitter {
536
548
  }
537
549
  // Fetch and emit an event at `interval` with session network stats
538
550
  startNetworkMonitoring(session, interval = 1000) {
539
- const sessionId = getSipSessionId(session);
551
+ const sessionId = getSipCallId(session);
540
552
  logger.info('starting network inspection', {
541
553
  id: sessionId,
542
554
  clientId: this.clientId,
@@ -545,7 +557,7 @@ export default class WebRTCClient extends Emitter {
545
557
  this.networkMonitoringInterval[sessionId] = setInterval(() => this._fetchNetworkStats(sessionId), interval);
546
558
  }
547
559
  stopNetworkMonitoring(session) {
548
- const sessionId = getSipSessionId(session);
560
+ const sessionId = getSipCallId(session);
549
561
  const exists = (sessionId in this.networkMonitoringInterval);
550
562
  logger.info('stopping network inspection', {
551
563
  clientId: this.clientId,
@@ -561,7 +573,7 @@ export default class WebRTCClient extends Emitter {
561
573
  async reject(session) {
562
574
  logger.info('sdk webrtc reject call', {
563
575
  clientId: this.clientId,
564
- id: session.id,
576
+ id: getSipCallId(session),
565
577
  });
566
578
  try {
567
579
  if (session instanceof Invitation) {
@@ -615,13 +627,13 @@ export default class WebRTCClient extends Emitter {
615
627
  }
616
628
  mute(session) {
617
629
  logger.info('sdk webrtc mute', {
618
- id: session.id,
630
+ id: getSipCallId(session),
619
631
  });
620
632
  toggleAudio(session, true);
621
633
  }
622
634
  unmute(session) {
623
635
  logger.info('sdk webrtc unmute', {
624
- id: session.id,
636
+ id: getSipCallId(session),
625
637
  });
626
638
  toggleAudio(session, false);
627
639
  }
@@ -658,18 +670,18 @@ export default class WebRTCClient extends Emitter {
658
670
  }
659
671
  toggleCameraOn(session) {
660
672
  logger.info('sdk webrtc toggle camera on', {
661
- id: session.id,
673
+ id: getSipCallId(session),
662
674
  });
663
675
  toggleVideo(session, false);
664
676
  }
665
677
  toggleCameraOff(session) {
666
678
  logger.info('sdk webrtc toggle camera off', {
667
- id: session.id,
679
+ id: getSipCallId(session),
668
680
  });
669
681
  toggleVideo(session, true);
670
682
  }
671
683
  hold(session, isConference = false, hadVideo = false) {
672
- const sessionId = getSipSessionId(session);
684
+ const sessionId = getSipCallId(session);
673
685
  const hasVideo = hadVideo || this.hasLocalVideo(sessionId);
674
686
  logger.info('sdk webrtc hold', {
675
687
  sessionId,
@@ -717,7 +729,7 @@ export default class WebRTCClient extends Emitter {
717
729
  });
718
730
  }
719
731
  unhold(session, isConference = false) {
720
- const sessionId = getSipSessionId(session);
732
+ const sessionId = getSipCallId(session);
721
733
  const hasVideo = sessionId in this.heldSessions && this.heldSessions[sessionId].hasVideo;
722
734
  logger.info('sdk webrtc unhold', {
723
735
  sessionId,
@@ -732,7 +744,7 @@ export default class WebRTCClient extends Emitter {
732
744
  return Promise.resolve();
733
745
  }
734
746
  this.unmute(session);
735
- delete this.heldSessions[getSipSessionId(session)];
747
+ delete this.heldSessions[getSipCallId(session)];
736
748
  session.sessionDescriptionHandlerOptionsReInvite = {
737
749
  hold: false,
738
750
  conference: isConference,
@@ -757,7 +769,7 @@ export default class WebRTCClient extends Emitter {
757
769
  return this.heldSessions[sessionId];
758
770
  }
759
771
  isCallHeld(session) {
760
- return getSipSessionId(session) in this.heldSessions;
772
+ return getSipCallId(session) in this.heldSessions;
761
773
  }
762
774
  isVideoRemotelyHeld(sessionId) {
763
775
  return isVideoRemotelyHeld(this.heldSessions[sessionId]);
@@ -767,7 +779,7 @@ export default class WebRTCClient extends Emitter {
767
779
  return false;
768
780
  }
769
781
  logger.info('Sending DTMF', {
770
- id: getSipSessionId(session),
782
+ id: getSipCallId(session),
771
783
  tone,
772
784
  });
773
785
  return session.sessionDescriptionHandler.sendDtmf(tone);
@@ -784,7 +796,7 @@ export default class WebRTCClient extends Emitter {
784
796
  transfer(session, target) {
785
797
  this.hold(session);
786
798
  logger.info('Transfering a session', {
787
- id: getSipSessionId(session),
799
+ id: getSipCallId(session),
788
800
  target,
789
801
  });
790
802
  const options = {
@@ -807,21 +819,21 @@ export default class WebRTCClient extends Emitter {
807
819
  atxfer(session) {
808
820
  this.hold(session);
809
821
  logger.info('webrtc transfer started', {
810
- id: getSipSessionId(session),
822
+ id: getSipCallId(session),
811
823
  });
812
824
  const result = {
813
825
  newSession: null,
814
826
  init: async (target) => {
815
827
  logger.info('webrtc transfer initialized', {
816
- id: getSipSessionId(session),
828
+ id: getSipCallId(session),
817
829
  target,
818
830
  });
819
831
  result.newSession = await this.call(target);
820
832
  },
821
833
  complete: () => {
822
834
  logger.info('webrtc transfer completed', {
823
- id: getSipSessionId(session),
824
- referId: getSipSessionId(result.newSession),
835
+ id: getSipCallId(session),
836
+ referId: getSipCallId(result.newSession),
825
837
  });
826
838
  session.refer(result.newSession);
827
839
  },
@@ -838,7 +850,7 @@ export default class WebRTCClient extends Emitter {
838
850
  return;
839
851
  }
840
852
  logger.info('send WebRTC message', {
841
- sipId: sipSession.id,
853
+ sipId: getSipCallId(sipSession),
842
854
  contentType,
843
855
  });
844
856
  try {
@@ -988,7 +1000,7 @@ export default class WebRTCClient extends Emitter {
988
1000
  }
989
1001
  const sdh = session.sessionDescriptionHandler;
990
1002
  const pc = sdh.peerConnection;
991
- const sessionId = getSipSessionId(session);
1003
+ const sessionId = getSipCallId(session);
992
1004
  const localStream = this.getLocalStream(sessionId);
993
1005
  logger.info('changing video input device', {
994
1006
  id,
@@ -1049,7 +1061,7 @@ export default class WebRTCClient extends Emitter {
1049
1061
  const desktop = newConstraints && newConstraints.desktop;
1050
1062
  logger.info('Sending reinvite', {
1051
1063
  clientId: this.clientId,
1052
- id: getSipSessionId(sipSession),
1064
+ id: getSipCallId(sipSession),
1053
1065
  newConstraints,
1054
1066
  conference,
1055
1067
  audioOnly,
@@ -1083,7 +1095,7 @@ export default class WebRTCClient extends Emitter {
1083
1095
  sipSession.incomingInviteRequest.message.body = response.message.body;
1084
1096
  }
1085
1097
  logger.info('on re-INVITE accepted', {
1086
- id: getSipSessionId(sipSession),
1098
+ id: getSipCallId(sipSession),
1087
1099
  wasMuted,
1088
1100
  shouldDoScreenSharing,
1089
1101
  });
@@ -1194,7 +1206,7 @@ export default class WebRTCClient extends Emitter {
1194
1206
  }
1195
1207
  onCallEnded(session) {
1196
1208
  this._cleanupMedia(session);
1197
- delete this.sipSessions[getSipSessionId(session)];
1209
+ delete this.sipSessions[getSipCallId(session)];
1198
1210
  this._stopSendingStats(session);
1199
1211
  this.stopNetworkMonitoring(session);
1200
1212
  }
@@ -1207,14 +1219,14 @@ export default class WebRTCClient extends Emitter {
1207
1219
  this.userAgent.attemptReconnection();
1208
1220
  }
1209
1221
  storeSipSession(session) {
1210
- const id = getSipSessionId(session);
1222
+ const id = getSipCallId(session);
1211
1223
  logger.info('storing sip session', { id, clientId: this.clientId });
1212
1224
  this.sipSessions[id] = session;
1213
1225
  }
1214
1226
  getSipSession(id) {
1215
- return id in this.sipSessions ? this.sipSessions[id.substring(0, SIP_ID_LENGTH)] : null;
1227
+ return id in this.sipSessions ? this.sipSessions[id] : null;
1216
1228
  }
1217
- getSipSessionIds() {
1229
+ getSipCallIds() {
1218
1230
  return Object.keys(this.sipSessions);
1219
1231
  }
1220
1232
  async updateLocalStream(sipSessionId, newStream) {
@@ -1253,8 +1265,8 @@ export default class WebRTCClient extends Emitter {
1253
1265
  audio.autoplay = true;
1254
1266
  return audio;
1255
1267
  }
1256
- _onTransportError() {
1257
- logger.error('on transport error');
1268
+ _onTransportError(reason) {
1269
+ logger.warn('on transport error', { reason });
1258
1270
  this.eventEmitter.emit(TRANSPORT_ERROR);
1259
1271
  this.attemptReconnection();
1260
1272
  }
@@ -1307,7 +1319,7 @@ export default class WebRTCClient extends Emitter {
1307
1319
  }
1308
1320
  }
1309
1321
  // We can invoke disconnect() with an error that can be catcher by `onDisconnect`, so we have to trigger it here.
1310
- this._onTransportError();
1322
+ this._onTransportError('heartbeat timeout');
1311
1323
  }
1312
1324
  }
1313
1325
  _hasAudio() {
@@ -1369,7 +1381,7 @@ export default class WebRTCClient extends Emitter {
1369
1381
  }
1370
1382
  // Invitation and Inviter extends Session
1371
1383
  _setupSession(session) {
1372
- const sipSessionId = getSipSessionId(session);
1384
+ const sipSessionId = getSipCallId(session);
1373
1385
  // When receiving an Invitation, the delegate is not defined.
1374
1386
  if (!session.delegate) {
1375
1387
  session.delegate = {};
@@ -1432,17 +1444,14 @@ export default class WebRTCClient extends Emitter {
1432
1444
  }
1433
1445
  async _onProgress(session, early = false) {
1434
1446
  await this._setupMedias(session);
1435
- const sessionId = getSipSessionId(session).substr(0, 20);
1447
+ const sessionId = getSipCallId(session);
1436
1448
  updateRemoteStream(session);
1437
- logger.info('progress received', {
1438
- sessionId,
1439
- early,
1440
- });
1449
+ logger.info('progress received', { sessionId, early });
1441
1450
  this.eventEmitter.emit(early ? ON_EARLY_MEDIA : ON_PROGRESS, session);
1442
1451
  }
1443
1452
  async _onAccepted(session, sessionDialog, withEvent = true, initAllTracks = true) {
1444
1453
  logger.info('on call accepted', {
1445
- id: session.id,
1454
+ id: getSipCallId(session),
1446
1455
  clientId: this.clientId,
1447
1456
  remoteTag: session.remoteTag,
1448
1457
  });
@@ -1486,7 +1495,7 @@ export default class WebRTCClient extends Emitter {
1486
1495
  return;
1487
1496
  }
1488
1497
  // Safari hack, because you cannot call .play() from a non user action
1489
- const sessionId = getSipSessionId(session);
1498
+ const sessionId = getSipCallId(session);
1490
1499
  const isConference = this.isConference(sessionId);
1491
1500
  if (sessionId in this.audioElements) {
1492
1501
  logger.info('html element already exists for session', {
@@ -1531,7 +1540,7 @@ export default class WebRTCClient extends Emitter {
1531
1540
  audioElement.play().catch(() => { });
1532
1541
  }
1533
1542
  _cleanupMedia(session) {
1534
- const sessionId = getSipSessionId(session);
1543
+ const sessionId = getSipCallId(session);
1535
1544
  const localStream = this.getLocalStream(sessionId);
1536
1545
  if (localStream) {
1537
1546
  cleanupStream(localStream);
@@ -1550,7 +1559,7 @@ export default class WebRTCClient extends Emitter {
1550
1559
  };
1551
1560
  if (this._hasAudio() && isWeb()) {
1552
1561
  if (session) {
1553
- cleanLocalElement(getSipSessionId(session));
1562
+ cleanLocalElement(getSipCallId(session));
1554
1563
  }
1555
1564
  else {
1556
1565
  Object.keys(this.audioElements).forEach(id => cleanLocalElement(id));
@@ -1570,7 +1579,7 @@ export default class WebRTCClient extends Emitter {
1570
1579
  if (!pc) {
1571
1580
  return;
1572
1581
  }
1573
- const sessionId = getSipSessionId(session);
1582
+ const sessionId = getSipCallId(session);
1574
1583
  getStats(pc, (result) => {
1575
1584
  const { results, internal, nomore, ...stats } = result;
1576
1585
  this.statsIntervals[sessionId] = nomore;
@@ -1581,7 +1590,7 @@ export default class WebRTCClient extends Emitter {
1581
1590
  }, SEND_STATS_DELAY);
1582
1591
  }
1583
1592
  _stopSendingStats(session) {
1584
- const sessionId = getSipSessionId(session);
1593
+ const sessionId = getSipCallId(session);
1585
1594
  logger.trace('Check for stopping stats', {
1586
1595
  sessionId,
1587
1596
  ids: Object.keys(this.statsIntervals),
@@ -1632,18 +1641,18 @@ export default class WebRTCClient extends Emitter {
1632
1641
  async _accept(session, options = {}) {
1633
1642
  if (session.state === SessionState.Terminated || session.state === SessionState.Terminating) {
1634
1643
  const error = 'Trying to accept a terminated sipSession.';
1635
- logger.warn(error, { state: session.state, sessionId: session.id });
1644
+ logger.warn(error, { state: session.state, sessionId: getSipCallId(session) });
1636
1645
  throw new Error(error);
1637
1646
  }
1638
1647
  if (session.state !== SessionState.Initial) {
1639
1648
  const error = 'Trying to accept a non Initial sipSession.';
1640
- logger.warn(error, { state: session.state, sessionId: session.id });
1649
+ logger.warn(error, { state: session.state, sessionId: getSipCallId(session) });
1641
1650
  throw new Error(error);
1642
1651
  }
1643
1652
  // @ts-ignore: private
1644
1653
  if (!session.incomingInviteRequest.acceptable) {
1645
1654
  const error = 'Trying to reject a non `acceptable` session';
1646
- logger.warn(error, { state: session.state, sessionId: session.id });
1655
+ logger.warn(error, { state: session.state, sessionId: getSipCallId(session) });
1647
1656
  throw new Error(error);
1648
1657
  }
1649
1658
  try {
@@ -1655,12 +1664,12 @@ export default class WebRTCClient extends Emitter {
1655
1664
  }
1656
1665
  async _reject(session, options = {}) {
1657
1666
  if (session.state !== SessionState.Initial && session.state !== SessionState.Establishing) {
1658
- logger.warn('Trying to reject a session in a wrong state', { state: session.state, sessionId: session.id });
1667
+ logger.warn('Trying to reject a session in a wrong state', { state: session.state, sessionId: getSipCallId(session) });
1659
1668
  return;
1660
1669
  }
1661
1670
  // @ts-ignore: private
1662
1671
  if (!session.incomingInviteRequest.rejectable) {
1663
- logger.warn('Trying to reject a non `rejectable` session', { state: session.state, sessionId: session.id });
1672
+ logger.warn('Trying to reject a non `rejectable` session', { state: session.state, sessionId: getSipCallId(session) });
1664
1673
  return;
1665
1674
  }
1666
1675
  try {
@@ -1672,7 +1681,7 @@ export default class WebRTCClient extends Emitter {
1672
1681
  }
1673
1682
  async _cancel(session, options = {}) {
1674
1683
  if (session.state !== SessionState.Initial && session.state !== SessionState.Establishing) {
1675
- logger.warn('Trying to cancel a session in a wrong state', { state: session.state, sessionId: session.id });
1684
+ logger.warn('Trying to cancel a session in a wrong state', { state: session.state, sessionId: getSipCallId(session) });
1676
1685
  return;
1677
1686
  }
1678
1687
  try {
@@ -1684,7 +1693,7 @@ export default class WebRTCClient extends Emitter {
1684
1693
  }
1685
1694
  async _bye(session, options = {}) {
1686
1695
  if (session.state !== SessionState.Established) {
1687
- logger.warn('Trying to end a session in a wrong state', { state: session.state, sessionId: session.id });
1696
+ logger.warn('Trying to end a session in a wrong state', { state: session.state, sessionId: getSipCallId(session) });
1688
1697
  return null;
1689
1698
  }
1690
1699
  try {
@@ -41,6 +41,9 @@ jest.mock('../..', () => ({
41
41
  }));
42
42
  jest.mock('../../web-rtc-client', () => jest.fn().mockImplementation(() => ({
43
43
  answer: jest.fn(() => Promise.resolve()),
44
+ mute: jest.fn(() => Promise.resolve()),
45
+ hold: jest.fn(() => Promise.resolve()),
46
+ unhold: jest.fn(() => Promise.resolve()),
44
47
  hangup: jest.fn(() => Promise.resolve()),
45
48
  register: jest.fn(() => Promise.resolve()),
46
49
  onCallEnded: jest.fn(() => Promise.resolve()),
@@ -80,9 +83,17 @@ describe('Call', () => {
80
83
  expect(call.id).toBe(apiCall.sipCallId);
81
84
  expect(call.apiId).toBe(apiCall.id);
82
85
  expect(call.number).toBe(apiCall.calleeNumber);
83
- expect(call.answerTime).toStrictEqual(apiCall.startingTime);
86
+ expect(call.answerTime).toStrictEqual(undefined);
87
+ expect(call.creationTime).toStrictEqual(apiCall.startingTime);
84
88
  expect(call.isMuted()).toBeTruthy();
85
- expect(call.state).toStrictEqual({ [call_state_machine_1.States.ESTABLISHED]: call_state_machine_1.EstablishedStates.MUTED });
89
+ expect(call.state).toStrictEqual({
90
+ [call_state_machine_1.States.ESTABLISHED]: {
91
+ [call_state_machine_1.EstablishedStates.ONGOING]: {},
92
+ [call_state_machine_1.EstablishedSubStates.MUTE]: call_state_machine_1.EstablishedStates.MUTED,
93
+ [call_state_machine_1.EstablishedSubStates.HOLD]: call_state_machine_1.EstablishedStates.UN_HELD,
94
+ },
95
+ });
96
+ expect(call.isEstablished()).toBeTruthy();
86
97
  }));
87
98
  });
88
99
  describe('accept', () => {
@@ -125,4 +136,42 @@ describe('Call', () => {
125
136
  expect(softphone_1.default.client.hangup).toHaveBeenCalled();
126
137
  }));
127
138
  });
139
+ describe('state propagation', () => {
140
+ it('Should update the call state in events', (done) => {
141
+ const call = new call_1.default(sipCall, softphone_1.default);
142
+ // @ts-ignore: private method
143
+ call._sendAction(call_state_machine_1.Actions.MAKE_CALL);
144
+ call.on('remotelyAccepted', () => {
145
+ expect(call.state).toStrictEqual({
146
+ [call_state_machine_1.States.ESTABLISHED]: {
147
+ [call_state_machine_1.EstablishedStates.ONGOING]: {},
148
+ [call_state_machine_1.EstablishedSubStates.MUTE]: call_state_machine_1.EstablishedStates.UN_MUTED,
149
+ [call_state_machine_1.EstablishedSubStates.HOLD]: call_state_machine_1.EstablishedStates.UN_HELD,
150
+ },
151
+ });
152
+ done();
153
+ });
154
+ call.onAccepted();
155
+ });
156
+ });
157
+ describe('Multiple sub state', () => {
158
+ it('Should mute and hold a call', () => {
159
+ const call = new call_1.default(sipCall, softphone_1.default);
160
+ // @ts-ignore: private method
161
+ call._sendAction(call_state_machine_1.Actions.MAKE_CALL);
162
+ expect(call.isMuted()).toBeFalsy();
163
+ expect(call.isHeld()).toBeFalsy();
164
+ // @ts-ignore: private method
165
+ call._sendAction(call_state_machine_1.Actions.REMOTLY_ACCEPTED);
166
+ call.mute();
167
+ expect(call.isMuted()).toBeTruthy();
168
+ expect(call.isHeld()).toBeFalsy();
169
+ call.hold();
170
+ expect(call.isMuted()).toBeTruthy();
171
+ expect(call.isHeld()).toBeTruthy();
172
+ call.resume();
173
+ expect(call.isMuted()).toBeTruthy();
174
+ expect(call.isHeld()).toBeFalsy();
175
+ });
176
+ });
128
177
  });
package/lib/api/auth.js CHANGED
@@ -64,6 +64,9 @@ exports.default = ((client, baseUrl) => ({
64
64
  }
65
65
  return client.post(`${baseUrl}/token`, body, headers).then(Session_1.default.parse);
66
66
  }),
67
+ samlLogOut() {
68
+ return client.get(`${baseUrl}/saml/logout`, null, null);
69
+ },
67
70
  initiateIdpAuthentication: (domain, redirectUrl) => __awaiter(void 0, void 0, void 0, function* () {
68
71
  const body = {
69
72
  domain,
@@ -43,7 +43,7 @@ exports.default = {
43
43
  });
44
44
  client.on(client.REGISTERED, () => {
45
45
  const sipSession = client.call('*10');
46
- if (!sipSession || !(0, sdp_1.getSipSessionId)(sipSession)) {
46
+ if (!sipSession || !(0, sdp_1.getSipCallId)(sipSession)) {
47
47
  return handleError('Unable to make call through WebRTC');
48
48
  }
49
49
  setTimeout(() => {
@@ -46,8 +46,7 @@ class CallLog {
46
46
  }
47
47
  static parseNew(plain, session) {
48
48
  const callLog = CallLog.parse(plain);
49
- // @TODO: FIXME add verification declined vs missed call
50
- callLog.newMissedCall = session && session.hasExtension((plain.requested_extension || plain.destination_extension)) && !plain.answered;
49
+ callLog.newMissedCall = session && plain.destination_user_uuid === session.uuid && !plain.answered;
51
50
  return callLog;
52
51
  }
53
52
  static newFrom(profile) {
@@ -105,7 +104,7 @@ class CallLog {
105
104
  return this.callDirection === 'inbound';
106
105
  }
107
106
  isAnOutgoingCall(session) {
108
- console.warn(`@wazo/sdk
107
+ console.warn(`@wazo/sdk
109
108
  CallLog.isAnOutgoingcall(session) method is obsolete.
110
109
  Please use CallLog.isOutgoing(session).
111
110
  `);
@@ -119,7 +119,7 @@ class CTIPhone extends Emitter_1.default {
119
119
  number: callSession.number,
120
120
  });
121
121
  try {
122
- yield CallApi_1.default.cancelCall(callSession);
122
+ yield CallApi_1.default.cancelCall({ apiId: callSession.getId() });
123
123
  if (this.currentCall && callSession.callId === this.currentCall.id) {
124
124
  this.endCurrentCall(callSession);
125
125
  }
@@ -143,7 +143,7 @@ class CTIPhone extends Emitter_1.default {
143
143
  callId: callSession.getId(),
144
144
  number: callSession.number,
145
145
  });
146
- yield CallApi_1.default.cancelCall(callSession);
146
+ yield CallApi_1.default.cancelCall({ id: callSession.getId() });
147
147
  this.eventEmitter.emit('onCallEnded', callSession);
148
148
  });
149
149
  }