@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
@@ -305,7 +305,13 @@ class IssueReporter {
305
305
  }
306
306
 
307
307
  _isLevelAbove(level1 , level2 ) {
308
- return LOG_LEVELS.indexOf(level1) > LOG_LEVELS.indexOf(level2);
308
+ const index1 = LOG_LEVELS.indexOf(level1);
309
+ const index2 = LOG_LEVELS.indexOf(level2);
310
+ if (index1 === -1 || index2 === -1) {
311
+ return false;
312
+ }
313
+
314
+ return index1 > index2;
309
315
  }
310
316
 
311
317
  _makeCategory(category ) {
@@ -363,6 +369,7 @@ class ApiRequester {
363
369
 
364
370
 
365
371
 
372
+
366
373
 
367
374
 
368
375
 
@@ -402,6 +409,8 @@ class ApiRequester {
402
409
  this.token = token;
403
410
  }
404
411
 
412
+ this.shouldLogErrors = true;
413
+
405
414
  methods.forEach(method => {
406
415
  // $FlowFixMe
407
416
  ApiRequester.prototype[method] = function sugar(...args) {
@@ -425,6 +434,14 @@ class ApiRequester {
425
434
  this.fetchOptions = options;
426
435
  }
427
436
 
437
+ disableErrorLogging() {
438
+ this.shouldLogErrors = false;
439
+ }
440
+
441
+ enableErrorLogging() {
442
+ this.shouldLogErrors = true;
443
+ }
444
+
428
445
  async call(
429
446
  path ,
430
447
  method = 'get',
@@ -485,7 +502,9 @@ class ApiRequester {
485
502
  ? exceptionClass.fromText(err, response.status)
486
503
  : exceptionClass.fromResponse(err, response.status);
487
504
 
488
- logger.error('API error', error);
505
+ if (this.shouldLogErrors) {
506
+ logger.error('API error', error);
507
+ }
489
508
 
490
509
  throw error;
491
510
  });
@@ -493,7 +512,9 @@ class ApiRequester {
493
512
 
494
513
  return newParse(response, isJson);
495
514
  }).catch(error => {
496
- logger.error('Fetch failed', { url, options, message: error.message, stack: error.stack });
515
+ if (this.shouldLogErrors) {
516
+ logger.error('Fetch failed', { url, options, message: error.message, stack: error.stack });
517
+ }
497
518
 
498
519
  throw error;
499
520
  });
@@ -615,11 +636,16 @@ describe('Retrieving headers', () => {
615
636
  });
616
637
 
617
638
  describe('Calling fetch', () => {
618
- it('should call fetch without body but query string in get method', () => {
639
+ it('should call fetch without body but query string in get method', async () => {
619
640
  jest.mock('node-fetch/lib/index', () => {});
620
- global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve({}) }));
641
+ global.fetch = jest.fn(() => Promise.resolve({
642
+ json: () => Promise.resolve({}),
643
+ headers: {
644
+ get: () => '',
645
+ },
646
+ }));
621
647
 
622
- new ApiRequester({ server }).call(path, method, body, {});
648
+ await new ApiRequester({ server }).call(path, method, body, {});
623
649
  expect(global.fetch).toBeCalledWith(url, { method: 'get', body: null, headers: {}, agent: null });
624
650
  });
625
651
  });
@@ -305,7 +305,13 @@ class IssueReporter {
305
305
  }
306
306
 
307
307
  _isLevelAbove(level1 , level2 ) {
308
- return LOG_LEVELS.indexOf(level1) > LOG_LEVELS.indexOf(level2);
308
+ const index1 = LOG_LEVELS.indexOf(level1);
309
+ const index2 = LOG_LEVELS.indexOf(level2);
310
+ if (index1 === -1 || index2 === -1) {
311
+ return false;
312
+ }
313
+
314
+ return index1 > index2;
309
315
  }
310
316
 
311
317
  _makeCategory(category ) {
@@ -363,6 +369,7 @@ class ApiRequester {
363
369
 
364
370
 
365
371
 
372
+
366
373
 
367
374
 
368
375
 
@@ -402,6 +409,8 @@ class ApiRequester {
402
409
  this.token = token;
403
410
  }
404
411
 
412
+ this.shouldLogErrors = true;
413
+
405
414
  methods.forEach(method => {
406
415
  // $FlowFixMe
407
416
  ApiRequester.prototype[method] = function sugar(...args) {
@@ -425,6 +434,14 @@ class ApiRequester {
425
434
  this.fetchOptions = options;
426
435
  }
427
436
 
437
+ disableErrorLogging() {
438
+ this.shouldLogErrors = false;
439
+ }
440
+
441
+ enableErrorLogging() {
442
+ this.shouldLogErrors = true;
443
+ }
444
+
428
445
  async call(
429
446
  path ,
430
447
  method = 'get',
@@ -485,7 +502,9 @@ class ApiRequester {
485
502
  ? exceptionClass.fromText(err, response.status)
486
503
  : exceptionClass.fromResponse(err, response.status);
487
504
 
488
- logger.error('API error', error);
505
+ if (this.shouldLogErrors) {
506
+ logger.error('API error', error);
507
+ }
489
508
 
490
509
  throw error;
491
510
  });
@@ -493,7 +512,9 @@ class ApiRequester {
493
512
 
494
513
  return newParse(response, isJson);
495
514
  }).catch(error => {
496
- logger.error('Fetch failed', { url, options, message: error.message, stack: error.stack });
515
+ if (this.shouldLogErrors) {
516
+ logger.error('Fetch failed', { url, options, message: error.message, stack: error.stack });
517
+ }
497
518
 
498
519
  throw error;
499
520
  });
@@ -1,11 +1,10 @@
1
1
  import 'webrtc-adapter';
2
2
  import { UserAgentState } from 'sip.js/lib/api/user-agent-state';
3
- import 'sip.js/lib/api/message';
4
3
  import { Parser } from 'sip.js/lib/core/messages/parser';
5
4
  import { C } from 'sip.js/lib/core/messages/methods/constants';
6
5
  import { URI } from 'sip.js/lib/grammar/uri';
7
6
  import { UserAgent } from 'sip.js/lib/api/user-agent';
8
- import { holdModifier } from 'sip.js/lib/platform/web/modifiers/modifiers';
7
+ import { stripVideo, holdModifier } from 'sip.js/lib/platform/web/modifiers/modifiers';
9
8
  import { Registerer } from 'sip.js/lib/api/registerer';
10
9
  import { Inviter } from 'sip.js/lib/api/inviter';
11
10
  import { Messager } from 'sip.js/lib/api/messager';
@@ -136,6 +135,7 @@ class ApiRequester {
136
135
 
137
136
 
138
137
 
138
+
139
139
 
140
140
 
141
141
 
@@ -175,6 +175,8 @@ class ApiRequester {
175
175
  this.token = token;
176
176
  }
177
177
 
178
+ this.shouldLogErrors = true;
179
+
178
180
  methods.forEach(method => {
179
181
  // $FlowFixMe
180
182
  ApiRequester.prototype[method] = function sugar(...args) {
@@ -198,6 +200,14 @@ class ApiRequester {
198
200
  this.fetchOptions = options;
199
201
  }
200
202
 
203
+ disableErrorLogging() {
204
+ this.shouldLogErrors = false;
205
+ }
206
+
207
+ enableErrorLogging() {
208
+ this.shouldLogErrors = true;
209
+ }
210
+
201
211
  async call(
202
212
  path ,
203
213
  method = 'get',
@@ -258,7 +268,9 @@ class ApiRequester {
258
268
  ? exceptionClass.fromText(err, response.status)
259
269
  : exceptionClass.fromResponse(err, response.status);
260
270
 
261
- logger$2.error('API error', error);
271
+ if (this.shouldLogErrors) {
272
+ logger$2.error('API error', error);
273
+ }
262
274
 
263
275
  throw error;
264
276
  });
@@ -266,7 +278,9 @@ class ApiRequester {
266
278
 
267
279
  return newParse(response, isJson);
268
280
  }).catch(error => {
269
- logger$2.error('Fetch failed', { url, options, message: error.message, stack: error.stack });
281
+ if (this.shouldLogErrors) {
282
+ logger$2.error('Fetch failed', { url, options, message: error.message, stack: error.stack });
283
+ }
270
284
 
271
285
  throw error;
272
286
  });
@@ -577,7 +591,13 @@ class IssueReporter {
577
591
  }
578
592
 
579
593
  _isLevelAbove(level1 , level2 ) {
580
- return LOG_LEVELS.indexOf(level1) > LOG_LEVELS.indexOf(level2);
594
+ const index1 = LOG_LEVELS.indexOf(level1);
595
+ const index2 = LOG_LEVELS.indexOf(level2);
596
+ if (index1 === -1 || index2 === -1) {
597
+ return false;
598
+ }
599
+
600
+ return index1 > index2;
581
601
  }
582
602
 
583
603
  _makeCategory(category ) {
@@ -606,23 +626,6 @@ const areCandidateValid = (candidates ) =>
606
626
  getSrflxOrRelay(candidates)
607
627
  .length > 0;
608
628
 
609
- const toggleVideoDirection = (sdp , direction ) => {
610
- const parsedSdp = sdpParser.parse(sdp);
611
- parsedSdp.media = parsedSdp.media.map(media => ({
612
- ...media,
613
- ...(media.type === 'video' ? { direction } : {}),
614
- }));
615
-
616
- return sdpParser.write(parsedSdp);
617
- };
618
-
619
- const deactivateVideoModifier = (rawDescription ) => {
620
- const description = rawDescription;
621
- description.sdp = toggleVideoDirection(description.sdp, 'inactive');
622
-
623
- return Promise.resolve(description);
624
- };
625
-
626
629
  const hasAnActiveVideo = (sdp ) => {
627
630
  if (!sdp) {
628
631
  return false;
@@ -734,6 +737,7 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
734
737
  const iceRestart = options.offerOptions ? options.offerOptions.iceRestart : false;
735
738
 
736
739
  const isConference = options ? !!options.conference : false;
740
+ const audioOnly = options ? !!options.audioOnly : false;
737
741
 
738
742
  // We should wait for ice when iceRestart (reinvite) or for the first invite
739
743
  // We shouldn't wait for ice when holding or resuming the call
@@ -760,16 +764,16 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
760
764
  };
761
765
 
762
766
  return this.getLocalMediaStream(options)
763
- .then(() => this.updateDirection(options, isConference))
767
+ .then(() => this.updateDirection(options, isConference, audioOnly))
764
768
  .then(() => this.createDataChannel(options))
765
769
  .then(() => {
766
- if (isConference && (options.constraints && !options.constraints.video) && !('hold' in options)) {
770
+ if (isConference && (options.constraints && !options.constraints.video) && !('hold' in options) && !audioOnly) {
767
771
  // Add a video an empty bundle to be able to replaceTrack when joining a conference without video
768
772
  this.peerConnection.addTransceiver('video', { streams: [this._localMediaStream], direction: 'sendrecv' });
769
773
  }
770
774
  })
771
775
  .then(() => this.createLocalOfferOrAnswer(options))
772
- .then((sessionDescription) => this.setLocalSessionDescription(sessionDescription))
776
+ .then(sessionDescription => this.setLocalSessionDescription(sessionDescription))
773
777
  .then(() => this.waitForIceGatheringComplete(iceRestart, iceTimeout))
774
778
  .then(() => this.getLocalSessionDescription())
775
779
  .then(description => {
@@ -892,7 +896,8 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
892
896
  }
893
897
 
894
898
  // Overridden to send `inactive` in conference
895
- updateDirection(options , isConference = false) {
899
+ updateDirection(options , isConference = false,
900
+ audioOnly = false) {
896
901
  if (this._peerConnection === undefined) {
897
902
  return Promise.reject(new Error('Peer connection closed.'));
898
903
  }
@@ -907,14 +912,17 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
907
912
  // if we are stable, assume we are creating a local offer
908
913
  this.logger.debug('SessionDescriptionHandler.updateDirection - setting offer direction');
909
914
  // determine the direction to offer given the current direction and hold state
910
- const directionToOffer = (currentDirection ) => {
915
+ const directionToOffer = (currentDirection , transceiver /* RTCRtpTransceiver */) => {
911
916
  if (isConference) {
917
+ if (audioOnly && transceiver.receiver.track && transceiver.receiver.track.kind === 'video') {
918
+ return 'sendonly';
919
+ }
912
920
  return options && options.hold ? 'sendonly' : 'sendrecv';
913
921
  }
914
922
 
915
923
  switch (currentDirection) {
916
924
  case 'inactive':
917
- return options && options.hold ? 'inactive' : 'recvonly';
925
+ return options && options.hold ? 'inactive' : (isConference && !audioOnly ? 'sendrecv' : 'recvonly');
918
926
  case 'recvonly':
919
927
  return options && options.hold ? 'inactive' : 'recvonly';
920
928
  case 'sendonly':
@@ -930,7 +938,7 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
930
938
  // set the transceiver direction to the offer direction
931
939
  this._peerConnection.getTransceivers().forEach((transceiver) => {
932
940
  if (transceiver.direction /* guarding, but should always be true */) {
933
- const offerDirection = directionToOffer(transceiver.direction);
941
+ const offerDirection = directionToOffer(transceiver.direction, transceiver);
934
942
  if (transceiver.direction !== offerDirection) {
935
943
  // eslint-disable-next-line no-param-reassign
936
944
  transceiver.direction = offerDirection;
@@ -989,6 +997,13 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
989
997
  // set the transceiver direction to the answer direction
990
998
  this._peerConnection.getTransceivers().forEach((transceiver) => {
991
999
  if (transceiver.direction /* guarding, but should always be true */) {
1000
+ const { receiver } = transceiver;
1001
+ if (isConference && audioOnly && receiver.track && receiver.track.kind === 'video') {
1002
+ // eslint-disable-next-line no-param-reassign
1003
+ transceiver.direction = 'inactive';
1004
+ return;
1005
+ }
1006
+
992
1007
  if (transceiver.direction !== 'stopped' && transceiver.direction !== answerDirection) {
993
1008
  // eslint-disable-next-line no-param-reassign
994
1009
  transceiver.direction = answerDirection;
@@ -1027,8 +1042,8 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
1027
1042
  }
1028
1043
  const sender = pc.getSenders().find((otherSender) => otherSender.track && otherSender.track.kind === kind);
1029
1044
 
1030
- // Do not reuse sender tracks in SFU
1031
- if (sender && !sfu) {
1045
+ // Do not reuse sender video tracks in SFU
1046
+ if (sender && (!sfu || newTrack.kind === 'audio')) {
1032
1047
  trackUpdates.push(
1033
1048
  new Promise((resolve) => {
1034
1049
  this.logger.debug(`SessionDescriptionHandler.setLocalMediaStream - replacing sender ${kind} track`);
@@ -2094,7 +2109,7 @@ class Contact {
2094
2109
 
2095
2110
  return new Contact({
2096
2111
  sourceId: single.id || '',
2097
- name: single.displayName,
2112
+ name: single.displayName || '',
2098
2113
  number: numbers.length ? numbers[0].number : '',
2099
2114
  numbers,
2100
2115
  emails,
@@ -2125,7 +2140,7 @@ class Contact {
2125
2140
 
2126
2141
  return new Contact({
2127
2142
  sourceId: single.id || '',
2128
- name: single.name,
2143
+ name: single.name || '',
2129
2144
  number: numbers.length ? numbers[0].number : '',
2130
2145
  numbers,
2131
2146
  emails,
@@ -2226,7 +2241,7 @@ class Contact {
2226
2241
  } = {}) {
2227
2242
  this.id = id;
2228
2243
  this.uuid = uuid;
2229
- this.name = name;
2244
+ this.name = name || '';
2230
2245
  this.number = number;
2231
2246
  this.numbers = numbers;
2232
2247
  this.email = email;
@@ -3535,17 +3550,66 @@ var updateFrom = (instance , from ) => {
3535
3550
 
3536
3551
  //
3537
3552
 
3553
+
3554
+
3555
+
3556
+
3557
+
3558
+
3559
+
3560
+
3561
+
3562
+
3563
+
3564
+
3565
+
3566
+
3567
+
3568
+
3569
+ class Recording {
3570
+
3571
+
3572
+
3573
+
3574
+
3575
+
3576
+ static parseMany(recordings ) {
3577
+ return recordings.map(item => Recording.parse(item));
3578
+ }
3579
+
3580
+ static parse(plain ) {
3581
+ return new Recording({
3582
+ deleted: plain.deleted,
3583
+ fileName: plain.filename,
3584
+ end: plain.end_time ? moment(plain.end_time).toDate() : null,
3585
+ start: moment(plain.start_time).toDate(),
3586
+ uuid: plain.uuid,
3587
+ });
3588
+ }
3589
+
3590
+ constructor({ deleted, end, fileName, start, uuid } ) {
3591
+ this.deleted = deleted;
3592
+ this.end = end;
3593
+ this.fileName = fileName;
3594
+ this.start = start;
3595
+ this.uuid = uuid;
3596
+ }
3597
+ }
3598
+
3599
+ //
3600
+
3538
3601
 
3539
-
3602
+
3540
3603
 
3541
3604
 
3542
3605
 
3543
3606
 
3544
3607
 
3545
-
3608
+
3546
3609
 
3547
3610
 
3548
3611
 
3612
+
3549
3613
 
3550
3614
 
3551
3615
 
@@ -3558,7 +3622,7 @@ var updateFrom = (instance , from ) => {
3558
3622
 
3559
3623
 
3560
3624
 
3561
-
3625
+
3562
3626
 
3563
3627
 
3564
3628
 
@@ -3573,13 +3637,14 @@ var updateFrom = (instance , from ) => {
3573
3637
 
3574
3638
 
3575
3639
 
3576
-
3640
+
3641
+
3577
3642
 
3578
3643
 
3579
3644
  class CallLog {
3580
3645
 
3581
3646
 
3582
-
3647
+
3583
3648
 
3584
3649
 
3585
3650
 
@@ -3596,7 +3661,7 @@ class CallLog {
3596
3661
 
3597
3662
 
3598
3663
 
3599
-
3664
+
3600
3665
 
3601
3666
  static merge(current , toMerge ) {
3602
3667
  const onlyUnique = (value, index, self) => self.indexOf(value) === index;
@@ -3613,7 +3678,7 @@ class CallLog {
3613
3678
 
3614
3679
  static parse(plain ) {
3615
3680
  return new CallLog({
3616
- answer: moment(plain.answer).toDate(),
3681
+ answer: plain.answer ? moment(plain.answer).toDate() : null,
3617
3682
  answered: plain.answered,
3618
3683
  callDirection: plain.call_direction,
3619
3684
  destination: {
@@ -3627,13 +3692,14 @@ class CallLog {
3627
3692
  id: plain.id,
3628
3693
  duration: (plain.duration || 0) * 1000, // duration is in seconds
3629
3694
  start: moment(plain.start).toDate(),
3630
- end: moment(plain.end).toDate(),
3695
+ end: plain.end ? moment(plain.end).toDate() : null,
3696
+ recordings: Recording.parseMany(plain.recordings),
3631
3697
  });
3632
3698
  }
3633
3699
 
3634
3700
  static parseNew(plain , session ) {
3635
3701
  return new CallLog({
3636
- answer: moment(plain.answer).toDate(),
3702
+ answer: plain.answer ? moment(plain.answer).toDate() : null,
3637
3703
  answered: plain.answered,
3638
3704
  callDirection: plain.call_direction,
3639
3705
  destination: {
@@ -3647,7 +3713,8 @@ class CallLog {
3647
3713
  id: plain.id,
3648
3714
  duration: (plain.duration || 0) * 1000, // duration is in seconds
3649
3715
  start: moment(plain.start).toDate(),
3650
- end: moment(plain.end).toDate(),
3716
+ end: plain.end ? moment(plain.end).toDate() : null,
3717
+ recordings: Recording.parseMany(plain.recordings),
3651
3718
  // @TODO: FIXME add verification declined vs missed call
3652
3719
  newMissedCall: session
3653
3720
  && session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered,
@@ -4883,6 +4950,10 @@ class ApiClient {
4883
4950
  this.fetchOptions = fetchOptions;
4884
4951
  this.client.setFetchOptions(fetchOptions);
4885
4952
  }
4953
+
4954
+ disableErrorLogging() {
4955
+ this.client.disableErrorLogging();
4956
+ }
4886
4957
  }
4887
4958
 
4888
4959
  //
@@ -5375,9 +5446,15 @@ class WebRTCClient extends Emitter {
5375
5446
 
5376
5447
  call(number , enableVideo , audioOnly = false, conference = false) {
5377
5448
  logger.info('sdk webrtc creating call', { number, enableVideo, audioOnly });
5378
- const inviterOptions = {};
5449
+ const inviterOptions = {
5450
+ sessionDescriptionHandlerOptionsReInvite: {
5451
+ conference,
5452
+ audioOnly,
5453
+ },
5454
+ };
5455
+
5379
5456
  if (audioOnly) {
5380
- inviterOptions.sessionDescriptionHandlerModifiersReInvite = [deactivateVideoModifier];
5457
+ inviterOptions.sessionDescriptionHandlerModifiersReInvite = [stripVideo];
5381
5458
  }
5382
5459
 
5383
5460
  const session = new Inviter(this.userAgent, this._makeURI(number), inviterOptions);
@@ -5406,10 +5483,11 @@ class WebRTCClient extends Emitter {
5406
5483
  sessionDescriptionHandlerOptions: this.getMediaConfiguration(enableVideo || false, conference),
5407
5484
  };
5408
5485
 
5409
- inviteOptions.sessionDescriptionHandlerModifiers = [replaceLocalIpModifier];
5486
+ inviteOptions.sessionDescriptionHandlerOptions.audioOnly = audioOnly;
5410
5487
 
5488
+ inviteOptions.sessionDescriptionHandlerModifiers = [replaceLocalIpModifier];
5411
5489
  if (audioOnly) {
5412
- inviteOptions.sessionDescriptionHandlerModifiers.push(deactivateVideoModifier);
5490
+ inviteOptions.sessionDescriptionHandlerModifiers.push(stripVideo);
5413
5491
  }
5414
5492
 
5415
5493
  // Do not await invite here or we'll miss the Establishing state transition
@@ -5623,6 +5701,9 @@ class WebRTCClient extends Emitter {
5623
5701
  if (!this._isWeb()) {
5624
5702
  options.sessionDescriptionHandlerModifiers = [holdModifier];
5625
5703
  }
5704
+ options.sessionDescriptionHandlerOptions = {
5705
+ constraints: options.constraints,
5706
+ };
5626
5707
 
5627
5708
  // Send re-INVITE
5628
5709
  return session.invite(options);
@@ -5662,6 +5743,9 @@ class WebRTCClient extends Emitter {
5662
5743
  // (eg: holdModifier)
5663
5744
  options.sessionDescriptionHandlerModifiers = [];
5664
5745
  }
5746
+ options.sessionDescriptionHandlerOptions = {
5747
+ constraints: options.constraints,
5748
+ };
5665
5749
 
5666
5750
  // Send re-INVITE
5667
5751
  return session.invite(options);
@@ -5891,7 +5975,8 @@ class WebRTCClient extends Emitter {
5891
5975
  ? this.video.deviceId.exact : undefined;
5892
5976
  }
5893
5977
 
5894
- reinvite(sipSession , newConstraints = null, conference = false) {
5978
+ reinvite(sipSession , newConstraints = null, conference = false,
5979
+ audioOnly = false) {
5895
5980
  if (!sipSession) {
5896
5981
  return false;
5897
5982
  }
@@ -5901,16 +5986,17 @@ class WebRTCClient extends Emitter {
5901
5986
  }
5902
5987
 
5903
5988
  const wasMuted = this.isAudioMuted(sipSession);
5904
- // When upgrading to video, remove the `deactivateVideoModifier` modifiers
5989
+ // When upgrading to video, remove the `stripVideo` modifiers
5905
5990
  if (newConstraints && newConstraints.video) {
5906
5991
  const modifiers = sipSession.sessionDescriptionHandlerModifiersReInvite;
5907
5992
  sipSession.sessionDescriptionHandlerModifiersReInvite = modifiers.filter(modifier =>
5908
- modifier !== deactivateVideoModifier);
5993
+ modifier !== stripVideo);
5909
5994
  }
5910
5995
 
5911
5996
  sipSession.sessionDescriptionHandlerOptionsReInvite = {
5912
5997
  ...sipSession.sessionDescriptionHandlerOptionsReInvite,
5913
5998
  conference,
5999
+ audioOnly,
5914
6000
  };
5915
6001
 
5916
6002
  const shouldDoVideo = newConstraints ? newConstraints.video : this.sessionWantsToDoVideo(sipSession);
@@ -5950,6 +6036,8 @@ class WebRTCClient extends Emitter {
5950
6036
  },
5951
6037
  sessionDescriptionHandlerOptions: {
5952
6038
  constraints,
6039
+ conference,
6040
+ audioOnly,
5953
6041
  offerOptions: {
5954
6042
  iceRestart: false,
5955
6043
  },
@@ -6409,6 +6497,8 @@ class WebRTCClient extends Emitter {
6409
6497
 
6410
6498
  // Invitation and Inviter extends Session
6411
6499
  _setupSession(session ) {
6500
+ const sipSessionId = this.getSipSessionId(session);
6501
+
6412
6502
  // When receiving an Invitation, the delegate is not defined.
6413
6503
  if (!session.delegate) {
6414
6504
  session.delegate = {};
@@ -6432,8 +6522,18 @@ class WebRTCClient extends Emitter {
6432
6522
  };
6433
6523
  };
6434
6524
 
6525
+ const oldInviteRequest = session.onInviteRequest.bind(session);
6526
+ let hadRemoteVideo = false;
6527
+
6528
+ // Monkey patch `onInviteRequest` to be able to know if there was a remote video stream before `onInvite` is called
6529
+ // Becase when `onInvite` is called we already got the video track
6530
+ session.onInviteRequest = (request) => {
6531
+ hadRemoteVideo = this.hasARemoteVideoTrack(sipSessionId);
6532
+
6533
+ oldInviteRequest(request);
6534
+ };
6535
+
6435
6536
  session.delegate.onInvite = (request ) => {
6436
- const sipSessionId = this.getSipSessionId(session);
6437
6537
  let updatedCalleeName = null;
6438
6538
  let updatedNumber = null;
6439
6539
  if (session.assertedIdentity) {
@@ -6443,7 +6543,6 @@ class WebRTCClient extends Emitter {
6443
6543
  logger.info('re-invite received', { updatedCalleeName, updatedNumber });
6444
6544
 
6445
6545
  // Useful to know if it's a video upgrade or an unhold from a remote peer with a video stream
6446
- const hadRemoteVideo = this.hasARemoteVideoTrack(sipSessionId);
6447
6546
 
6448
6547
  // Update SDP
6449
6548
  // Remote video is handled by the `track` event. Here we're dealing with video stream removal.
@@ -6493,7 +6592,7 @@ class WebRTCClient extends Emitter {
6493
6592
  }
6494
6593
 
6495
6594
  _isAudioOnly(session ) {
6496
- return session.sessionDescriptionHandlerModifiersReInvite.find(modifier => modifier === deactivateVideoModifier);
6595
+ return session.sessionDescriptionHandlerModifiersReInvite.find(modifier => modifier === stripVideo);
6497
6596
  }
6498
6597
 
6499
6598
  _setupMedias(session , newStream ) {
@@ -300,7 +300,13 @@ class IssueReporter {
300
300
  }
301
301
 
302
302
  _isLevelAbove(level1 , level2 ) {
303
- return LOG_LEVELS.indexOf(level1) > LOG_LEVELS.indexOf(level2);
303
+ const index1 = LOG_LEVELS.indexOf(level1);
304
+ const index2 = LOG_LEVELS.indexOf(level2);
305
+ if (index1 === -1 || index2 === -1) {
306
+ return false;
307
+ }
308
+
309
+ return index1 > index2;
304
310
  }
305
311
 
306
312
  _makeCategory(category ) {