@wazo/sdk 0.39.0-beta.9 → 0.39.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.
Files changed (139) hide show
  1. package/README.md +7 -7
  2. package/dist/wazo-sdk.js +12 -12
  3. package/esm/__tests__/api-client.test.js +7 -1
  4. package/esm/__tests__/web-rtc-client.test.js +10 -0
  5. package/esm/api/dird.js +2 -2
  6. package/esm/api-client.js +4 -0
  7. package/esm/checker/checks/ice-ipv4.js +2 -1
  8. package/esm/domain/Contact.js +15 -8
  9. package/esm/domain/Phone/WebRTCPhone.js +9 -11
  10. package/esm/domain/Session.js +24 -0
  11. package/esm/domain/__tests__/Contact.test.js +200 -48
  12. package/esm/lib/WazoSessionDescriptionHandler.js +14 -11
  13. package/esm/service/IssueReporter.js +12 -2
  14. package/esm/service/__tests__/IssueReporter.test.js +12 -0
  15. package/esm/service/getApiClient.js +9 -0
  16. package/esm/simple/Auth.js +40 -5
  17. package/esm/simple/Phone.js +3 -1
  18. package/esm/simple/Websocket.js +1 -0
  19. package/esm/simple/room/Stream.js +3 -3
  20. package/esm/types/api/dird.d.ts.map +1 -1
  21. package/esm/types/api-client.d.ts +3 -2
  22. package/esm/types/api-client.d.ts.map +1 -1
  23. package/esm/types/checker/checks/ice-ipv4.d.ts.map +1 -1
  24. package/esm/types/domain/Contact.d.ts +1 -1
  25. package/esm/types/domain/Contact.d.ts.map +1 -1
  26. package/esm/types/domain/Phone/WebRTCPhone.d.ts +1 -1
  27. package/esm/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  28. package/esm/types/domain/Session.d.ts +2 -0
  29. package/esm/types/domain/Session.d.ts.map +1 -1
  30. package/esm/types/domain/types.d.ts +40 -14
  31. package/esm/types/domain/types.d.ts.map +1 -1
  32. package/esm/types/index.d.ts +2 -0
  33. package/esm/types/index.d.ts.map +1 -1
  34. package/esm/types/lib/WazoSessionDescriptionHandler.d.ts +1 -1
  35. package/esm/types/lib/WazoSessionDescriptionHandler.d.ts.map +1 -1
  36. package/esm/types/service/IssueReporter.d.ts +1 -0
  37. package/esm/types/service/IssueReporter.d.ts.map +1 -1
  38. package/esm/types/service/getApiClient.d.ts +2 -0
  39. package/esm/types/service/getApiClient.d.ts.map +1 -1
  40. package/esm/types/simple/Auth.d.ts.map +1 -1
  41. package/esm/types/simple/Phone.d.ts +4 -3
  42. package/esm/types/simple/Phone.d.ts.map +1 -1
  43. package/esm/types/simple/Websocket.d.ts.map +1 -1
  44. package/esm/types/simple/index.d.ts +1 -0
  45. package/esm/types/simple/index.d.ts.map +1 -1
  46. package/esm/types/utils/Emitter.d.ts +1 -1
  47. package/esm/types/utils/Heartbeat.d.ts +3 -2
  48. package/esm/types/utils/Heartbeat.d.ts.map +1 -1
  49. package/esm/types/utils/api-requester.d.ts +5 -1
  50. package/esm/types/utils/api-requester.d.ts.map +1 -1
  51. package/esm/types/web-rtc-client.d.ts +5 -29
  52. package/esm/types/web-rtc-client.d.ts.map +1 -1
  53. package/esm/types/websocket-client.d.ts +5 -1
  54. package/esm/types/websocket-client.d.ts.map +1 -1
  55. package/esm/utils/__tests__/api-requester.test.js +7 -2
  56. package/esm/utils/api-requester.js +23 -5
  57. package/esm/web-rtc-client.js +69 -40
  58. package/esm/websocket-client.js +9 -2
  59. package/lib/__tests__/api-client.test.js +7 -1
  60. package/lib/__tests__/web-rtc-client.test.js +16 -0
  61. package/lib/api/dird.js +2 -2
  62. package/lib/api-client.js +4 -0
  63. package/lib/checker/checks/ice-ipv4.js +2 -1
  64. package/lib/domain/Contact.js +15 -8
  65. package/lib/domain/Phone/WebRTCPhone.js +9 -11
  66. package/lib/domain/Session.js +23 -0
  67. package/lib/domain/__tests__/Contact.test.js +175 -50
  68. package/lib/lib/WazoSessionDescriptionHandler.js +15 -12
  69. package/lib/service/IssueReporter.js +12 -2
  70. package/lib/service/__tests__/IssueReporter.test.js +12 -0
  71. package/lib/service/getApiClient.js +12 -1
  72. package/lib/simple/Auth.js +34 -4
  73. package/lib/simple/Phone.js +3 -1
  74. package/lib/simple/Websocket.js +1 -0
  75. package/lib/simple/room/Stream.js +3 -3
  76. package/lib/types/api/dird.d.ts.map +1 -1
  77. package/lib/types/api-client.d.ts +3 -2
  78. package/lib/types/api-client.d.ts.map +1 -1
  79. package/lib/types/checker/checks/ice-ipv4.d.ts.map +1 -1
  80. package/lib/types/domain/Contact.d.ts +1 -1
  81. package/lib/types/domain/Contact.d.ts.map +1 -1
  82. package/lib/types/domain/Phone/WebRTCPhone.d.ts +1 -1
  83. package/lib/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  84. package/lib/types/domain/Session.d.ts +2 -0
  85. package/lib/types/domain/Session.d.ts.map +1 -1
  86. package/lib/types/domain/types.d.ts +40 -14
  87. package/lib/types/domain/types.d.ts.map +1 -1
  88. package/lib/types/index.d.ts +2 -0
  89. package/lib/types/index.d.ts.map +1 -1
  90. package/lib/types/lib/WazoSessionDescriptionHandler.d.ts +1 -1
  91. package/lib/types/lib/WazoSessionDescriptionHandler.d.ts.map +1 -1
  92. package/lib/types/service/IssueReporter.d.ts +1 -0
  93. package/lib/types/service/IssueReporter.d.ts.map +1 -1
  94. package/lib/types/service/getApiClient.d.ts +2 -0
  95. package/lib/types/service/getApiClient.d.ts.map +1 -1
  96. package/lib/types/simple/Auth.d.ts.map +1 -1
  97. package/lib/types/simple/Phone.d.ts +4 -3
  98. package/lib/types/simple/Phone.d.ts.map +1 -1
  99. package/lib/types/simple/Websocket.d.ts.map +1 -1
  100. package/lib/types/simple/index.d.ts +1 -0
  101. package/lib/types/simple/index.d.ts.map +1 -1
  102. package/lib/types/utils/Emitter.d.ts +1 -1
  103. package/lib/types/utils/Heartbeat.d.ts +3 -2
  104. package/lib/types/utils/Heartbeat.d.ts.map +1 -1
  105. package/lib/types/utils/api-requester.d.ts +5 -1
  106. package/lib/types/utils/api-requester.d.ts.map +1 -1
  107. package/lib/types/web-rtc-client.d.ts +5 -29
  108. package/lib/types/web-rtc-client.d.ts.map +1 -1
  109. package/lib/types/websocket-client.d.ts +5 -1
  110. package/lib/types/websocket-client.d.ts.map +1 -1
  111. package/lib/utils/__tests__/api-requester.test.js +7 -2
  112. package/lib/utils/api-requester.js +19 -5
  113. package/lib/web-rtc-client.js +67 -39
  114. package/lib/websocket-client.js +8 -2
  115. package/package.json +37 -35
  116. package/src/__tests__/api-client.test.ts +9 -1
  117. package/src/__tests__/web-rtc-client.test.ts +12 -0
  118. package/src/api/dird.ts +3 -2
  119. package/src/api-client.ts +8 -2
  120. package/src/checker/checks/ice-ipv4.ts +2 -1
  121. package/src/domain/Contact.ts +16 -8
  122. package/src/domain/Phone/WebRTCPhone.ts +11 -12
  123. package/src/domain/Session.ts +28 -0
  124. package/src/domain/__tests__/Contact.test.ts +213 -54
  125. package/src/domain/types.ts +42 -14
  126. package/src/lib/WazoSessionDescriptionHandler.ts +15 -11
  127. package/src/service/IssueReporter.ts +13 -2
  128. package/src/service/__tests__/IssueReporter.test.ts +14 -0
  129. package/src/service/getApiClient.ts +12 -0
  130. package/src/simple/Auth.ts +58 -6
  131. package/src/simple/Phone.ts +15 -6
  132. package/src/simple/Websocket.ts +1 -0
  133. package/src/simple/room/Stream.ts +3 -3
  134. package/src/types/global.d.ts +1 -0
  135. package/src/utils/Heartbeat.ts +2 -2
  136. package/src/utils/__tests__/api-requester.test.ts +8 -2
  137. package/src/utils/api-requester.ts +31 -4
  138. package/src/web-rtc-client.ts +84 -70
  139. package/src/websocket-client.ts +14 -1
