@wazo/sdk 0.38.6 → 0.38.9

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 +2 -2
  2. package/esm/__tests__/api-client.test.js +142 -2
  3. package/esm/__tests__/web-rtc-client.test.js +249 -28
  4. package/esm/api/calld.js +8 -0
  5. package/esm/api/confd.js +134 -2
  6. package/esm/api-client.js +142 -2
  7. package/esm/checker/Checker.js +205 -25
  8. package/esm/checker/checks/aor.js +142 -2
  9. package/esm/checker/checks/api.js +142 -2
  10. package/esm/checker/checks/index.js +205 -25
  11. package/esm/checker/checks/wazo-websocket.js +2 -0
  12. package/esm/checker/checks/webrtc-transport.js +203 -25
  13. package/esm/checker/checks/webrtc.js +203 -25
  14. package/esm/domain/AdHocAPIConference.js +142 -2
  15. package/esm/domain/Features.js +142 -2
  16. package/esm/domain/Meeting.js +18 -0
  17. package/esm/domain/MeetingAuthorization.js +70 -0
  18. package/esm/domain/Phone/CTIPhone.js +142 -2
  19. package/esm/domain/Phone/WebRTCPhone.js +1 -3
  20. package/esm/domain/__tests__/Meeting.test.js +87 -1
  21. package/esm/domain/__tests__/MeetingAuthorization.test.js +109 -0
  22. package/esm/index.js +249 -31
  23. package/esm/lib/WazoSessionDescriptionHandler.js +22 -1
  24. package/esm/service/CallApi.js +142 -2
  25. package/esm/service/getApiClient.js +142 -2
  26. package/esm/simple/Auth.js +249 -31
  27. package/esm/simple/Configuration.js +249 -31
  28. package/esm/simple/Directory.js +142 -2
  29. package/esm/simple/Phone.js +249 -31
  30. package/esm/simple/Websocket.js +3 -0
  31. package/esm/simple/index.js +249 -31
  32. package/esm/simple/room/LocalParticipant.js +249 -31
  33. package/esm/simple/room/Participant.js +249 -31
  34. package/esm/simple/room/RemoteParticipant.js +249 -31
  35. package/esm/simple/room/Room.js +249 -31
  36. package/esm/simple/room/SipRoom.js +249 -31
  37. package/esm/simple/room/Stream.js +249 -31
  38. package/esm/simple/room/__tests__/Participant.test.js +14745 -0
  39. package/esm/simple/room/__tests__/RemoteParticipant.test.js +249 -31
  40. package/esm/simple/utils.js +249 -31
  41. package/esm/utils/__tests__/object.test.js +57 -0
  42. package/esm/utils/__tests__/sdp.test.js +50 -0
  43. package/esm/utils/__tests__/string.test.js +30 -0
  44. package/esm/utils/object.js +24 -0
  45. package/esm/utils/sdp.js +19 -1
  46. package/esm/utils/string.js +10 -0
  47. package/esm/web-rtc-client.js +203 -25
  48. package/esm/websocket-client.js +3 -1
  49. package/lib/__tests__/api-client.test.js +142 -2
  50. package/lib/__tests__/web-rtc-client.test.js +249 -28
  51. package/lib/api/calld.js +8 -0
  52. package/lib/api/confd.js +134 -2
  53. package/lib/api-client.js +142 -2
  54. package/lib/checker/Checker.js +205 -25
  55. package/lib/checker/checks/aor.js +142 -2
  56. package/lib/checker/checks/api.js +142 -2
  57. package/lib/checker/checks/index.js +205 -25
  58. package/lib/checker/checks/wazo-websocket.js +2 -0
  59. package/lib/checker/checks/webrtc-transport.js +203 -25
  60. package/lib/checker/checks/webrtc.js +203 -25
  61. package/lib/domain/AdHocAPIConference.js +142 -2
  62. package/lib/domain/Features.js +142 -2
  63. package/lib/domain/Meeting.js +18 -0
  64. package/lib/domain/MeetingAuthorization.js +79 -0
  65. package/lib/domain/Phone/CTIPhone.js +142 -2
  66. package/lib/domain/Phone/WebRTCPhone.js +1 -3
  67. package/lib/domain/__tests__/Meeting.test.js +87 -1
  68. package/lib/domain/__tests__/MeetingAuthorization.test.js +111 -0
  69. package/lib/index.js +249 -31
  70. package/lib/lib/WazoSessionDescriptionHandler.js +22 -1
  71. package/lib/service/CallApi.js +142 -2
  72. package/lib/service/getApiClient.js +142 -2
  73. package/lib/simple/Auth.js +249 -31
  74. package/lib/simple/Configuration.js +249 -31
  75. package/lib/simple/Directory.js +142 -2
  76. package/lib/simple/Phone.js +249 -31
  77. package/lib/simple/Websocket.js +3 -0
  78. package/lib/simple/index.js +249 -31
  79. package/lib/simple/room/LocalParticipant.js +249 -31
  80. package/lib/simple/room/Participant.js +249 -31
  81. package/lib/simple/room/RemoteParticipant.js +249 -31
  82. package/lib/simple/room/Room.js +249 -31
  83. package/lib/simple/room/SipRoom.js +249 -31
  84. package/lib/simple/room/Stream.js +249 -31
  85. package/lib/simple/room/__tests__/Participant.test.js +14755 -0
  86. package/lib/simple/room/__tests__/RemoteParticipant.test.js +249 -31
  87. package/lib/simple/utils.js +249 -31
  88. package/lib/utils/__tests__/object.test.js +59 -0
  89. package/lib/utils/__tests__/sdp.test.js +50 -0
  90. package/lib/utils/__tests__/string.test.js +32 -0
  91. package/lib/utils/object.js +28 -0
  92. package/lib/utils/sdp.js +19 -0
  93. package/lib/utils/string.js +14 -0
  94. package/lib/web-rtc-client.js +203 -25
  95. package/lib/websocket-client.js +3 -0
  96. package/package.json +2 -2
  97. package/src/__tests__/web-rtc-client.test.js +46 -3
  98. package/src/api/calld.js +8 -0
  99. package/src/api/confd.js +43 -2
  100. package/src/domain/Meeting.js +20 -0
  101. package/src/domain/MeetingAuthorization.js +67 -0
  102. package/src/domain/Phone/WebRTCPhone.js +1 -3
  103. package/src/domain/__tests__/Meeting.test.js +70 -1
  104. package/src/domain/__tests__/MeetingAuthorization.test.js +58 -0
  105. package/src/lib/WazoSessionDescriptionHandler.js +5 -2
  106. package/src/simple/room/Participant.js +40 -0
  107. package/src/simple/room/Room.js +2 -2
  108. package/src/simple/room/SipRoom.js +1 -1
  109. package/src/simple/room/__tests__/Participant.test.js +81 -0
  110. package/src/types/WebSocketMessage.js +7 -1
  111. package/src/utils/__tests__/object.test.js +35 -0
  112. package/src/utils/__tests__/sdp.test.js +33 -1
  113. package/src/utils/__tests__/string.test.js +23 -0
  114. package/src/utils/object.js +15 -0
  115. package/src/utils/sdp.js +18 -0
  116. package/src/utils/string.js +8 -0
  117. package/src/web-rtc-client.js +39 -22
  118. package/src/websocket-client.js +2 -0
