@wazo/sdk 0.38.5 → 0.38.8

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 (135) hide show
  1. package/README.md +1 -1
  2. package/dist/wazo-sdk.js +2 -2
  3. package/esm/__tests__/api-client.test.js +148 -2
  4. package/esm/__tests__/web-rtc-client.test.js +303 -26
  5. package/esm/api/calld.js +14 -0
  6. package/esm/api/confd.js +134 -2
  7. package/esm/api/ctid-ng.js +6 -0
  8. package/esm/api-client.js +148 -2
  9. package/esm/checker/Checker.js +212 -26
  10. package/esm/checker/checks/aor.js +148 -2
  11. package/esm/checker/checks/api.js +148 -2
  12. package/esm/checker/checks/index.js +212 -26
  13. package/esm/checker/checks/wazo-websocket.js +2 -0
  14. package/esm/checker/checks/webrtc-transport.js +210 -26
  15. package/esm/checker/checks/webrtc.js +210 -26
  16. package/esm/domain/AdHocAPIConference.js +148 -2
  17. package/esm/domain/Call.js +6 -0
  18. package/esm/domain/CallSession.js +1 -1
  19. package/esm/domain/Features.js +148 -2
  20. package/esm/domain/Meeting.js +18 -0
  21. package/esm/domain/MeetingAuthorization.js +70 -0
  22. package/esm/domain/Phone/CTIPhone.js +149 -3
  23. package/esm/domain/Phone/WebRTCPhone.js +3 -3
  24. package/esm/domain/__tests__/Call.test.js +6 -0
  25. package/esm/domain/__tests__/CallSession.test.js +1 -1
  26. package/esm/domain/__tests__/IndirectTransfer.test.js +1 -1
  27. package/esm/domain/__tests__/Meeting.test.js +87 -1
  28. package/esm/domain/__tests__/MeetingAuthorization.test.js +109 -0
  29. package/esm/domain/__tests__/Room.test.js +1 -1
  30. package/esm/index.js +258 -32
  31. package/esm/lib/WazoSessionDescriptionHandler.js +22 -1
  32. package/esm/service/CallApi.js +148 -2
  33. package/esm/service/getApiClient.js +148 -2
  34. package/esm/simple/Auth.js +258 -32
  35. package/esm/simple/Configuration.js +258 -32
  36. package/esm/simple/Directory.js +148 -2
  37. package/esm/simple/Phone.js +258 -32
  38. package/esm/simple/Websocket.js +3 -0
  39. package/esm/simple/index.js +258 -32
  40. package/esm/simple/room/LocalParticipant.js +258 -32
  41. package/esm/simple/room/Participant.js +258 -32
  42. package/esm/simple/room/RemoteParticipant.js +258 -32
  43. package/esm/simple/room/Room.js +258 -32
  44. package/esm/simple/room/SipRoom.js +258 -32
  45. package/esm/simple/room/Stream.js +258 -32
  46. package/esm/simple/room/__tests__/Participant.test.js +14747 -0
  47. package/esm/simple/room/__tests__/RemoteParticipant.test.js +258 -32
  48. package/esm/simple/utils.js +258 -32
  49. package/esm/utils/__tests__/object.test.js +57 -0
  50. package/esm/utils/__tests__/sdp.test.js +50 -0
  51. package/esm/utils/__tests__/string.test.js +30 -0
  52. package/esm/utils/object.js +24 -0
  53. package/esm/utils/sdp.js +19 -1
  54. package/esm/utils/string.js +10 -0
  55. package/esm/web-rtc-client.js +210 -26
  56. package/esm/websocket-client.js +3 -1
  57. package/lib/__tests__/api-client.test.js +148 -2
  58. package/lib/__tests__/web-rtc-client.test.js +303 -26
  59. package/lib/api/calld.js +14 -0
  60. package/lib/api/confd.js +134 -2
  61. package/lib/api/ctid-ng.js +6 -0
  62. package/lib/api-client.js +148 -2
  63. package/lib/checker/Checker.js +212 -26
  64. package/lib/checker/checks/aor.js +148 -2
  65. package/lib/checker/checks/api.js +148 -2
  66. package/lib/checker/checks/index.js +212 -26
  67. package/lib/checker/checks/wazo-websocket.js +2 -0
  68. package/lib/checker/checks/webrtc-transport.js +210 -26
  69. package/lib/checker/checks/webrtc.js +210 -26
  70. package/lib/domain/AdHocAPIConference.js +148 -2
  71. package/lib/domain/Call.js +6 -0
  72. package/lib/domain/CallSession.js +1 -1
  73. package/lib/domain/Features.js +148 -2
  74. package/lib/domain/Meeting.js +18 -0
  75. package/lib/domain/MeetingAuthorization.js +79 -0
  76. package/lib/domain/Phone/CTIPhone.js +149 -3
  77. package/lib/domain/Phone/WebRTCPhone.js +3 -3
  78. package/lib/domain/__tests__/Call.test.js +6 -0
  79. package/lib/domain/__tests__/CallSession.test.js +1 -1
  80. package/lib/domain/__tests__/IndirectTransfer.test.js +1 -1
  81. package/lib/domain/__tests__/Meeting.test.js +87 -1
  82. package/lib/domain/__tests__/MeetingAuthorization.test.js +111 -0
  83. package/lib/domain/__tests__/Room.test.js +1 -1
  84. package/lib/index.js +258 -32
  85. package/lib/lib/WazoSessionDescriptionHandler.js +22 -1
  86. package/lib/service/CallApi.js +148 -2
  87. package/lib/service/getApiClient.js +148 -2
  88. package/lib/simple/Auth.js +258 -32
  89. package/lib/simple/Configuration.js +258 -32
  90. package/lib/simple/Directory.js +148 -2
  91. package/lib/simple/Phone.js +258 -32
  92. package/lib/simple/Websocket.js +3 -0
  93. package/lib/simple/index.js +258 -32
  94. package/lib/simple/room/LocalParticipant.js +258 -32
  95. package/lib/simple/room/Participant.js +258 -32
  96. package/lib/simple/room/RemoteParticipant.js +258 -32
  97. package/lib/simple/room/Room.js +258 -32
  98. package/lib/simple/room/SipRoom.js +258 -32
  99. package/lib/simple/room/Stream.js +258 -32
  100. package/lib/simple/room/__tests__/Participant.test.js +14757 -0
  101. package/lib/simple/room/__tests__/RemoteParticipant.test.js +258 -32
  102. package/lib/simple/utils.js +258 -32
  103. package/lib/utils/__tests__/object.test.js +59 -0
  104. package/lib/utils/__tests__/sdp.test.js +50 -0
  105. package/lib/utils/__tests__/string.test.js +32 -0
  106. package/lib/utils/object.js +28 -0
  107. package/lib/utils/sdp.js +19 -0
  108. package/lib/utils/string.js +14 -0
  109. package/lib/web-rtc-client.js +210 -26
  110. package/lib/websocket-client.js +3 -0
  111. package/package.json +2 -2
  112. package/src/__tests__/web-rtc-client.test.js +93 -0
  113. package/src/api/calld.js +8 -0
  114. package/src/api/confd.js +43 -2
  115. package/src/domain/Call.js +6 -0
  116. package/src/domain/CallSession.js +1 -1
  117. package/src/domain/Meeting.js +20 -0
  118. package/src/domain/MeetingAuthorization.js +67 -0
  119. package/src/domain/Phone/WebRTCPhone.js +2 -2
  120. package/src/domain/__tests__/Meeting.test.js +70 -1
  121. package/src/domain/__tests__/MeetingAuthorization.test.js +58 -0
  122. package/src/lib/WazoSessionDescriptionHandler.js +5 -2
  123. package/src/simple/room/Participant.js +40 -0
  124. package/src/simple/room/Room.js +2 -2
  125. package/src/simple/room/SipRoom.js +1 -1
  126. package/src/simple/room/__tests__/Participant.test.js +81 -0
  127. package/src/types/WebSocketMessage.js +7 -1
  128. package/src/utils/__tests__/object.test.js +35 -0
  129. package/src/utils/__tests__/sdp.test.js +33 -1
  130. package/src/utils/__tests__/string.test.js +23 -0
  131. package/src/utils/object.js +15 -0
  132. package/src/utils/sdp.js +18 -0
  133. package/src/utils/string.js +8 -0
  134. package/src/web-rtc-client.js +40 -23
  135. package/src/websocket-client.js +2 -0
