@wazo/sdk 0.38.4 → 0.38.7

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 (99) hide show
  1. package/README.md +4 -4
  2. package/dist/wazo-sdk.js +2 -2
  3. package/esm/__tests__/api-client.test.js +6 -0
  4. package/esm/__tests__/web-rtc-client.test.js +88 -3
  5. package/esm/api/calld.js +6 -0
  6. package/esm/api/ctid-ng.js +6 -0
  7. package/esm/api-client.js +6 -0
  8. package/esm/checker/Checker.js +38 -3
  9. package/esm/checker/checks/aor.js +6 -0
  10. package/esm/checker/checks/api.js +6 -0
  11. package/esm/checker/checks/index.js +38 -3
  12. package/esm/checker/checks/webrtc-transport.js +38 -3
  13. package/esm/checker/checks/webrtc.js +38 -3
  14. package/esm/domain/AdHocAPIConference.js +6 -0
  15. package/esm/domain/Call.js +6 -0
  16. package/esm/domain/CallSession.js +1 -1
  17. package/esm/domain/Features.js +6 -0
  18. package/esm/domain/Phone/CTIPhone.js +7 -1
  19. package/esm/domain/Phone/WebRTCPhone.js +3 -3
  20. package/esm/domain/__tests__/Call.test.js +6 -0
  21. package/esm/domain/__tests__/CallSession.test.js +1 -1
  22. package/esm/domain/__tests__/IndirectTransfer.test.js +1 -1
  23. package/esm/domain/__tests__/Line.test.js +5 -5
  24. package/esm/domain/__tests__/Room.test.js +1 -1
  25. package/esm/index.js +42 -8
  26. package/esm/lib/WazoSessionDescriptionHandler.js +30 -1
  27. package/esm/service/CallApi.js +6 -0
  28. package/esm/service/getApiClient.js +6 -0
  29. package/esm/simple/Auth.js +42 -8
  30. package/esm/simple/Configuration.js +42 -8
  31. package/esm/simple/Directory.js +6 -0
  32. package/esm/simple/Phone.js +42 -8
  33. package/esm/simple/Softphone.js +1 -2
  34. package/esm/simple/index.js +42 -8
  35. package/esm/simple/room/LocalParticipant.js +42 -8
  36. package/esm/simple/room/Participant.js +42 -8
  37. package/esm/simple/room/RemoteParticipant.js +42 -8
  38. package/esm/simple/room/Room.js +42 -8
  39. package/esm/simple/room/SipRoom.js +42 -8
  40. package/esm/simple/room/Stream.js +42 -8
  41. package/esm/simple/room/__tests__/RemoteParticipant.test.js +42 -8
  42. package/esm/simple/utils.js +42 -8
  43. package/esm/utils/__tests__/sdp.test.js +50 -0
  44. package/esm/utils/sdp.js +19 -1
  45. package/esm/web-rtc-client.js +38 -3
  46. package/lib/__tests__/api-client.test.js +6 -0
  47. package/lib/__tests__/web-rtc-client.test.js +88 -3
  48. package/lib/api/calld.js +6 -0
  49. package/lib/api/ctid-ng.js +6 -0
  50. package/lib/api-client.js +6 -0
  51. package/lib/checker/Checker.js +38 -3
  52. package/lib/checker/checks/aor.js +6 -0
  53. package/lib/checker/checks/api.js +6 -0
  54. package/lib/checker/checks/index.js +38 -3
  55. package/lib/checker/checks/webrtc-transport.js +38 -3
  56. package/lib/checker/checks/webrtc.js +38 -3
  57. package/lib/domain/AdHocAPIConference.js +6 -0
  58. package/lib/domain/Call.js +6 -0
  59. package/lib/domain/CallSession.js +1 -1
  60. package/lib/domain/Features.js +6 -0
  61. package/lib/domain/Phone/CTIPhone.js +7 -1
  62. package/lib/domain/Phone/WebRTCPhone.js +3 -3
  63. package/lib/domain/__tests__/Call.test.js +6 -0
  64. package/lib/domain/__tests__/CallSession.test.js +1 -1
  65. package/lib/domain/__tests__/IndirectTransfer.test.js +1 -1
  66. package/lib/domain/__tests__/Line.test.js +5 -5
  67. package/lib/domain/__tests__/Room.test.js +1 -1
  68. package/lib/index.js +42 -8
  69. package/lib/lib/WazoSessionDescriptionHandler.js +30 -1
  70. package/lib/service/CallApi.js +6 -0
  71. package/lib/service/getApiClient.js +6 -0
  72. package/lib/simple/Auth.js +42 -8
  73. package/lib/simple/Configuration.js +42 -8
  74. package/lib/simple/Directory.js +6 -0
  75. package/lib/simple/Phone.js +42 -8
  76. package/lib/simple/Softphone.js +1 -2
  77. package/lib/simple/index.js +42 -8
  78. package/lib/simple/room/LocalParticipant.js +42 -8
  79. package/lib/simple/room/Participant.js +42 -8
  80. package/lib/simple/room/RemoteParticipant.js +42 -8
  81. package/lib/simple/room/Room.js +42 -8
  82. package/lib/simple/room/SipRoom.js +42 -8
  83. package/lib/simple/room/Stream.js +42 -8
  84. package/lib/simple/room/__tests__/RemoteParticipant.test.js +42 -8
  85. package/lib/simple/utils.js +42 -8
  86. package/lib/utils/__tests__/sdp.test.js +50 -0
  87. package/lib/utils/sdp.js +19 -0
  88. package/lib/web-rtc-client.js +38 -3
  89. package/package.json +1 -1
  90. package/src/__tests__/web-rtc-client.test.js +50 -0
  91. package/src/domain/Call.js +6 -0
  92. package/src/domain/CallSession.js +1 -1
  93. package/src/domain/Phone/WebRTCPhone.js +2 -2
  94. package/src/domain/__tests__/Line.test.js +5 -5
  95. package/src/lib/WazoSessionDescriptionHandler.js +13 -2
  96. package/src/simple/Softphone.js +1 -2
  97. package/src/utils/__tests__/sdp.test.js +33 -1
  98. package/src/utils/sdp.js +18 -0
  99. package/src/web-rtc-client.js +2 -2
