@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.
Files changed (149) hide show
  1. package/dist/wazo-sdk.js +5 -5
  2. package/esm/__tests__/api-client.test.js +93 -16
  3. package/esm/__tests__/web-rtc-client.test.js +153 -54
  4. package/esm/api/agentd.js +24 -3
  5. package/esm/api/amid.js +7 -1
  6. package/esm/api/application.js +24 -3
  7. package/esm/api/auth.js +24 -3
  8. package/esm/api/call-logd.js +78 -20
  9. package/esm/api/calld.js +24 -20
  10. package/esm/api/chatd.js +24 -3
  11. package/esm/api/confd.js +24 -3
  12. package/esm/api/ctid-ng.js +24 -3
  13. package/esm/api/dird.js +27 -6
  14. package/esm/api/webhookd.js +7 -1
  15. package/esm/api-client.js +93 -16
  16. package/esm/checker/Checker.js +156 -54
  17. package/esm/checker/checks/aor.js +95 -16
  18. package/esm/checker/checks/api.js +94 -16
  19. package/esm/checker/checks/index.js +156 -54
  20. package/esm/checker/checks/wazo-websocket.js +7 -1
  21. package/esm/checker/checks/webrtc-transport.js +153 -54
  22. package/esm/checker/checks/webrtc.js +153 -54
  23. package/esm/domain/AdHocAPIConference.js +1717 -1640
  24. package/esm/domain/CallLog.js +71 -19
  25. package/esm/domain/CallSession.js +1 -18
  26. package/esm/domain/Contact.js +3 -3
  27. package/esm/domain/Features.js +93 -16
  28. package/esm/domain/IndirectTransfer.js +0 -17
  29. package/esm/domain/Phone/CTIPhone.js +100 -19
  30. package/esm/domain/Phone/Phone.js +0 -17
  31. package/esm/domain/Phone/WebRTCPhone.js +51 -36
  32. package/esm/domain/Recording.js +51 -0
  33. package/esm/domain/Room.js +0 -17
  34. package/esm/domain/SwitchboardCall.js +0 -17
  35. package/esm/domain/__tests__/CallLog.test.js +89 -19
  36. package/esm/domain/__tests__/CallSession.test.js +1 -18
  37. package/esm/domain/__tests__/Contact.test.js +3 -3
  38. package/esm/domain/__tests__/IndirectTransfer.test.js +1 -18
  39. package/esm/domain/__tests__/Room.test.js +1 -18
  40. package/esm/domain/__tests__/Session.test.js +7 -0
  41. package/esm/index.js +3436 -2911
  42. package/esm/lib/WazoSessionDescriptionHandler.js +28 -10
  43. package/esm/service/CallApi.js +93 -16
  44. package/esm/service/IssueReporter.js +7 -1
  45. package/esm/service/__tests__/IssueReporter.test.js +26 -2
  46. package/esm/service/getApiClient.js +93 -16
  47. package/esm/simple/Auth.js +619 -97
  48. package/esm/simple/Configuration.js +619 -97
  49. package/esm/simple/Directory.js +93 -16
  50. package/esm/simple/Phone.js +3261 -2739
  51. package/esm/simple/Websocket.js +7 -1
  52. package/esm/simple/index.js +619 -97
  53. package/esm/simple/room/LocalParticipant.js +619 -97
  54. package/esm/simple/room/Participant.js +619 -97
  55. package/esm/simple/room/RemoteParticipant.js +619 -97
  56. package/esm/simple/room/Room.js +619 -97
  57. package/esm/simple/room/Stream.js +619 -97
  58. package/esm/simple/room/__tests__/RemoteParticipant.test.js +619 -97
  59. package/esm/simple/utils.js +619 -97
  60. package/esm/utils/__tests__/api-requester.test.js +32 -6
  61. package/esm/utils/api-requester.js +24 -3
  62. package/esm/web-rtc-client.js +153 -54
  63. package/esm/websocket-client.js +7 -1
  64. package/lib/__tests__/api-client.test.js +93 -16
  65. package/lib/__tests__/web-rtc-client.test.js +154 -55
  66. package/lib/api/agentd.js +24 -3
  67. package/lib/api/amid.js +7 -1
  68. package/lib/api/application.js +24 -3
  69. package/lib/api/auth.js +24 -3
  70. package/lib/api/call-logd.js +78 -20
  71. package/lib/api/calld.js +24 -20
  72. package/lib/api/chatd.js +24 -3
  73. package/lib/api/confd.js +24 -3
  74. package/lib/api/ctid-ng.js +24 -3
  75. package/lib/api/dird.js +27 -6
  76. package/lib/api/webhookd.js +7 -1
  77. package/lib/api-client.js +93 -16
  78. package/lib/checker/Checker.js +157 -55
  79. package/lib/checker/checks/aor.js +95 -16
  80. package/lib/checker/checks/api.js +94 -16
  81. package/lib/checker/checks/index.js +157 -55
  82. package/lib/checker/checks/wazo-websocket.js +7 -1
  83. package/lib/checker/checks/webrtc-transport.js +154 -55
  84. package/lib/checker/checks/webrtc.js +154 -55
  85. package/lib/domain/AdHocAPIConference.js +1717 -1640
  86. package/lib/domain/CallLog.js +71 -19
  87. package/lib/domain/CallSession.js +1 -18
  88. package/lib/domain/Contact.js +3 -3
  89. package/lib/domain/Features.js +93 -16
  90. package/lib/domain/IndirectTransfer.js +0 -17
  91. package/lib/domain/Phone/CTIPhone.js +100 -19
  92. package/lib/domain/Phone/Phone.js +0 -16
  93. package/lib/domain/Phone/WebRTCPhone.js +51 -36
  94. package/lib/domain/Recording.js +57 -0
  95. package/lib/domain/Room.js +0 -17
  96. package/lib/domain/SwitchboardCall.js +0 -17
  97. package/lib/domain/__tests__/CallLog.test.js +89 -19
  98. package/lib/domain/__tests__/CallSession.test.js +1 -18
  99. package/lib/domain/__tests__/Contact.test.js +3 -3
  100. package/lib/domain/__tests__/IndirectTransfer.test.js +1 -18
  101. package/lib/domain/__tests__/Room.test.js +1 -18
  102. package/lib/domain/__tests__/Session.test.js +7 -0
  103. package/lib/index.js +3437 -2912
  104. package/lib/lib/WazoSessionDescriptionHandler.js +28 -10
  105. package/lib/service/CallApi.js +93 -16
  106. package/lib/service/IssueReporter.js +7 -1
  107. package/lib/service/__tests__/IssueReporter.test.js +26 -2
  108. package/lib/service/getApiClient.js +93 -16
  109. package/lib/simple/Auth.js +620 -98
  110. package/lib/simple/Configuration.js +620 -98
  111. package/lib/simple/Directory.js +93 -16
  112. package/lib/simple/Phone.js +3262 -2740
  113. package/lib/simple/Websocket.js +7 -1
  114. package/lib/simple/index.js +620 -98
  115. package/lib/simple/room/LocalParticipant.js +620 -98
  116. package/lib/simple/room/Participant.js +620 -98
  117. package/lib/simple/room/RemoteParticipant.js +620 -98
  118. package/lib/simple/room/Room.js +620 -98
  119. package/lib/simple/room/Stream.js +620 -98
  120. package/lib/simple/room/__tests__/RemoteParticipant.test.js +620 -98
  121. package/lib/simple/utils.js +620 -98
  122. package/lib/utils/__tests__/api-requester.test.js +32 -6
  123. package/lib/utils/api-requester.js +24 -3
  124. package/lib/web-rtc-client.js +154 -55
  125. package/lib/websocket-client.js +7 -1
  126. package/package.json +1 -1
  127. package/src/api-client.js +4 -0
  128. package/src/checker/checks/aor.js +2 -0
  129. package/src/checker/checks/api.js +1 -0
  130. package/src/domain/CallLog.js +18 -11
  131. package/src/domain/CallSession.js +1 -2
  132. package/src/domain/Contact.js +3 -3
  133. package/src/domain/Phone/CTIPhone.js +6 -2
  134. package/src/domain/Phone/Phone.js +2 -0
  135. package/src/domain/Phone/WebRTCPhone.js +26 -16
  136. package/src/domain/Recording.js +48 -0
  137. package/src/domain/__tests__/CallLog.test.js +19 -0
  138. package/src/domain/__tests__/Session.test.js +7 -0
  139. package/src/index.js +6 -0
  140. package/src/lib/WazoSessionDescriptionHandler.js +21 -9
  141. package/src/service/IssueReporter.js +7 -1
  142. package/src/service/__tests__/IssueReporter.test.js +20 -1
  143. package/src/simple/Phone.js +5 -1
  144. package/src/simple/index.js +6 -0
  145. package/src/simple/room/Room.js +18 -15
  146. package/src/simple/room/Stream.js +1 -4
  147. package/src/utils/__tests__/api-requester.test.js +9 -4
  148. package/src/utils/api-requester.js +17 -2
  149. 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';
