@wazo/sdk 0.39.0-beta.8 → 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.
- package/README.md +7 -7
- package/dist/wazo-sdk.js +12 -12
- package/esm/__tests__/api-client.test.js +7 -1
- package/esm/__tests__/web-rtc-client.test.js +10 -0
- package/esm/api/chatd.js +17 -3
- package/esm/api/dird.js +2 -2
- package/esm/api-client.js +4 -0
- package/esm/checker/checks/ice-ipv4.js +2 -1
- package/esm/domain/Contact.js +15 -8
- package/esm/domain/Phone/WebRTCPhone.js +9 -11
- package/esm/domain/Session.js +24 -0
- package/esm/domain/__tests__/Contact.test.js +200 -48
- package/esm/lib/WazoSessionDescriptionHandler.js +14 -11
- package/esm/service/IssueReporter.js +12 -2
- package/esm/service/__tests__/IssueReporter.test.js +12 -0
- package/esm/service/getApiClient.js +9 -0
- package/esm/simple/Auth.js +40 -5
- package/esm/simple/Phone.js +3 -1
- package/esm/simple/Websocket.js +1 -0
- package/esm/simple/room/Stream.js +3 -3
- package/esm/types/api/chatd.d.ts +1 -1
- package/esm/types/api/chatd.d.ts.map +1 -1
- package/esm/types/api/dird.d.ts.map +1 -1
- package/esm/types/api-client.d.ts +3 -2
- package/esm/types/api-client.d.ts.map +1 -1
- package/esm/types/checker/checks/ice-ipv4.d.ts.map +1 -1
- package/esm/types/domain/Contact.d.ts +1 -1
- package/esm/types/domain/Contact.d.ts.map +1 -1
- package/esm/types/domain/Phone/WebRTCPhone.d.ts +1 -1
- package/esm/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
- package/esm/types/domain/Session.d.ts +2 -0
- package/esm/types/domain/Session.d.ts.map +1 -1
- package/esm/types/domain/types.d.ts +40 -14
- package/esm/types/domain/types.d.ts.map +1 -1
- package/esm/types/index.d.ts +2 -0
- package/esm/types/index.d.ts.map +1 -1
- package/esm/types/lib/WazoSessionDescriptionHandler.d.ts +1 -1
- package/esm/types/lib/WazoSessionDescriptionHandler.d.ts.map +1 -1
- package/esm/types/service/IssueReporter.d.ts +1 -0
- package/esm/types/service/IssueReporter.d.ts.map +1 -1
- package/esm/types/service/getApiClient.d.ts +2 -0
- package/esm/types/service/getApiClient.d.ts.map +1 -1
- package/esm/types/simple/Auth.d.ts.map +1 -1
- package/esm/types/simple/Phone.d.ts +4 -3
- package/esm/types/simple/Phone.d.ts.map +1 -1
- package/esm/types/simple/Websocket.d.ts.map +1 -1
- package/esm/types/simple/index.d.ts +1 -0
- package/esm/types/simple/index.d.ts.map +1 -1
- package/esm/types/utils/Emitter.d.ts +1 -1
- package/esm/types/utils/Heartbeat.d.ts +3 -2
- package/esm/types/utils/Heartbeat.d.ts.map +1 -1
- package/esm/types/utils/api-requester.d.ts +5 -1
- package/esm/types/utils/api-requester.d.ts.map +1 -1
- package/esm/types/web-rtc-client.d.ts +5 -29
- package/esm/types/web-rtc-client.d.ts.map +1 -1
- package/esm/types/websocket-client.d.ts +5 -1
- package/esm/types/websocket-client.d.ts.map +1 -1
- package/esm/utils/__tests__/api-requester.test.js +7 -2
- package/esm/utils/api-requester.js +23 -5
- package/esm/web-rtc-client.js +69 -40
- package/esm/websocket-client.js +9 -2
- package/lib/__tests__/api-client.test.js +7 -1
- package/lib/__tests__/web-rtc-client.test.js +16 -0
- package/lib/api/chatd.js +23 -7
- package/lib/api/dird.js +2 -2
- package/lib/api-client.js +4 -0
- package/lib/checker/checks/ice-ipv4.js +2 -1
- package/lib/domain/Contact.js +15 -8
- package/lib/domain/Phone/WebRTCPhone.js +9 -11
- package/lib/domain/Session.js +23 -0
- package/lib/domain/__tests__/Contact.test.js +175 -50
- package/lib/lib/WazoSessionDescriptionHandler.js +15 -12
- package/lib/service/IssueReporter.js +12 -2
- package/lib/service/__tests__/IssueReporter.test.js +12 -0
- package/lib/service/getApiClient.js +12 -1
- package/lib/simple/Auth.js +34 -4
- package/lib/simple/Phone.js +3 -1
- package/lib/simple/Websocket.js +1 -0
- package/lib/simple/room/Stream.js +3 -3
- package/lib/types/api/chatd.d.ts +1 -1
- package/lib/types/api/chatd.d.ts.map +1 -1
- package/lib/types/api/dird.d.ts.map +1 -1
- package/lib/types/api-client.d.ts +3 -2
- package/lib/types/api-client.d.ts.map +1 -1
- package/lib/types/checker/checks/ice-ipv4.d.ts.map +1 -1
- package/lib/types/domain/Contact.d.ts +1 -1
- package/lib/types/domain/Contact.d.ts.map +1 -1
- package/lib/types/domain/Phone/WebRTCPhone.d.ts +1 -1
- package/lib/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
- package/lib/types/domain/Session.d.ts +2 -0
- package/lib/types/domain/Session.d.ts.map +1 -1
- package/lib/types/domain/types.d.ts +40 -14
- package/lib/types/domain/types.d.ts.map +1 -1
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/lib/WazoSessionDescriptionHandler.d.ts +1 -1
- package/lib/types/lib/WazoSessionDescriptionHandler.d.ts.map +1 -1
- package/lib/types/service/IssueReporter.d.ts +1 -0
- package/lib/types/service/IssueReporter.d.ts.map +1 -1
- package/lib/types/service/getApiClient.d.ts +2 -0
- package/lib/types/service/getApiClient.d.ts.map +1 -1
- package/lib/types/simple/Auth.d.ts.map +1 -1
- package/lib/types/simple/Phone.d.ts +4 -3
- package/lib/types/simple/Phone.d.ts.map +1 -1
- package/lib/types/simple/Websocket.d.ts.map +1 -1
- package/lib/types/simple/index.d.ts +1 -0
- package/lib/types/simple/index.d.ts.map +1 -1
- package/lib/types/utils/Emitter.d.ts +1 -1
- package/lib/types/utils/Heartbeat.d.ts +3 -2
- package/lib/types/utils/Heartbeat.d.ts.map +1 -1
- package/lib/types/utils/api-requester.d.ts +5 -1
- package/lib/types/utils/api-requester.d.ts.map +1 -1
- package/lib/types/web-rtc-client.d.ts +5 -29
- package/lib/types/web-rtc-client.d.ts.map +1 -1
- package/lib/types/websocket-client.d.ts +5 -1
- package/lib/types/websocket-client.d.ts.map +1 -1
- package/lib/utils/__tests__/api-requester.test.js +7 -2
- package/lib/utils/api-requester.js +19 -5
- package/lib/web-rtc-client.js +67 -39
- package/lib/websocket-client.js +8 -2
- package/package.json +37 -35
- package/src/__tests__/api-client.test.ts +9 -1
- package/src/__tests__/web-rtc-client.test.ts +12 -0
- package/src/api/chatd.ts +22 -4
- package/src/api/dird.ts +3 -2
- package/src/api-client.ts +8 -2
- package/src/checker/checks/ice-ipv4.ts +2 -1
- package/src/domain/Contact.ts +16 -8
- package/src/domain/Phone/WebRTCPhone.ts +11 -12
- package/src/domain/Session.ts +28 -0
- package/src/domain/__tests__/Contact.test.ts +213 -54
- package/src/domain/types.ts +42 -14
- package/src/lib/WazoSessionDescriptionHandler.ts +15 -11
- package/src/service/IssueReporter.ts +13 -2
- package/src/service/__tests__/IssueReporter.test.ts +14 -0
- package/src/service/getApiClient.ts +12 -0
- package/src/simple/Auth.ts +58 -6
- package/src/simple/Phone.ts +15 -6
- package/src/simple/Websocket.ts +1 -0
- package/src/simple/room/Stream.ts +3 -3
- package/src/types/global.d.ts +1 -0
- package/src/utils/Heartbeat.ts +2 -2
- package/src/utils/__tests__/api-requester.test.ts +8 -2
- package/src/utils/api-requester.ts +31 -4
- package/src/web-rtc-client.ts +84 -70
- package/src/websocket-client.ts +14 -1
package/esm/web-rtc-client.js
CHANGED
|
@@ -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
|
|
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(
|
|
177
|
-
const
|
|
178
|
-
logger.info('sdk webrtc, creating UA', {
|
|
179
|
-
|
|
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(
|
|
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
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
|
|
716
|
-
|
|
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
|
-
|
|
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()
|
|
1121
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
-
...(
|
|
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
|
-
...(
|
|
1697
|
+
...(uaOptionsOverrides?.peerConnectionOptions || {}),
|
|
1686
1698
|
},
|
|
1687
1699
|
},
|
|
1688
1700
|
};
|
|
1689
|
-
|
|
1690
|
-
|
|
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 (
|
|
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
|
-
|
|
2110
|
-
|
|
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) {
|
package/esm/websocket-client.js
CHANGED
|
@@ -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
|
-
|
|
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/
|
|
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/chatd.js
CHANGED
|
@@ -16,6 +16,8 @@ const api_requester_1 = __importDefault(require("../utils/api-requester"));
|
|
|
16
16
|
const Profile_1 = __importDefault(require("../domain/Profile"));
|
|
17
17
|
const ChatRoom_1 = __importDefault(require("../domain/ChatRoom"));
|
|
18
18
|
const ChatMessage_1 = __importDefault(require("../domain/ChatMessage"));
|
|
19
|
+
// split contact status retrieval to avoid `414 Request-URI Too Large`.
|
|
20
|
+
const MAX_PRESENCE_FETCHED = 30;
|
|
19
21
|
exports.default = ((client, baseUrl) => ({
|
|
20
22
|
updateState: (contactUuid, state) => client.put(`${baseUrl}/users/${contactUuid}/presences`, {
|
|
21
23
|
state,
|
|
@@ -30,13 +32,27 @@ exports.default = ((client, baseUrl) => ({
|
|
|
30
32
|
getState: (contactUuid) => __awaiter(void 0, void 0, void 0, function* () { return client.get(`${baseUrl}/users/${contactUuid}/presences`).then((response) => response.state); }),
|
|
31
33
|
getContactStatusInfo: (contactUuid) => __awaiter(void 0, void 0, void 0, function* () { return client.get(`${baseUrl}/users/${contactUuid}/presences`).then((response) => response); }),
|
|
32
34
|
getLineState: (contactUuid) => __awaiter(void 0, void 0, void 0, function* () { return client.get(`${baseUrl}/users/${contactUuid}/presences`).then((response) => Profile_1.default.getLinesState(response.lines)); }),
|
|
33
|
-
getMultipleLineState
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
getMultipleLineState(contactUuids) {
|
|
36
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
37
|
+
const body = {};
|
|
38
|
+
const uuids = contactUuids || [];
|
|
39
|
+
if (uuids.length > MAX_PRESENCE_FETCHED) {
|
|
40
|
+
const requests = uuids.reduce((acc, _, i) => {
|
|
41
|
+
if (i % MAX_PRESENCE_FETCHED === 0) {
|
|
42
|
+
// @ts-ignore
|
|
43
|
+
acc.push(this.getMultipleLineState(uuids.slice(i, i + MAX_PRESENCE_FETCHED)));
|
|
44
|
+
}
|
|
45
|
+
return acc;
|
|
46
|
+
}, []);
|
|
47
|
+
const splittedStatuses = yield Promise.all(requests);
|
|
48
|
+
return splittedStatuses.flat();
|
|
49
|
+
}
|
|
50
|
+
if (uuids.length) {
|
|
51
|
+
body.user_uuid = uuids.join(',');
|
|
52
|
+
}
|
|
53
|
+
return client.get(`${baseUrl}/users/presences`, body).then((response) => response.items);
|
|
54
|
+
});
|
|
55
|
+
},
|
|
40
56
|
getUserRooms: () => __awaiter(void 0, void 0, void 0, function* () { return client.get(`${baseUrl}/users/me/rooms`).then(ChatRoom_1.default.parseMany); }),
|
|
41
57
|
createRoom: (name, users) => __awaiter(void 0, void 0, void 0, function* () {
|
|
42
58
|
return client.post(`${baseUrl}/users/me/rooms`, {
|
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
|
-
|
|
20
|
+
resolve('Skipped on node');
|
|
21
|
+
return;
|
|
21
22
|
}
|
|
22
23
|
const mobile = (0, isMobile_1.default)();
|
|
23
24
|
const offerOptions = {
|
package/lib/domain/Contact.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
146
|
+
name,
|
|
141
147
|
number: plain.phoneNumbers.length ? plain.phoneNumbers[0].number : '',
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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()
|
|
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([
|
|
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;
|
package/lib/domain/Session.js
CHANGED
|
@@ -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)) {
|