@@ -789,6 +789,24 @@ const fixSdp = (sdp , candidates , forcePort = true)
789
789
  return fixed;
790
790
  };
791
791
 
792
+ const addIcesInAllBundles = (sdp ) => {
793
+ const parsedSdp = sdpParser__default['default'].parse(sdp);
794
+
795
+ const mediaWithCandidate = parsedSdp.media.find(media => !!media.candidates);
796
+ if (!mediaWithCandidate) {
797
+ return sdp;
798
+ }
799
+
800
+ const { candidates } = mediaWithCandidate;
801
+
802
+ parsedSdp.media = parsedSdp.media.map(media => ({
803
+ ...media,
804
+ candidates: media.candidates || candidates,
805
+ }));
806
+
807
+ return sdpParser__default['default'].write(parsedSdp);
808
+ };
809
+
792
810
  /* global RTCSessionDescriptionInit, navigator */
793
811
 
794
812
  const wazoLogger = IssueReporter$1 ? IssueReporter$1.loggerFor('webrtc-sdh') : console;
@@ -887,6 +905,14 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
887
905
  wazoLogger.trace('onicecandidate', event.candidate ? event.candidate.candidate : { done: true });
888
906
  if (event.candidate) {
889
907
  this.gatheredCandidates.push(parseCandidate(event.candidate.candidate));
908
+
909
+ // When receiving a `srflx` or a `relay` candidate, consider the negotiation done.
910
+ if (event.candidate.candidate.indexOf('srflx') !== -1 || event.candidate.candidate.indexOf('relay') !== -1) {
911
+ wazoLogger.info('A valid ice was found, triggering ice gathering complete callback.', {
912
+ ice: event.candidate.candidate,
913
+ });
914
+ this.iceGatheringComplete();
915
+ }
890
916
  }
891
917
  };
892
918
 
@@ -910,7 +936,10 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
910
936
 
