@webex/plugin-meetings 3.8.1-next.2 → 3.8.1-next.21
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.
- package/README.md +26 -13
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +2 -1
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +35 -16
- package/dist/locus-info/index.js.map +1 -1
- package/dist/media/index.js +2 -2
- package/dist/media/index.js.map +1 -1
- package/dist/meeting/brbState.js +12 -12
- package/dist/meeting/brbState.js.map +1 -1
- package/dist/meeting/index.js +85 -78
- package/dist/meeting/index.js.map +1 -1
- package/dist/members/index.js +8 -6
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +3 -3
- package/dist/members/request.js.map +1 -1
- package/dist/members/util.js +18 -6
- package/dist/members/util.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +32 -2
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/index.js +5 -10
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/meeting/brbState.d.ts +0 -1
- package/dist/types/meeting/index.d.ts +12 -3
- package/dist/types/members/index.d.ts +8 -3
- package/dist/types/members/request.d.ts +1 -1
- package/dist/types/members/util.d.ts +5 -2
- package/dist/types/multistream/sendSlotManager.d.ts +16 -0
- package/dist/types/reachability/index.d.ts +2 -2
- package/dist/webinar/index.js +1 -1
- package/package.json +24 -24
- package/src/constants.ts +1 -0
- package/src/locus-info/index.ts +46 -19
- package/src/media/index.ts +2 -2
- package/src/meeting/brbState.ts +8 -7
- package/src/meeting/index.ts +48 -32
- package/src/members/index.ts +7 -5
- package/src/members/request.ts +2 -2
- package/src/members/util.ts +14 -3
- package/src/multistream/sendSlotManager.ts +34 -2
- package/src/reachability/index.ts +5 -13
- package/test/unit/spec/locus-info/index.js +140 -44
- package/test/unit/spec/media/index.ts +107 -0
- package/test/unit/spec/meeting/brbState.ts +11 -9
- package/test/unit/spec/meeting/index.js +131 -39
- package/test/unit/spec/members/index.js +32 -9
- package/test/unit/spec/members/request.js +2 -2
- package/test/unit/spec/members/utils.js +27 -7
- package/test/unit/spec/multistream/sendSlotManager.ts +59 -0
- package/test/unit/spec/reachability/index.ts +2 -6
package/package.json
CHANGED
@@ -43,13 +43,13 @@
|
|
43
43
|
"@webex/eslint-config-legacy": "0.0.0",
|
44
44
|
"@webex/jest-config-legacy": "0.0.0",
|
45
45
|
"@webex/legacy-tools": "0.0.0",
|
46
|
-
"@webex/plugin-meetings": "3.8.1-next.
|
47
|
-
"@webex/plugin-rooms": "3.8.
|
48
|
-
"@webex/test-helper-chai": "3.8.
|
49
|
-
"@webex/test-helper-mocha": "3.8.
|
50
|
-
"@webex/test-helper-mock-webex": "3.8.
|
51
|
-
"@webex/test-helper-retry": "3.8.
|
52
|
-
"@webex/test-helper-test-users": "3.8.
|
46
|
+
"@webex/plugin-meetings": "3.8.1-next.21",
|
47
|
+
"@webex/plugin-rooms": "3.8.1-next.4",
|
48
|
+
"@webex/test-helper-chai": "3.8.1-next.7",
|
49
|
+
"@webex/test-helper-mocha": "3.8.1-next.7",
|
50
|
+
"@webex/test-helper-mock-webex": "3.8.1-next.7",
|
51
|
+
"@webex/test-helper-retry": "3.8.1-next.7",
|
52
|
+
"@webex/test-helper-test-users": "3.8.1-next.7",
|
53
53
|
"chai": "^4.3.4",
|
54
54
|
"chai-as-promised": "^7.1.1",
|
55
55
|
"eslint": "^8.24.0",
|
@@ -61,23 +61,23 @@
|
|
61
61
|
"typescript": "^4.7.4"
|
62
62
|
},
|
63
63
|
"dependencies": {
|
64
|
-
"@webex/common": "3.8.
|
65
|
-
"@webex/event-dictionary-ts": "^1.0.
|
66
|
-
"@webex/internal-media-core": "2.
|
67
|
-
"@webex/internal-plugin-conversation": "3.8.
|
68
|
-
"@webex/internal-plugin-device": "3.8.
|
69
|
-
"@webex/internal-plugin-llm": "3.8.
|
70
|
-
"@webex/internal-plugin-mercury": "3.8.
|
71
|
-
"@webex/internal-plugin-metrics": "3.8.
|
72
|
-
"@webex/internal-plugin-support": "3.8.
|
73
|
-
"@webex/internal-plugin-user": "3.8.
|
74
|
-
"@webex/internal-plugin-voicea": "3.8.1-next.
|
75
|
-
"@webex/media-helpers": "3.8.
|
76
|
-
"@webex/plugin-people": "3.8.
|
77
|
-
"@webex/plugin-rooms": "3.8.
|
64
|
+
"@webex/common": "3.8.1-next.7",
|
65
|
+
"@webex/event-dictionary-ts": "^1.0.1819",
|
66
|
+
"@webex/internal-media-core": "2.18.4",
|
67
|
+
"@webex/internal-plugin-conversation": "3.8.1-next.7",
|
68
|
+
"@webex/internal-plugin-device": "3.8.1-next.7",
|
69
|
+
"@webex/internal-plugin-llm": "3.8.1-next.7",
|
70
|
+
"@webex/internal-plugin-mercury": "3.8.1-next.7",
|
71
|
+
"@webex/internal-plugin-metrics": "3.8.1-next.7",
|
72
|
+
"@webex/internal-plugin-support": "3.8.1-next.7",
|
73
|
+
"@webex/internal-plugin-user": "3.8.1-next.7",
|
74
|
+
"@webex/internal-plugin-voicea": "3.8.1-next.21",
|
75
|
+
"@webex/media-helpers": "3.8.1-next.10",
|
76
|
+
"@webex/plugin-people": "3.8.1-next.7",
|
77
|
+
"@webex/plugin-rooms": "3.8.1-next.4",
|
78
78
|
"@webex/ts-sdp": "^1.8.1",
|
79
|
-
"@webex/web-capabilities": "^1.
|
80
|
-
"@webex/webex-core": "3.8.
|
79
|
+
"@webex/web-capabilities": "^1.6.0",
|
80
|
+
"@webex/webex-core": "3.8.1-next.7",
|
81
81
|
"ampersand-collection": "^2.0.2",
|
82
82
|
"bowser": "^2.11.0",
|
83
83
|
"btoa": "^1.2.1",
|
@@ -93,5 +93,5 @@
|
|
93
93
|
"//": [
|
94
94
|
"TODO: upgrade jwt-decode when moving to node 18"
|
95
95
|
],
|
96
|
-
"version": "3.8.1-next.
|
96
|
+
"version": "3.8.1-next.21"
|
97
97
|
}
|
package/src/constants.ts
CHANGED
@@ -217,6 +217,7 @@ export const DIALER_REGEX = {
|
|
217
217
|
PHONE_NUMBER:
|
218
218
|
/^(?:(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?)?([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})(?:\s*(?:#|x\.?|ext\.?|extension)\s*(\d+))?$/,
|
219
219
|
E164_FORMAT: /^\+[1-9]\d{1,14}$/,
|
220
|
+
INTERNAL_NUMBER: /^\d{1,14}$/,
|
220
221
|
};
|
221
222
|
|
222
223
|
// eslint-disable-next-line max-len
|
package/src/locus-info/index.ts
CHANGED
@@ -99,6 +99,7 @@ export default class LocusInfo extends EventsScope {
|
|
99
99
|
private doLocusSync(meeting: any) {
|
100
100
|
let isDelta;
|
101
101
|
let url;
|
102
|
+
let meetingDestroyed = false;
|
102
103
|
|
103
104
|
if (this.locusParser.workingCopy.syncUrl) {
|
104
105
|
url = this.locusParser.workingCopy.syncUrl;
|
@@ -134,32 +135,56 @@ export default class LocusInfo extends EventsScope {
|
|
134
135
|
|
135
136
|
isDelta = false;
|
136
137
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
138
|
+
// Locus sometimes returns 403, for example if meeting has ended, no point trying the fallback to full sync in that case
|
139
|
+
if (e.statusCode !== 403) {
|
140
|
+
return meeting.meetingRequest.getLocusDTO({url: meeting.locusUrl}).catch((err) => {
|
141
|
+
LoggerProxy.logger.info(
|
142
|
+
'Locus-info:index#doLocusSync --> fallback full sync failed, destroying the meeting'
|
143
|
+
);
|
144
|
+
this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.LOCUS_DTO_SYNC_FAILED);
|
145
|
+
meetingDestroyed = true;
|
146
|
+
throw err;
|
147
|
+
});
|
148
|
+
}
|
149
|
+
LoggerProxy.logger.info(
|
150
|
+
'Locus-info:index#doLocusSync --> got 403 from Locus, skipping fallback to full sync, destroying the meeting'
|
151
|
+
);
|
152
|
+
} else {
|
153
|
+
LoggerProxy.logger.info(
|
154
|
+
'Locus-info:index#doLocusSync --> fallback full sync failed, destroying the meeting'
|
155
|
+
);
|
144
156
|
}
|
145
|
-
LoggerProxy.logger.info(
|
146
|
-
'Locus-info:index#doLocusSync --> fallback full sync failed, destroying the meeting'
|
147
|
-
);
|
148
157
|
this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.LOCUS_DTO_SYNC_FAILED);
|
158
|
+
meetingDestroyed = true;
|
149
159
|
throw e;
|
150
160
|
})
|
151
161
|
.then((res) => {
|
152
|
-
if (
|
153
|
-
if (
|
154
|
-
meeting.locusInfo.handleLocusDelta(res.body, meeting);
|
155
|
-
} else {
|
162
|
+
if (isEmpty(res.body)) {
|
163
|
+
if (isDelta) {
|
156
164
|
LoggerProxy.logger.info(
|
157
165
|
'Locus-info:index#doLocusSync --> received empty body from syncUrl, so we already have latest Locus DTO'
|
158
166
|
);
|
167
|
+
} else {
|
168
|
+
LoggerProxy.logger.info(
|
169
|
+
'Locus-info:index#doLocusSync --> received empty body from full DTO sync request'
|
170
|
+
);
|
159
171
|
}
|
160
|
-
|
161
|
-
|
172
|
+
|
173
|
+
return;
|
174
|
+
}
|
175
|
+
|
176
|
+
if (isDelta) {
|
177
|
+
if (res.body.baseSequence) {
|
178
|
+
meeting.locusInfo.handleLocusDelta(res.body, meeting);
|
179
|
+
|
180
|
+
return;
|
181
|
+
}
|
182
|
+
// in some cases Locus might return us full DTO even when we asked for a delta
|
183
|
+
LoggerProxy.logger.info(
|
184
|
+
'Locus-info:index#doLocusSync --> got full DTO when we asked for delta'
|
185
|
+
);
|
162
186
|
}
|
187
|
+
meeting.locusInfo.onFullLocus(res.body);
|
163
188
|
})
|
164
189
|
.catch((e) => {
|
165
190
|
LoggerProxy.logger.info(
|
@@ -176,9 +201,11 @@ export default class LocusInfo extends EventsScope {
|
|
176
201
|
});
|
177
202
|
})
|
178
203
|
.finally(() => {
|
179
|
-
|
180
|
-
|
181
|
-
|
204
|
+
if (!meetingDestroyed) {
|
205
|
+
// Notify parser to resume processing delta events.
|
206
|
+
// Any deltas in the queue that have now been superseded by this sync will simply be ignored
|
207
|
+
this.locusParser.resume();
|
208
|
+
}
|
182
209
|
});
|
183
210
|
}
|
184
211
|
|
package/src/media/index.ts
CHANGED
@@ -239,8 +239,8 @@ Media.createMediaConnection = (
|
|
239
239
|
screenShareAudio: shareAudioStream?.outputStream?.getTracks()[0], // TODO: add type for screenShareAudio in internal-media-core SPARK-446923
|
240
240
|
} as unknown,
|
241
241
|
direction: {
|
242
|
-
audio: Media.getDirection(
|
243
|
-
video: Media.getDirection(
|
242
|
+
audio: Media.getDirection(false, mediaDirection.receiveAudio, mediaDirection.sendAudio),
|
243
|
+
video: Media.getDirection(false, mediaDirection.receiveVideo, mediaDirection.sendVideo),
|
244
244
|
screenShareVideo: Media.getDirection(
|
245
245
|
false,
|
246
246
|
mediaDirection.receiveShare,
|
package/src/meeting/brbState.ts
CHANGED
@@ -58,7 +58,12 @@ export class BrbState {
|
|
58
58
|
public enable(enabled: boolean, sendSlotManager: SendSlotManager) {
|
59
59
|
this.state.client.enabled = enabled;
|
60
60
|
|
61
|
-
return this.applyClientStateToServer(sendSlotManager)
|
61
|
+
return this.applyClientStateToServer(sendSlotManager).finally(() => {
|
62
|
+
sendSlotManager.setSourceStateOverride(
|
63
|
+
MediaType.VideoMain,
|
64
|
+
this.state.client.enabled ? 'away' : null
|
65
|
+
);
|
66
|
+
});
|
62
67
|
}
|
63
68
|
|
64
69
|
/**
|
@@ -92,7 +97,7 @@ export class BrbState {
|
|
92
97
|
|
93
98
|
this.state.syncToServerInProgress = true;
|
94
99
|
|
95
|
-
return this.sendLocalBrbStateToServer(
|
100
|
+
return this.sendLocalBrbStateToServer()
|
96
101
|
.then(() => {
|
97
102
|
this.state.syncToServerInProgress = false;
|
98
103
|
|
@@ -120,10 +125,9 @@ export class BrbState {
|
|
120
125
|
/**
|
121
126
|
* Send the local brb state to the server
|
122
127
|
*
|
123
|
-
* @param {SendSlotManager} sendSlotManager
|
124
128
|
* @returns {Promise}
|
125
129
|
*/
|
126
|
-
private async sendLocalBrbStateToServer(
|
130
|
+
private async sendLocalBrbStateToServer() {
|
127
131
|
const {enabled} = this.state.client;
|
128
132
|
|
129
133
|
if (!this.meeting.isMultistream) {
|
@@ -153,9 +157,6 @@ export class BrbState {
|
|
153
157
|
deviceUrl: this.meeting.deviceUrl,
|
154
158
|
selfId: this.meeting.selfId,
|
155
159
|
})
|
156
|
-
.then(() => {
|
157
|
-
sendSlotManager.setSourceStateOverride(MediaType.VideoMain, enabled ? 'away' : null);
|
158
|
-
})
|
159
160
|
.catch((error) => {
|
160
161
|
LoggerProxy.logger.error('Meeting:brbState#sendLocalBrbStateToServer: Error ', error);
|
161
162
|
|
package/src/meeting/index.ts
CHANGED
@@ -231,6 +231,14 @@ export type AddMediaOptions = {
|
|
231
231
|
remoteMediaManagerConfig?: RemoteMediaManagerConfiguration; // applies only to multistream meetings
|
232
232
|
bundlePolicy?: BundlePolicy; // applies only to multistream meetings
|
233
233
|
allowMediaInLobby?: boolean; // allows adding media when in the lobby
|
234
|
+
additionalMediaOptions?: AdditionalMediaOptions; // allows adding additional options like send/receive audio/video
|
235
|
+
};
|
236
|
+
|
237
|
+
export type AdditionalMediaOptions = {
|
238
|
+
sendVideo?: boolean; // if not specified, default value of videoEnabled is used
|
239
|
+
receiveVideo?: boolean; // if not specified, default value of videoEnabled is used
|
240
|
+
sendAudio?: boolean; // if not specified, default value of audioEnabled true is used
|
241
|
+
receiveAudio?: boolean; // if not specified, default value of audioEnabled true is used
|
234
242
|
};
|
235
243
|
|
236
244
|
export type CallStateForMetrics = {
|
@@ -263,8 +271,9 @@ type FetchMeetingInfoParams = {
|
|
263
271
|
};
|
264
272
|
|
265
273
|
type MediaReachabilityMetrics = ReachabilityMetrics & {
|
266
|
-
|
267
|
-
|
274
|
+
subnet_reachable: boolean;
|
275
|
+
selected_cluster: string | null;
|
276
|
+
selected_subnet: string | null;
|
268
277
|
};
|
269
278
|
|
270
279
|
/**
|
@@ -2757,9 +2766,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
2757
2766
|
LOCUSINFO.EVENTS.CONTROLS_MEETING_TRANSCRIPTION_SPOKEN_LANGUAGE_UPDATED,
|
2758
2767
|
({spokenLanguage}) => {
|
2759
2768
|
if (spokenLanguage) {
|
2760
|
-
this.transcription
|
2769
|
+
if (this.transcription?.languageOptions) {
|
2770
|
+
this.transcription.languageOptions.currentSpokenLanguage = spokenLanguage;
|
2771
|
+
}
|
2761
2772
|
// @ts-ignore
|
2762
|
-
this.webex.internal.voicea.onSpokenLanguageUpdate(spokenLanguage);
|
2773
|
+
this.webex.internal.voicea.onSpokenLanguageUpdate(spokenLanguage, this.id);
|
2763
2774
|
|
2764
2775
|
Trigger.trigger(
|
2765
2776
|
this,
|
@@ -2768,7 +2779,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
2768
2779
|
function: 'setupLocusControlsListener',
|
2769
2780
|
},
|
2770
2781
|
EVENT_TRIGGERS.MEETING_TRANSCRIPTION_SPOKEN_LANGUAGE_UPDATED,
|
2771
|
-
{spokenLanguage}
|
2782
|
+
{spokenLanguage, meetingId: this.id}
|
2772
2783
|
);
|
2773
2784
|
}
|
2774
2785
|
}
|
@@ -3850,15 +3861,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
3850
3861
|
}
|
3851
3862
|
|
3852
3863
|
/**
|
3853
|
-
* Cancel an SIP call invitation made during a meeting
|
3864
|
+
* Cancel an SIP/phone call invitation made during a meeting
|
3854
3865
|
* @param {Object} invitee
|
3855
3866
|
* @param {String} invitee.memberId
|
3856
|
-
* @
|
3867
|
+
* @param {Boolean} [invitee.isInternalNumber] - When cancel phone invitation, if the number is internal
|
3868
|
+
* @returns {Promise} see #members.cancelInviteByMemberId
|
3857
3869
|
* @public
|
3858
3870
|
* @memberof Meeting
|
3859
3871
|
*/
|
3860
|
-
public
|
3861
|
-
return this.members.
|
3872
|
+
public cancelInviteByMemberId(invitee: {memberId: string; isInternalNumber?: boolean}) {
|
3873
|
+
return this.members.cancelInviteByMemberId(invitee);
|
3862
3874
|
}
|
3863
3875
|
|
3864
3876
|
/**
|
@@ -4915,11 +4927,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
4915
4927
|
|
4916
4928
|
// Only send restore event when it was disconnected before and for connected later
|
4917
4929
|
if (!this.hasWebsocketConnected) {
|
4918
|
-
// @ts-ignore
|
4919
|
-
this.webex.internal.newMetrics.submitClientEvent({
|
4920
|
-
name: 'client.mercury.connection.restored',
|
4921
|
-
options: {meetingId: this.id},
|
4922
|
-
});
|
4923
4930
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MERCURY_CONNECTION_RESTORED, {
|
4924
4931
|
correlation_id: this.correlationId,
|
4925
4932
|
});
|
@@ -4930,11 +4937,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
4930
4937
|
// @ts-ignore
|
4931
4938
|
this.webex.internal.mercury.on(OFFLINE, () => {
|
4932
4939
|
LoggerProxy.logger.error('Meeting:index#setMercuryListener --> Web socket offline');
|
4933
|
-
// @ts-ignore
|
4934
|
-
this.webex.internal.newMetrics.submitClientEvent({
|
4935
|
-
name: 'client.mercury.connection.lost',
|
4936
|
-
options: {meetingId: this.id},
|
4937
|
-
});
|
4938
4940
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MERCURY_CONNECTION_FAILURE, {
|
4939
4941
|
correlation_id: this.correlationId,
|
4940
4942
|
});
|
@@ -7764,8 +7766,21 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
7764
7766
|
shareVideoEnabled = true,
|
7765
7767
|
remoteMediaManagerConfig,
|
7766
7768
|
bundlePolicy = 'max-bundle',
|
7769
|
+
additionalMediaOptions = {},
|
7767
7770
|
} = options;
|
7768
7771
|
|
7772
|
+
const {
|
7773
|
+
sendVideo: rawSendVideo,
|
7774
|
+
receiveVideo: rawReceiveVideo,
|
7775
|
+
sendAudio: rawSendAudio,
|
7776
|
+
receiveAudio: rawReceiveAudio,
|
7777
|
+
} = additionalMediaOptions;
|
7778
|
+
|
7779
|
+
const sendVideo = videoEnabled && (rawSendVideo ?? true);
|
7780
|
+
const receiveVideo = videoEnabled && (rawReceiveVideo ?? true);
|
7781
|
+
const sendAudio = audioEnabled && (rawSendAudio ?? true);
|
7782
|
+
const receiveAudio = audioEnabled && (rawReceiveAudio ?? true);
|
7783
|
+
|
7769
7784
|
this.allowMediaInLobby = options?.allowMediaInLobby;
|
7770
7785
|
|
7771
7786
|
// If the user is unjoined or guest waiting in lobby dont allow the user to addMedia
|
@@ -7801,11 +7816,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
7801
7816
|
// when audioEnabled/videoEnabled is true, we set sendAudio/sendVideo to true even before any streams are published
|
7802
7817
|
// to avoid doing an extra SDP exchange when they are published for the first time
|
7803
7818
|
this.mediaProperties.setMediaDirection({
|
7804
|
-
sendAudio
|
7805
|
-
sendVideo
|
7819
|
+
sendAudio,
|
7820
|
+
sendVideo,
|
7806
7821
|
sendShare: false,
|
7807
|
-
receiveAudio
|
7808
|
-
receiveVideo
|
7822
|
+
receiveAudio,
|
7823
|
+
receiveVideo,
|
7809
7824
|
receiveShare: shareAudioEnabled || shareVideoEnabled,
|
7810
7825
|
});
|
7811
7826
|
|
@@ -9731,21 +9746,22 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
9731
9746
|
return total;
|
9732
9747
|
}, 0);
|
9733
9748
|
|
9749
|
+
const selectedSubnetFirstOctet = this.mediaServerIp?.split('.')[0];
|
9750
|
+
|
9734
9751
|
let isSubnetReachable = null;
|
9735
|
-
if (totalSuccessCases > 0) {
|
9736
|
-
|
9737
|
-
|
9752
|
+
if (totalSuccessCases > 0 && selectedSubnetFirstOctet) {
|
9753
|
+
isSubnetReachable =
|
9754
|
+
// @ts-ignore
|
9755
|
+
this.webex.meetings.reachability.isSubnetReachable(selectedSubnetFirstOctet);
|
9738
9756
|
}
|
9739
9757
|
|
9740
|
-
|
9741
|
-
if (this.mediaConnections && this.mediaConnections.length > 0) {
|
9742
|
-
selectedCluster = this.mediaConnections[0].mediaAgentCluster;
|
9743
|
-
}
|
9758
|
+
const selectedCluster = this.mediaConnections?.[0]?.mediaAgentCluster ?? null;
|
9744
9759
|
|
9745
9760
|
return {
|
9746
9761
|
...reachabilityMetrics,
|
9747
|
-
isSubnetReachable,
|
9748
|
-
selectedCluster,
|
9762
|
+
subnet_reachable: isSubnetReachable,
|
9763
|
+
selected_cluster: selectedCluster,
|
9764
|
+
selected_subnet: selectedSubnetFirstOctet ? `${selectedSubnetFirstOctet}.X.X.X` : null,
|
9749
9765
|
};
|
9750
9766
|
}
|
9751
9767
|
}
|
package/src/members/index.ts
CHANGED
@@ -846,12 +846,14 @@ export default class Members extends StatelessWebexPlugin {
|
|
846
846
|
}
|
847
847
|
|
848
848
|
/**
|
849
|
-
* Cancels an SIP call to the associated meeting
|
850
|
-
* @param {
|
849
|
+
* Cancels an SIP/phone call to the associated meeting
|
850
|
+
* @param {Object} invitee
|
851
|
+
* @param {String} invitee.memberId - The memberId of the invitee
|
852
|
+
* @param {Boolean} [invitee.isInternalNumber] - When cancel phone invitation, if the number is internal
|
851
853
|
* @returns {Promise}
|
852
854
|
* @memberof Members
|
853
855
|
*/
|
854
|
-
|
856
|
+
cancelInviteByMemberId(invitee: {memberId: string; isInternalNumber?: boolean}) {
|
855
857
|
if (!this.locusUrl) {
|
856
858
|
return Promise.reject(
|
857
859
|
new ParameterError('The associated locus url for this meeting object must be defined.')
|
@@ -862,9 +864,9 @@ export default class Members extends StatelessWebexPlugin {
|
|
862
864
|
new ParameterError('The invitee must be defined with a memberId property.')
|
863
865
|
);
|
864
866
|
}
|
865
|
-
const options = MembersUtil.
|
867
|
+
const options = MembersUtil.cancelInviteByMemberIdOptions(invitee, this.locusUrl);
|
866
868
|
|
867
|
-
return this.membersRequest.
|
869
|
+
return this.membersRequest.cancelInviteByMemberId(options);
|
868
870
|
}
|
869
871
|
|
870
872
|
/**
|
package/src/members/request.ts
CHANGED
@@ -285,14 +285,14 @@ export default class MembersRequest extends StatelessWebexPlugin {
|
|
285
285
|
* @throws {Error} if the options are not valid and complete, must have invitee with memberId AND locusUrl
|
286
286
|
* @memberof MembersRequest
|
287
287
|
*/
|
288
|
-
|
288
|
+
cancelInviteByMemberId(options: any) {
|
289
289
|
if (!options?.invitee?.memberId || !options?.locusUrl) {
|
290
290
|
throw new ParameterError(
|
291
291
|
'invitee must be passed and the associated locus url for this meeting object must be defined.'
|
292
292
|
);
|
293
293
|
}
|
294
294
|
|
295
|
-
const requestParams = MembersUtil.
|
295
|
+
const requestParams = MembersUtil.generateCancelInviteByMemberIdRequestParams(options);
|
296
296
|
|
297
297
|
return this.locusDeltaRequest(requestParams);
|
298
298
|
}
|
package/src/members/util.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import uuid from 'uuid';
|
2
|
+
import {has} from 'lodash';
|
2
3
|
import {
|
3
4
|
HTTP_VERBS,
|
4
5
|
CONTROLS,
|
@@ -47,6 +48,9 @@ const MembersUtil = {
|
|
47
48
|
address:
|
48
49
|
options.invitee.emailAddress || options.invitee.email || options.invitee.phoneNumber,
|
49
50
|
...(options.invitee.roles ? {roles: options.invitee.roles} : {}),
|
51
|
+
...(has(options.invitee, 'isInternalNumber')
|
52
|
+
? {isInternalNumber: options.invitee.isInternalNumber}
|
53
|
+
: {}),
|
50
54
|
},
|
51
55
|
],
|
52
56
|
alertIfActive: options.alertIfActive,
|
@@ -107,6 +111,10 @@ const MembersUtil = {
|
|
107
111
|
}
|
108
112
|
|
109
113
|
if (invitee.phoneNumber) {
|
114
|
+
if (invitee.isInternalNumber) {
|
115
|
+
return !DIALER_REGEX.INTERNAL_NUMBER.test(invitee.phoneNumber);
|
116
|
+
}
|
117
|
+
|
110
118
|
return !DIALER_REGEX.E164_FORMAT.test(invitee.phoneNumber);
|
111
119
|
}
|
112
120
|
|
@@ -371,17 +379,20 @@ const MembersUtil = {
|
|
371
379
|
return requestParams;
|
372
380
|
},
|
373
381
|
|
374
|
-
|
382
|
+
cancelInviteByMemberIdOptions: (invitee, locusUrl) => ({
|
375
383
|
invitee,
|
376
384
|
locusUrl,
|
377
385
|
}),
|
378
386
|
|
379
|
-
|
387
|
+
generateCancelInviteByMemberIdRequestParams: (options) => {
|
388
|
+
const {memberId, isInternalNumber} = options.invitee;
|
389
|
+
const hasIsInternalNumberProp = has(options.invitee, 'isInternalNumber');
|
380
390
|
const body = {
|
381
391
|
actionType: _REMOVE_,
|
382
392
|
invitees: [
|
383
393
|
{
|
384
|
-
address:
|
394
|
+
address: memberId,
|
395
|
+
...(hasIsInternalNumberProp ? {isInternalNumber} : {}),
|
385
396
|
},
|
386
397
|
],
|
387
398
|
};
|
@@ -7,10 +7,20 @@ import {
|
|
7
7
|
StreamState,
|
8
8
|
} from '@webex/internal-media-core';
|
9
9
|
|
10
|
+
/**
|
11
|
+
* This class is used to manage the sendSlots for the given media types.
|
12
|
+
*/
|
10
13
|
export default class SendSlotManager {
|
11
14
|
private readonly slots: Map<MediaType, SendSlot> = new Map();
|
12
15
|
private readonly LoggerProxy: any;
|
16
|
+
private readonly sourceStateOverrides: Map<MediaType, StreamState> = new Map();
|
13
17
|
|
18
|
+
/**
|
19
|
+
* Constructor for SendSlotManager
|
20
|
+
*
|
21
|
+
* @param {any} LoggerProxy is used to log the messages
|
22
|
+
* @constructor
|
23
|
+
*/
|
14
24
|
constructor(LoggerProxy: any) {
|
15
25
|
this.LoggerProxy = LoggerProxy;
|
16
26
|
}
|
@@ -93,7 +103,7 @@ export default class SendSlotManager {
|
|
93
103
|
public setSourceStateOverride(mediaType: MediaType, state: StreamState | null) {
|
94
104
|
if (mediaType !== MediaType.VideoMain) {
|
95
105
|
throw new Error(
|
96
|
-
`
|
106
|
+
`Invalid media type '${mediaType}'. Source state overrides are only applicable to ${MediaType.VideoMain}.`
|
97
107
|
);
|
98
108
|
}
|
99
109
|
|
@@ -103,17 +113,39 @@ export default class SendSlotManager {
|
|
103
113
|
throw new Error(`Slot for ${mediaType} does not exist`);
|
104
114
|
}
|
105
115
|
|
116
|
+
const currentStateOverride = this.getSourceStateOverride(mediaType);
|
117
|
+
if (currentStateOverride === state) {
|
118
|
+
return;
|
119
|
+
}
|
120
|
+
|
106
121
|
if (state) {
|
107
122
|
slot.setSourceStateOverride(state);
|
123
|
+
this.sourceStateOverrides.set(mediaType, state);
|
108
124
|
} else {
|
109
125
|
slot.clearSourceStateOverride();
|
126
|
+
this.sourceStateOverrides.delete(mediaType);
|
110
127
|
}
|
111
128
|
|
112
129
|
this.LoggerProxy.logger.info(
|
113
|
-
`
|
130
|
+
`SendSlotManager->setSourceStateOverride#set source state override for ${mediaType} to ${state}`
|
114
131
|
);
|
115
132
|
}
|
116
133
|
|
134
|
+
/**
|
135
|
+
* Gets the source state override for the given media type.
|
136
|
+
* @param {MediaType} mediaType - The type of media to get the source state override for.
|
137
|
+
* @returns {StreamState | null} - The current source state override or null if not set.
|
138
|
+
*/
|
139
|
+
private getSourceStateOverride(mediaType: MediaType): StreamState | null {
|
140
|
+
if (mediaType !== MediaType.VideoMain) {
|
141
|
+
throw new Error(
|
142
|
+
`Invalid media type '${mediaType}'. Source state overrides are only applicable to ${MediaType.VideoMain}.`
|
143
|
+
);
|
144
|
+
}
|
145
|
+
|
146
|
+
return this.sourceStateOverrides.get(mediaType) || null;
|
147
|
+
}
|
148
|
+
|
117
149
|
/**
|
118
150
|
* This method publishes the given stream to the sendSlot for the given mediaType
|
119
151
|
* @param {MediaType} mediaType MediaType of the sendSlot to which a stream needs to be published (AUDIO_MAIN/VIDEO_MAIN/AUDIO_SLIDES/VIDEO_SLIDES)
|
@@ -140,22 +140,14 @@ export default class Reachability extends EventsScope {
|
|
140
140
|
|
141
141
|
/**
|
142
142
|
* Checks if the given subnet is reachable
|
143
|
-
* @param {string}
|
143
|
+
* @param {string} selectedSubnetFirstOctet - selected subnet first octet, e.g. "10" for "10.X.X.X"
|
144
144
|
* @returns {boolean | null} true if reachable, false if not reachable, null if mediaServerIp is not provided
|
145
145
|
* @public
|
146
146
|
* @memberof Reachability
|
147
147
|
*/
|
148
|
-
public isSubnetReachable(
|
149
|
-
if (!mediaServerIp) {
|
150
|
-
LoggerProxy.logger.error(`Reachability:index#isSubnetReachable --> mediaServerIp is null`);
|
151
|
-
|
152
|
-
return null;
|
153
|
-
}
|
154
|
-
|
155
|
-
const subnetFirstOctet = mediaServerIp.split('.')[0];
|
156
|
-
|
148
|
+
public isSubnetReachable(selectedSubnetFirstOctet: string): boolean | null {
|
157
149
|
LoggerProxy.logger.info(
|
158
|
-
`Reachability:index#isSubnetReachable --> Looking for subnet: ${
|
150
|
+
`Reachability:index#isSubnetReachable --> Looking for subnet: ${selectedSubnetFirstOctet}.X.X.X`
|
159
151
|
);
|
160
152
|
|
161
153
|
const matchingReachedClusters = Object.values(this.clusterReachability).reduce(
|
@@ -167,7 +159,7 @@ export default class Reachability extends EventsScope {
|
|
167
159
|
const subnet = reachedSubnetsArray[i];
|
168
160
|
const reachedSubnetFirstOctet = subnet.split('.')[0];
|
169
161
|
|
170
|
-
if (
|
162
|
+
if (selectedSubnetFirstOctet === reachedSubnetFirstOctet) {
|
171
163
|
acc.add(cluster.name);
|
172
164
|
}
|
173
165
|
|
@@ -186,7 +178,7 @@ export default class Reachability extends EventsScope {
|
|
186
178
|
);
|
187
179
|
|
188
180
|
LoggerProxy.logger.info(
|
189
|
-
`Reachability:index#isSubnetReachable --> Found ${matchingReachedClusters.size} clusters that use the subnet ${
|
181
|
+
`Reachability:index#isSubnetReachable --> Found ${matchingReachedClusters.size} clusters that use the subnet ${selectedSubnetFirstOctet}.X.X.X`
|
190
182
|
);
|
191
183
|
|
192
184
|
return matchingReachedClusters.size > 0;
|