@wazo/sdk 0.37.21 → 0.37.25

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 (118) hide show
  1. package/dist/wazo-sdk.js +5 -5
  2. package/esm/__tests__/api-client.test.js +36 -5
  3. package/esm/__tests__/web-rtc-client.test.js +77 -66
  4. package/esm/api/call-logd.js +7 -0
  5. package/esm/api/calld.js +10 -0
  6. package/esm/api/chatd.js +7 -0
  7. package/esm/api/confd.js +13 -5
  8. package/esm/api/ctid-ng.js +10 -0
  9. package/esm/api/dird.js +3 -0
  10. package/esm/api-client.js +36 -5
  11. package/esm/checker/Checker.js +77 -66
  12. package/esm/checker/checks/aor.js +36 -5
  13. package/esm/checker/checks/api.js +36 -5
  14. package/esm/checker/checks/index.js +77 -66
  15. package/esm/checker/checks/webrtc-transport.js +77 -66
  16. package/esm/checker/checks/webrtc.js +77 -66
  17. package/esm/domain/AdHocAPIConference.js +36 -5
  18. package/esm/domain/Call.js +3 -0
  19. package/esm/domain/CallLog.js +7 -0
  20. package/esm/domain/ChatMessage.js +4 -0
  21. package/esm/domain/ChatRoom.js +3 -0
  22. package/esm/domain/Contact.js +3 -0
  23. package/esm/domain/Features.js +36 -5
  24. package/esm/domain/Meeting.js +15 -5
  25. package/esm/domain/Phone/CTIPhone.js +40 -5
  26. package/esm/domain/Phone/WebRTCPhone.js +45 -16
  27. package/esm/domain/Recording.js +3 -0
  28. package/esm/domain/Voicemail.js +3 -0
  29. package/esm/domain/__tests__/Call.test.js +3 -0
  30. package/esm/domain/__tests__/CallLog.test.js +7 -0
  31. package/esm/domain/__tests__/Contact.test.js +3 -0
  32. package/esm/domain/__tests__/Meeting.test.js +144 -0
  33. package/esm/domain/__tests__/Voicemail.test.js +3 -0
  34. package/esm/index.js +125 -81
  35. package/esm/lib/WazoSessionDescriptionHandler.js +0 -38
  36. package/esm/service/CallApi.js +36 -5
  37. package/esm/service/getApiClient.js +36 -5
  38. package/esm/simple/Auth.js +125 -81
  39. package/esm/simple/Configuration.js +125 -81
  40. package/esm/simple/Directory.js +36 -5
  41. package/esm/simple/Phone.js +125 -81
  42. package/esm/simple/index.js +125 -81
  43. package/esm/simple/room/LocalParticipant.js +125 -81
  44. package/esm/simple/room/Participant.js +125 -81
  45. package/esm/simple/room/RemoteParticipant.js +125 -81
  46. package/esm/simple/room/Room.js +125 -81
  47. package/esm/simple/room/SipRoom.js +125 -81
  48. package/esm/simple/room/Stream.js +125 -81
  49. package/esm/simple/room/__tests__/RemoteParticipant.test.js +125 -81
  50. package/esm/simple/utils.js +125 -81
  51. package/esm/types/WebSocketMessage.js +1 -1
  52. package/esm/web-rtc-client.js +77 -66
  53. package/lib/__tests__/api-client.test.js +36 -5
  54. package/lib/__tests__/web-rtc-client.test.js +77 -66
  55. package/lib/api/call-logd.js +7 -0
  56. package/lib/api/calld.js +10 -0
  57. package/lib/api/chatd.js +7 -0
  58. package/lib/api/confd.js +13 -5
  59. package/lib/api/ctid-ng.js +10 -0
  60. package/lib/api/dird.js +3 -0
  61. package/lib/api-client.js +36 -5
  62. package/lib/checker/Checker.js +77 -66
  63. package/lib/checker/checks/aor.js +36 -5
  64. package/lib/checker/checks/api.js +36 -5
  65. package/lib/checker/checks/index.js +77 -66
  66. package/lib/checker/checks/webrtc-transport.js +77 -66
  67. package/lib/checker/checks/webrtc.js +77 -66
  68. package/lib/domain/AdHocAPIConference.js +36 -5
  69. package/lib/domain/Call.js +3 -0
  70. package/lib/domain/CallLog.js +7 -0
  71. package/lib/domain/ChatMessage.js +4 -0
  72. package/lib/domain/ChatRoom.js +3 -0
  73. package/lib/domain/Contact.js +3 -0
  74. package/lib/domain/Features.js +36 -5
  75. package/lib/domain/Meeting.js +19 -5
  76. package/lib/domain/Phone/CTIPhone.js +40 -5
  77. package/lib/domain/Phone/WebRTCPhone.js +45 -16
  78. package/lib/domain/Recording.js +3 -0
  79. package/lib/domain/Voicemail.js +3 -0
  80. package/lib/domain/__tests__/Call.test.js +3 -0
  81. package/lib/domain/__tests__/CallLog.test.js +7 -0
  82. package/lib/domain/__tests__/Contact.test.js +3 -0
  83. package/lib/domain/__tests__/Meeting.test.js +150 -0
  84. package/lib/domain/__tests__/Voicemail.test.js +3 -0
  85. package/lib/index.js +125 -81
  86. package/lib/lib/WazoSessionDescriptionHandler.js +0 -38
  87. package/lib/service/CallApi.js +36 -5
  88. package/lib/service/getApiClient.js +36 -5
  89. package/lib/simple/Auth.js +125 -81
  90. package/lib/simple/Configuration.js +125 -81
  91. package/lib/simple/Directory.js +36 -5
  92. package/lib/simple/Phone.js +125 -81
  93. package/lib/simple/index.js +125 -81
  94. package/lib/simple/room/LocalParticipant.js +125 -81
  95. package/lib/simple/room/Participant.js +125 -81
  96. package/lib/simple/room/RemoteParticipant.js +125 -81
  97. package/lib/simple/room/Room.js +125 -81
  98. package/lib/simple/room/SipRoom.js +125 -81
  99. package/lib/simple/room/Stream.js +125 -81
  100. package/lib/simple/room/__tests__/RemoteParticipant.test.js +125 -81
  101. package/lib/simple/utils.js +125 -81
  102. package/lib/types/WebSocketMessage.js +2 -0
  103. package/lib/web-rtc-client.js +77 -66
  104. package/package.json +1 -1
  105. package/src/domain/Call.js +3 -0
  106. package/src/domain/CallLog.js +4 -0
  107. package/src/domain/ChatMessage.js +4 -0
  108. package/src/domain/ChatRoom.js +3 -0
  109. package/src/domain/Contact.js +3 -0
  110. package/src/domain/Meeting.js +15 -5
  111. package/src/domain/Phone/CTIPhone.js +4 -0
  112. package/src/domain/Phone/Phone.js +2 -0
  113. package/src/domain/Phone/WebRTCPhone.js +45 -16
  114. package/src/domain/Recording.js +3 -0
  115. package/src/domain/Voicemail.js +3 -0
  116. package/src/domain/__tests__/Meeting.test.js +50 -0
  117. package/src/lib/WazoSessionDescriptionHandler.js +1 -35
  118. package/src/web-rtc-client.js +41 -23