@@ -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(session: Session, call: Call): CallSession {
313
+ static parseCall(call: Call): CallSession {
315
314
  return new CallSession({
316
315
  callId: call.id,
317
316
  sipCallId: call.sipCallId,
@@ -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.session, this.currentCall);
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.session, this.currentCall) : null;
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', { sessionId: sipSession ? sipSession.id : null, constraints });
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 localVideoStream = sipSession.sessionDescriptionHandler.localMediaStream;
302
+ const localStream = sipSession.sessionDescriptionHandler.localMediaStream;
298
303
  const pc = sipSession.sessionDescriptionHandler.peerConnection;
299
- const videoTracks = localVideoStream.getVideoTracks();
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
- localVideoStream.removeTrack(videoTrack);
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
- if (this.currentScreenShare.sender && this.currentScreenShare.hadVideo) {
516
- // When stopping screenshare and we had video before that, we have to re-upgrade
517
- await this.sendReinvite(targetCallSession, { audio: false, video: true }, conference);
518
- } else {
519
- // When upgrading directly to screenshare (eg: we don't have a videoLocalStream to replace)
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((sessionDescription) => this.setLocalSessionDescription(sessionDescription))
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): Promise<void> {
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
- return LOG_LEVELS.indexOf(level1) > LOG_LEVELS.indexOf(level2);
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
- IssueReporter.enable();
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', () => {
@@ -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.getRe is deprecated, use Phone.getLocalStream instead');
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
 
@@ -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