@@ -2645,6 +2645,70 @@ class ExternalApp {
2645
2645
 
2646
2646
  //
2647
2647
 
2648
+ class MeetingAuthorization {
2649
+
2650
+
2651
+
2652
+
2653
+
2654
+
2655
+ constructor({
2656
+ meetingUuid,
2657
+ uuid,
2658
+ userUuid,
2659
+ userName,
2660
+ } = {}) {
2661
+ this.meetingUuid = meetingUuid;
2662
+ this.uuid = uuid;
2663
+ this.userUuid = userUuid;
2664
+ this.userName = userName;
2665
+ }
2666
+
2667
+ setStatus(status ) {
2668
+ this.status = status;
2669
+ }
2670
+
2671
+ getStatus() {
2672
+ return this.status;
2673
+ }
2674
+
2675
+ static parse(plain ) {
2676
+ const {
2677
+ meeting_uuid: meetingUuid,
2678
+ uuid,
2679
+ guest_uuid: userUuid,
2680
+ guest_name: userName,
2681
+ } = plain;
2682
+
2683
+ return new MeetingAuthorization({
2684
+ meetingUuid,
2685
+ uuid,
2686
+ userUuid,
2687
+ userName,
2688
+ });
2689
+ }
2690
+
2691
+ static parseMany(items ) {
2692
+ if (!items) {
2693
+ return [];
2694
+ }
2695
+ return items.map(MeetingAuthorization.parse);
2696
+ }
2697
+ }
2698
+
2699
+ //
2700
+
2701
+
2702
+
2703
+
2704
+
2705
+
2706
+
2707
+
2708
+
2709
+
2710
+
2711
+
2648
2712
 
2649
2713
 
2650
2714
 
@@ -2655,6 +2719,7 @@ class ExternalApp {
2655
2719
 
2656
2720
 
2657
2721
 
2722
+
2658
2723
 
2659
2724
 
2660
2725
  class Meeting {
@@ -2669,6 +2734,8 @@ class Meeting {
2669
2734
 
2670
2735
 
2671
2736
 
2737
+
2738
+
2672
2739
 
2673
2740
  static parse(plain ) {
2674
2741
  return new Meeting({
@@ -2681,6 +2748,7 @@ class Meeting {
2681
2748
  extension: plain.exten,
2682
2749
  persistent: plain.persistent,
2683
2750
  creationTime: moment(plain.creation_time).toDate(),
2751
+ requireAuthorization: plain.require_authorization,
2684
2752
  });
2685
2753
  }
2686
2754
 
@@ -2705,6 +2773,7 @@ class Meeting {
2705
2773
  extension,
2706
2774
  persistent,
2707
2775
  creationTime,
2776
+ requireAuthorization,
2708
2777
  } = {}) {
2709
2778
  this.guestSipAuthorization = guestSipAuthorization;
2710
2779
  this.uri = uri;
@@ -2715,6 +2784,7 @@ class Meeting {
2715
2784
  this.extension = extension;
2716
2785
  this.persistent = persistent;
2717
2786
  this.creationTime = creationTime;
2787
+ this.requireAuthorization = requireAuthorization;
2718
2788
 
2719
2789
  // Useful to compare instead of instanceof with minified code
2720
2790
  this.type = 'Meeting';
@@ -2725,8 +2795,32 @@ class Meeting {
2725
2795
  const [username, secret] = atob(this.guestSipAuthorization).split(':');
2726
2796
  return { username, secret };
2727
2797
  }
2798
+
2728
2799
  }
2729
2800
 
2801
+ //
2802
+
2803
+ const camelToUnderscore = (key ) => {
2804
+ if (typeof key !== 'string') {
2805
+ throw new Error('Input is not a string');
2806
+ }
2807
+ return key.charAt(0).toLowerCase() + key.substring(1).replace(/([A-Z])/g, '_$1').toLowerCase();
2808
+ };
2809
+
2810
+ //
2811
+
2812
+ const convertKeysFromCamelToUnderscore = (args ) => {
2813
+ if (!args || typeof args !== 'object' || Array.isArray(args)) {
2814
+ throw new Error('Input is not an object');
2815
+ }
2816
+
2817
+ return Object.keys(args).reduce((acc, key) => {
2818
+ acc[camelToUnderscore(key)] = typeof args[key] === 'object' && !Array.isArray(args[key])
2819
+ ? convertKeysFromCamelToUnderscore(args[key]) : args[key];
2820
+ return acc;
2821
+ }, {});
2822
+ };
2823
+
2730
2824
  /* */
2731
2825
 
2732
2826
  var confdMethods = (client , baseUrl ) => ({
@@ -2795,8 +2889,18 @@ var confdMethods = (client , baseUrl ) => ({
2795
2889
  getMyMeetings: () =>
2796
2890
  client.get(`${baseUrl}/users/me/meetings`).then(response => Meeting.parseMany(response.items)),
2797
2891
 
2798
- createMyMeeting: (name , persistent ) =>
2799
- client.post(`${baseUrl}/users/me/meetings`, { name, persistent }).then(Meeting.parse),
2892
+ createMyMeeting: (args ) =>
2893
+ client
2894
+ .post(`${baseUrl}/users/me/meetings`, convertKeysFromCamelToUnderscore(args))
2895
+ .then(Meeting.parse),
2896
+
2897
+ updateMyMeeting: (meetingUuid , data ) =>
2898
+ client.put(
2899
+ `${baseUrl}/users/me/meetings/${meetingUuid}`,
2900
+ convertKeysFromCamelToUnderscore(data),
2901
+ null,
2902
+ ApiRequester.successResponseParser,
2903
+ ),
2800
2904
 
2801
2905
  deleteMyMeeting: (meetingUuid ) =>
2802
2906
  client.delete(`${baseUrl}/users/me/meetings/${meetingUuid}`, null),
@@ -2804,8 +2908,36 @@ var confdMethods = (client , baseUrl ) => ({
2804
2908
  getMeeting: (meetingUuid ) =>
2805
2909
  client.get(`${baseUrl}/meetings/${meetingUuid}`, null).then(Meeting.parse),
2806
2910
 
2911
+ meetingAuthorizations: (meetingUuid ) =>
2912
+ client.get(`${baseUrl}/users/me/meetings/${meetingUuid}/authorizations`, null).then(MeetingAuthorization.parseMany),
2913
+
2914
+ meetingAuthorizationReject: (meetingUuid , authorizationUuid ) =>
2915
+ client.put(
2916
+ `${baseUrl}/users/me/meetings/${meetingUuid}/authorizations/${authorizationUuid}/reject`,
2917
+ {},
2918
+ null,
2919
+ ApiRequester.successResponseParser,
2920
+ ),
2921
+
2922
+ meetingAuthorizationAccept: (meetingUuid , authorizationUuid ) =>
2923
+ client.put(
2924
+ `${baseUrl}/users/me/meetings/${meetingUuid}/authorizations/${authorizationUuid}/accept`,
2925
+ {},
2926
+ null,
2927
+ ApiRequester.successResponseParser,
2928
+ ),
2929
+
2807
2930
  guestGetMeeting: (meetingUuid ) =>
2808
2931
  client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}`, null).then(Meeting.parse),
2932
+
2933
+ guestAuthorizationRequest: (userUuid , meetingUuid , username ) =>
2934
+ client
2935
+ .post(`${baseUrl}/guests/${userUuid}/meetings/${meetingUuid}/authorizations`, { guest_name: username })
2936
+ .then(MeetingAuthorization.parse),
2937
+
2938
+ guestAuthorizationCheck: (userUuid , meetingUuid , authorizationUuid ) =>
2939
+ client.get(`${baseUrl}/guests/${userUuid}/meetings/${meetingUuid}/authorizations/${authorizationUuid}`, null),
2940
+
2809
2941
  });
2810
2942
 
2811
2943
  //
@@ -5056,6 +5188,14 @@ var calldMethods = (client , baseUrl ) => ({
5056
5188
  getMeetingParticipantsAsUser: async (meetingUuid ) =>
5057
5189
  client.get(`${baseUrl}/users/me/meetings/${meetingUuid}/participants`),
5058
5190
 
5191
+ banMeetingParticipant: (meetingUuid , participantUuid ) =>
5192
+ client.delete(
5193
+ `${baseUrl}/users/me/meetings/${meetingUuid}/participants/${participantUuid}`,
5194
+ null,
5195
+ null,
5196
+ ApiRequester.successResponseParser,
5197
+ ),
5198
+
5059
5199
  listTrunks: () => client.get(`${baseUrl}/trunks`),
5060
5200
 
5061
5201
  mute: (callId ) =>
@@ -6204,6 +6344,24 @@ const fixSdp = (sdp , candidates , forcePort = true)
6204
6344
  return fixed;
6205
6345
  };
6206
6346
 
6347
+ const addIcesInAllBundles = (sdp ) => {
6348
+ const parsedSdp = sdpParser.parse(sdp);
6349
+
6350
+ const mediaWithCandidate = parsedSdp.media.find(media => !!media.candidates);
6351
+ if (!mediaWithCandidate) {
6352
+ return sdp;
6353
+ }
6354
+
6355
+ const { candidates } = mediaWithCandidate;
6356
+
6357
+ parsedSdp.media = parsedSdp.media.map(media => ({
6358
+ ...media,
6359
+ candidates: media.candidates || candidates,
6360
+ }));
6361
+
6362
+ return sdpParser.write(parsedSdp);
6363
+ };
6364
+
6207
6365
  /* global RTCSessionDescriptionInit, navigator */
6208
6366
 
6209
6367
  const wazoLogger = IssueReporter$1 ? IssueReporter$1.loggerFor('webrtc-sdh') : console;
@@ -6333,7 +6491,10 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
6333
6491
 
6334
6492
  // Check if we got ICEs
6335
6493
  if (sdp.indexOf('a=candidate') !== -1) {
6336
- return description;
6494
+ return {
6495
+ type: description.type,
6496
+ sdp: addIcesInAllBundles(sdp),
6497
+ };
6337
6498
  }
6338
6499
 
6339
6500
  wazoLogger.info('No ICE candidates found in SDP, fixing it with gathered ices', this.gatheredCandidates);
@@ -7793,7 +7954,7 @@ class WebRTCClient extends Emitter {
7793
7954
  });
7794
7955
  }
7795
7956
 
7796
- changeAudioInputDevice(id , session , force ) {
7957
+ async changeAudioInputDevice(id , session , force ) {
7797
7958
  const currentId = this.getAudioDeviceId();
7798
7959
  logger$8.info('setting audio input device', { id, currentId, session: !!session });
7799
7960
 
@@ -7801,33 +7962,50 @@ class WebRTCClient extends Emitter {
7801
7962
  return null;
7802
7963
  }
7803
7964
 
7965
+ // in order to handle an audio track change for default devices
7966
+ // we need the actual id of the device (not 'default')
7967
+ let deviceId = id;
7968
+ if (id === 'default' && navigator.mediaDevices) {
7969
+ const devices = await navigator.mediaDevices.enumerateDevices();
7970
+ const defaultDevice = devices.find(device => device.deviceId === 'default');
7971
+ if (defaultDevice) {
7972
+ const deviceLabel = defaultDevice.label.replace('Default - ', '');
7973
+ const targetDevice = devices.find(device => device.label === deviceLabel);
7974
+ if (targetDevice) {
7975
+ deviceId = targetDevice.deviceId;
7976
+ if (!force && deviceId === currentId) {
7977
+ return null;
7978
+ }
7979
+ }
7980
+ }
7981
+ }
7982
+
7804
7983
  // let's update the local audio value
7805
7984
  if (this.audio) {
7806
7985
  this.audio = {
7807
7986
  deviceId: {
7808
- exact: id,
7987
+ exact: deviceId,
7809
7988
  },
7810
7989
  };
7811
7990
  }
7812
7991
 
7813
- if (session) {
7992
+ if (session && navigator.mediaDevices) {
7814
7993
  const sdh = session.sessionDescriptionHandler;
7815
7994
  const pc = sdh.peerConnection;
7816
7995
 
7817
- // $FlowFixMe
7818
- return navigator.mediaDevices.getUserMedia({ audio: { deviceId: { exact: id } } }).then(async stream => {
7819
- const audioTrack = stream.getAudioTracks()[0];
7820
- const sender = pc && pc.getSenders().find(s => audioTrack && s && s.track && s.track.kind === audioTrack.kind);
7996
+ const constraints = { audio: { deviceId: { exact: deviceId } } };
7997
+ const stream = await navigator.mediaDevices.getUserMedia(constraints);
7998
+ const audioTrack = stream.getAudioTracks()[0];
7999
+ const sender = pc && pc.getSenders().find(s => audioTrack && s && s.track && s.track.kind === audioTrack.kind);
7821
8000
 
7822
- if (sender) {
7823
- if (sender.track) {
7824
- audioTrack.enabled = sender.track.enabled;
7825
- }
7826
- sender.replaceTrack(audioTrack);
8001
+ if (sender) {
8002
+ if (sender.track) {
8003
+ audioTrack.enabled = sender.track.enabled;
7827
8004
  }
8005
+ sender.replaceTrack(audioTrack);
8006
+ }
7828
8007
 
7829
- return stream;
7830
- });
8008
+ return stream;
7831
8009
  }
7832
8010
  }
7833
8011
 
@@ -7847,14 +8025,14 @@ class WebRTCClient extends Emitter {
7847
8025
  return null;
7848
8026
  }
7849
8027
 
7850
- // let's update the local video value
7851
- if (this.video) {
7852
- this.video = {
7853
- deviceId: {
7854
- exact: id,
7855
- },
7856
- };
7857
- }
8028
+ // let's make sure we don't lose other video constraints settings -- width, height, frameRate...
8029
+ const videoObject = typeof this.video === 'object' ? this.video : {};
8030
+ this.video = {
8031
+ ...videoObject,
8032
+ deviceId: {
8033
+ exact: id,
8034
+ },
8035
+ };
7858
8036
  }
7859
8037
 
7860
8038
  changeSessionVideoInputDevice(id , session ) {
@@ -9456,9 +9634,7 @@ class WebRTCPhone extends Emitter {
9456
9634
 
9457
9635
  try {
9458
9636
  if (this.currentScreenShare.stream) {
9459
- this.currentScreenShare.stream.getTracks()
9460
- .filter(track => track.kind === 'video')
9461
- .forEach(track => track.stop());
9637
+ this.currentScreenShare.stream.getTracks().forEach(track => track.stop());
9462
9638
  }
9463
9639
 
9464
9640
  if (restoreLocalStream) {
@@ -11249,6 +11425,7 @@ const AGENT_UNPAUSED = 'agent_unpaused';
11249
11425
  const CONFERENCE_ADHOC_PARTICIPANT_LEFT = 'conference_adhoc_participant_left';
11250
11426
  const CONFERENCE_ADHOC_DELETED = 'conference_adhoc_deleted';
11251
11427
  const MEETING_USER_PROGRESS = 'meeting_user_progress';
11428
+ const MEETING_USER_GUEST_AUTHORIZATION_CREATED = 'meeting_user_guest_authorization_created';
11252
11429
 
11253
11430
  const BLACKLIST_EVENTS = [
11254
11431
  CHAT_MESSAGE_SENT,
@@ -11600,6 +11777,7 @@ WebSocketClient.eventLists = [
11600
11777
  CONFERENCE_ADHOC_PARTICIPANT_LEFT,
11601
11778
  CONFERENCE_ADHOC_DELETED,
11602
11779
  MEETING_USER_PROGRESS,
11780
+ MEETING_USER_GUEST_AUTHORIZATION_CREATED,
11603
11781
  ];
11604
11782
 
11605
11783
  var WebSocketClient$1 = /*#__PURE__*/Object.freeze({
@@ -11671,6 +11849,7 @@ var WebSocketClient$1 = /*#__PURE__*/Object.freeze({
11671
11849
  CONFERENCE_ADHOC_PARTICIPANT_LEFT: CONFERENCE_ADHOC_PARTICIPANT_LEFT,
11672
11850
  CONFERENCE_ADHOC_DELETED: CONFERENCE_ADHOC_DELETED,
11673
11851
  MEETING_USER_PROGRESS: MEETING_USER_PROGRESS,
11852
+ MEETING_USER_GUEST_AUTHORIZATION_CREATED: MEETING_USER_GUEST_AUTHORIZATION_CREATED,
11674
11853
  HEARTBEAT_ENGINE_VERSION: HEARTBEAT_ENGINE_VERSION,
11675
11854
  'default': WebSocketClient
11676
11855
  });
@@ -11775,6 +11954,7 @@ class Participant extends Emitter {
11775
11954
 
11776
11955
 
11777
11956
 
11957
+
11778
11958
 
11779
11959
 
11780
11960
 
@@ -11792,6 +11972,7 @@ class Participant extends Emitter {
11792
11972
 
11793
11973
 
11794
11974
 
11975
+
11795
11976
 
11796
11977
  /**
11797
11978
  * @param room Room The room where the participant is
@@ -11815,6 +11996,7 @@ class Participant extends Emitter {
11815
11996
  this.screensharing = false;
11816
11997
  this.isOnHold = false;
11817
11998
  this.extra = extra;
11999
+ this.banned = false;
11818
12000
 
11819
12001
  this.ON_UPDATED = 'participant/ON_UPDATED';
11820
12002
  this.ON_START_TALKING = 'participant/ON_START_TALKING';
@@ -11831,6 +12013,7 @@ class Participant extends Emitter {
11831
12013
  this.ON_EXTRA_CHANGE = 'participant/ON_EXTRA_CHANGE';
11832
12014
  this.ON_HOLD = 'participant/ON_HOLD';
11833
12015
  this.ON_RESUME = 'participant/ON_RESUME';
12016
+ this.ON_BAN = 'participant/ON_BAN';
11834
12017
  }
11835
12018
 
11836
12019
  triggerEvent(name , ...args ) {
@@ -11871,6 +12054,10 @@ class Participant extends Emitter {
11871
12054
  status.extra = this.extra;
11872
12055
  break;
11873
12056
  }
12057
+ case this.ON_BAN: {
12058
+ status.banned = true;
12059
+ break;
12060
+ }
11874
12061
  }
11875
12062
 
11876
12063
  if (broadcast) {
@@ -11973,6 +12160,11 @@ class Participant extends Emitter {
11973
12160
  this.triggerUpdate(this.ON_RESUME, broadcast);
11974
12161
  }
11975
12162
 
12163
+ onBan(broadcast = true) {
12164
+ this.banned = true;
12165
+ this.triggerUpdate(this.ON_BAN, broadcast);
12166
+ }
12167
+
11976
12168
  getStatus() {
11977
12169
  return {
11978
12170
  callId: this.callId,
@@ -12019,6 +12211,10 @@ class Participant extends Emitter {
12019
12211
  }
12020
12212
  }
12021
12213
 
12214
+ if (status.banned) {
12215
+ this.onBan(false);
12216
+ }
12217
+
12022
12218
  // Poor man's object comparison
12023
12219
  if (typeof status.extra !== 'undefined' && JSON.stringify(this.extra) !== JSON.stringify(status.extra)) {
12024
12220
  this.extra = { ...this.extra, ...status.extra };
@@ -12046,6 +12242,28 @@ class Participant extends Emitter {
12046
12242
  this.streams = streams;
12047
12243
  this.videoStreams = streams;
12048
12244
  }
12245
+
12246
+ async ban(apiRequestDelay = null) {
12247
+ const { meetingUuid } = this.room;
12248
+ if (!meetingUuid) {
12249
+ throw new Error('Attempting to ban a participant without a `meetingUuid`');
12250
+ }
12251
+
12252
+ // this notifies all that someone is being banned
12253
+ this.onBan(true);
12254
+
12255
+ // this allows to delay the actual ban, in order for the banned participant as well as others to react to the situation
12256
+ if (apiRequestDelay) {
12257
+ await this.delay(apiRequestDelay);
12258
+ }
12259
+
12260
+ // proceed with the actual kick
12261
+ return getApiClient().calld.banMeetingParticipant(meetingUuid, this.callId);
12262
+ }
12263
+
12264
+ async delay(ms ) {
12265
+ return new Promise(resolve => setTimeout(resolve, ms));
12266
+ }
12049
12267
  }
12050
12268
 
12051
12269
  //
@@ -12198,7 +12416,7 @@ class Room$1 extends Emitter {
12198
12416
  logger$2.info('connecting to room', { extension, audioOnly, room: !!room });
12199
12417
 
12200
12418
  if (!room) {
12201
- await Wazo.Phone.connect();
12419
+ await Wazo.Phone.connect({ media: constraints });
12202
12420
 
12203
12421
  const withCamera = constraints && !!constraints.video;
12204
12422
 
@@ -12643,7 +12861,7 @@ class Room$1 extends Emitter {
12643
12861
  const participant = this._getParticipantFromCallId(status.callId);
12644
12862
 
12645
12863
  if (participant) {
12646
- // we're received, so no need to broadcast, hence the input false arg
12864
+ // we're receiving, so no need to broadcast
12647
12865
  participant.updateStatus(status, false);
12648
12866
  }
12649
12867
  break;
@@ -12910,7 +13128,7 @@ class SipRoom extends Room$1 {
12910
13128
  logger$1.info('connecting to sip room', { extension, audioOnly, room: !!room });
12911
13129
 
12912
13130
  if (!room) {
12913
- await Wazo.Phone.connect();
13131
+ await Wazo.Phone.connect({ media: constraints });
12914
13132
 
12915
13133
  const withCamera = constraints && !!constraints.video;
12916
13134
 
@@ -0,0 +1,57 @@
1
+ //
2
+
3
+ const camelToUnderscore = (key ) => {
4
+ if (typeof key !== 'string') {
5
+ throw new Error('Input is not a string');
6
+ }
7
+ return key.charAt(0).toLowerCase() + key.substring(1).replace(/([A-Z])/g, '_$1').toLowerCase();
8
+ };
9
+
10
+ //
11
+
12
+ const convertKeysFromCamelToUnderscore = (args ) => {
13
+ if (!args || typeof args !== 'object' || Array.isArray(args)) {
14
+ throw new Error('Input is not an object');
15
+ }
16
+
17
+ return Object.keys(args).reduce((acc, key) => {
18
+ acc[camelToUnderscore(key)] = typeof args[key] === 'object' && !Array.isArray(args[key])
19
+ ? convertKeysFromCamelToUnderscore(args[key]) : args[key];
20
+ return acc;
21
+ }, {});
22
+ };
23
+
24
+ //
25
+
26
+ describe('object utils', () => {
27
+ describe('convertKeysFromCamelToUnderscore', () => {
28
+ it('should throw an error when input is not an object', () => {
29
+ const msg = 'Input is not an object';
30
+ expect(() => convertKeysFromCamelToUnderscore(true)).toThrow(msg);
31
+ expect(() => convertKeysFromCamelToUnderscore(null)).toThrow(msg);
32
+ expect(() => convertKeysFromCamelToUnderscore(undefined)).toThrow(msg);
33
+ expect(() => convertKeysFromCamelToUnderscore([])).toThrow(msg);
34
+ expect(() => convertKeysFromCamelToUnderscore('some-string')).toThrow(msg);
35
+ });
36
+
37
+ it('should convert camel-cased keys to underscore', () => {
38
+ let value = Math.random();
39
+ expect(convertKeysFromCamelToUnderscore({ LoremIpsum: value })).toEqual({ lorem_ipsum: value });
40
+ value = Math.random();
41
+ expect(convertKeysFromCamelToUnderscore({ loremIpsum: value })).toEqual({ lorem_ipsum: value });
42
+ value = Math.random();
43
+ expect(convertKeysFromCamelToUnderscore({ 'lorem-ipsum': value })).toEqual({ 'lorem-ipsum': value });
44
+ value = Math.random();
45
+ expect(convertKeysFromCamelToUnderscore({ L123: value })).toEqual({ l123: value });
46
+ });
47
+
48
+ it('should handle multi-leveled object', () => {
49
+ const value = Math.random();
50
+ expect(convertKeysFromCamelToUnderscore({ LoremIpsum: { DoloreSit: { Amet: value }, regular: 'reg' } }))
51
+ .toEqual({ lorem_ipsum: { dolore_sit: { amet: value }, regular: 'reg' } });
52
+
53
+ expect(convertKeysFromCamelToUnderscore({ LoremIpsum: { DoloreSit: { Amet: [value] }, regular: ['something'] } }))
54
+ .toEqual({ lorem_ipsum: { dolore_sit: { amet: [value] }, regular: ['something'] } });
55
+ });
56
+ });
57
+ });
@@ -98,6 +98,24 @@ const fixSdp = (sdp , candidates , forcePort = true)
98
98
  return fixed;
99
99
  };
100
100
 
101
+ const addIcesInAllBundles = (sdp ) => {
102
+ const parsedSdp = sdpParser.parse(sdp);
103
+
104
+ const mediaWithCandidate = parsedSdp.media.find(media => !!media.candidates);
105
+ if (!mediaWithCandidate) {
106
+ return sdp;
107
+ }
108
+
109
+ const { candidates } = mediaWithCandidate;
110
+
111
+ parsedSdp.media = parsedSdp.media.map(media => ({
112
+ ...media,
113
+ candidates: media.candidates || candidates,
114
+ }));
115
+
116
+ return sdpParser.write(parsedSdp);
117
+ };
118
+
101
119
  /* eslint-disable max-len */
102
120
 
103
121
  const goodSdp = `
@@ -262,4 +280,36 @@ describe('SDP utils', () => {
262
280
  expect(hasAnActiveVideo(inactiveVideo)).toBeFalsy();
263
281
  });
264
282
  });
283
+
284
+ describe('Adding candidate in all bundles ', () => {
285
+ it('should add candidate everywhere', async () => {
286
+ const audioWithoutCandidate = `
287
+ c=IN IP4 203.0.113.1
288
+ m=audio 54400 RTP/SAVPF 0 96
289
+ m=video 54400 RTP/SAVPF 0 96
290
+ a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host
291
+ a=candidate:3996450952 1 udp 41819903 14.72.21.2 65092 typ relay raddr 0.0.0.0 rport 0 generation 0 network-id 3 network-cost 10
292
+ `;
293
+ const fixedAudioWithoutCandidate = sdpParser.parse(addIcesInAllBundles(audioWithoutCandidate));
294
+
295
+ expect(fixedAudioWithoutCandidate.media[0].candidates[0].type).toBe('host');
296
+ expect(fixedAudioWithoutCandidate.media[0].candidates[1].type).toBe('relay');
297
+ expect(fixedAudioWithoutCandidate.media[1].candidates[0].type).toBe('host');
298
+ expect(fixedAudioWithoutCandidate.media[1].candidates[1].type).toBe('relay');
299
+
300
+ // It should not add candidate if already present
301
+ const audioWithCandidate = `
302
+ c=IN IP4 203.0.113.1
303
+ m=audio 54400 RTP/SAVPF 0 96
304
+ a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ srflx
305
+
306
+ m=video 54400 RTP/SAVPF 0 96
307
+ a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host
308
+ `;
309
+ const fixedAudioWithCandidate = sdpParser.parse(addIcesInAllBundles(audioWithCandidate));
310
+
311
+ expect(fixedAudioWithCandidate.media[0].candidates[0].type).toBe('srflx');
312
+ expect(fixedAudioWithCandidate.media[1].candidates[0].type).toBe('host');
313
+ });
314
+ });
265
315
  });
@@ -0,0 +1,30 @@
1
+ //
2
+
3
+ const camelToUnderscore = (key ) => {
4
+ if (typeof key !== 'string') {
5
+ throw new Error('Input is not a string');
6
+ }
7
+ return key.charAt(0).toLowerCase() + key.substring(1).replace(/([A-Z])/g, '_$1').toLowerCase();
8
+ };
9
+
10
+ describe('string utils', () => {
11
+ describe('camelToUnderscore', () => {
12
+ it('', () => {
13
+ expect(() => camelToUnderscore(null)).toThrow('Input is not a string');
14
+ expect(() => camelToUnderscore(undefined)).toThrow('Input is not a string');
15
+ expect(() => camelToUnderscore({})).toThrow('Input is not a string');
16
+ expect(() => camelToUnderscore([])).toThrow('Input is not a string');
17
+ });
18
+
19
+ it('should convert camel-cased string to underscore', () => {
20
+ expect(camelToUnderscore('loremIpsum')).toEqual('lorem_ipsum');
21
+ expect(camelToUnderscore('lorem-ipsum')).toEqual('lorem-ipsum');
22
+ expect(camelToUnderscore('')).toEqual('');
23
+ });
24
+
25
+ it('should not prefix string with an underscore', () => {
26
+ expect(camelToUnderscore('LoremIpsum')).toEqual('lorem_ipsum');
27
+ expect(camelToUnderscore('L123')).toEqual('l123');
28
+ });
29
+ });
30
+ });
@@ -0,0 +1,24 @@
1
+ //
2
+
3
+ const camelToUnderscore = (key ) => {
4
+ if (typeof key !== 'string') {
5
+ throw new Error('Input is not a string');
6
+ }
7
+ return key.charAt(0).toLowerCase() + key.substring(1).replace(/([A-Z])/g, '_$1').toLowerCase();
8
+ };
9
+
10
+ //
11
+
12
+ const convertKeysFromCamelToUnderscore = (args ) => {
13
+ if (!args || typeof args !== 'object' || Array.isArray(args)) {
14
+ throw new Error('Input is not an object');
15
+ }
16
+
17
+ return Object.keys(args).reduce((acc, key) => {
18
+ acc[camelToUnderscore(key)] = typeof args[key] === 'object' && !Array.isArray(args[key])
19
+ ? convertKeysFromCamelToUnderscore(args[key]) : args[key];
20
+ return acc;
21
+ }, {});
22
+ };
23
+
24
+ export { convertKeysFromCamelToUnderscore };
package/esm/utils/sdp.js CHANGED
@@ -122,4 +122,22 @@ const fixSdp = (sdp , candidates , forcePort = true)
122
122
  return fixed;
123
123
  };
124
124
 
125
- export { activateVideoModifier, areCandidateValid, deactivateVideoModifier, fixBundle, fixSdp, getCandidates, getVideoDirection, hasAnActiveVideo, isSdpValid, parseCandidate, toggleVideoDirection };
125
+ const addIcesInAllBundles = (sdp ) => {
126
+ const parsedSdp = sdpParser.parse(sdp);
127
+
128
+ const mediaWithCandidate = parsedSdp.media.find(media => !!media.candidates);
129
+ if (!mediaWithCandidate) {
130
+ return sdp;
131
+ }
132
+
133
+ const { candidates } = mediaWithCandidate;
134
+
135
+ parsedSdp.media = parsedSdp.media.map(media => ({
136
+ ...media,
137
+ candidates: media.candidates || candidates,
138
+ }));
139
+
140
+ return sdpParser.write(parsedSdp);
141
+ };
142
+
143
+ export { activateVideoModifier, addIcesInAllBundles, areCandidateValid, deactivateVideoModifier, fixBundle, fixSdp, getCandidates, getVideoDirection, hasAnActiveVideo, isSdpValid, parseCandidate, toggleVideoDirection };