@@ -735,10 +735,6 @@ const getSrflxOrRelay = (candidates ) =>
735
735
  candidates
736
736
  .filter(candidate => candidate && (candidate.type === 'srflx' || candidate.type === 'relay'));
737
737
 
738
- const areCandidateValid = (candidates ) =>
739
- getSrflxOrRelay(candidates)
740
- .length > 0;
741
-
742
738
  const getVideoDirection = (sdp ) => {
743
739
  const parsedSdp = sdpParser__default['default'].parse(sdp);
744
740
  const videoMedia = parsedSdp.media.find(media => media.type === 'video');
@@ -791,8 +787,6 @@ const fixSdp = (sdp , candidates , forcePort = true)
791
787
  /* global RTCSessionDescriptionInit, navigator */
792
788
 
793
789
  const wazoLogger = IssueReporter$1 ? IssueReporter$1.loggerFor('webrtc-sdh') : console;
794
- const MAX_WAIT_FOR_ICE_TRIES = 20;
795
- const WAIT_FOR_ICE_TIMEOUT = 500;
796
790
 
797
791
  // Customized mediaStreamFactory allowing to send screensharing stream directory when upgrading
798
792
  const wazoMediaStreamFactory = (constraints ) => {
@@ -982,38 +976,6 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
982
976
  this._peerConnection = undefined;
983
977
  }
984
978
 
985
- async _waitForValidGatheredIce() {
986
- let tries = 0;
987
-
988
- while (!areCandidateValid(this.gatheredCandidates) && tries < MAX_WAIT_FOR_ICE_TRIES) {
989
- wazoLogger.trace('SessionDescriptionHandler._waitForValidGatheredIce, waiting for ice', {
990
- tries,
991
- max: MAX_WAIT_FOR_ICE_TRIES,
992
- });
993
-
994
- tries++;
995
-
996
- // eslint-disable-next-line no-await-in-loop
997
- await new Promise(resolve => setTimeout(resolve, WAIT_FOR_ICE_TIMEOUT));
998
- }
999
-
1000
- if (tries >= MAX_WAIT_FOR_ICE_TRIES) {
1001
- const errorMsg = 'No valid candidates found, can\'t answer the call';
1002
- const error = new Error(errorMsg);
1003
- wazoLogger.error(errorMsg, { tries, max: MAX_WAIT_FOR_ICE_TRIES });
1004
-
1005
- // Emit an error, because sip.js catches the exception and switch to status Terminated.
1006
- this.eventEmitter.emit('error', error);
1007
-
1008
- wazoLogger.error('No valid candidates found', { tries, candidates: JSON.stringify(this.gatheredCandidates) });
1009
- }
1010
-
1011
- // eslint-disable-next-line
1012
- wazoLogger.trace('Found valid candidates', { tries, candidates: JSON.stringify(this.gatheredCandidates) });
1013
-
1014
- return this.getLocalSessionDescription();
1015
- }
1016
-
1017
979
  // Overridden to send `inactive` in conference
1018
980
  updateDirection(options , isConference = false,
1019
981
  audioOnly = false) {
@@ -2123,6 +2085,9 @@ class Contact {
2123
2085
  }
2124
2086
 
2125
2087
  static parseMany(response ) {
2088
+ if (!response || !response.results) {
2089
+ return [];
2090
+ }
2126
2091
  return response.results.map(r => Contact.parse(r, response.column_types));
2127
2092
  }
2128
2093
 
@@ -3200,8 +3165,9 @@ class ExternalApp {
3200
3165
 
3201
3166
 
3202
3167
 
3203
-
3168
+
3204
3169
 
3170
+
3205
3171
 
3206
3172
 
3207
3173
  class Meeting {
@@ -3212,9 +3178,10 @@ class Meeting {
3212
3178
 
3213
3179
 
3214
3180
 
3215
-
3181
+
3216
3182
 
3217
3183
 
3184
+
3218
3185
 
3219
3186
  static parse(plain ) {
3220
3187
  return new Meeting({
@@ -3224,12 +3191,16 @@ class Meeting {
3224
3191
  ownerUuids: plain.owner_uuids,
3225
3192
  port: plain.port,
3226
3193
  uuid: plain.uuid,
3227
- number: plain.number,
3194
+ extension: plain.exten,
3228
3195
  persistent: plain.persistent,
3196
+ creationTime: moment__default['default'](plain.creation_time).toDate(),
3229
3197
  });
3230
3198
  }
3231
3199
 
3232
3200
  static parseMany(items ) {
3201
+ if (!items) {
3202
+ return [];
3203
+ }
3233
3204
  return items.map(plain => Meeting.parse(plain));
3234
3205
  }
3235
3206
 
@@ -3244,8 +3215,9 @@ class Meeting {
3244
3215
  ownerUuids,
3245
3216
  port,
3246
3217
  uri,
3247
- number,
3218
+ extension,
3248
3219
  persistent,
3220
+ creationTime,
3249
3221
  } = {}) {
3250
3222
  this.guestSipAuthorization = guestSipAuthorization;
3251
3223
  this.uri = uri;
@@ -3253,8 +3225,9 @@ class Meeting {
3253
3225
  this.ownerUuids = ownerUuids;
3254
3226
  this.port = port;
3255
3227
  this.uuid = uuid;
3256
- this.number = number;
3228
+ this.extension = extension;
3257
3229
  this.persistent = persistent;
3230
+ this.creationTime = creationTime;
3258
3231
 
3259
3232
  // Useful to compare instead of instanceof with minified code
3260
3233
  this.type = 'Meeting';
@@ -3432,6 +3405,10 @@ class ChatMessage {
3432
3405
 
3433
3406
 
3434
3407
  static parseMany(plain ) {
3408
+ if (!plain || !plain.items) {
3409
+ return [];
3410
+ }
3411
+
3435
3412
  return plain.items.map(item => ChatMessage.parse(item));
3436
3413
  }
3437
3414
 
@@ -3541,6 +3518,9 @@ class Voicemail {
3541
3518
  }
3542
3519
 
3543
3520
  static parseMany(plain ) {
3521
+ if (!plain) {
3522
+ return [];
3523
+ }
3544
3524
  const plainUnread = plain.folders.filter(folder => folder.type === 'new')[0].messages;
3545
3525
  const plainRead = plain.folders.filter(folder => folder.type === 'old')[0].messages;
3546
3526
 
@@ -3650,6 +3630,9 @@ class Call {
3650
3630
 
3651
3631
 
3652
3632
  static parseMany(plain ) {
3633
+ if (!plain) {
3634
+ return [];
3635
+ }
3653
3636
  return plain.map((plainCall ) => Call.parse(plainCall));
3654
3637
  }
3655
3638
 
@@ -3822,6 +3805,9 @@ class Recording {
3822
3805
 
3823
3806
 
3824
3807
  static parseMany(recordings = []) {
3808
+ if (!recordings) {
3809
+ return [];
3810
+ }
3825
3811
  return recordings.map(item => Recording.parse(item));
3826
3812
  }
3827
3813
 
@@ -3923,6 +3909,10 @@ class CallLog {
3923
3909
  }
3924
3910
 
3925
3911
  static parseMany(plain ) {
3912
+ if (!plain || !plain.items) {
3913
+ return [];
3914
+ }
3915
+
3926
3916
  return plain.items.map(item => CallLog.parse(item));
3927
3917
  }
3928
3918
 
@@ -4495,6 +4485,9 @@ class ChatRoom {
4495
4485
 
4496
4486
 
4497
4487
  static parseMany(plain ) {
4488
+ if (!plain || !plain.items) {
4489
+ return [];
4490
+ }
4498
4491
  return plain.items.map(item => ChatRoom.parse(item));
4499
4492
  }
4500
4493
 
@@ -5726,7 +5719,7 @@ class WebRTCClient extends Emitter {
5726
5719
  }
5727
5720
 
5728
5721
  call(number , enableVideo , audioOnly = false, conference = false) {
5729
- logger.info('sdk webrtc creating call', { number, enableVideo, audioOnly });
5722
+ logger.info('sdk webrtc creating call', { number, enableVideo, audioOnly, conference });
5730
5723
  const inviterOptions = {
5731
5724
  sessionDescriptionHandlerOptionsReInvite: {
5732
5725
  conference,
@@ -6802,6 +6795,7 @@ class WebRTCClient extends Emitter {
6802
6795
  const screenSharing = constraints && 'screen' in constraints ? constraints.screen : false;
6803
6796
  const isDesktop = constraints && 'desktop' in constraints ? constraints.desktop : false;
6804
6797
  const withAudio = constraints && 'audio' in constraints ? constraints.audio : true;
6798
+ const mandatoryVideo = constraints && typeof constraints.video === 'object' ? constraints.video.mandatory : {};
6805
6799
 
6806
6800
  logger.info('Retrieving media a configuration', { enableVideo, screenSharing, isDesktop, withAudio, constraints });
6807
6801
 
@@ -6809,7 +6803,8 @@ class WebRTCClient extends Emitter {
6809
6803
  constraints: {
6810
6804
  // Exact constraint are not supported with `getDisplayMedia` and we must have a video=false in desktop screenshare
6811
6805
  audio: screenSharing ? !isDesktop : (withAudio ? this._getAudioConstraints() : false),
6812
- video: screenSharing ? (isDesktop ? ({ mandatory: { chromeMediaSource: 'desktop' } }) : { cursor: 'always' })
6806
+ video: screenSharing ? (isDesktop
6807
+ ? ({ mandatory: { chromeMediaSource: 'desktop', ...(mandatoryVideo || {}) } }) : { cursor: 'always' })
6813
6808
  : this._getVideoConstraints(enableVideo),
6814
6809
  screen: screenSharing,
6815
6810
  desktop: isDesktop,
@@ -7144,11 +7139,14 @@ class WebRTCClient extends Emitter {
7144
7139
 
7145
7140
  session.sessionDescriptionHandler.remoteMediaStream.onaddtrack = onTrack;
7146
7141
 
7147
- pc.oniceconnectionstatechange = () => {
7148
- if (pc.iceConnectionState === 'disconnected') {
7149
- this.eventEmitter.emit(ON_DISCONNECTED);
7150
- }
7151
- };
7142
+ if (pc) {
7143
+ pc.oniceconnectionstatechange = () => {
7144
+ logger.info('on ice connection state changed', { state: pc.iceConnectionState });
7145
+ if (pc.iceConnectionState === 'disconnected') {
7146
+ this.eventEmitter.emit(ON_DISCONNECTED);
7147
+ }
7148
+ };
7149
+ }
7152
7150
 
7153
7151
  if (withEvent) {
7154
7152
  this.eventEmitter.emit(ACCEPTED, session, sessionDialog);
@@ -7358,13 +7356,17 @@ class WebRTCClient extends Emitter {
7358
7356
  const nbStats = this.sessionNetworkStats[sessionId].length;
7359
7357
  const lastNetworkStats = this.sessionNetworkStats[sessionId][nbStats - 1];
7360
7358
  const lastAudioSent = lastNetworkStats ? lastNetworkStats.audioBytesSent : 0;
7359
+ const lastAudioContentSent = lastNetworkStats ? lastNetworkStats.audioContentSent : 0;
7361
7360
  const lastVideoSent = lastNetworkStats ? lastNetworkStats.videoBytesSent : 0;
7362
7361
  const lastAudioReceived = lastNetworkStats ? lastNetworkStats.audioBytesReceived : 0;
7362
+ const lastAudioContentReceived = lastNetworkStats ? lastNetworkStats.audioContentReceived : 0;
7363
7363
  const lastVideoReceived = lastNetworkStats ? lastNetworkStats.videoBytesReceived : 0;
7364
7364
  const lastTransportSent = lastNetworkStats ? lastNetworkStats.transportSent : 0;
7365
7365
  const lastTransportReceived = lastNetworkStats ? lastNetworkStats.transportReceived : 0;
7366
- let audioBytesSent = 0;
7367
- let audioBytesReceived = 0;
7366
+ let audioBytesSent = 0; // content + header
7367
+ let audioBytesReceived = 0; // content + header
7368
+ let audioContentSent = 0; // useful to detect blank call
7369
+ let audioContentReceived = 0;
7368
7370
  let videoBytesSent = 0;
7369
7371
  let videoBytesReceived = 0;
7370
7372
  let transportSent = 0;
@@ -7373,25 +7375,27 @@ class WebRTCClient extends Emitter {
7373
7375
 
7374
7376
  stats.forEach(report => {
7375
7377
  if (report.type === 'outbound-rtp' && report.kind === 'audio') {
7376
- audioBytesSent += report.bytesSent + report.headerBytesSent;
7378
+ audioBytesSent += Number(report.bytesSent) + Number(report.headerBytesSent);
7379
+ audioContentSent += Number(report.bytesSent);
7377
7380
  }
7378
7381
  if (report.type === 'remote-outbound-rtp' && report.kind === 'audio') {
7379
- audioBytesSent += report.bytesSent;
7382
+ audioBytesSent += Number(report.bytesSent);
7380
7383
  }
7381
7384
  if (report.type === 'outbound-rtp' && report.kind === 'video') {
7382
- videoBytesSent += report.bytesSent + report.headerBytesSent;
7385
+ videoBytesSent += Number(report.bytesSent) + Number(report.headerBytesSent);
7383
7386
  }
7384
7387
  if (report.type === 'inbound-rtp' && report.kind === 'audio') {
7385
- packetsLost += report.packetsLost;
7386
- networkStats.packetsReceived = report.packetsReceived;
7387
- audioBytesReceived += report.bytesReceived + report.headerBytesReceived;
7388
+ packetsLost += Number(report.packetsLost);
7389
+ networkStats.packetsReceived = Number(report.packetsReceived);
7390
+ audioBytesReceived += Number(report.bytesReceived) + Number(report.headerBytesReceived);
7391
+ audioContentReceived += Number(report.bytesReceived);
7388
7392
  }
7389
7393
  if (report.type === 'inbound-rtp' && report.kind === 'video') {
7390
- videoBytesReceived += report.bytesReceived + report.headerBytesReceived;
7394
+ videoBytesReceived += Number(report.bytesReceived) + Number(report.headerBytesReceived);
7391
7395
  }
7392
7396
  if (report.type === 'outbound-rtp' && report.kind === 'video') {
7393
7397
  if ('framesPerSecond' in report) {
7394
- networkStats.framesPerSecond = report.framesPerSecond;
7398
+ networkStats.framesPerSecond = Number(report.framesPerSecond);
7395
7399
  }
7396
7400
  if ('framerateMean' in report) {
7397
7401
  // framerateMean is only available in FF
@@ -7399,24 +7403,31 @@ class WebRTCClient extends Emitter {
7399
7403
  }
7400
7404
  }
7401
7405
  if (report.type === 'remote-inbound-rtp' && report.kind === 'audio') {
7402
- packetsLost += report.packetsLost;
7403
- networkStats.roundTripTime = report.roundTripTime;
7404
- networkStats.jitter = report.jitter;
7406
+ packetsLost += Number(report.packetsLost);
7407
+ networkStats.roundTripTime = Number(report.roundTripTime);
7408
+ networkStats.jitter = Number(report.jitter);
7405
7409
  }
7406
7410
  if (report.type === 'remote-inbound-rtp' && report.kind === 'video') {
7407
- packetsLost += report.packetsLost;
7411
+ packetsLost += Number(report.packetsLost);
7408
7412
  }
7409
7413
  if (report.type === 'transport') {
7410
- transportSent += report.bytesSent;
7411
- transportReceived += report.bytesReceived;
7414
+ transportSent += Number(report.bytesSent);
7415
+ transportReceived += Number(report.bytesReceived);
7412
7416
  }
7413
7417
  });
7414
7418
 
7415
7419
  networkStats.packetsLost = packetsLost;
7420
+
7416
7421
  networkStats.totalAudioBytesSent = audioBytesSent;
7417
7422
  networkStats.audioBytesSent = audioBytesSent - lastAudioSent;
7418
7423
  networkStats.totalAudioBytesReceived = audioBytesReceived - lastAudioReceived;
7419
7424
  networkStats.audioBytesReceived = audioBytesReceived;
7425
+
7426
+ networkStats.totalAudioContentSent = audioContentSent;
7427
+ networkStats.audioContentSent = audioContentSent - lastAudioContentSent;
7428
+ networkStats.totalAudioBytesReceived = audioContentReceived - lastAudioContentReceived;
7429
+ networkStats.audioContentReceived = audioContentReceived;
7430
+
7420
7431
  networkStats.totalVideoBytesSent = videoBytesSent;
7421
7432
  networkStats.videoBytesSent = videoBytesSent - lastVideoSent;
7422
7433
  networkStats.totalVideoBytesReceived = videoBytesReceived;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wazo/sdk",
3
- "version": "0.37.21",
3
+ "version": "0.37.25",
4
4
  "description": "Wazo's JavaScript Software Development Kit.",
5
5
  "main": "dist/wazo-sdk.js",
6
6
  "author": "Wazo (http://wazo.io)",
@@ -61,6 +61,9 @@ export default class Call {
61
61
  recording: boolean;
62
62
 
63
63
  static parseMany(plain: Array<CallResponse>): Array<Call> {
64
+ if (!plain) {
65
+ return [];
66
+ }
64
67
  return plain.map((plainCall: CallResponse) => Call.parse(plainCall));
65
68
  }
66
69
 
@@ -84,6 +84,10 @@ export default class CallLog {
84
84
  }
85
85
 
86
86
  static parseMany(plain: Response): Array<CallLog> {
87
+ if (!plain || !plain.items) {
88
+ return [];
89
+ }
90
+
87
91
  return plain.items.map(item => CallLog.parse(item));
88
92
  }
89
93
 
@@ -42,6 +42,10 @@ export default class ChatMessage {
42
42
  read: boolean;
43
43
 
44
44
  static parseMany(plain: ChatMessageListResponse): Array<ChatMessage> {
45
+ if (!plain || !plain.items) {
46
+ return [];
47
+ }
48
+
45
49
  return plain.items.map(item => ChatMessage.parse(item));
46
50
  }
47
51
 
@@ -22,6 +22,9 @@ export default class ChatRoom {
22
22
  users: Array<ChatUser>;
23
23
 
24
24
  static parseMany(plain: RootListResponse): Array<ChatRoom> {
25
+ if (!plain || !plain.items) {
26
+ return [];
27
+ }
25
28
  return plain.items.map(item => ChatRoom.parse(item));
26
29
  }
27
30
 
@@ -281,6 +281,9 @@ export default class Contact {
281
281
  }
282
282
 
283
283
  static parseMany(response: ContactsResponse): Array<Contact> {
284
+ if (!response || !response.results) {
285
+ return [];
286
+ }
284
287
  return response.results.map(r => Contact.parse(r, response.column_types));
285
288
  }
286
289
 
@@ -1,4 +1,6 @@
1
1
  // @flow
2
+ import moment from 'moment';
3
+
2
4
  import newFrom from '../utils/new-from';
3
5
 
4
6
  export type MeetingCreationResponse = {
@@ -8,8 +10,9 @@ export type MeetingCreationResponse = {
8
10
  owner_uuids: Array<string>,
9
11
  port: string,
10
12
  uuid: string,
11
- number: string,
13
+ exten: string,
12
14
  persistent: boolean,
15
+ creation_time: string,
13
16
  }
14
17
 
15
18
  export default class Meeting {
@@ -20,9 +23,10 @@ export default class Meeting {
20
23
  uuid: string;
21
24
  name: string;
22
25
  port: string;
23
- number: string;
26
+ extension: string;
24
27
  persistent: boolean;
25
28
  ownerUuids: Array<string>;
29
+ creationTime: Date;
26
30
 
27
31
  static parse(plain: MeetingCreationResponse): Meeting {
28
32
  return new Meeting({
@@ -32,12 +36,16 @@ export default class Meeting {
32
36
  ownerUuids: plain.owner_uuids,
33
37
  port: plain.port,
34
38
  uuid: plain.uuid,
35
- number: plain.number,
39
+ extension: plain.exten,
36
40
  persistent: plain.persistent,
41
+ creationTime: moment(plain.creation_time).toDate(),
37
42
  });
38
43
  }
39
44
 
40
45
  static parseMany(items: MeetingCreationResponse[]): Array<Meeting> {
46
+ if (!items) {
47
+ return [];
48
+ }
41
49
  return items.map(plain => Meeting.parse(plain));
42
50
  }
43
51
 
@@ -52,8 +60,9 @@ export default class Meeting {
52
60
  ownerUuids,
53
61
  port,
54
62
  uri,
55
- number,
63
+ extension,
56
64
  persistent,
65
+ creationTime,
57
66
  }: Object = {}) {
58
67
  this.guestSipAuthorization = guestSipAuthorization;
59
68
  this.uri = uri;
@@ -61,8 +70,9 @@ export default class Meeting {
61
70
  this.ownerUuids = ownerUuids;
62
71
  this.port = port;
63
72
  this.uuid = uuid;
64
- this.number = number;
73
+ this.extension = extension;
65
74
  this.persistent = persistent;
75
+ this.creationTime = creationTime;
66
76
 
67
77
  // Useful to compare instead of instanceof with minified code
68
78
  this.type = 'Meeting';
@@ -280,6 +280,10 @@ export default class CTIPhone extends Emitter implements Phone {
280
280
  return false;
281
281
  }
282
282
 
283
+ hasAVideoTrack(): boolean {
284
+ return false;
285
+ }
286
+
283
287
  getLocalStreamForCall(): ?MediaStream {
284
288
  return null;
285
289
  }
@@ -80,6 +80,8 @@ export interface Phone {
80
80
 
81
81
  hasVideo(callSession: CallSession): boolean;
82
82
 
83
+ hasAVideoTrack(callSession: CallSession): boolean;
84
+
83
85
  isWebRTC(): boolean;
84
86
 
85
87
  getUserAgent(): string;
@@ -203,6 +203,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
203
203
  constraints,
204
204
  audioOnly,
205
205
  conference,
206
+ iceRestart,
206
207
  });
207
208
 
208
209
  if (!sipSession) {
@@ -306,6 +307,11 @@ export default class WebRTCPhone extends Emitter implements Phone {
306
307
 
307
308
  this.client.downgradeToAudio(sipSession);
308
309
 
310
+ if (callSession) {
311
+ callSession.cameraEnabled = false;
312
+ this._updateCallSession(callSession);
313
+ }
314
+
309
315
  if (withMessage) {
310
316
  this._sendReinviteMessage(callSession, false);
311
317
  }
@@ -333,6 +339,11 @@ export default class WebRTCPhone extends Emitter implements Phone {
333
339
 
334
340
  const newStream = await this.client.upgradeToVideo(sipSession, constraints, isConference);
335
341
 
342
+ if (callSession) {
343
+ callSession.cameraEnabled = true;
344
+ this._updateCallSession(callSession);
345
+ }
346
+
336
347
  // If no stream is returned, it means we have to reinvite
337
348
  if (!newStream) {
338
349
  return true;
@@ -385,7 +396,10 @@ export default class WebRTCPhone extends Emitter implements Phone {
385
396
 
386
397
  onCancel(message);
387
398
  const elsewhere = message.data.indexOf('cause=26') !== -1 && message.data.indexOf('completed elsewhere') !== -1;
388
- this.eventEmitter.emit(ON_CALL_CANCELED, this._createCallSession(sipSession), elsewhere);
399
+ const callSession = this._createCallSession(sipSession);
400
+ delete this.callSessions[callSession.getId()];
401
+
402
+ this.eventEmitter.emit(ON_CALL_CANCELED, callSession, elsewhere);
389
403
  };
390
404
  } else if (sipSession instanceof Invitation) {
391
405
  console.warn('sipSession._onCancel not found, please update the wazo SDK accordingly');
@@ -568,18 +582,20 @@ export default class WebRTCPhone extends Emitter implements Phone {
568
582
  screenTrack,
569
583
  });
570
584
 
571
- screenTrack.onended = () => {
572
- logger.info('WebRTC phone - on screenshare ended', {
573
- hadVideo,
574
- id: this.getSipSessionId(sipSession),
575
- screenTrack,
576
- });
577
-
578
- this.eventEmitter.emit(
579
- ON_SHARE_SCREEN_ENDING,
580
- this._createCallSession(sipSession, callSession),
581
- );
582
- };
585
+ if (screenTrack) {
586
+ screenTrack.onended = () => {
587
+ logger.info('WebRTC phone - on screenshare ended', {
588
+ hadVideo,
589
+ id: this.getSipSessionId(sipSession),
590
+ screenTrack,
591
+ });
592
+
593
+ this.eventEmitter.emit(
594
+ ON_SHARE_SCREEN_ENDING,
595
+ this._createCallSession(sipSession, callSession),
596
+ );
597
+ };
598
+ }
583
599
 
584
600
  this.currentScreenShare = {
585
601
  stream: screenStream,
@@ -599,7 +615,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
599
615
  }
600
616
 
601
617
  _onCallAccepted(sipSession: Session, cameraEnabled: boolean): CallSession {
602
- logger.info('WebRTC phone - on call accepted', { sipId: sipSession.id, cameraEnabled });
618
+ logger.info('WebRTC phone - on call accepted', { sipId: sipSession.id, cameraEnabled });
603
619
 
604
620
  const callSession = this._createAcceptedCallSession(sipSession, cameraEnabled);
605
621
  this.currentSipSession = sipSession;
@@ -1111,7 +1127,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1111
1127
  // @TODO: line is not used here
1112
1128
  async makeCall(number: string, line: any, cameraEnabled?: boolean,
1113
1129
  audioOnly: boolean = false, conference: boolean = false): Promise<?CallSession> {
1114
- logger.info('make WebRTC call', { number, lineId: line ? line.id : null, cameraEnabled });
1130
+ logger.info('make WebRTC call', { number, lineId: line ? line.id : null, cameraEnabled, audioOnly, conference });
1115
1131
  if (!number) {
1116
1132
  return Promise.resolve(null);
1117
1133
  }
@@ -1472,9 +1488,13 @@ export default class WebRTCPhone extends Emitter implements Phone {
1472
1488
  if (this.shouldSendReinvite && this.currentSipSession
1473
1489
  && this.currentSipSession.state === SessionState.Established) {
1474
1490
  this.shouldSendReinvite = false;
1491
+ const pc = this.currentSipSession.sessionDescriptionHandler.peerConnection;
1492
+ const isConference = pc ? pc.sfu : false;
1493
+ const hasVideo = this.currentCallSession && this.currentCallSession.cameraEnabled;
1494
+
1475
1495
  try {
1476
1496
  // Send reinvite with iceRestart
1477
- this.sendReinvite(this.currentCallSession, null, false, false, true);
1497
+ this.sendReinvite(this.currentCallSession, null, isConference, !hasVideo, true);
1478
1498
  } catch (e) {
1479
1499
  logger.error('WebRTC reinvite after register, error', { message: e.message, stack: e.stack });
1480
1500
  }
@@ -1558,6 +1578,15 @@ export default class WebRTCPhone extends Emitter implements Phone {
1558
1578
  return this.client.getSipSessionId(sipSession);
1559
1579
  }
1560
1580
 
1581
+ _updateCallSession(callSession: CallSession) {
1582
+ const sipSession = this.findSipSession(callSession);
1583
+ const sipSessionId = sipSession ? this.getSipSessionId(sipSession) : null;
1584
+
1585
+ if (sipSessionId) {
1586
+ this.callSessions[sipSessionId] = callSession;
1587
+ }
1588
+ }
1589
+
1561
1590
  _onMessage(message: Message) {
1562
1591
  if (!message || message.method !== 'MESSAGE') {
1563
1592
  return;
@@ -25,6 +25,9 @@ export default class Recording {
25
25
  uuid: string;
26
26
 
27
27
  static parseMany(recordings: RecordingResponse[] = []): Recording[] {
28
+ if (!recordings) {
29
+ return [];
30
+ }
28
31
  return recordings.map(item => Recording.parse(item));
29
32
  }
30
33
 
@@ -61,6 +61,9 @@ export default class Voicemail {
61
61
  }
62
62
 
63
63
  static parseMany(plain: Response): Array<Voicemail> {
64
+ if (!plain) {
65
+ return [];
66
+ }
64
67
  const plainUnread = plain.folders.filter(folder => folder.type === 'new')[0].messages;
65
68
  const plainRead = plain.folders.filter(folder => folder.type === 'old')[0].messages;
66
69
 
@@ -0,0 +1,50 @@
1
+ // @flow
2
+ /* global btoa */
3
+
4
+ import Meeting from '../Meeting';
5
+
6
+ describe('Meeting domain', () => {
7
+ it('can return username and secret from an base64 encrypted string', () => {
8
+ const USERNAME = 'username';
9
+ const SECRET = 'secret';
10
+
11
+ const args = {
12
+ guest_sip_authorization: btoa(`${USERNAME}:${SECRET}`),
13
+ creation_time: '',
14
+ ingress_http_uri: '',
15
+ name: '',
16
+ owner_uuids: [],
17
+ port: '',
18
+ uuid: '',
19
+ exten: '',
20
+ persistent: false,
21
+ };
22
+
23
+ const meeting = Meeting.parse(args);
24
+
25
+ const { username, secret } = meeting.getGuestSipCredentials();
26
+
27
+ expect(username).toEqual(USERNAME);
28
+ expect(secret).toEqual(SECRET);
29
+ });
30
+
31
+ it('should return a date from a string', () => {
32
+ const args = {
33
+ creation_time: '2021-12-21T15:56:42.880Z',
34
+ guest_sip_authorization: '',
35
+ ingress_http_uri: '',
36
+ name: '',
37
+ owner_uuids: [],
38
+ port: '',
39
+ uuid: '',
40
+ exten: '',
41
+ persistent: false,
42
+ };
43
+
44
+ const { creationTime } = Meeting.parse(args);
45
+
46
+ expect(creationTime.getDate()).toEqual(21);
47
+ expect(creationTime.getFullYear()).toEqual(2021);
48
+ expect(creationTime.getMonth()).toEqual(11);
49
+ });
50
+ });