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