package/src/api/confd.js CHANGED
@@ -1,10 +1,13 @@
1
1
  /* @flow */
2
2
  import ApiRequester from '../utils/api-requester';
3
3
  import type { UUID, ListConfdUsersResponse, ListApplicationsResponse } from '../domain/types';
4
+ import type { MeetingCreateArguments, MeetingUpdateArguments } from '../domain/Meeting';
4
5
  import Profile from '../domain/Profile';
5
6
  import SipLine from '../domain/SipLine';
6
7
  import ExternalApp from '../domain/ExternalApp';
7
8
  import Meeting from '../domain/Meeting';
9
+ import MeetingAuthorization from '../domain/MeetingAuthorization';
10
+ import { convertKeysFromCamelToUnderscore } from '../utils/object';
8
11
 
9
12
  export default (client: ApiRequester, baseUrl: string) => ({
10
13
  listUsers: (): Promise<ListConfdUsersResponse> => client.get(`${baseUrl}/users`, null),
@@ -72,8 +75,18 @@ export default (client: ApiRequester, baseUrl: string) => ({
72
75
  getMyMeetings: (): Promise<Meeting> =>
73
76
  client.get(`${baseUrl}/users/me/meetings`).then(response => Meeting.parseMany(response.items)),
74
77
 
75
- createMyMeeting: (name: string, persistent: boolean): Promise<Meeting> =>
76
- client.post(`${baseUrl}/users/me/meetings`, { name, persistent }).then(Meeting.parse),
78
+ createMyMeeting: (args: MeetingCreateArguments): Promise<Meeting> =>
79
+ client
80
+ .post(`${baseUrl}/users/me/meetings`, convertKeysFromCamelToUnderscore(args))
81
+ .then(Meeting.parse),
82
+
83
+ updateMyMeeting: (meetingUuid: string, data: MeetingUpdateArguments): Promise<Boolean> =>
84
+ client.put(
85
+ `${baseUrl}/users/me/meetings/${meetingUuid}`,
86
+ convertKeysFromCamelToUnderscore(data),
87
+ null,
88
+ ApiRequester.successResponseParser,
89
+ ),
77
90
 
78
91
  deleteMyMeeting: (meetingUuid: string): Promise<Meeting> =>
79
92
  client.delete(`${baseUrl}/users/me/meetings/${meetingUuid}`, null),
@@ -81,6 +94,34 @@ export default (client: ApiRequester, baseUrl: string) => ({
81
94
  getMeeting: (meetingUuid: string): Promise<Meeting> =>
82
95
  client.get(`${baseUrl}/meetings/${meetingUuid}`, null).then(Meeting.parse),
83
96
 
97
+ meetingAuthorizations: (meetingUuid: string): Promise<Array<MeetingAuthorization>> =>
98
+ client.get(`${baseUrl}/users/me/meetings/${meetingUuid}/authorizations`, null).then(MeetingAuthorization.parseMany),
99
+
100
+ meetingAuthorizationReject: (meetingUuid: string, authorizationUuid: string): Promise<Boolean> =>
101
+ client.put(
102
+ `${baseUrl}/users/me/meetings/${meetingUuid}/authorizations/${authorizationUuid}/reject`,
103
+ {},
104
+ null,
105
+ ApiRequester.successResponseParser,
106
+ ),
107
+
108
+ meetingAuthorizationAccept: (meetingUuid: string, authorizationUuid: string): Promise<Boolean> =>
109
+ client.put(
110
+ `${baseUrl}/users/me/meetings/${meetingUuid}/authorizations/${authorizationUuid}/accept`,
111
+ {},
112
+ null,
113
+ ApiRequester.successResponseParser,
114
+ ),
115
+
84
116
  guestGetMeeting: (meetingUuid: string): Promise<Meeting> =>
85
117
  client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}`, null).then(Meeting.parse),
118
+
119
+ guestAuthorizationRequest: (userUuid: string, meetingUuid: string, username: string): Promise<> =>
120
+ client
121
+ .post(`${baseUrl}/guests/${userUuid}/meetings/${meetingUuid}/authorizations`, { guest_name: username })
122
+ .then(MeetingAuthorization.parse),
123
+
124
+ guestAuthorizationCheck: (userUuid: string, meetingUuid: string, authorizationUuid: string): Promise<> =>
125
+ client.get(`${baseUrl}/guests/${userUuid}/meetings/${meetingUuid}/authorizations/${authorizationUuid}`, null),
126
+
86
127
  });
@@ -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?
@@ -3,6 +3,19 @@ import moment from 'moment';
3
3
 
4
4
  import newFrom from '../utils/new-from';
5
5
 
6
+ import MeetingAuthorization from './MeetingAuthorization';
7
+
8
+ export type MeetingCreateArguments = {
9
+ name: string,
10
+ requireAuthorization: boolean,
11
+ persistent: boolean,
12
+ }
13
+
14
+ export type MeetingUpdateArguments = {
15
+ name?: string,
16
+ requireAuthorization?: boolean,
17
+ }
18
+
6
19
  export type MeetingCreationResponse = {
7
20
  guest_sip_authorization: string,
8
21
  ingress_http_uri: string,
@@ -13,6 +26,7 @@ export type MeetingCreationResponse = {
13
26
  exten: string,
14
27
  persistent: boolean,
15
28
  creation_time: string,
29
+ require_authorization: boolean,
16
30
  }
17
31
 
18
32
  export default class Meeting {
@@ -27,6 +41,8 @@ export default class Meeting {
27
41
  persistent: boolean;
28
42
  ownerUuids: Array<string>;
29
43
  creationTime: Date;
44
+ pendingAuthorizations: Array<MeetingAuthorization>
45
+ requireAuthorization: boolean;
30
46
 
31
47
  static parse(plain: MeetingCreationResponse): Meeting {
32
48
  return new Meeting({
@@ -39,6 +55,7 @@ export default class Meeting {
39
55
  extension: plain.exten,
40
56
  persistent: plain.persistent,
41
57
  creationTime: moment(plain.creation_time).toDate(),
58
+ requireAuthorization: plain.require_authorization,
42
59
  });
43
60
  }
44
61
 
@@ -63,6 +80,7 @@ export default class Meeting {
63
80
  extension,
64
81
  persistent,
65
82
  creationTime,
83
+ requireAuthorization,
66
84
  }: Object = {}) {
67
85
  this.guestSipAuthorization = guestSipAuthorization;
68
86
  this.uri = uri;
@@ -73,6 +91,7 @@ export default class Meeting {
73
91
  this.extension = extension;
74
92
  this.persistent = persistent;
75
93
  this.creationTime = creationTime;
94
+ this.requireAuthorization = requireAuthorization;
76
95
 
77
96
  // Useful to compare instead of instanceof with minified code
78
97
  this.type = 'Meeting';
@@ -83,4 +102,5 @@ export default class Meeting {
83
102
  const [username, secret] = atob(this.guestSipAuthorization).split(':');
84
103
  return { username, secret };
85
104
  }
105
+
86
106
  }
@@ -0,0 +1,67 @@
1
+ // @flow
2
+
3
+ export type RawMeetingAuthorization = {
4
+ created_at: string,
5
+ meeting_uuid: string,
6
+ uuid: string,
7
+ guest_uuid: string,
8
+ guest_name: string,
9
+ }
10
+
11
+ export const PENDING = 'pending';
12
+ export const ACCEPTED = 'accepted';
13
+ export const REJECTED = 'rejected';
14
+ export const POST_PROCESSING = 'post_processing';
15
+ export const POST_PROCESSED_SUCCESS = 'post_processed_success';
16
+ export const POST_PROCESSED_ERROR = 'post_processed_error';
17
+
18
+ export default class MeetingAuthorization {
19
+ meetingUuid: string;
20
+ uuid: string;
21
+ userUuid: string;
22
+ userName: string;
23
+ status: string;
24
+
25
+ constructor({
26
+ meetingUuid,
27
+ uuid,
28
+ userUuid,
29
+ userName,
30
+ }: Object = {}) {
31
+ this.meetingUuid = meetingUuid;
32
+ this.uuid = uuid;
33
+ this.userUuid = userUuid;
34
+ this.userName = userName;
35
+ }
36
+
37
+ setStatus(status: string) {
38
+ this.status = status;
39
+ }
40
+
41
+ getStatus() {
42
+ return this.status;
43
+ }
44
+
45
+ static parse(plain: RawMeetingAuthorization) {
46
+ const {
47
+ meeting_uuid: meetingUuid,
48
+ uuid,
49
+ guest_uuid: userUuid,
50
+ guest_name: userName,
51
+ } = plain;
52
+
53
+ return new MeetingAuthorization({
54
+ meetingUuid,
55
+ uuid,
56
+ userUuid,
57
+ userName,
58
+ });
59
+ }
60
+
61
+ static parseMany(items: RawMeetingAuthorization[]): Array<MeetingAuthorization> {
62
+ if (!items) {
63
+ return [];
64
+ }
65
+ return items.map(MeetingAuthorization.parse);
66
+ }
67
+ }
@@ -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) {
@@ -1,9 +1,76 @@
1
1
  // @flow
2
2
  /* global btoa */
3
3
 
4
+ import moment from 'moment';
4
5
  import Meeting from '../Meeting';
5
6
 
7
+ const rawCreationTime = '2021-12-21T15:56:42.880Z';
8
+
6
9
  describe('Meeting domain', () => {
10
+ it('should retain its values when parsed', () => {
11
+ const args = {
12
+ guest_sip_authorization: 'some-guest-sip-authorization',
13
+ ingress_http_uri: 'some-uri',
14
+ name: 'some-name',
15
+ owner_uuids: ['some-owner-uuid'],
16
+ port: 'some-port',
17
+ uuid: 'some-uuid',
18
+ exten: 'some-exten',
19
+ persistent: true,
20
+ creation_time: rawCreationTime,
21
+ require_authorization: true,
22
+ };
23
+
24
+ const meeting = Meeting.parse(args);
25
+ const {
26
+ uri,
27
+ uuid,
28
+ name,
29
+ port,
30
+ extension,
31
+ persistent,
32
+ ownerUuids,
33
+ creationTime,
34
+ requireAuthorization,
35
+ } = meeting;
36
+
37
+ expect(uri).toEqual(args.ingress_http_uri);
38
+ expect(uuid).toEqual(args.uuid);
39
+ expect(name).toEqual(args.name);
40
+ expect(port).toEqual(args.port);
41
+ expect(extension).toEqual(args.exten);
42
+ expect(persistent).toEqual(args.persistent);
43
+ expect(ownerUuids).toEqual(args.owner_uuids);
44
+ expect(creationTime).toEqual(moment(rawCreationTime).toDate());
45
+ expect(requireAuthorization).toEqual(args.require_authorization);
46
+ });
47
+
48
+ it('should retain its values when instantiated', () => {
49
+ const keys = [
50
+ 'uuid',
51
+ 'name',
52
+ 'guestSipAuthorization',
53
+ 'ownerUuids',
54
+ 'port',
55
+ 'uri',
56
+ 'extension',
57
+ 'persistent',
58
+ 'creationTime',
59
+ 'requireAuthorization',
60
+ ];
61
+
62
+ const args = keys.reduce((acc, key) => {
63
+ acc[key] = `some-${key}`;
64
+ return acc;
65
+ }, {});
66
+
67
+ const meeting = new Meeting(args);
68
+
69
+ keys.forEach(key =>
70
+ // $FlowFixMe
71
+ expect(meeting[key]).toEqual(`some-${key}`));
72
+ });
73
+
7
74
  it('can return username and secret from an base64 encrypted string', () => {
8
75
  const USERNAME = 'username';
9
76
  const SECRET = 'secret';
@@ -18,6 +85,7 @@ describe('Meeting domain', () => {
18
85
  uuid: '',
19
86
  exten: '',
20
87
  persistent: false,
88
+ require_authorization: false,
21
89
  };
22
90
 
23
91
  const meeting = Meeting.parse(args);
@@ -30,7 +98,7 @@ describe('Meeting domain', () => {
30
98
 
31
99
  it('should return a date from a string', () => {
32
100
  const args = {
33
- creation_time: '2021-12-21T15:56:42.880Z',
101
+ creation_time: rawCreationTime,
34
102
  guest_sip_authorization: '',
35
103
  ingress_http_uri: '',
36
104
  name: '',
@@ -39,6 +107,7 @@ describe('Meeting domain', () => {
39
107
  uuid: '',
40
108
  exten: '',
41
109
  persistent: false,
110
+ require_authorization: false,
42
111
  };
43
112
 
44
113
  const { creationTime } = Meeting.parse(args);
@@ -0,0 +1,58 @@
1
+ import MeetingAuthorization from '../MeetingAuthorization';
2
+
3
+ describe('MeetingAuthorization', () => {
4
+ describe('parse', () => {
5
+ it('should parse a raw input', () => {
6
+ const rawAuthorization = {
7
+ created_at: 'today',
8
+ meeting_uuid: 'meeting_uuid',
9
+ uuid: 'uuid',
10
+ guest_uuid: 'guest_uuid',
11
+ guest_name: 'guest_name',
12
+ };
13
+
14
+ const authorization = {
15
+ meetingUuid: rawAuthorization.meeting_uuid,
16
+ uuid: rawAuthorization.uuid,
17
+ userUuid: rawAuthorization.guest_uuid,
18
+ userName: rawAuthorization.guest_name,
19
+ status: undefined,
20
+ };
21
+
22
+ const response = MeetingAuthorization.parse(rawAuthorization);
23
+ expect(response).toEqual(authorization);
24
+ });
25
+ });
26
+
27
+ describe('parseMany', () => {
28
+ it('should parse multiple raw inputs', () => {
29
+ const rawAuthorizations = [
30
+ {
31
+ created_at: 'today1',
32
+ meeting_uuid: 'meeting_uuid1',
33
+ uuid: 'uuid1',
34
+ guest_uuid: 'guest_uuid1',
35
+ guest_name: 'guest_name1',
36
+ },
37
+ {
38
+ created_at: 'today2',
39
+ meeting_uuid: 'meeting_uuid2',
40
+ uuid: 'uuid2',
41
+ guest_uuid: 'guest_uuid2',
42
+ guest_name: 'guest_name2',
43
+ },
44
+ ];
45
+
46
+ const authorizations = rawAuthorizations.map(rawAuthorization => ({
47
+ meetingUuid: rawAuthorization.meeting_uuid,
48
+ uuid: rawAuthorization.uuid,
49
+ userUuid: rawAuthorization.guest_uuid,
50
+ userName: rawAuthorization.guest_name,
51
+ status: undefined,
52
+ }));
53
+
54
+ const response = MeetingAuthorization.parseMany(rawAuthorizations);
55
+ expect(response).toEqual(authorizations);
56
+ });
57
+ });
58
+ });
@@ -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
 
@@ -141,7 +141,10 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
141
141
 
142
142
  // Check if we got ICEs
143
143
  if (sdp.indexOf('a=candidate') !== -1) {
144
- return description;
144
+ return {
145
+ type: description.type,
146
+ sdp: addIcesInAllBundles(sdp),
147
+ };
145
148
  }
146
149
 
147
150
  wazoLogger.info('No ICE candidates found in SDP, fixing it with gathered ices', this.gatheredCandidates);
@@ -4,6 +4,7 @@ import IssueReporter from '../../service/IssueReporter';
4
4
 
5
5
  import Room, { SIGNAL_TYPE_PARTICIPANT_UPDATE } from './Room';
6
6
  import Contact from '../../domain/Contact';
7
+ import getApiClient from '../../service/getApiClient';
7
8
 
8
9
  const logger = IssueReporter.loggerFor('room');
9
10
 
@@ -20,6 +21,7 @@ class Participant extends Emitter {
20
21
  videoMuted: boolean;
21
22
  screensharing: boolean;
22
23
  isOnHold: boolean;
24
+ banned: boolean;
23
25
  extra: Object;
24
26
 
25
27
  ON_UPDATED: string;
@@ -37,6 +39,7 @@ class Participant extends Emitter {
37
39
  ON_EXTRA_CHANGE: string;
38
40
  ON_HOLD: string;
39
41
  ON_RESUME: string;
42
+ ON_BAN: string;
40
43
 
41
44
  /**
42
45
  * @param room Room The room where the participant is
@@ -60,6 +63,7 @@ class Participant extends Emitter {
60
63
  this.screensharing = false;
61
64
  this.isOnHold = false;
62
65
  this.extra = extra;
66
+ this.banned = false;
63
67
 
64
68
  this.ON_UPDATED = 'participant/ON_UPDATED';
65
69
  this.ON_START_TALKING = 'participant/ON_START_TALKING';
@@ -76,6 +80,7 @@ class Participant extends Emitter {
76
80
  this.ON_EXTRA_CHANGE = 'participant/ON_EXTRA_CHANGE';
77
81
  this.ON_HOLD = 'participant/ON_HOLD';
78
82
  this.ON_RESUME = 'participant/ON_RESUME';
83
+ this.ON_BAN = 'participant/ON_BAN';
79
84
  }
80
85
 
81
86
  triggerEvent(name: string, ...args: any[]) {
@@ -116,6 +121,10 @@ class Participant extends Emitter {
116
121
  status.extra = this.extra;
117
122
  break;
118
123
  }
124
+ case this.ON_BAN: {
125
+ status.banned = true;
126
+ break;
127
+ }
119
128
  default:
120
129
  break;
121
130
  }
@@ -220,6 +229,11 @@ class Participant extends Emitter {
220
229
  this.triggerUpdate(this.ON_RESUME, broadcast);
221
230
  }
222
231
 
232
+ onBan(broadcast: boolean = true) {
233
+ this.banned = true;
234
+ this.triggerUpdate(this.ON_BAN, broadcast);
235
+ }
236
+
223
237
  getStatus() {
224
238
  return {
225
239
  callId: this.callId,
@@ -266,6 +280,10 @@ class Participant extends Emitter {
266
280
  }
267
281
  }
268
282
 
283
+ if (status.banned) {
284
+ this.onBan(false);
285
+ }
286
+
269
287
  // Poor man's object comparison
270
288
  if (typeof status.extra !== 'undefined' && JSON.stringify(this.extra) !== JSON.stringify(status.extra)) {
271
289
  this.extra = { ...this.extra, ...status.extra };
@@ -293,6 +311,28 @@ class Participant extends Emitter {
293
311
  this.streams = streams;
294
312
  this.videoStreams = streams;
295
313
  }
314
+
315
+ async ban(apiRequestDelay: ?number = null) {
316
+ const { meetingUuid } = this.room;
317
+ if (!meetingUuid) {
318
+ throw new Error('Attempting to ban a participant without a `meetingUuid`');
319
+ }
320
+
321
+ // this notifies all that someone is being banned
322
+ this.onBan(true);
323
+
324
+ // this allows to delay the actual ban, in order for the banned participant as well as others to react to the situation
325
+ if (apiRequestDelay) {
326
+ await this.delay(apiRequestDelay);
327
+ }
328
+
329
+ // proceed with the actual kick
330
+ return getApiClient().calld.banMeetingParticipant(meetingUuid, this.callId);
331
+ }
332
+
333
+ async delay(ms: number) {
334
+ return new Promise(resolve => setTimeout(resolve, ms));
335
+ }
296
336
  }
297
337
 
298
338
  export default Participant;
@@ -143,7 +143,7 @@ class Room extends Emitter {
143
143
  logger.info('connecting to room', { extension, audioOnly, room: !!room });
144
144
 
145
145
  if (!room) {
146
- await Wazo.Phone.connect();
146
+ await Wazo.Phone.connect({ media: constraints });
147
147
 
148
148
  const withCamera = constraints && !!constraints.video;
149
149
 
@@ -590,7 +590,7 @@ class Room extends Emitter {
590
590
  const participant: ?Participant = this._getParticipantFromCallId(status.callId);
591
591
 
592
592
  if (participant) {
593
- // we're received, so no need to broadcast, hence the input false arg
593
+ // we're receiving, so no need to broadcast
594
594
  participant.updateStatus(status, false);
595
595
  }
596
596
  break;
@@ -13,7 +13,7 @@ class SipRoom extends Room {
13
13
  logger.info('connecting to sip room', { extension, audioOnly, room: !!room });
14
14
 
15
15
  if (!room) {
16
- await Wazo.Phone.connect();
16
+ await Wazo.Phone.connect({ media: constraints });
17
17
 
18
18
  const withCamera = constraints && !!constraints.video;
19
19
 
@@ -0,0 +1,81 @@
1
+ // Using directly Wazo to avoid issues with require cycle
2
+ import getApiClient from '../../../service/getApiClient';
3
+ import Wazo from '../../index';
4
+ import Room from '../Room';
5
+
6
+ jest.mock('../../../service/getApiClient');
7
+
8
+ describe('Participant', () => {
9
+ describe('ban', () => {
10
+ it('should throw an error when there is no meeting', async () => {
11
+ const room = new Room();
12
+ const participant = new Wazo.RemoteParticipant(room, {
13
+ call_id: 'id',
14
+ caller_id_name: 'Alice',
15
+ caller_id_number: 'number',
16
+ });
17
+
18
+ await expect(async () => participant.ban())
19
+ .rejects.toThrow('Attempting to ban a participant without a `meetingUuid`');
20
+ });
21
+
22
+ it('should call the api', async () => {
23
+ const number = '1234';
24
+ const meetingUuid = 'some-meeting-uuid';
25
+ const callId = 'some-call-id';
26
+ const room = new Room();
27
+ room.setMeetingUuid(meetingUuid);
28
+
29
+ const banMeetingParticipant = jest.fn();
30
+
31
+ getApiClient.mockImplementation(() => ({
32
+ calld: {
33
+ banMeetingParticipant,
34
+ },
35
+ }));
36
+
37
+ const participant = new Wazo.RemoteParticipant(room, {
38
+ call_id: callId,
39
+ caller_id_name: 'Alice',
40
+ caller_id_number: number,
41
+ });
42
+
43
+ participant.onBan = jest.fn();
44
+
45
+ await participant.ban();
46
+ expect(banMeetingParticipant).toHaveBeenCalledWith(meetingUuid, callId);
47
+ expect(participant.onBan).toHaveBeenCalledWith(true);
48
+ });
49
+
50
+ it('should call the api with a delay', async () => {
51
+ const number = '1234';
52
+ const meetingUuid = 'some-meeting-uuid';
53
+ const callId = 'some-call-id';
54
+ const someDelay = 2;
55
+ const room = new Room();
56
+ room.setMeetingUuid(meetingUuid);
57
+
58
+ const banMeetingParticipant = jest.fn();
59
+
60
+ getApiClient.mockImplementation(() => ({
61
+ calld: {
62
+ banMeetingParticipant,
63
+ },
64
+ }));
65
+
66
+ const participant = new Wazo.RemoteParticipant(room, {
67
+ call_id: callId,
68
+ caller_id_name: 'Alice',
69
+ caller_id_number: number,
70
+ });
71
+
72
+ participant.delay = jest.fn();
73
+ participant.onBan = jest.fn();
74
+
75
+ await participant.ban(someDelay);
76
+ expect(banMeetingParticipant).toHaveBeenCalledWith(meetingUuid, callId);
77
+ expect(participant.delay).toHaveBeenCalledWith(someDelay);
78
+ expect(participant.onBan).toHaveBeenCalledWith(true);
79
+ });
80
+ });
81
+ });
@@ -8,6 +8,7 @@ import type {
8
8
  import type { ChatMessageResponse } from '../domain/ChatMessage';
9
9
  import ChatRoom from '../domain/ChatRoom';
10
10
  import Meeting from '../domain/Meeting';
11
+ import type MeetingAuthorization from '../domain/MeetingAuthorization';
11
12
 
12
13
  type WebSocketBaseMessage = {
13
14
  op: string,
@@ -189,6 +190,10 @@ type AgentPaused = WebSocketBaseMessage & { data: any, name: 'agent_paused' };
189
190
  type AgentUnpaused = WebSocketBaseMessage & { data: any, name: 'agent_unpaused' };
190
191
 
191
192
  type MeetingUserProgress = WebSocketBaseMessage & { data: Meeting, name: 'meeting_user_progress' };
193
+ type MeetingAuthorizationPending = WebSocketBaseMessage & {
194
+ data: MeetingAuthorization,
195
+ name: 'meeting_authorization_pending'
196
+ };
192
197
 
193
198
  export type WebSocketMessage =
194
199
  | CallCreated
@@ -223,4 +228,5 @@ export type WebSocketMessage =
223
228
  | AgentStatusUpdate
224
229
  | AgentPaused
225
230
  | AgentUnpaused
226
- | MeetingUserProgress;
231
+ | MeetingUserProgress
232
+ | MeetingAuthorizationPending;