911
937
  // Check if we got ICEs
912
938
  if (sdp.indexOf('a=candidate') !== -1) {
913
- return description;
939
+ return {
940
+ type: description.type,
941
+ sdp: addIcesInAllBundles(sdp),
942
+ };
914
943
  }
915
944
 
916
945
  wazoLogger.info('No ICE candidates found in SDP, fixing it with gathered ices', this.gatheredCandidates);
@@ -3610,6 +3639,7 @@ const RECORD_STATE_ACTIVE = 'active';
3610
3639
 
3611
3640
 
3612
3641
 
3642
+
3613
3643
 
3614
3644
 
3615
3645
 
@@ -3620,6 +3650,7 @@ const RECORD_STATE_ACTIVE = 'active';
3620
3650
 
3621
3651
 
3622
3652
 
3653
+
3623
3654
 
3624
3655
 
3625
3656
 
@@ -3648,6 +3679,7 @@ class Call {
3648
3679
 
3649
3680
 
3650
3681
 
3682
+
3651
3683
 
3652
3684
 
3653
3685
 
@@ -3672,6 +3704,7 @@ class Call {
3672
3704
  calleeNumber: plain.peer_caller_id_number,
3673
3705
  dialedExtension: plain.dialed_extension,
3674
3706
  isCaller: plain.is_caller,
3707
+ isVideo: plain.is_video,
3675
3708
  muted: plain.muted,
3676
3709
  onHold: plain.on_hold,
3677
3710
  status: plain.status,
@@ -3695,6 +3728,7 @@ class Call {
3695
3728
  calleeNumber,
3696
3729
  dialedExtension,
3697
3730
  isCaller,
3731
+ isVideo,
3698
3732
  lineId,
3699
3733
  muted,
3700
3734
  onHold,
@@ -3718,6 +3752,7 @@ class Call {
3718
3752
  this.startingTime = startingTime;
3719
3753
  this.talkingToIds = talkingToIds || [];
3720
3754
  this.recording = recording;
3755
+ this.isVideo = !!isVideo;
3721
3756
 
3722
3757
  // Useful to compare instead of instanceof with minified code
3723
3758
  this.type = 'Call';
@@ -6420,11 +6455,11 @@ class WebRTCClient extends Emitter {
6420
6455
  });
6421
6456
  }
6422
6457
 
6423
- changeAudioInputDevice(id , session ) {
6458
+ changeAudioInputDevice(id , session , force ) {
6424
6459
  const currentId = this.getAudioDeviceId();
6425
6460
  logger.info('setting audio input device', { id, currentId, session: !!session });
6426
6461
 
6427
- if (id === currentId) {
6462
+ if (!force && id === currentId) {
6428
6463
  return null;
6429
6464
  }
6430
6465
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wazo/sdk",
3
- "version": "0.38.4",
3
+ "version": "0.38.7",
4
4
  "description": "Wazo's JavaScript Software Development Kit.",
5
5
  "main": "dist/wazo-sdk.js",
6
6
  "author": "Wazo (http://wazo.io)",
@@ -43,3 +43,53 @@ describe('WebRTC client', () => {
43
43
  expect(client.isAudioMuted(oldKindUnmuted)).toBeFalsy();
44
44
  });
45
45
  });
46
+
47
+ describe('changeAudioInputDevice', () => {
48
+ const defaultId = 'default';
49
+ const deviceId = 'device1';
50
+ const constraints = { audio: { deviceId: { exact: defaultId } }, video: null };
51
+
52
+ const session = {
53
+ sessionDescriptionHandler: {
54
+ peerConnection: {
55
+ getSenders: () => [{ track: { kind: 'audio', enabled: true } }],
56
+ },
57
+ },
58
+ };
59
+
60
+ const stream = {};
61
+ const getAudioTracksMock = jest.fn(() => []);
62
+ Object.defineProperty(stream, 'getAudioTracks', {
63
+ value: getAudioTracksMock,
64
+ });
65
+
66
+ const getUserMediaMock = jest.fn(async () => {
67
+ return new Promise(resolve => {
68
+ resolve(stream);
69
+ });
70
+ });
71
+
72
+ Object.defineProperty(global.navigator, 'mediaDevices', {
73
+ value: {
74
+ getUserMedia: getUserMediaMock,
75
+ },
76
+ });
77
+
78
+ it('should change the audio input track if the provided id is different', async () => {
79
+ client.setMediaConstraints(constraints);
80
+ expect(client.getAudioDeviceId()).toBe(defaultId);
81
+ expect(client.changeAudioInputDevice(deviceId, session)).toBeTruthy();
82
+ });
83
+
84
+ it('should NOT change the audio input track if the provided id is the same', async () => {
85
+ client.setMediaConstraints(constraints);
86
+ expect(client.getAudioDeviceId()).toBe(defaultId);
87
+ expect(client.changeAudioInputDevice(defaultId, session)).toBeFalsy();
88
+ });
89
+
90
+ it('should change the audio input track if the provided id is the same and force param is TRUE', async () => {
91
+ client.setMediaConstraints(constraints);
92
+ expect(client.getAudioDeviceId()).toBe(defaultId);
93
+ expect(client.changeAudioInputDevice(defaultId, session, true)).toBeTruthy();
94
+ });
95
+ });
@@ -15,6 +15,7 @@ type CallResponse = {
15
15
  dialed_extension: string,
16
16
  status: string,
17
17
  is_caller: boolean,
18
+ is_video?: boolean,
18
19
  line_id: ?number,
19
20
  creation_time: string,
20
21
  on_hold: boolean,
@@ -27,6 +28,7 @@ type CallArguments = {
27
28
  id: string,
28
29
  sipCallId: string,
29
30
  isCaller: boolean,
31
+ isVideo?: boolean,
30
32
  callerName: string,
31
33
  callerNumber: string,
32
34
  calleeName: string,
@@ -53,6 +55,7 @@ export default class Call {
53
55
  dialedExtension: string;
54
56
  lineId: ?number;
55
57
  isCaller: boolean;
58
+ isVideo: boolean;
56
59
  onHold: boolean;
57
60
  muted: boolean;
58
61
  status: string;
@@ -77,6 +80,7 @@ export default class Call {
77
80
  calleeNumber: plain.peer_caller_id_number,
78
81
  dialedExtension: plain.dialed_extension,
79
82
  isCaller: plain.is_caller,
83
+ isVideo: plain.is_video,
80
84
  muted: plain.muted,
81
85
  onHold: plain.on_hold,
82
86
  status: plain.status,
@@ -100,6 +104,7 @@ export default class Call {
100
104
  calleeNumber,
101
105
  dialedExtension,
102
106
  isCaller,
107
+ isVideo,
103
108
  lineId,
104
109
  muted,
105
110
  onHold,
@@ -123,6 +128,7 @@ export default class Call {
123
128
  this.startingTime = startingTime;
124
129
  this.talkingToIds = talkingToIds || [];
125
130
  this.recording = recording;
131
+ this.isVideo = !!isVideo;
126
132
 
127
133
  // Useful to compare instead of instanceof with minified code
128
134
  this.type = 'Call';
@@ -390,7 +390,7 @@ export default class CallSession {
390
390
  ringing: call.isRinging(),
391
391
  answered: call.isUp(),
392
392
  answeredBySystem: call.isUp() && call.talkingToIds.length === 0,
393
- cameraEnabled: false,
393
+ cameraEnabled: call.isVideo,
394
394
  dialedExtension: call.dialedExtension,
395
395
  call,
396
396
  conference: false, // @FIXME?
@@ -668,10 +668,10 @@ export default class WebRTCPhone extends Emitter implements Phone {
668
668
  this.audioRingVolume = volume;
669
669
  }
670
670
 
671
- changeAudioInputDevice(id: string) {
671
+ changeAudioInputDevice(id: string, force: ?boolean) {
672
672
  logger.info('WebRTC phone - changeAudio input device', { deviceId: id });
673
673
 
674
- return this.client.changeAudioInputDevice(id, this.currentSipSession);
674
+ return this.client.changeAudioInputDevice(id, this.currentSipSession, force);
675
675
  }
676
676
 
677
677
  changeVideoInputDevice(id: ?string) {
@@ -11,7 +11,7 @@ describe('Line domain', () => {
11
11
  username: 'ipcor1pj',
12
12
  links: [
13
13
  {
14
- href: 'https://stack.dev.wazo.io/api/confd/1.1/endpoints/sip/19',
14
+ href: 'https://stack.example.com/api/confd/1.1/endpoints/sip/19',
15
15
  rel: 'endpoint_sip',
16
16
  },
17
17
  ],
@@ -25,7 +25,7 @@ describe('Line domain', () => {
25
25
  context: 'default',
26
26
  links: [
27
27
  {
28
- href: 'https://stack.dev.wazo.io/api/confd/1.1/extensions/59',
28
+ href: 'https://stack.example.com/api/confd/1.1/extensions/59',
29
29
  rel: 'extensions',
30
30
  },
31
31
  ],
@@ -33,7 +33,7 @@ describe('Line domain', () => {
33
33
  ],
34
34
  links: [
35
35
  {
36
- href: 'https://stack.dev.wazo.io/api/confd/1.1/lines/8',
36
+ href: 'https://stack.example.com/api/confd/1.1/lines/8',
37
37
  rel: 'lines',
38
38
  },
39
39
  ],
@@ -51,7 +51,7 @@ describe('Line domain', () => {
51
51
  context: 'default',
52
52
  links: [
53
53
  {
54
- href: 'https://stack.dev.wazo.io/api/confd/1.1/extensions/59',
54
+ href: 'https://stack.example.com/api/confd/1.1/extensions/59',
55
55
  rel: 'extensions',
56
56
  },
57
57
  ],
@@ -61,7 +61,7 @@ describe('Line domain', () => {
61
61
  id: 19,
62
62
  links: [
63
63
  {
64
- href: 'https://stack.dev.wazo.io/api/confd/1.1/endpoints/sip/19',
64
+ href: 'https://stack.example.com/api/confd/1.1/endpoints/sip/19',
65
65
  rel: 'endpoint_sip',
66
66
  },
67
67
  ],
@@ -12,7 +12,7 @@ import { SessionDescriptionHandler }
12
12
  import { SessionDescriptionHandlerOptions }
13
13
  from 'sip.js/lib/platform/web/session-description-handler/session-description-handler-options';
14
14
  import IssueReporter from '../service/IssueReporter';
15
- import { fixSdp, parseCandidate } from '../utils/sdp';
15
+ import { addIcesInAllBundles, fixSdp, parseCandidate } from '../utils/sdp';
16
16
 
17
17
  const wazoLogger = IssueReporter ? IssueReporter.loggerFor('webrtc-sdh') : console;
18
18
 
@@ -110,6 +110,14 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
110
110
  wazoLogger.trace('onicecandidate', event.candidate ? event.candidate.candidate : { done: true });
111
111
  if (event.candidate) {
112
112
  this.gatheredCandidates.push(parseCandidate(event.candidate.candidate));
113
+
114
+ // When receiving a `srflx` or a `relay` candidate, consider the negotiation done.
115
+ if (event.candidate.candidate.indexOf('srflx') !== -1 || event.candidate.candidate.indexOf('relay') !== -1) {
116
+ wazoLogger.info('A valid ice was found, triggering ice gathering complete callback.', {
117
+ ice: event.candidate.candidate,
118
+ });
119
+ this.iceGatheringComplete();
120
+ }
113
121
  }
114
122
  };
115
123
 
@@ -133,7 +141,10 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
133
141
 
134
142
  // Check if we got ICEs
135
143
  if (sdp.indexOf('a=candidate') !== -1) {
136
- return description;
144
+ return {
145
+ type: description.type,
146
+ sdp: addIcesInAllBundles(sdp),
147
+ };
137
148
  }
138
149
 
139
150
  wazoLogger.info('No ICE candidates found in SDP, fixing it with gathered ices', this.gatheredCandidates);
@@ -120,8 +120,7 @@ class Softphone {
120
120
  window.addEventListener('message', this._onMessage.bind(this), false);
121
121
 
122
122
  if (!server) {
123
- // eslint-disable-next-line no-param-reassign
124
- server = 'stack.dev.wazo.io';
123
+ throw new Error('`server` is not set');
125
124
  }
126
125
 
127
126
  const config: Object = { server };
@@ -9,7 +9,7 @@ import {
9
9
  fixSdp,
10
10
  fixBundle,
11
11
  toggleVideoDirection,
12
- hasAnActiveVideo,
12
+ hasAnActiveVideo, addIcesInAllBundles,
13
13
  } from '../sdp';
14
14
 
15
15
  const goodSdp = `
@@ -174,4 +174,36 @@ describe('SDP utils', () => {
174
174
  expect(hasAnActiveVideo(inactiveVideo)).toBeFalsy();
175
175
  });
176
176
  });
177
+
178
+ describe('Adding candidate in all bundles ', () => {
179
+ it('should add candidate everywhere', async () => {
180
+ const audioWithoutCandidate = `
181
+ c=IN IP4 203.0.113.1
182
+ m=audio 54400 RTP/SAVPF 0 96
183
+ m=video 54400 RTP/SAVPF 0 96
184
+ a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host
185
+ 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
186
+ `;
187
+ const fixedAudioWithoutCandidate = sdpParser.parse(addIcesInAllBundles(audioWithoutCandidate));
188
+
189
+ expect(fixedAudioWithoutCandidate.media[0].candidates[0].type).toBe('host');
190
+ expect(fixedAudioWithoutCandidate.media[0].candidates[1].type).toBe('relay');
191
+ expect(fixedAudioWithoutCandidate.media[1].candidates[0].type).toBe('host');
192
+ expect(fixedAudioWithoutCandidate.media[1].candidates[1].type).toBe('relay');
193
+
194
+ // It should not add candidate if already present
195
+ const audioWithCandidate = `
196
+ c=IN IP4 203.0.113.1
197
+ m=audio 54400 RTP/SAVPF 0 96
198
+ a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ srflx
199
+
200
+ m=video 54400 RTP/SAVPF 0 96
201
+ a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host
202
+ `;
203
+ const fixedAudioWithCandidate = sdpParser.parse(addIcesInAllBundles(audioWithCandidate));
204
+
205
+ expect(fixedAudioWithCandidate.media[0].candidates[0].type).toBe('srflx');
206
+ expect(fixedAudioWithCandidate.media[1].candidates[0].type).toBe('host');
207
+ });
208
+ });
177
209
  });
package/src/utils/sdp.js CHANGED
@@ -120,3 +120,21 @@ export const fixSdp = (sdp: string, candidates: Object[], forcePort: boolean = t
120
120
 
121
121
  return fixed;
122
122
  };
123
+
124
+ export const addIcesInAllBundles = (sdp: string) => {
125
+ const parsedSdp = sdpParser.parse(sdp);
126
+
127
+ const mediaWithCandidate = parsedSdp.media.find(media => !!media.candidates);
128
+ if (!mediaWithCandidate) {
129
+ return sdp;
130
+ }
131
+
132
+ const { candidates } = mediaWithCandidate;
133
+
134
+ parsedSdp.media = parsedSdp.media.map(media => ({
135
+ ...media,
136
+ candidates: media.candidates || candidates,
137
+ }));
138
+
139
+ return sdpParser.write(parsedSdp);
140
+ };
@@ -1070,11 +1070,11 @@ export default class WebRTCClient extends Emitter {
1070
1070
  });
1071
1071
  }
1072
1072
 
1073
- changeAudioInputDevice(id: string, session: ?Inviter) {
1073
+ changeAudioInputDevice(id: string, session: ?Inviter, force: ?boolean) {
1074
1074
  const currentId = this.getAudioDeviceId();
1075
1075
  logger.info('setting audio input device', { id, currentId, session: !!session });
1076
1076
 
1077
- if (id === currentId) {
1077
+ if (!force && id === currentId) {
1078
1078
  return null;
1079
1079
  }
1080
1080