@wazo/sdk 0.37.0-rc.5 → 0.37.2
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 +5 -5
- package/esm/__tests__/api-client.test.js +93 -16
- package/esm/__tests__/web-rtc-client.test.js +153 -54
- package/esm/api/agentd.js +24 -3
- package/esm/api/amid.js +7 -1
- package/esm/api/application.js +24 -3
- package/esm/api/auth.js +24 -3
- package/esm/api/call-logd.js +78 -20
- package/esm/api/calld.js +24 -20
- package/esm/api/chatd.js +24 -3
- package/esm/api/confd.js +24 -3
- package/esm/api/ctid-ng.js +24 -3
- package/esm/api/dird.js +27 -6
- package/esm/api/webhookd.js +7 -1
- package/esm/api-client.js +93 -16
- package/esm/checker/Checker.js +156 -54
- package/esm/checker/checks/aor.js +95 -16
- package/esm/checker/checks/api.js +94 -16
- package/esm/checker/checks/index.js +156 -54
- package/esm/checker/checks/wazo-websocket.js +7 -1
- package/esm/checker/checks/webrtc-transport.js +153 -54
- package/esm/checker/checks/webrtc.js +153 -54
- package/esm/domain/AdHocAPIConference.js +1717 -1640
- package/esm/domain/CallLog.js +71 -19
- package/esm/domain/CallSession.js +1 -18
- package/esm/domain/Contact.js +3 -3
- package/esm/domain/Features.js +93 -16
- package/esm/domain/IndirectTransfer.js +0 -17
- package/esm/domain/Phone/CTIPhone.js +100 -19
- package/esm/domain/Phone/Phone.js +0 -17
- package/esm/domain/Phone/WebRTCPhone.js +51 -36
- package/esm/domain/Recording.js +51 -0
- package/esm/domain/Room.js +0 -17
- package/esm/domain/SwitchboardCall.js +0 -17
- package/esm/domain/__tests__/CallLog.test.js +89 -19
- package/esm/domain/__tests__/CallSession.test.js +1 -18
- package/esm/domain/__tests__/Contact.test.js +3 -3
- package/esm/domain/__tests__/IndirectTransfer.test.js +1 -18
- package/esm/domain/__tests__/Room.test.js +1 -18
- package/esm/domain/__tests__/Session.test.js +7 -0
- package/esm/index.js +3436 -2911
- package/esm/lib/WazoSessionDescriptionHandler.js +28 -10
- package/esm/service/CallApi.js +93 -16
- package/esm/service/IssueReporter.js +7 -1
- package/esm/service/__tests__/IssueReporter.test.js +26 -2
- package/esm/service/getApiClient.js +93 -16
- package/esm/simple/Auth.js +619 -97
- package/esm/simple/Configuration.js +619 -97
- package/esm/simple/Directory.js +93 -16
- package/esm/simple/Phone.js +3261 -2739
- package/esm/simple/Websocket.js +7 -1
- package/esm/simple/index.js +619 -97
- package/esm/simple/room/LocalParticipant.js +619 -97
- package/esm/simple/room/Participant.js +619 -97
- package/esm/simple/room/RemoteParticipant.js +619 -97
- package/esm/simple/room/Room.js +619 -97
- package/esm/simple/room/Stream.js +619 -97
- package/esm/simple/room/__tests__/RemoteParticipant.test.js +619 -97
- package/esm/simple/utils.js +619 -97
- package/esm/utils/__tests__/api-requester.test.js +32 -6
- package/esm/utils/api-requester.js +24 -3
- package/esm/web-rtc-client.js +153 -54
- package/esm/websocket-client.js +7 -1
- package/lib/__tests__/api-client.test.js +93 -16
- package/lib/__tests__/web-rtc-client.test.js +154 -55
- package/lib/api/agentd.js +24 -3
- package/lib/api/amid.js +7 -1
- package/lib/api/application.js +24 -3
- package/lib/api/auth.js +24 -3
- package/lib/api/call-logd.js +78 -20
- package/lib/api/calld.js +24 -20
- package/lib/api/chatd.js +24 -3
- package/lib/api/confd.js +24 -3
- package/lib/api/ctid-ng.js +24 -3
- package/lib/api/dird.js +27 -6
- package/lib/api/webhookd.js +7 -1
- package/lib/api-client.js +93 -16
- package/lib/checker/Checker.js +157 -55
- package/lib/checker/checks/aor.js +95 -16
- package/lib/checker/checks/api.js +94 -16
- package/lib/checker/checks/index.js +157 -55
- package/lib/checker/checks/wazo-websocket.js +7 -1
- package/lib/checker/checks/webrtc-transport.js +154 -55
- package/lib/checker/checks/webrtc.js +154 -55
- package/lib/domain/AdHocAPIConference.js +1717 -1640
- package/lib/domain/CallLog.js +71 -19
- package/lib/domain/CallSession.js +1 -18
- package/lib/domain/Contact.js +3 -3
- package/lib/domain/Features.js +93 -16
- package/lib/domain/IndirectTransfer.js +0 -17
- package/lib/domain/Phone/CTIPhone.js +100 -19
- package/lib/domain/Phone/Phone.js +0 -16
- package/lib/domain/Phone/WebRTCPhone.js +51 -36
- package/lib/domain/Recording.js +57 -0
- package/lib/domain/Room.js +0 -17
- package/lib/domain/SwitchboardCall.js +0 -17
- package/lib/domain/__tests__/CallLog.test.js +89 -19
- package/lib/domain/__tests__/CallSession.test.js +1 -18
- package/lib/domain/__tests__/Contact.test.js +3 -3
- package/lib/domain/__tests__/IndirectTransfer.test.js +1 -18
- package/lib/domain/__tests__/Room.test.js +1 -18
- package/lib/domain/__tests__/Session.test.js +7 -0
- package/lib/index.js +3437 -2912
- package/lib/lib/WazoSessionDescriptionHandler.js +28 -10
- package/lib/service/CallApi.js +93 -16
- package/lib/service/IssueReporter.js +7 -1
- package/lib/service/__tests__/IssueReporter.test.js +26 -2
- package/lib/service/getApiClient.js +93 -16
- package/lib/simple/Auth.js +620 -98
- package/lib/simple/Configuration.js +620 -98
- package/lib/simple/Directory.js +93 -16
- package/lib/simple/Phone.js +3262 -2740
- package/lib/simple/Websocket.js +7 -1
- package/lib/simple/index.js +620 -98
- package/lib/simple/room/LocalParticipant.js +620 -98
- package/lib/simple/room/Participant.js +620 -98
- package/lib/simple/room/RemoteParticipant.js +620 -98
- package/lib/simple/room/Room.js +620 -98
- package/lib/simple/room/Stream.js +620 -98
- package/lib/simple/room/__tests__/RemoteParticipant.test.js +620 -98
- package/lib/simple/utils.js +620 -98
- package/lib/utils/__tests__/api-requester.test.js +32 -6
- package/lib/utils/api-requester.js +24 -3
- package/lib/web-rtc-client.js +154 -55
- package/lib/websocket-client.js +7 -1
- package/package.json +1 -1
- package/src/api-client.js +4 -0
- package/src/checker/checks/aor.js +2 -0
- package/src/checker/checks/api.js +1 -0
- package/src/domain/CallLog.js +18 -11
- package/src/domain/CallSession.js +1 -2
- package/src/domain/Contact.js +3 -3
- package/src/domain/Phone/CTIPhone.js +6 -2
- package/src/domain/Phone/Phone.js +2 -0
- package/src/domain/Phone/WebRTCPhone.js +26 -16
- package/src/domain/Recording.js +48 -0
- package/src/domain/__tests__/CallLog.test.js +19 -0
- package/src/domain/__tests__/Session.test.js +7 -0
- package/src/index.js +6 -0
- package/src/lib/WazoSessionDescriptionHandler.js +21 -9
- package/src/service/IssueReporter.js +7 -1
- package/src/service/__tests__/IssueReporter.test.js +20 -1
- package/src/simple/Phone.js +5 -1
- package/src/simple/index.js +6 -0
- package/src/simple/room/Room.js +18 -15
- package/src/simple/room/Stream.js +1 -4
- package/src/utils/__tests__/api-requester.test.js +9 -4
- package/src/utils/api-requester.js +17 -2
- package/src/web-rtc-client.js +40 -13
|
@@ -5,6 +5,8 @@ export default {
|
|
|
5
5
|
check: async (server, session) => {
|
|
6
6
|
const apiClient = new WazoApiClient({ server });
|
|
7
7
|
apiClient.setToken(session.token);
|
|
8
|
+
apiClient.disableErrorLogging();
|
|
9
|
+
|
|
8
10
|
const line = session.primaryWebRtcLine();
|
|
9
11
|
if (!line) {
|
|
10
12
|
return 'No WebRTC line for this user';
|
|
@@ -6,6 +6,7 @@ export default {
|
|
|
6
6
|
check: async (server, session) => {
|
|
7
7
|
const client = new WazoApiClient({ server });
|
|
8
8
|
client.setToken(session.token);
|
|
9
|
+
client.disableErrorLogging();
|
|
9
10
|
|
|
10
11
|
const handleApiError = (apiName, error) => {
|
|
11
12
|
const statusText = error instanceof ServerError ? 'server error' : 'api error';
|
package/src/domain/CallLog.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import moment from 'moment';
|
|
3
3
|
|
|
4
|
-
import Session from './Session';
|
|
5
4
|
import newFrom from '../utils/new-from';
|
|
6
5
|
|
|
6
|
+
import Session from './Session';
|
|
7
|
+
import type { RecordingResponse } from './Recording';
|
|
8
|
+
import Recording from './Recording';
|
|
9
|
+
|
|
7
10
|
type CallLogResponse = {
|
|
8
|
-
answer: string,
|
|
11
|
+
answer: ?string,
|
|
9
12
|
answered: boolean,
|
|
10
13
|
call_direction: string,
|
|
11
14
|
destination_extension: string,
|
|
12
15
|
destination_name: ?string,
|
|
13
16
|
duration: number,
|
|
14
|
-
end: string,
|
|
17
|
+
end: ?string,
|
|
15
18
|
id: number,
|
|
16
19
|
source_extension: string,
|
|
17
20
|
source_name: string,
|
|
21
|
+
recordings: RecordingResponse[],
|
|
18
22
|
requested_extension: string,
|
|
19
23
|
requested_name: string,
|
|
20
24
|
start: string,
|
|
@@ -27,7 +31,7 @@ type Response = {
|
|
|
27
31
|
};
|
|
28
32
|
|
|
29
33
|
type CallLogArguments = {
|
|
30
|
-
answer: Date,
|
|
34
|
+
answer: ?Date,
|
|
31
35
|
answered: boolean,
|
|
32
36
|
newMissedCall?: boolean,
|
|
33
37
|
callDirection: string,
|
|
@@ -42,13 +46,14 @@ type CallLogArguments = {
|
|
|
42
46
|
id: number,
|
|
43
47
|
duration: number,
|
|
44
48
|
start: Date,
|
|
45
|
-
end: Date,
|
|
49
|
+
end: ?Date,
|
|
50
|
+
recordings: Recording[],
|
|
46
51
|
};
|
|
47
52
|
|
|
48
53
|
export default class CallLog {
|
|
49
54
|
type: string;
|
|
50
55
|
|
|
51
|
-
answer: Date;
|
|
56
|
+
answer: ?Date;
|
|
52
57
|
answered: boolean;
|
|
53
58
|
newMissedCall: boolean;
|
|
54
59
|
callDirection: string;
|
|
@@ -65,7 +70,7 @@ export default class CallLog {
|
|
|
65
70
|
id: number;
|
|
66
71
|
duration: number;
|
|
67
72
|
start: Date;
|
|
68
|
-
end: Date;
|
|
73
|
+
end: ?Date;
|
|
69
74
|
|
|
70
75
|
static merge(current: Array<CallLog>, toMerge: Array<CallLog>): Array<?CallLog> {
|
|
71
76
|
const onlyUnique = (value, index, self) => self.indexOf(value) === index;
|
|
@@ -82,7 +87,7 @@ export default class CallLog {
|
|
|
82
87
|
|
|
83
88
|
static parse(plain: CallLogResponse): CallLog {
|
|
84
89
|
return new CallLog({
|
|
85
|
-
answer: moment(plain.answer).toDate(),
|
|
90
|
+
answer: plain.answer ? moment(plain.answer).toDate() : null,
|
|
86
91
|
answered: plain.answered,
|
|
87
92
|
callDirection: plain.call_direction,
|
|
88
93
|
destination: {
|
|
@@ -96,13 +101,14 @@ export default class CallLog {
|
|
|
96
101
|
id: plain.id,
|
|
97
102
|
duration: (plain.duration || 0) * 1000, // duration is in seconds
|
|
98
103
|
start: moment(plain.start).toDate(),
|
|
99
|
-
end: moment(plain.end).toDate(),
|
|
104
|
+
end: plain.end ? moment(plain.end).toDate() : null,
|
|
105
|
+
recordings: Recording.parseMany(plain.recordings),
|
|
100
106
|
});
|
|
101
107
|
}
|
|
102
108
|
|
|
103
109
|
static parseNew(plain: CallLogResponse, session: Session): CallLog {
|
|
104
110
|
return new CallLog({
|
|
105
|
-
answer: moment(plain.answer).toDate(),
|
|
111
|
+
answer: plain.answer ? moment(plain.answer).toDate() : null,
|
|
106
112
|
answered: plain.answered,
|
|
107
113
|
callDirection: plain.call_direction,
|
|
108
114
|
destination: {
|
|
@@ -116,7 +122,8 @@ export default class CallLog {
|
|
|
116
122
|
id: plain.id,
|
|
117
123
|
duration: (plain.duration || 0) * 1000, // duration is in seconds
|
|
118
124
|
start: moment(plain.start).toDate(),
|
|
119
|
-
end: moment(plain.end).toDate(),
|
|
125
|
+
end: plain.end ? moment(plain.end).toDate() : null,
|
|
126
|
+
recordings: Recording.parseMany(plain.recordings),
|
|
120
127
|
// @TODO: FIXME add verification declined vs missed call
|
|
121
128
|
newMissedCall: session
|
|
122
129
|
&& session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered,
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { SessionState } from 'sip.js/lib/api/session-state';
|
|
3
3
|
|
|
4
4
|
import Call from './Call';
|
|
5
|
-
import Session from './Session';
|
|
6
5
|
import newFrom from '../utils/new-from';
|
|
7
6
|
import updateFrom from '../utils/update-from';
|
|
8
7
|
|
|
@@ -311,7 +310,7 @@ export default class CallSession {
|
|
|
311
310
|
return newFrom(callSession, CallSession);
|
|
312
311
|
}
|
|
313
312
|
|
|
314
|
-
static parseCall(
|
|
313
|
+
static parseCall(call: Call): CallSession {
|
|
315
314
|
return new CallSession({
|
|
316
315
|
callId: call.id,
|
|
317
316
|
sipCallId: call.sipCallId,
|
package/src/domain/Contact.js
CHANGED
|
@@ -420,7 +420,7 @@ export default class Contact {
|
|
|
420
420
|
|
|
421
421
|
return new Contact({
|
|
422
422
|
sourceId: single.id || '',
|
|
423
|
-
name: single.displayName,
|
|
423
|
+
name: single.displayName || '',
|
|
424
424
|
number: numbers.length ? numbers[0].number : '',
|
|
425
425
|
numbers,
|
|
426
426
|
emails,
|
|
@@ -451,7 +451,7 @@ export default class Contact {
|
|
|
451
451
|
|
|
452
452
|
return new Contact({
|
|
453
453
|
sourceId: single.id || '',
|
|
454
|
-
name: single.name,
|
|
454
|
+
name: single.name || '',
|
|
455
455
|
number: numbers.length ? numbers[0].number : '',
|
|
456
456
|
numbers,
|
|
457
457
|
emails,
|
|
@@ -552,7 +552,7 @@ export default class Contact {
|
|
|
552
552
|
}: ContactArguments = {}) {
|
|
553
553
|
this.id = id;
|
|
554
554
|
this.uuid = uuid;
|
|
555
|
-
this.name = name;
|
|
555
|
+
this.name = name || '';
|
|
556
556
|
this.number = number;
|
|
557
557
|
this.numbers = numbers;
|
|
558
558
|
this.email = email;
|
|
@@ -102,7 +102,7 @@ export default class CTIPhone extends Emitter implements Phone {
|
|
|
102
102
|
if (!this.currentCall) {
|
|
103
103
|
return null;
|
|
104
104
|
}
|
|
105
|
-
const callSession = CallSession.parseCall(this.
|
|
105
|
+
const callSession = CallSession.parseCall(this.currentCall);
|
|
106
106
|
this.eventEmitter.emit('onCallOutgoing', callSession);
|
|
107
107
|
|
|
108
108
|
return callSession;
|
|
@@ -286,6 +286,10 @@ export default class CTIPhone extends Emitter implements Phone {
|
|
|
286
286
|
return null;
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
+
getLocalVideoStream(): ?MediaStream {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
|
|
289
293
|
setActiveSipSession() {}
|
|
290
294
|
|
|
291
295
|
isRegistered(): boolean {
|
|
@@ -301,7 +305,7 @@ export default class CTIPhone extends Emitter implements Phone {
|
|
|
301
305
|
}
|
|
302
306
|
|
|
303
307
|
getCurrentCallSession(): ?CallSession {
|
|
304
|
-
return this.currentCall ? CallSession.parseCall(this.
|
|
308
|
+
return this.currentCall ? CallSession.parseCall(this.currentCall) : null;
|
|
305
309
|
}
|
|
306
310
|
|
|
307
311
|
enableRinging() {}
|
|
@@ -56,6 +56,8 @@ export interface Phone {
|
|
|
56
56
|
|
|
57
57
|
getLocalStreamForCall(callSession: CallSession): ?MediaStream;
|
|
58
58
|
|
|
59
|
+
getLocalVideoStream(callSession: CallSession): ?MediaStream;
|
|
60
|
+
|
|
59
61
|
getOptions(): AvailablePhoneOptions;
|
|
60
62
|
|
|
61
63
|
getRemoteStreamForCall(callSession: CallSession): ?MediaStream;
|
|
@@ -190,9 +190,14 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
190
190
|
return true;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
async sendReinvite(callSession: ?CallSession, constraints: ?Object = null, conference: boolean = false
|
|
193
|
+
async sendReinvite(callSession: ?CallSession, constraints: ?Object = null, conference: boolean = false,
|
|
194
|
+
audioOnly: boolean = false) {
|
|
194
195
|
const sipSession = this.findSipSession(callSession);
|
|
195
|
-
logger.info('WebRTC phone - send reinvite', {
|
|
196
|
+
logger.info('WebRTC phone - send reinvite', {
|
|
197
|
+
sessionId: sipSession ? sipSession.id : null,
|
|
198
|
+
constraints,
|
|
199
|
+
audioOnly,
|
|
200
|
+
});
|
|
196
201
|
|
|
197
202
|
if (!sipSession) {
|
|
198
203
|
return null;
|
|
@@ -218,7 +223,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
218
223
|
// Force reinvite in SDH
|
|
219
224
|
sipSession.sessionDescriptionHandler.localMediaStreamConstraints = null;
|
|
220
225
|
|
|
221
|
-
return this.client.reinvite(sipSession, constraints, conference);
|
|
226
|
+
return this.client.reinvite(sipSession, constraints, conference, audioOnly);
|
|
222
227
|
}
|
|
223
228
|
|
|
224
229
|
getUserAgent() {
|
|
@@ -294,9 +299,9 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
294
299
|
}
|
|
295
300
|
|
|
296
301
|
// Release local video stream when downgrading to audio
|
|
297
|
-
const
|
|
302
|
+
const localStream = sipSession.sessionDescriptionHandler.localMediaStream;
|
|
298
303
|
const pc = sipSession.sessionDescriptionHandler.peerConnection;
|
|
299
|
-
const videoTracks =
|
|
304
|
+
const videoTracks = localStream.getVideoTracks();
|
|
300
305
|
|
|
301
306
|
// Remove video senders
|
|
302
307
|
pc.getSenders().filter(sender => sender.track && sender.track.kind === 'video').forEach(videoSender => {
|
|
@@ -311,7 +316,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
311
316
|
videoTracks.forEach(videoTrack => {
|
|
312
317
|
videoTrack.enabled = false;
|
|
313
318
|
videoTrack.stop();
|
|
314
|
-
|
|
319
|
+
localStream.removeTrack(videoTrack);
|
|
315
320
|
});
|
|
316
321
|
|
|
317
322
|
this._sendReinviteMessage(callSession, false);
|
|
@@ -328,12 +333,14 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
328
333
|
|
|
329
334
|
const pc = sipSession.sessionDescriptionHandler.peerConnection;
|
|
330
335
|
const shouldScreenShare = constraints && constraints.screen;
|
|
336
|
+
const options = sipSession.sessionDescriptionHandlerOptionsReInvite;
|
|
337
|
+
const wasAudioOnly = options && options.audioOnly;
|
|
331
338
|
|
|
332
339
|
// Check if a video sender already exists
|
|
333
340
|
let videoSender;
|
|
334
341
|
if (isConference) {
|
|
335
342
|
const trans = pc.getTransceivers().find(transceiver => transceiver.mid === '1');
|
|
336
|
-
videoSender = trans.sender;
|
|
343
|
+
videoSender = trans ? trans.sender : null;
|
|
337
344
|
} else {
|
|
338
345
|
videoSender = pc.getSenders().find(sender => sender.track === null);
|
|
339
346
|
}
|
|
@@ -370,6 +377,11 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
370
377
|
this._onScreenSharing(newStream, sipSession, callSession, false);
|
|
371
378
|
}
|
|
372
379
|
|
|
380
|
+
// We have to reinvite to change the direction on the bundle when upgrading from an audioOnly conference
|
|
381
|
+
if (isConference && wasAudioOnly) {
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
|
|
373
385
|
// No reinvite needed here
|
|
374
386
|
return false;
|
|
375
387
|
}
|
|
@@ -456,7 +468,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
456
468
|
event.track.enabled = false;
|
|
457
469
|
}
|
|
458
470
|
|
|
459
|
-
return this.eventEmitter.emit(ON_VIDEO_STREAM, stream, event.track.id, event);
|
|
471
|
+
return this.eventEmitter.emit(ON_VIDEO_STREAM, stream, event.track.id, event, sipSession);
|
|
460
472
|
};
|
|
461
473
|
|
|
462
474
|
peerConnection.onremovestream = event => {
|
|
@@ -512,14 +524,11 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
512
524
|
const targetCallSession = callSession || this.currentCallSession;
|
|
513
525
|
const conference = this.isConference(targetCallSession);
|
|
514
526
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
// We have to downgrade to audio.
|
|
521
|
-
await this.sendReinvite(targetCallSession, { audio: false, video: false }, conference);
|
|
522
|
-
}
|
|
527
|
+
// When stopping screenshare and we had video before that, we have to re-upgrade
|
|
528
|
+
// When upgrading directly to screenshare (eg: we don't have a videoLocalStream to replace)
|
|
529
|
+
// We have to downgrade to audio.
|
|
530
|
+
const screenshareStopped = this.currentScreenShare.sender && this.currentScreenShare.hadVideo;
|
|
531
|
+
await this.sendReinvite(targetCallSession, { audio: false, video: screenshareStopped }, conference);
|
|
523
532
|
}
|
|
524
533
|
} catch (e) {
|
|
525
534
|
console.warn(e);
|
|
@@ -856,6 +865,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
856
865
|
}
|
|
857
866
|
}
|
|
858
867
|
|
|
868
|
+
// @Deprecated
|
|
859
869
|
unhold(callSession: CallSession, withEvent: boolean = true, isConference: boolean = false): void {
|
|
860
870
|
logger.info('WebRTC unhold', { id: callSession ? callSession.getId() : null });
|
|
861
871
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import moment from 'moment';
|
|
3
|
+
|
|
4
|
+
export type RecordingResponse = {
|
|
5
|
+
deleted: boolean,
|
|
6
|
+
end_time: ?string,
|
|
7
|
+
filename: string,
|
|
8
|
+
start_time: string,
|
|
9
|
+
uuid: string,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type RecordingArguments = {
|
|
13
|
+
deleted: boolean,
|
|
14
|
+
end: ?Date,
|
|
15
|
+
fileName: string,
|
|
16
|
+
start: Date,
|
|
17
|
+
uuid: string,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default class Recording {
|
|
21
|
+
deleted: boolean;
|
|
22
|
+
end: ?Date;
|
|
23
|
+
fileName: string;
|
|
24
|
+
start: Date;
|
|
25
|
+
uuid: string;
|
|
26
|
+
|
|
27
|
+
static parseMany(recordings: RecordingResponse[]): Recording[] {
|
|
28
|
+
return recordings.map(item => Recording.parse(item));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static parse(plain: RecordingResponse): Recording {
|
|
32
|
+
return new Recording({
|
|
33
|
+
deleted: plain.deleted,
|
|
34
|
+
fileName: plain.filename,
|
|
35
|
+
end: plain.end_time ? moment(plain.end_time).toDate() : null,
|
|
36
|
+
start: moment(plain.start_time).toDate(),
|
|
37
|
+
uuid: plain.uuid,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
constructor({ deleted, end, fileName, start, uuid }: RecordingArguments) {
|
|
42
|
+
this.deleted = deleted;
|
|
43
|
+
this.end = end;
|
|
44
|
+
this.fileName = fileName;
|
|
45
|
+
this.start = start;
|
|
46
|
+
this.uuid = uuid;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import moment from 'moment';
|
|
3
3
|
|
|
4
4
|
import CallLog from '../CallLog';
|
|
5
|
+
import Recording from '../Recording';
|
|
5
6
|
|
|
6
7
|
describe('CallLog domain', () => {
|
|
7
8
|
it('can parse a plain call log to domain', () => {
|
|
@@ -22,6 +23,15 @@ describe('CallLog domain', () => {
|
|
|
22
23
|
requested_extension: '',
|
|
23
24
|
requested_name: '',
|
|
24
25
|
id: 215,
|
|
26
|
+
recordings: [
|
|
27
|
+
{
|
|
28
|
+
deleted: false,
|
|
29
|
+
end_time: '2021-08-11T13:22:50.519733+00:00',
|
|
30
|
+
filename: '2021-08-11T13_22_45UTC-61886-xxxx-5c72-4072-8503-e3ad9a41c0a0.wav',
|
|
31
|
+
start_time: '2021-08-11T13:22:45.161954+00:00',
|
|
32
|
+
uuid: '0394ad6f-5c72-4072-8503-e3ad9a41c0a0',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
25
35
|
},
|
|
26
36
|
],
|
|
27
37
|
total: 233,
|
|
@@ -46,6 +56,15 @@ describe('CallLog domain', () => {
|
|
|
46
56
|
duration: 4000,
|
|
47
57
|
start: moment('2017-08-06T13:50:55.819057+00:00').toDate(),
|
|
48
58
|
end: moment('2017-08-08T13:51:23.822529+00:00').toDate(),
|
|
59
|
+
recordings: [
|
|
60
|
+
new Recording({
|
|
61
|
+
deleted: false,
|
|
62
|
+
end: moment('2021-08-11T13:22:50.519733+00:00').toDate(),
|
|
63
|
+
fileName: '2021-08-11T13_22_45UTC-61886-xxxx-5c72-4072-8503-e3ad9a41c0a0.wav',
|
|
64
|
+
start: moment('2021-08-11T13:22:45.161954+00:00').toDate(),
|
|
65
|
+
uuid: '0394ad6f-5c72-4072-8503-e3ad9a41c0a0',
|
|
66
|
+
}),
|
|
67
|
+
],
|
|
49
68
|
}),
|
|
50
69
|
]);
|
|
51
70
|
});
|
|
@@ -394,7 +394,14 @@ describe('Session domain', () => {
|
|
|
394
394
|
const acl = ['calld.lines.*.presences.read'];
|
|
395
395
|
const session = new Session({ acl, expiresAt: new Date(), token: 'abc' });
|
|
396
396
|
|
|
397
|
+
const oldWarn = console.warn;
|
|
398
|
+
// $FlowFixMe
|
|
399
|
+
console.warn = () => {};
|
|
400
|
+
|
|
397
401
|
expect(session.acls).toEqual(acl);
|
|
398
402
|
expect(session.acl).toEqual(acl);
|
|
403
|
+
|
|
404
|
+
// $FlowFixMe
|
|
405
|
+
console.warn = oldWarn;
|
|
399
406
|
});
|
|
400
407
|
});
|
package/src/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import BadResponse from './domain/BadResponse';
|
|
|
11
11
|
import ServerError from './domain/ServerError';
|
|
12
12
|
import Call from './domain/Call';
|
|
13
13
|
import CallLog from './domain/CallLog';
|
|
14
|
+
import Recording from './domain/Recording';
|
|
14
15
|
import ChatMessage from './domain/ChatMessage';
|
|
15
16
|
import ChatRoom from './domain/ChatRoom';
|
|
16
17
|
import Contact from './domain/Contact';
|
|
@@ -27,6 +28,8 @@ import Room from './domain/Room';
|
|
|
27
28
|
import CallSession from './domain/CallSession';
|
|
28
29
|
import IndirectTransfer from './domain/IndirectTransfer';
|
|
29
30
|
import SwitchboardCall from './domain/SwitchboardCall';
|
|
31
|
+
import WebRTCPhone from './domain/Phone/WebRTCPhone';
|
|
32
|
+
import CTIPhone from './domain/Phone/CTIPhone';
|
|
30
33
|
import type {
|
|
31
34
|
NewContact as NewContactType,
|
|
32
35
|
ContactResponse as ContactResponseType,
|
|
@@ -93,10 +96,12 @@ export default {
|
|
|
93
96
|
ServerError,
|
|
94
97
|
Call,
|
|
95
98
|
CallSession,
|
|
99
|
+
CTIPhone,
|
|
96
100
|
Features,
|
|
97
101
|
IndirectTransfer,
|
|
98
102
|
SwitchboardCall,
|
|
99
103
|
CallLog,
|
|
104
|
+
Recording,
|
|
100
105
|
ChatMessage,
|
|
101
106
|
ChatRoom,
|
|
102
107
|
Contact,
|
|
@@ -116,4 +121,5 @@ export default {
|
|
|
116
121
|
LINE_STATE,
|
|
117
122
|
SOCKET_EVENTS,
|
|
118
123
|
Wazo,
|
|
124
|
+
WebRTCPhone,
|
|
119
125
|
};
|
|
@@ -84,6 +84,7 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
|
|
|
84
84
|
const iceRestart = options.offerOptions ? options.offerOptions.iceRestart : false;
|
|
85
85
|
|
|
86
86
|
const isConference = options ? !!options.conference : false;
|
|
87
|
+
const audioOnly = options ? !!options.audioOnly : false;
|
|
87
88
|
|
|
88
89
|
// We should wait for ice when iceRestart (reinvite) or for the first invite
|
|
89
90
|
// We shouldn't wait for ice when holding or resuming the call
|
|
@@ -110,16 +111,16 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
|
|
|
110
111
|
};
|
|
111
112
|
|
|
112
113
|
return this.getLocalMediaStream(options)
|
|
113
|
-
.then(() => this.updateDirection(options, isConference))
|
|
114
|
+
.then(() => this.updateDirection(options, isConference, audioOnly))
|
|
114
115
|
.then(() => this.createDataChannel(options))
|
|
115
116
|
.then(() => {
|
|
116
|
-
if (isConference && (options.constraints && !options.constraints.video) && !('hold' in options)) {
|
|
117
|
+
if (isConference && (options.constraints && !options.constraints.video) && !('hold' in options) && !audioOnly) {
|
|
117
118
|
// Add a video an empty bundle to be able to replaceTrack when joining a conference without video
|
|
118
119
|
this.peerConnection.addTransceiver('video', { streams: [this._localMediaStream], direction: 'sendrecv' });
|
|
119
120
|
}
|
|
120
121
|
})
|
|
121
122
|
.then(() => this.createLocalOfferOrAnswer(options))
|
|
122
|
-
.then(
|
|
123
|
+
.then(sessionDescription => this.setLocalSessionDescription(sessionDescription))
|
|
123
124
|
.then(() => this.waitForIceGatheringComplete(iceRestart, iceTimeout))
|
|
124
125
|
.then(() => this.getLocalSessionDescription())
|
|
125
126
|
.then(description => {
|
|
@@ -242,7 +243,8 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
|
|
|
242
243
|
}
|
|
243
244
|
|
|
244
245
|
// Overridden to send `inactive` in conference
|
|
245
|
-
updateDirection(options?: SessionDescriptionHandlerOptions, isConference: boolean = false
|
|
246
|
+
updateDirection(options?: SessionDescriptionHandlerOptions, isConference: boolean = false,
|
|
247
|
+
audioOnly: boolean = false): Promise<void> {
|
|
246
248
|
if (this._peerConnection === undefined) {
|
|
247
249
|
return Promise.reject(new Error('Peer connection closed.'));
|
|
248
250
|
}
|
|
@@ -257,14 +259,17 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
|
|
|
257
259
|
// if we are stable, assume we are creating a local offer
|
|
258
260
|
this.logger.debug('SessionDescriptionHandler.updateDirection - setting offer direction');
|
|
259
261
|
// determine the direction to offer given the current direction and hold state
|
|
260
|
-
const directionToOffer = (currentDirection: Object): Object => {
|
|
262
|
+
const directionToOffer = (currentDirection: string, transceiver: Object /* RTCRtpTransceiver */): Object => {
|
|
261
263
|
if (isConference) {
|
|
264
|
+
if (audioOnly && transceiver.receiver.track && transceiver.receiver.track.kind === 'video') {
|
|
265
|
+
return 'sendonly';
|
|
266
|
+
}
|
|
262
267
|
return options && options.hold ? 'sendonly' : 'sendrecv';
|
|
263
268
|
}
|
|
264
269
|
|
|
265
270
|
switch (currentDirection) {
|
|
266
271
|
case 'inactive':
|
|
267
|
-
return options && options.hold ? 'inactive' : 'recvonly';
|
|
272
|
+
return options && options.hold ? 'inactive' : (isConference && !audioOnly ? 'sendrecv' : 'recvonly');
|
|
268
273
|
case 'recvonly':
|
|
269
274
|
return options && options.hold ? 'inactive' : 'recvonly';
|
|
270
275
|
case 'sendonly':
|
|
@@ -280,7 +285,7 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
|
|
|
280
285
|
// set the transceiver direction to the offer direction
|
|
281
286
|
this._peerConnection.getTransceivers().forEach((transceiver) => {
|
|
282
287
|
if (transceiver.direction /* guarding, but should always be true */) {
|
|
283
|
-
const offerDirection = directionToOffer(transceiver.direction);
|
|
288
|
+
const offerDirection = directionToOffer(transceiver.direction, transceiver);
|
|
284
289
|
if (transceiver.direction !== offerDirection) {
|
|
285
290
|
// eslint-disable-next-line no-param-reassign
|
|
286
291
|
transceiver.direction = offerDirection;
|
|
@@ -339,6 +344,13 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
|
|
|
339
344
|
// set the transceiver direction to the answer direction
|
|
340
345
|
this._peerConnection.getTransceivers().forEach((transceiver) => {
|
|
341
346
|
if (transceiver.direction /* guarding, but should always be true */) {
|
|
347
|
+
const { receiver } = transceiver;
|
|
348
|
+
if (isConference && audioOnly && receiver.track && receiver.track.kind === 'video') {
|
|
349
|
+
// eslint-disable-next-line no-param-reassign
|
|
350
|
+
transceiver.direction = 'inactive';
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
342
354
|
if (transceiver.direction !== 'stopped' && transceiver.direction !== answerDirection) {
|
|
343
355
|
// eslint-disable-next-line no-param-reassign
|
|
344
356
|
transceiver.direction = answerDirection;
|
|
@@ -377,8 +389,8 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
|
|
|
377
389
|
}
|
|
378
390
|
const sender = pc.getSenders().find((otherSender) => otherSender.track && otherSender.track.kind === kind);
|
|
379
391
|
|
|
380
|
-
// Do not reuse sender tracks in SFU
|
|
381
|
-
if (sender && !sfu) {
|
|
392
|
+
// Do not reuse sender video tracks in SFU
|
|
393
|
+
if (sender && (!sfu || newTrack.kind === 'audio')) {
|
|
382
394
|
trackUpdates.push(
|
|
383
395
|
new Promise((resolve) => {
|
|
384
396
|
this.logger.debug(`SessionDescriptionHandler.setLocalMediaStream - replacing sender ${kind} track`);
|
|
@@ -243,7 +243,13 @@ class IssueReporter {
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
_isLevelAbove(level1: string, level2: string) {
|
|
246
|
-
|
|
246
|
+
const index1 = LOG_LEVELS.indexOf(level1);
|
|
247
|
+
const index2 = LOG_LEVELS.indexOf(level2);
|
|
248
|
+
if (index1 === -1 || index2 === -1) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return index1 > index2;
|
|
247
253
|
}
|
|
248
254
|
|
|
249
255
|
_makeCategory(category: string) {
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
/* eslint-disable no-underscore-dangle */
|
|
2
2
|
/* eslint-disable no-useless-escape */
|
|
3
|
+
/* eslint-disable no-console */
|
|
3
4
|
import IssueReporter from '../IssueReporter';
|
|
4
5
|
import { realFetch } from '../../utils/api-requester';
|
|
5
6
|
|
|
6
7
|
jest.mock('../../utils/api-requester');
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
let oldLog;
|
|
10
|
+
let oldError;
|
|
9
11
|
|
|
10
12
|
class MyError extends Error {}
|
|
11
13
|
|
|
12
14
|
describe('IssueReporter', () => {
|
|
15
|
+
beforeAll(() => {
|
|
16
|
+
oldLog = console.log;
|
|
17
|
+
oldError = console.error;
|
|
18
|
+
|
|
19
|
+
console.log = () => {};
|
|
20
|
+
console.error = () => {};
|
|
21
|
+
|
|
22
|
+
IssueReporter.enable();
|
|
23
|
+
});
|
|
24
|
+
|
|
13
25
|
beforeEach(() => {
|
|
14
26
|
jest.resetAllMocks();
|
|
15
27
|
|
|
@@ -18,12 +30,19 @@ describe('IssueReporter', () => {
|
|
|
18
30
|
}));
|
|
19
31
|
});
|
|
20
32
|
|
|
33
|
+
afterAll(() => {
|
|
34
|
+
console.log = oldLog;
|
|
35
|
+
console.error = oldError;
|
|
36
|
+
});
|
|
37
|
+
|
|
21
38
|
it('should compute level order', () => {
|
|
22
39
|
expect(IssueReporter._isLevelAbove('info', 'trace')).toBeTruthy();
|
|
23
40
|
expect(IssueReporter._isLevelAbove('trace', 'warn')).toBeFalsy();
|
|
24
41
|
expect(IssueReporter._isLevelAbove('error', 'warn')).toBeTruthy();
|
|
25
42
|
expect(IssueReporter._isLevelAbove('info', 'warn')).toBeFalsy();
|
|
26
43
|
expect(IssueReporter._isLevelAbove('trace', 'trace')).toBeFalsy();
|
|
44
|
+
expect(IssueReporter._isLevelAbove('trace', 'something')).toBeFalsy();
|
|
45
|
+
expect(IssueReporter._isLevelAbove('something', 'trace')).toBeFalsy();
|
|
27
46
|
});
|
|
28
47
|
|
|
29
48
|
it('should send if verbosity is higher than required', () => {
|
package/src/simple/Phone.js
CHANGED
|
@@ -283,6 +283,7 @@ class Phone extends Emitter {
|
|
|
283
283
|
return this.phone && this.phone.hasLocalVideo(callSession);
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
+
// @Deprecated
|
|
286
287
|
getLocalMediaStream(callSession: CallSession) {
|
|
287
288
|
logger.warn('Phone.getLocalMediaStream is deprecated, use Phone.getLocalStream instead');
|
|
288
289
|
|
|
@@ -301,17 +302,20 @@ class Phone extends Emitter {
|
|
|
301
302
|
return this.phone ? this.phone.getRemoteVideoStream(callSession) : null;
|
|
302
303
|
}
|
|
303
304
|
|
|
305
|
+
// @Deprecated
|
|
304
306
|
getRemoteStreamForCall(callSession: CallSession) {
|
|
305
307
|
logger.warn('Phone.getRemoteStreamForCall is deprecated, use Phone.getRemoteStream instead');
|
|
306
308
|
return this.getRemoteStream(callSession);
|
|
307
309
|
}
|
|
308
310
|
|
|
309
311
|
// Returns remote streams directly from the peerConnection
|
|
312
|
+
// @Deprecated
|
|
310
313
|
getRemoteStreamsForCall(callSession: CallSession) {
|
|
311
|
-
logger.warn('Phone.
|
|
314
|
+
logger.warn('Phone.getRemoteStreamsForCall is deprecated, use Phone.getLocalStream instead');
|
|
312
315
|
return this.getLocalStream(callSession);
|
|
313
316
|
}
|
|
314
317
|
|
|
318
|
+
// @Deprecated
|
|
315
319
|
getRemoteVideoStreamForCall(callSession: CallSession) {
|
|
316
320
|
logger.warn('Phone.getRemoteVideoStreamForCall is deprecated, use Phone.getRemoteVideoStream instead');
|
|
317
321
|
|
package/src/simple/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import BadResponse from '../domain/BadResponse';
|
|
|
16
16
|
import ServerError from '../domain/ServerError';
|
|
17
17
|
import Call from '../domain/Call';
|
|
18
18
|
import CallLog from '../domain/CallLog';
|
|
19
|
+
import Recording from '../domain/Recording';
|
|
19
20
|
import ChatMessage from '../domain/ChatMessage';
|
|
20
21
|
import ChatRoom from '../domain/ChatRoom';
|
|
21
22
|
import Contact from '../domain/Contact';
|
|
@@ -28,6 +29,8 @@ import Voicemail from '../domain/Voicemail';
|
|
|
28
29
|
import Relocation from '../domain/Relocation';
|
|
29
30
|
import ConferenceRoom from '../domain/Room';
|
|
30
31
|
import CallSession from '../domain/CallSession';
|
|
32
|
+
import WebRTCPhone from '../domain/Phone/WebRTCPhone';
|
|
33
|
+
import CTIPhone from '../domain/Phone/CTIPhone';
|
|
31
34
|
import IndirectTransfer from '../domain/IndirectTransfer';
|
|
32
35
|
import SwitchboardCall from '../domain/SwitchboardCall';
|
|
33
36
|
import IssueReporter from '../service/IssueReporter';
|
|
@@ -58,6 +61,8 @@ const Wazo = {
|
|
|
58
61
|
ServerError,
|
|
59
62
|
Call,
|
|
60
63
|
CallLog,
|
|
64
|
+
CTIPhone,
|
|
65
|
+
Recording,
|
|
61
66
|
ChatMessage,
|
|
62
67
|
ChatRoom,
|
|
63
68
|
Contact,
|
|
@@ -72,6 +77,7 @@ const Wazo = {
|
|
|
72
77
|
CallSession,
|
|
73
78
|
IndirectTransfer,
|
|
74
79
|
SwitchboardCall,
|
|
80
|
+
WebRTCPhone,
|
|
75
81
|
},
|
|
76
82
|
|
|
77
83
|
// Api
|