@@ -55,6 +55,8 @@ export const transportEvents = [CONNECTED, DISCONNECTED, TRANSPORT_ERROR, MESSAG
55
55
  export class CanceledCallError extends Error {
56
56
  }
57
57
  const MAX_REGISTER_TRIES = 5;
58
+ // setting a 24hr timeout and letting the backend define the actual value
59
+ const NO_ANSWER_TIMEOUT = 60 * 60 * 24; // in seconds
58
60
  export default class WebRTCClient extends Emitter {
59
61
  clientId;
60
62
  config;
@@ -112,7 +114,7 @@ export default class WebRTCClient extends Emitter {
112
114
  }
113
115
  return [];
114
116
  }
115
- constructor(config, session, uaConfigOverrides = undefined) {
117
+ constructor(config, session, uaConfigOverrides) {
116
118
  super();
117
119
  // For debug purpose
118
120
  this.clientId = Math.ceil(Math.random() * 1000);
@@ -173,10 +175,10 @@ export default class WebRTCClient extends Emitter {
173
175
  this.video = media.video;
174
176
  this.audio = media.audio;
175
177
  }
176
- createUserAgent(configOverrides) {
177
- const webRTCConfiguration = this._createWebRTCConfiguration(configOverrides);
178
- logger.info('sdk webrtc, creating UA', { webRTCConfiguration, clientId: this.clientId });
179
- webRTCConfiguration.delegate = {
178
+ createUserAgent(uaConfigOverrides) {
179
+ const uaOptions = this._createUaOptions(uaConfigOverrides);
180
+ logger.info('sdk webrtc, creating UA', { uaOptions, clientId: this.clientId });
181
+ uaOptions.delegate = {
180
182
  onConnect: this.onConnect.bind(this),
181
183
  onDisconnect: this.onDisconnect.bind(this),
182
184
  onInvite: (invitation) => {
@@ -192,7 +194,8 @@ export default class WebRTCClient extends Emitter {
192
194
  this.eventEmitter.emit(INVITE, invitation, this.sessionWantsToDoVideo(invitation), shouldAutoAnswer);
193
195
  },
194
196
  };
195
- const ua = new UserAgent(webRTCConfiguration);
197
+ const ua = new UserAgent(uaOptions);
198
+ ua.start();
196
199
  if (ua.transport && ua.transport.connectPromise) {
197
200
  ua.transport.connectPromise.catch((e) => {
198
201
  logger.warn('Transport connect error', e);
@@ -372,16 +375,27 @@ export default class WebRTCClient extends Emitter {
372
375
  userAgent: !!this.userAgent,
373
376
  registerer: !!this.registerer,
374
377
  });
375
- if (!this.registerer) {
376
- return Promise.resolve();
377
- }
378
378
  try {
379
- return this.registerer.unregister().then(() => {
380
- logger.info('sdk webrtc unregistered', { clientId: this.clientId });
381
- this._cleanupRegister();
382
- }).catch(e => {
383
- logger.error('sdk webrtc unregistering, promise error', e);
384
- this._cleanupRegister();
379
+ return new Promise((resolve, reject) => {
380
+ if (!this.registerer) {
381
+ return resolve();
382
+ }
383
+ const onRegisterStateChange = (state) => {
384
+ if (state === RegistererState.Unregistered) {
385
+ logger.info('sdk webrtc unregistered', { clientId: this.clientId });
386
+ if (this.registerer) {
387
+ this.registerer.stateChange.addListener(onRegisterStateChange);
388
+ }
389
+ this._cleanupRegister();
390
+ resolve();
391
+ }
392
+ };
393
+ this.registerer.stateChange.addListener(onRegisterStateChange);
394
+ this.registerer.unregister().then().catch(e => {
395
+ logger.error('sdk webrtc unregistering, promise error', e);
396
+ this._cleanupRegister();
397
+ reject();
398
+ });
385
399
  });
386
400
  }
387
401
  catch (e) {
@@ -441,9 +455,7 @@ export default class WebRTCClient extends Emitter {
441
455
  const inviteOptions = {
442
456
  requestDelegate: {
443
457
  onAccept: (response) => {
444
- // @ts-ignore
445
458
  if (session.sessionDescriptionHandler?.peerConnection) {
446
- // @ts-ignore
447
459
  session.sessionDescriptionHandler.peerConnection.sfu = conference;
448
460
  }
449
461
  // @ts-ignore
@@ -712,9 +724,9 @@ export default class WebRTCClient extends Emitter {
712
724
  hasVideo,
713
725
  isConference,
714
726
  };
715
- if (isConference) {
716
- this.mute(session);
717
- }
727
+ // We should also mute the call, because when holding a call during a voicemail, the audio is still sent with the
728
+ // `sendonly` direction
729
+ this.mute(session);
718
730
  session.sessionDescriptionHandlerOptionsReInvite = {
719
731
  // @ts-ignore
720
732
  hold: true,
@@ -754,9 +766,7 @@ export default class WebRTCClient extends Emitter {
754
766
  if (session.pendingReinvite) {
755
767
  return Promise.resolve();
756
768
  }
757
- if (isConference) {
758
- this.unmute(session);
759
- }
769
+ this.unmute(session);
760
770
  delete this.heldSessions[this.getSipSessionId(session)];
761
771
  session.sessionDescriptionHandlerOptionsReInvite = {
762
772
  // @ts-ignore
@@ -1117,9 +1127,9 @@ export default class WebRTCClient extends Emitter {
1117
1127
  });
1118
1128
  // Release old video stream
1119
1129
  if (localStream) {
1120
- localStream.getVideoTracks().forEach(track => {
1121
- track.stop();
1122
- });
1130
+ localStream.getVideoTracks()
1131
+ .filter((track) => track.enabled)
1132
+ .forEach((track) => track.stop());
1123
1133
  }
1124
1134
  const constraints = {
1125
1135
  video: id ? {
@@ -1439,7 +1449,7 @@ export default class WebRTCClient extends Emitter {
1439
1449
  if (!pc || !remoteStream) {
1440
1450
  return;
1441
1451
  }
1442
- remoteStream.getTracks().forEach(track => {
1452
+ remoteStream.getVideoTracks().forEach(track => {
1443
1453
  remoteStream.removeTrack(track);
1444
1454
  });
1445
1455
  if (pc.getReceivers) {
@@ -1494,6 +1504,7 @@ export default class WebRTCClient extends Emitter {
1494
1504
  createAudioElementFor(sessionId) {
1495
1505
  const audio = document.createElement('audio');
1496
1506
  audio.setAttribute('id', `audio-${sessionId}`);
1507
+ logger.info('creating audio element', { sessionId, audioOutputDeviceId: this.audioOutputDeviceId });
1497
1508
  if (audio.setSinkId && this.audioOutputDeviceId) {
1498
1509
  audio.setSinkId(this.audioOutputDeviceId);
1499
1510
  }
@@ -1620,7 +1631,7 @@ export default class WebRTCClient extends Emitter {
1620
1631
  ...config,
1621
1632
  }));
1622
1633
  }
1623
- _createWebRTCConfiguration(configOverrides = {}) {
1634
+ _createUaOptions(uaOptionsOverrides = {}) {
1624
1635
  let { host, } = this.config;
1625
1636
  let port = this.config.port || 443;
1626
1637
  if (this.config.websocketSip) {
@@ -1628,11 +1639,12 @@ export default class WebRTCClient extends Emitter {
1628
1639
  [host] = webSocketSip;
1629
1640
  port = Number(webSocketSip[1]);
1630
1641
  }
1631
- const config = {
1642
+ const { userUuid } = this.config;
1643
+ const uaOptions = {
1644
+ noAnswerTimeout: NO_ANSWER_TIMEOUT,
1632
1645
  authorizationUsername: this.config.authorizationUser,
1633
1646
  authorizationPassword: this.config.password,
1634
1647
  displayName: this.config.displayName,
1635
- autoStart: true,
1636
1648
  hackIpInContact: true,
1637
1649
  contactParams: {
1638
1650
  transport: 'wss',
@@ -1658,8 +1670,8 @@ export default class WebRTCClient extends Emitter {
1658
1670
  return new WazoSessionDescriptionHandler(uaLogger, wazoMediaStreamFactory, sdhOptions, isWeb, session);
1659
1671
  },
1660
1672
  transportOptions: {
1661
- traceSip: configOverrides?.traceSip || false,
1662
- wsServers: `wss://${host}:${port}/api/asterisk/ws`,
1673
+ traceSip: uaOptionsOverrides?.traceSip || false,
1674
+ wsServers: `wss://${host}:${port}/api/asterisk/ws${userUuid ? `?userUuid=${userUuid}` : ''}`,
1663
1675
  },
1664
1676
  sessionDescriptionHandlerFactoryOptions: {
1665
1677
  modifiers: [replaceLocalIpModifier],
@@ -1675,19 +1687,21 @@ export default class WebRTCClient extends Emitter {
1675
1687
  rtcpMuxPolicy: 'require',
1676
1688
  iceServers: WebRTCClient.getIceServers(this.config.host),
1677
1689
  ...this._getRtcOptions(),
1678
- ...(configOverrides?.peerConnectionOptions || {}),
1690
+ ...(uaOptionsOverrides?.peerConnectionOptions || {}),
1679
1691
  },
1680
1692
  },
1681
1693
  // Configuration used in SDH to create the PeerConnection
1682
1694
  peerConnectionConfiguration: {
1683
1695
  rtcpMuxPolicy: 'require',
1684
1696
  iceServers: WebRTCClient.getIceServers(this.config.host),
1685
- ...(configOverrides?.peerConnectionOptions || {}),
1697
+ ...(uaOptionsOverrides?.peerConnectionOptions || {}),
1686
1698
  },
1687
1699
  },
1688
1700
  };
1689
- return { ...config,
1690
- ...configOverrides,
1701
+ delete uaOptionsOverrides?.traceSip;
1702
+ return {
1703
+ ...uaOptions,
1704
+ ...uaOptionsOverrides,
1691
1705
  };
1692
1706
  }
1693
1707
  // eslint-disable-next-line no-unused-vars
@@ -1846,6 +1860,19 @@ export default class WebRTCClient extends Emitter {
1846
1860
  // @ts-ignore
1847
1861
  const earlyStream = sipSession && sipSession.sessionDescriptionHandler ? sipSession.sessionDescriptionHandler.remoteMediaStream : null;
1848
1862
  const stream = newStream || removeStream || earlyStream;
1863
+ const shouldPause = audioElement.currentTime > 0 && !audioElement.paused && !audioElement.ended && audioElement.readyState > 2;
1864
+ logger.info('setting up media', {
1865
+ sessionId,
1866
+ streams: {
1867
+ new: !!newStream,
1868
+ remove: !!removeStream,
1869
+ early: !!earlyStream,
1870
+ },
1871
+ isWeb: this._isWeb(),
1872
+ hasAudio: this._hasAudio(),
1873
+ isConference,
1874
+ shouldPause,
1875
+ });
1849
1876
  if (!this._isWeb() || !stream) {
1850
1877
  return;
1851
1878
  }
@@ -1853,7 +1880,7 @@ export default class WebRTCClient extends Emitter {
1853
1880
  // Conference local stream is handled in Room
1854
1881
  return;
1855
1882
  }
1856
- if (audioElement.currentTime > 0 && !audioElement.paused && !audioElement.ended && audioElement.readyState > 2) {
1883
+ if (shouldPause) {
1857
1884
  audioElement.pause();
1858
1885
  }
1859
1886
  audioElement.srcObject = stream;
@@ -1888,7 +1915,7 @@ export default class WebRTCClient extends Emitter {
1888
1915
  }
1889
1916
  }
1890
1917
  _cleanupStream(stream) {
1891
- stream.getTracks().forEach(track => track.stop());
1918
+ stream.getTracks().filter(track => track.enabled).forEach(track => track.stop());
1892
1919
  }
1893
1920
  _toggleAudio(session, muteAudio) {
1894
1921
  // @ts-ignore
@@ -2106,8 +2133,10 @@ export default class WebRTCClient extends Emitter {
2106
2133
  networkStats.totalTransportReceived = transportReceived;
2107
2134
  networkStats.transportReceived = transportReceived - lastTransportReceived;
2108
2135
  networkStats.bandwidth = networkStats.audioBytesSent + networkStats.audioBytesReceived + networkStats.videoBytesSent + networkStats.videoBytesReceived + networkStats.transportReceived + networkStats.transportSent;
2109
- this.eventEmitter.emit(ON_NETWORK_STATS, session, networkStats, this.sessionNetworkStats[sessionId]);
2110
- this.sessionNetworkStats[sessionId].push(networkStats);
2136
+ if (this.sessionNetworkStats[sessionId]) {
2137
+ this.eventEmitter.emit(ON_NETWORK_STATS, session, networkStats, this.sessionNetworkStats[sessionId]);
2138
+ this.sessionNetworkStats[sessionId].push(networkStats);
2139
+ }
2111
2140
  }
2112
2141
  async _accept(session, options = {}) {
2113
2142
  if (session.state === SessionState.Terminated || session.state === SessionState.Terminating) {
@@ -84,6 +84,7 @@ const logger = IssueReporter.loggerFor('wazo-ws');
84
84
  const messageLogger = IssueReporter.loggerFor('wazo-ws-message');
85
85
  class WebSocketClient extends Emitter {
86
86
  initialized;
87
+ session;
87
88
  host;
88
89
  version;
89
90
  token;
@@ -103,14 +104,16 @@ class WebSocketClient extends Emitter {
103
104
  * @param events
104
105
  * @param version
105
106
  * @param heartbeat
107
+ * @param session
106
108
  * @param options @see https://github.com/pladaria/reconnecting-websocket#available-options
107
109
  */
108
- constructor({ host, token, version, events, heartbeat, }, options = {
110
+ constructor({ host, token, version, events, heartbeat, session, }, options = {
109
111
  host,
110
112
  token,
111
113
  version: 1,
112
114
  events: [],
113
115
  heartbeat: {},
116
+ session: null,
114
117
  }) {
115
118
  super();
116
119
  this.initialized = false;
@@ -120,6 +123,7 @@ class WebSocketClient extends Emitter {
120
123
  this.events = events;
121
124
  this.options = options;
122
125
  this.version = version;
126
+ this.session = session;
123
127
  this._boundOnHeartbeat = this._onHeartbeat.bind(this);
124
128
  const { delay, timeout, max, } = heartbeat || {};
125
129
  this.heartbeat = new Heartbeat(delay, timeout, max);
@@ -346,7 +350,10 @@ class WebSocketClient extends Emitter {
346
350
  if (!this.host || !this.token) {
347
351
  this.close();
348
352
  }
349
- const url = `wss://${this.host || ''}/api/websocketd/?token=${this.token || ''}&version=${this.version}`;
353
+ let url = `wss://${this.host || ''}/api/websocketd/?token=${this.token || ''}&version=${this.version}`;
354
+ if (this.session) {
355
+ url += `&userUuid=${this.session.uuid}`;
356
+ }
350
357
  logger.info('Wazo WS url computed to reconnect', {
351
358
  url,
352
359
  });
@@ -12,11 +12,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
+ const node_abort_controller_1 = require("node-abort-controller");
15
16
  const js_base64_1 = require("js-base64");
16
17
  const api_client_1 = __importDefault(require("../api-client"));
17
18
  const ServerError_1 = __importDefault(require("../domain/ServerError"));
18
19
  const BadResponse_1 = __importDefault(require("../domain/BadResponse"));
19
20
  const Session_1 = __importDefault(require("../domain/Session"));
21
+ const controller = new node_abort_controller_1.AbortController();
20
22
  const mockedResponse = {
21
23
  data: {
22
24
  token: 1,
@@ -79,7 +81,7 @@ const server = 'localhost';
79
81
  const authVersion = '0.1';
80
82
  const username = 'wazo';
81
83
  const password = 'zowa';
82
- jest.mock('node-fetch/lib/index', () => { });
84
+ jest.mock('node-fetch/src/index', () => { });
83
85
  const token = '1234';
84
86
  const client = new api_client_1.default({
85
87
  server,
@@ -112,6 +114,7 @@ describe('With correct API results', () => {
112
114
  expect(global.fetch).toBeCalledWith(`https://${server}/api/auth/${authVersion}/token`, {
113
115
  method: 'post',
114
116
  body: JSON.stringify(data),
117
+ signal: controller.signal,
115
118
  headers,
116
119
  agent: null,
117
120
  });
@@ -125,6 +128,7 @@ describe('With correct API results', () => {
125
128
  expect(global.fetch).toBeCalledWith(`https://${server}/api/auth/${authVersion}/token/${oldToken}`, {
126
129
  method: 'delete',
127
130
  body: null,
131
+ signal: controller.signal,
128
132
  headers: {},
129
133
  agent: null,
130
134
  });
@@ -145,6 +149,7 @@ describe('With unAuthorized API results', () => {
145
149
  expect(global.fetch).toBeCalledWith(`https://${server}/api/auth/${authVersion}/token/${tokenToCheck}`, {
146
150
  method: 'head',
147
151
  body: null,
152
+ signal: controller.signal,
148
153
  headers: {},
149
154
  agent: null,
150
155
  });
@@ -173,6 +178,7 @@ describe('With not found API results', () => {
173
178
  expect(global.fetch).toBeCalledWith(`https://${server}/api/calld/1.0/users/me/voicemails`, {
174
179
  method: 'get',
175
180
  body: null,
181
+ signal: controller.signal,
176
182
  headers: {
177
183
  'X-Auth-Token': token,
178
184
  'Content-Type': 'application/json',
@@ -14,6 +14,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const web_rtc_client_1 = __importDefault(require("../web-rtc-client"));
16
16
  jest.mock('sip.js/lib/platform/web/transport');
17
+ jest.mock('sip.js/lib/api/user-agent', () => {
18
+ var _a;
19
+ return ({
20
+ start: () => { },
21
+ UserAgent: (_a = class UserAgent {
22
+ constructor() {
23
+ // @ts-ignore
24
+ this.start = () => { };
25
+ this.transport = {};
26
+ }
27
+ },
28
+ // @ts-ignore
29
+ _a.makeURI = () => { },
30
+ _a),
31
+ });
32
+ });
17
33
  // @ts-expect-error
18
34
  const client = new web_rtc_client_1.default({});
19
35
  describe('WebRTC client', () => {
package/lib/api/dird.js CHANGED
@@ -20,9 +20,9 @@ const getContactPayload = (contact) => ({
20
20
  note: contact.note ? contact.note : '',
21
21
  });
22
22
  exports.default = ((client, baseUrl) => ({
23
- search: (context, term) => client.get(`${baseUrl}/directories/lookup/${context}`, {
23
+ search: (context, term, offset = 0, limit = null) => client.get(`${baseUrl}/directories/lookup/${context}`, {
24
24
  term,
25
- }).then(Contact_1.default.parseMany),
25
+ }).then((response) => Contact_1.default.parseMany(response, offset, limit)),
26
26
  listPersonalContacts: (queryParams = null) => client.get(`${baseUrl}/personal`, queryParams).then((response) => Contact_1.default.parseManyPersonal(response.items)),
27
27
  fetchPersonalContact: (contactUuid) => client.get(`${baseUrl}/personal/${contactUuid}`).then(Contact_1.default.parsePersonal),
28
28
  addContact: (contact) => client.post(`${baseUrl}/personal`, getContactPayload(contact)).then(Contact_1.default.parsePersonal),
package/lib/api-client.js CHANGED
@@ -112,6 +112,7 @@ class ApiClient {
112
112
  this.onRefreshTokenError(error);
113
113
  }
114
114
  }
115
+ return null;
115
116
  });
116
117
  }
117
118
  setToken(token) {
@@ -123,6 +124,9 @@ class ApiClient {
123
124
  setRefreshToken(refreshToken) {
124
125
  this.refreshToken = refreshToken;
125
126
  }
127
+ setRequestTimeout(requestTimeout) {
128
+ this.client.setRequestTimeout(requestTimeout);
129
+ }
126
130
  setClientId(clientId) {
127
131
  this.client.clientId = clientId;
128
132
  }
@@ -17,7 +17,8 @@ exports.default = {
17
17
  name: 'Non IP v4 ice',
18
18
  check: (server, session, externalAppConfig) => new Promise((resolve, reject) => {
19
19
  if (typeof MediaStream === 'undefined') {
20
- return resolve('Skipped on node');
20
+ resolve('Skipped on node');
21
+ return;
21
22
  }
22
23
  const mobile = (0, isMobile_1.default)();
23
24
  const offerOptions = {
@@ -31,11 +31,12 @@ class Contact {
31
31
  }
32
32
  return aLastName.localeCompare(bLastName);
33
33
  }
34
- static parseMany(response) {
35
- if (!response || !response.results) {
34
+ static parseMany(response, offset = 0, limit = null) {
35
+ if (!response || !response.results || limit === 0) {
36
36
  return [];
37
37
  }
38
- return response.results.map(r => Contact.parse(r, response.column_types));
38
+ const results = limit !== null && limit > 0 ? response.results.slice(offset, limit) : offset > 0 ? response.results.slice(offset) : response.results;
39
+ return results.map(r => Contact.parse(r, response.column_types));
39
40
  }
40
41
  static manyGraphQlWithNumbersParser(numbers) {
41
42
  return (response) => {
@@ -136,13 +137,19 @@ class Contact {
136
137
  const postalAddress = plain.postalAddresses[0];
137
138
  address = `${postalAddress.street} ${postalAddress.city} ${postalAddress.postCode} ${postalAddress.country}`;
138
139
  }
140
+ const firstName = plain.givenName || '';
141
+ const lastName = plain.familyName || '';
142
+ const companyName = plain.company || '';
143
+ const isCompanyAccount = !firstName && !lastName;
144
+ const name = isCompanyAccount ? companyName : `${firstName} ${lastName}`;
139
145
  return new Contact({
140
- name: `${plain.givenName || ''} ${plain.familyName || ''}`,
146
+ name,
141
147
  number: plain.phoneNumbers.length ? plain.phoneNumbers[0].number : '',
142
- numbers: plain.phoneNumbers.length ? [{
143
- label: 'primary',
144
- number: plain.phoneNumbers[0].number,
145
- }] : [],
148
+ // Make numbers unique
149
+ numbers: [...new Map(plain.phoneNumbers.map(item => [item.number, item])).values()].map((item, idx) => ({
150
+ label: idx === 0 ? 'primary' : 'secondary',
151
+ number: item.number,
152
+ })),
146
153
  email: plain.emailAddresses.length ? plain.emailAddresses[0].email : '',
147
154
  emails: plain.emailAddresses.length ? [{
148
155
  label: 'primary',
@@ -306,9 +306,6 @@ class WebRTCPhone extends Emitter_1.default {
306
306
  this.eventEmitter.emit(exports.ON_CALL_CANCELED, callSession, elsewhere);
307
307
  };
308
308
  }
309
- else if (sipSession instanceof api_1.Invitation) {
310
- console.warn('sipSession._onCancel not found, please update the wazo SDK accordingly');
311
- }
312
309
  sipSession.stateChange.addListener((newState) => {
313
310
  switch (newState) {
314
311
  case session_state_1.SessionState.Establishing:
@@ -362,10 +359,6 @@ class WebRTCPhone extends Emitter_1.default {
362
359
  if (kind === 'audio') {
363
360
  return this.eventEmitter.emit(exports.ON_AUDIO_STREAM, stream);
364
361
  }
365
- // not sure this does anything
366
- if (kind === 'video') {
367
- event.track.enabled = false;
368
- }
369
362
  return this.eventEmitter.emit(exports.ON_VIDEO_STREAM, stream, event.track.id, event, sipSession);
370
363
  };
371
364
  peerConnection.onremovestream = (event) => {
@@ -417,10 +410,12 @@ class WebRTCPhone extends Emitter_1.default {
417
410
  logger.info('WebRTC phone - stop screen sharing', {
418
411
  restoreLocalStream,
419
412
  });
420
- let reinvited;
413
+ let reinvited = null;
421
414
  try {
422
415
  if (this.currentScreenShare.stream) {
423
- this.currentScreenShare.stream.getTracks().forEach((track) => track.stop());
416
+ this.currentScreenShare.stream.getTracks()
417
+ .filter((track) => track.enabled)
418
+ .forEach((track) => track.stop());
424
419
  }
425
420
  if (restoreLocalStream) {
426
421
  const targetCallSession = callSession || this.currentCallSession;
@@ -1124,12 +1119,15 @@ class WebRTCPhone extends Emitter_1.default {
1124
1119
  return __awaiter(this, void 0, void 0, function* () {
1125
1120
  logger.info('WebRTC close');
1126
1121
  try {
1127
- yield Promise.race([this.unregister(), new Promise((resolve, reject) => setTimeout(() => reject(new Error('Unregister, timed out')), 3000))]);
1122
+ yield Promise.race([
1123
+ this.unregister(),
1124
+ new Promise((resolve, reject) => setTimeout(() => reject(new Error('Unregister, timed out')), 3000)),
1125
+ ]);
1128
1126
  }
1129
1127
  catch (e) {
1130
1128
  logger.error('WebRTC close, unregister error', e);
1131
1129
  }
1132
- this.client.close(force);
1130
+ yield this.client.close(force);
1133
1131
  this.unbind();
1134
1132
  this.incomingSessions = [];
1135
1133
  this.currentSipSession = undefined;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  /* eslint-disable no-nested-ternary */
7
7
  const jsrsasign_1 = require("jsrsasign");
8
8
  const pubkey_1 = __importDefault(require("../pubkey"));
9
+ const Contact_1 = __importDefault(require("./Contact"));
9
10
  const new_from_1 = __importDefault(require("../utils/new-from"));
10
11
  const compare_version_1 = __importDefault(require("../utils/compare-version"));
11
12
  const { jws } = jsrsasign_1.KJUR;
@@ -89,6 +90,28 @@ class Session {
89
90
  primaryCtiLine() {
90
91
  return this.profile && (this.profile.sipLines || []).find(sipLine => sipLine && !sipLine.isWebRtc());
91
92
  }
93
+ getContact() {
94
+ if (this.contact) {
95
+ return this.contact;
96
+ }
97
+ const { email, firstName, lastName, mobileNumber, state, status } = this.profile || {};
98
+ const number = this.primaryNumber() || '';
99
+ const numbers = [{ label: 'primary', number }];
100
+ if (mobileNumber) {
101
+ numbers.push({ label: 'mobile', number: mobileNumber });
102
+ }
103
+ return new Contact_1.default({
104
+ uuid: this.uuid || '',
105
+ email,
106
+ name: `${firstName} ${lastName}`,
107
+ number,
108
+ numbers,
109
+ state: state || '',
110
+ status,
111
+ backend: 'wazo',
112
+ source: 'default',
113
+ });
114
+ }
92
115
  primaryContext() {
93
116
  if (this.engineVersion) {
94
117
  if (this.hasEngineVersionGte(MINIMUM_WAZO_ENGINE_VERSION_FOR_DEFAULT_CONTEXT)) {