@webex/plugin-meetings 3.0.0-beta.261 → 3.0.0-beta.263
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/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/common/errors/no-meeting-info.js +51 -0
- package/dist/common/errors/no-meeting-info.js.map +1 -0
- package/dist/constants.js +8 -1
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/meeting/index.js +171 -129
- package/dist/meeting/index.js.map +1 -1
- package/dist/meetings/index.js +39 -21
- package/dist/meetings/index.js.map +1 -1
- package/dist/reachability/index.js +95 -33
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/common/errors/no-meeting-info.d.ts +14 -0
- package/dist/types/constants.d.ts +6 -0
- package/dist/types/meeting/index.d.ts +2 -2
- package/dist/types/meetings/index.d.ts +3 -1
- package/dist/types/reachability/index.d.ts +18 -1
- package/package.json +19 -19
- package/src/common/errors/no-meeting-info.ts +24 -0
- package/src/constants.ts +6 -0
- package/src/meeting/index.ts +32 -26
- package/src/meetings/index.ts +18 -4
- package/src/reachability/index.ts +64 -1
- package/test/unit/spec/meeting/index.js +23 -13
- package/test/unit/spec/meetings/index.js +73 -33
- package/test/unit/spec/reachability/index.ts +208 -2
package/src/meeting/index.ts
CHANGED
|
@@ -5817,12 +5817,16 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5817
5817
|
return Promise.resolve();
|
|
5818
5818
|
})
|
|
5819
5819
|
.then(() => this.mediaProperties.getCurrentConnectionType())
|
|
5820
|
-
.then((connectionType) => {
|
|
5820
|
+
.then(async (connectionType) => {
|
|
5821
|
+
// @ts-ignore
|
|
5822
|
+
const reachabilityStats = await this.webex.meetings.reachability.getReachabilityMetrics();
|
|
5823
|
+
|
|
5821
5824
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_MEDIA_SUCCESS, {
|
|
5822
5825
|
correlation_id: this.correlationId,
|
|
5823
5826
|
locus_id: this.locusUrl.split('/').pop(),
|
|
5824
5827
|
connectionType,
|
|
5825
5828
|
isMultistream: this.isMultistream,
|
|
5829
|
+
...reachabilityStats,
|
|
5826
5830
|
});
|
|
5827
5831
|
// @ts-ignore
|
|
5828
5832
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
@@ -5838,9 +5842,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5838
5842
|
// We can log ReceiveSlot SSRCs only after the SDP exchange, so doing it here:
|
|
5839
5843
|
this.remoteMediaManager?.logAllReceiveSlots();
|
|
5840
5844
|
})
|
|
5841
|
-
.catch((error) => {
|
|
5845
|
+
.catch(async (error) => {
|
|
5842
5846
|
LoggerProxy.logger.error(`${LOG_HEADER} failed to establish media connection: `, error);
|
|
5843
5847
|
|
|
5848
|
+
// @ts-ignore
|
|
5849
|
+
const reachabilityMetrics = await this.webex.meetings.reachability.getReachabilityMetrics();
|
|
5850
|
+
|
|
5844
5851
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
|
|
5845
5852
|
correlation_id: this.correlationId,
|
|
5846
5853
|
locus_id: this.locusUrl.split('/').pop(),
|
|
@@ -5865,38 +5872,37 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5865
5872
|
?.iceConnectionState ||
|
|
5866
5873
|
this.mediaProperties.webrtcMediaConnection?.mediaConnection?.pc?.iceConnectionState ||
|
|
5867
5874
|
'unknown',
|
|
5875
|
+
...reachabilityMetrics,
|
|
5868
5876
|
});
|
|
5869
5877
|
|
|
5870
5878
|
// Clean up stats analyzer, peer connection, and turn off listeners
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5879
|
+
if (this.statsAnalyzer) {
|
|
5880
|
+
await this.statsAnalyzer.stopAnalyzer();
|
|
5881
|
+
}
|
|
5874
5882
|
|
|
5875
|
-
|
|
5876
|
-
this.statsAnalyzer = null;
|
|
5883
|
+
this.statsAnalyzer = null;
|
|
5877
5884
|
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5885
|
+
if (this.mediaProperties.webrtcMediaConnection) {
|
|
5886
|
+
this.closePeerConnections();
|
|
5887
|
+
this.unsetPeerConnections();
|
|
5888
|
+
}
|
|
5882
5889
|
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5890
|
+
// Upload logs on error while adding media
|
|
5891
|
+
Trigger.trigger(
|
|
5892
|
+
this,
|
|
5893
|
+
{
|
|
5894
|
+
file: 'meeting/index',
|
|
5895
|
+
function: 'addMedia',
|
|
5896
|
+
},
|
|
5897
|
+
EVENTS.REQUEST_UPLOAD_LOGS,
|
|
5898
|
+
this
|
|
5899
|
+
);
|
|
5893
5900
|
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5901
|
+
if (error instanceof Errors.SdpError) {
|
|
5902
|
+
this.leave({reason: MEETING_REMOVED_REASON.MEETING_CONNECTION_FAILED});
|
|
5903
|
+
}
|
|
5897
5904
|
|
|
5898
|
-
|
|
5899
|
-
});
|
|
5905
|
+
throw error;
|
|
5900
5906
|
});
|
|
5901
5907
|
}
|
|
5902
5908
|
|
package/src/meetings/index.ts
CHANGED
|
@@ -61,6 +61,7 @@ import MeetingsUtil from './util';
|
|
|
61
61
|
import PermissionError from '../common/errors/permission';
|
|
62
62
|
import {INoiseReductionEffect, IVirtualBackgroundEffect} from './meetings.types';
|
|
63
63
|
import {SpaceIDDeprecatedError} from '../common/errors/webex-errors';
|
|
64
|
+
import NoMeetingInfoError from '../common/errors/no-meeting-info';
|
|
64
65
|
|
|
65
66
|
let mediaLogger;
|
|
66
67
|
|
|
@@ -1043,6 +1044,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
1043
1044
|
* @param {Boolean} useRandomDelayForInfo - whether a random delay should be added to fetching meeting info
|
|
1044
1045
|
* @param {Object} infoExtraParams extra parameters to be provided when fetching meeting info
|
|
1045
1046
|
* @param {string} correlationId - the optional specified correlationId
|
|
1047
|
+
* @param {Boolean} failOnMissingMeetingInfo - whether to throw an error if meeting info fails to fetch (for calls that are not 1:1 or content share)
|
|
1046
1048
|
* @returns {Promise<Meeting>} A new Meeting.
|
|
1047
1049
|
* @public
|
|
1048
1050
|
* @memberof Meetings
|
|
@@ -1052,7 +1054,8 @@ export default class Meetings extends WebexPlugin {
|
|
|
1052
1054
|
type: string = null,
|
|
1053
1055
|
useRandomDelayForInfo = false,
|
|
1054
1056
|
infoExtraParams = {},
|
|
1055
|
-
correlationId: string = undefined
|
|
1057
|
+
correlationId: string = undefined,
|
|
1058
|
+
failOnMissingMeetingInfo = false
|
|
1056
1059
|
) {
|
|
1057
1060
|
// TODO: type should be from a dictionary
|
|
1058
1061
|
|
|
@@ -1106,7 +1109,8 @@ export default class Meetings extends WebexPlugin {
|
|
|
1106
1109
|
type,
|
|
1107
1110
|
useRandomDelayForInfo,
|
|
1108
1111
|
infoExtraParams,
|
|
1109
|
-
correlationId
|
|
1112
|
+
correlationId,
|
|
1113
|
+
failOnMissingMeetingInfo
|
|
1110
1114
|
).then((createdMeeting: any) => {
|
|
1111
1115
|
// If the meeting was successfully created.
|
|
1112
1116
|
if (createdMeeting && createdMeeting.on) {
|
|
@@ -1161,6 +1165,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
1161
1165
|
* @param {Boolean} useRandomDelayForInfo whether a random delay should be added to fetching meeting info
|
|
1162
1166
|
* @param {Object} infoExtraParams extra parameters to be provided when fetching meeting info
|
|
1163
1167
|
* @param {String} correlationId the optional specified correlationId
|
|
1168
|
+
* @param {Boolean} failOnMissingMeetingInfo - whether to throw an error if meeting info fails to fetch (for calls that are not 1:1 or content share)
|
|
1164
1169
|
* @returns {Promise} a new meeting instance complete with meeting info and destination
|
|
1165
1170
|
* @private
|
|
1166
1171
|
* @memberof Meetings
|
|
@@ -1170,7 +1175,8 @@ export default class Meetings extends WebexPlugin {
|
|
|
1170
1175
|
type: string = null,
|
|
1171
1176
|
useRandomDelayForInfo = false,
|
|
1172
1177
|
infoExtraParams = {},
|
|
1173
|
-
correlationId: string = undefined
|
|
1178
|
+
correlationId: string = undefined,
|
|
1179
|
+
failOnMissingMeetingInfo = false
|
|
1174
1180
|
) {
|
|
1175
1181
|
const meeting = new Meeting(
|
|
1176
1182
|
{
|
|
@@ -1239,10 +1245,18 @@ export default class Meetings extends WebexPlugin {
|
|
|
1239
1245
|
!(err instanceof PasswordError) &&
|
|
1240
1246
|
!(err instanceof PermissionError)
|
|
1241
1247
|
) {
|
|
1242
|
-
// if there is no meeting info we assume its a 1:1 call or wireless share
|
|
1243
1248
|
LoggerProxy.logger.info(
|
|
1244
1249
|
`Meetings:index#createMeeting --> Info Unable to fetch meeting info for ${destination}.`
|
|
1245
1250
|
);
|
|
1251
|
+
if (failOnMissingMeetingInfo) {
|
|
1252
|
+
LoggerProxy.logger.info(
|
|
1253
|
+
`Meetings:index#createMeeting --> Destroying meeting due to missing meeting info.`
|
|
1254
|
+
);
|
|
1255
|
+
// @ts-ignore
|
|
1256
|
+
this.destroy(meeting, MEETING_REMOVED_REASON.MISSING_MEETING_INFO);
|
|
1257
|
+
throw new NoMeetingInfoError();
|
|
1258
|
+
}
|
|
1259
|
+
// if there is no meeting info and no error should be thrown then we assume its a 1:1 call or wireless share
|
|
1246
1260
|
LoggerProxy.logger.info(
|
|
1247
1261
|
'Meetings:index#createMeeting --> Info assuming this destination is a 1:1 or wireless share'
|
|
1248
1262
|
);
|
|
@@ -16,9 +16,20 @@ import ReachabilityRequest from './request';
|
|
|
16
16
|
const DEFAULT_TIMEOUT = 3000;
|
|
17
17
|
const VIDEO_MESH_TIMEOUT = 1000;
|
|
18
18
|
|
|
19
|
+
export type ReachabilityMetrics = {
|
|
20
|
+
reachability_public_udp_success: number;
|
|
21
|
+
reachability_public_udp_failed: number;
|
|
22
|
+
reachability_public_tcp_success: number;
|
|
23
|
+
reachability_public_tcp_failed: number;
|
|
24
|
+
reachability_vmn_udp_success: number;
|
|
25
|
+
reachability_vmn_udp_failed: number;
|
|
26
|
+
reachability_vmn_tcp_success: number;
|
|
27
|
+
reachability_vmn_tcp_failed: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
19
30
|
// result for a specific transport protocol (like udp or tcp)
|
|
20
31
|
export type TransportResult = {
|
|
21
|
-
reachable
|
|
32
|
+
reachable?: 'true' | 'false';
|
|
22
33
|
latencyInMilliseconds?: string;
|
|
23
34
|
clientMediaIPs?: string[];
|
|
24
35
|
untested?: 'true';
|
|
@@ -138,6 +149,58 @@ export default class Reachability {
|
|
|
138
149
|
}
|
|
139
150
|
}
|
|
140
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Returns statistics about last reachability results. The returned value is an object
|
|
154
|
+
* with a flat list of properties so that it can be easily sent with metrics
|
|
155
|
+
*
|
|
156
|
+
* @returns {Promise} Promise with metrics values, it never rejects/throws.
|
|
157
|
+
*/
|
|
158
|
+
async getReachabilityMetrics(): Promise<ReachabilityMetrics> {
|
|
159
|
+
const stats: ReachabilityMetrics = {
|
|
160
|
+
reachability_public_udp_success: 0,
|
|
161
|
+
reachability_public_udp_failed: 0,
|
|
162
|
+
reachability_public_tcp_success: 0,
|
|
163
|
+
reachability_public_tcp_failed: 0,
|
|
164
|
+
reachability_vmn_udp_success: 0,
|
|
165
|
+
reachability_vmn_udp_failed: 0,
|
|
166
|
+
reachability_vmn_tcp_success: 0,
|
|
167
|
+
reachability_vmn_tcp_failed: 0,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const updateStats = (clusterType: 'public' | 'vmn', result: ReachabilityResult) => {
|
|
171
|
+
if (result.udp?.reachable) {
|
|
172
|
+
const outcome = result.udp.reachable === 'true' ? 'success' : 'failed';
|
|
173
|
+
stats[`reachability_${clusterType}_udp_${outcome}`] += 1;
|
|
174
|
+
}
|
|
175
|
+
if (result.tcp?.reachable) {
|
|
176
|
+
const outcome = result.tcp.reachable === 'true' ? 'success' : 'failed';
|
|
177
|
+
stats[`reachability_${clusterType}_tcp_${outcome}`] += 1;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
// @ts-ignore
|
|
183
|
+
const resultsJson = await this.webex.boundedStorage.get(
|
|
184
|
+
REACHABILITY.namespace,
|
|
185
|
+
REACHABILITY.localStorageResult
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
const internalResults: InternalReachabilityResults = JSON.parse(resultsJson);
|
|
189
|
+
|
|
190
|
+
Object.values(internalResults).forEach((result) => {
|
|
191
|
+
updateStats(result.isVideoMesh ? 'vmn' : 'public', result);
|
|
192
|
+
});
|
|
193
|
+
} catch (e) {
|
|
194
|
+
// empty storage, that's ok
|
|
195
|
+
LoggerProxy.logger.warn(
|
|
196
|
+
'Roap:request#getReachabilityMetrics --> Error parsing reachability data: ',
|
|
197
|
+
e
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return stats;
|
|
202
|
+
}
|
|
203
|
+
|
|
141
204
|
/**
|
|
142
205
|
* Reachability results as an object in the format that backend expects
|
|
143
206
|
*
|
|
@@ -44,14 +44,12 @@ import {
|
|
|
44
44
|
StreamEventNames,
|
|
45
45
|
} from '@webex/media-helpers';
|
|
46
46
|
import * as StatsAnalyzerModule from '@webex/plugin-meetings/src/statsAnalyzer';
|
|
47
|
-
import * as MuteStateModule from '@webex/plugin-meetings/src/meeting/muteState';
|
|
48
47
|
import EventsScope from '@webex/plugin-meetings/src/common/events/events-scope';
|
|
49
48
|
import Meetings, {CONSTANTS} from '@webex/plugin-meetings';
|
|
50
49
|
import Meeting from '@webex/plugin-meetings/src/meeting';
|
|
51
50
|
import Members from '@webex/plugin-meetings/src/members';
|
|
52
51
|
import * as MembersImport from '@webex/plugin-meetings/src/members';
|
|
53
52
|
import Roap from '@webex/plugin-meetings/src/roap';
|
|
54
|
-
import RoapRequest from '@webex/plugin-meetings/src/roap/request';
|
|
55
53
|
import MeetingRequest from '@webex/plugin-meetings/src/meeting/request';
|
|
56
54
|
import * as MeetingRequestImport from '@webex/plugin-meetings/src/meeting/request';
|
|
57
55
|
import LocusInfo from '@webex/plugin-meetings/src/locus-info';
|
|
@@ -81,7 +79,6 @@ import {
|
|
|
81
79
|
UserNotJoinedError,
|
|
82
80
|
MeetingNotActiveError,
|
|
83
81
|
UserInLobbyError,
|
|
84
|
-
NoMediaEstablishedYetError,
|
|
85
82
|
} from '../../../../src/common/errors/webex-errors';
|
|
86
83
|
import WebExMeetingsErrors from '../../../../src/common/errors/webex-meetings-error';
|
|
87
84
|
import ParameterError from '../../../../src/common/errors/parameter';
|
|
@@ -89,19 +86,14 @@ import PasswordError from '../../../../src/common/errors/password-error';
|
|
|
89
86
|
import CaptchaError from '../../../../src/common/errors/captcha-error';
|
|
90
87
|
import PermissionError from '../../../../src/common/errors/permission';
|
|
91
88
|
import IntentToJoinError from '../../../../src/common/errors/intent-to-join';
|
|
92
|
-
import DefaultSDKConfig from '../../../../src/config';
|
|
93
89
|
import testUtils from '../../../utils/testUtils';
|
|
94
90
|
import {
|
|
95
91
|
MeetingInfoV2CaptchaError,
|
|
96
92
|
MeetingInfoV2PasswordError,
|
|
97
93
|
MeetingInfoV2PolicyError,
|
|
98
94
|
} from '../../../../src/meeting-info/meeting-info-v2';
|
|
99
|
-
import {ANNOTATION_POLICY} from '../../../../src/annotation/constants';
|
|
100
95
|
|
|
101
96
|
|
|
102
|
-
// Non-stubbed function
|
|
103
|
-
const {getDisplayMedia} = Media;
|
|
104
|
-
|
|
105
97
|
describe('plugin-meetings', () => {
|
|
106
98
|
const logger = {
|
|
107
99
|
info: () => {},
|
|
@@ -228,6 +220,11 @@ describe('plugin-meetings', () => {
|
|
|
228
220
|
webex.credentials.getOrgId = sinon.stub().returns('fake-org-id');
|
|
229
221
|
webex.internal.metrics.submitClientMetrics = sinon.stub().returns(Promise.resolve());
|
|
230
222
|
webex.meetings.uploadLogs = sinon.stub().returns(Promise.resolve());
|
|
223
|
+
webex.meetings.reachability = {
|
|
224
|
+
isAnyPublicClusterReachable: sinon.stub().resolves(true),
|
|
225
|
+
getReachabilityResults: sinon.stub().resolves(undefined),
|
|
226
|
+
getReachabilityMetrics: sinon.stub().resolves({}),
|
|
227
|
+
};
|
|
231
228
|
webex.internal.llm.on = sinon.stub();
|
|
232
229
|
membersSpy = sinon.spy(MembersImport, 'default');
|
|
233
230
|
meetingRequestSpy = sinon.spy(MeetingRequestImport, 'default');
|
|
@@ -1170,8 +1167,15 @@ describe('plugin-meetings', () => {
|
|
|
1170
1167
|
}
|
|
1171
1168
|
});
|
|
1172
1169
|
|
|
1173
|
-
it('should reset the statsAnalyzer to null if addMedia throws an error', async () => {
|
|
1170
|
+
it('should send metrics and reset the statsAnalyzer to null if addMedia throws an error', async () => {
|
|
1174
1171
|
meeting.meetingState = 'ACTIVE';
|
|
1172
|
+
meeting.webex.meetings.reachability = {
|
|
1173
|
+
getReachabilityMetrics: sinon.stub().resolves({
|
|
1174
|
+
someReachabilityMetric1: 'some value1',
|
|
1175
|
+
someReachabilityMetric2: 'some value2'
|
|
1176
|
+
}),
|
|
1177
|
+
};
|
|
1178
|
+
|
|
1175
1179
|
// setup the mock to return an incomplete object - this will cause addMedia to fail
|
|
1176
1180
|
// because some methods (like on() or initiateOffer()) are missing
|
|
1177
1181
|
Media.createMediaConnection = sinon.stub().returns({
|
|
@@ -1195,6 +1199,8 @@ describe('plugin-meetings', () => {
|
|
|
1195
1199
|
signalingState: 'unknown',
|
|
1196
1200
|
connectionState: 'unknown',
|
|
1197
1201
|
iceConnectionState: 'unknown',
|
|
1202
|
+
someReachabilityMetric1: 'some value1',
|
|
1203
|
+
someReachabilityMetric2: 'some value2',
|
|
1198
1204
|
});
|
|
1199
1205
|
});
|
|
1200
1206
|
|
|
@@ -1546,6 +1552,12 @@ describe('plugin-meetings', () => {
|
|
|
1546
1552
|
|
|
1547
1553
|
it('should send ADD_MEDIA_SUCCESS metrics', async () => {
|
|
1548
1554
|
meeting.meetingState = 'ACTIVE';
|
|
1555
|
+
meeting.webex.meetings.reachability = {
|
|
1556
|
+
getReachabilityMetrics: sinon.stub().resolves({
|
|
1557
|
+
someReachabilityMetric1: 'some value1',
|
|
1558
|
+
someReachabilityMetric2: 'some value2'
|
|
1559
|
+
}),
|
|
1560
|
+
};
|
|
1549
1561
|
await meeting.addMedia({
|
|
1550
1562
|
mediaSettings: {},
|
|
1551
1563
|
});
|
|
@@ -1556,6 +1568,8 @@ describe('plugin-meetings', () => {
|
|
|
1556
1568
|
locus_id: meeting.locusUrl.split('/').pop(),
|
|
1557
1569
|
connectionType: 'udp',
|
|
1558
1570
|
isMultistream: false,
|
|
1571
|
+
someReachabilityMetric1: 'some value1',
|
|
1572
|
+
someReachabilityMetric2: 'some value2'
|
|
1559
1573
|
});
|
|
1560
1574
|
|
|
1561
1575
|
assert.called(webex.internal.newMetrics.submitClientEvent);
|
|
@@ -1792,10 +1806,6 @@ describe('plugin-meetings', () => {
|
|
|
1792
1806
|
meeting.setMercuryListener = sinon.stub();
|
|
1793
1807
|
meeting.locusInfo.onFullLocus = sinon.stub();
|
|
1794
1808
|
meeting.webex.meetings.geoHintInfo = {regionCode: 'EU', countryCode: 'UK'};
|
|
1795
|
-
meeting.webex.meetings.reachability = {
|
|
1796
|
-
isAnyPublicClusterReachable: sinon.stub().resolves(true),
|
|
1797
|
-
getReachabilityResults: sinon.stub().resolves(undefined),
|
|
1798
|
-
};
|
|
1799
1809
|
meeting.roap.doTurnDiscovery = sinon
|
|
1800
1810
|
.stub()
|
|
1801
1811
|
.resolves({turnServerInfo: {}, turnDiscoverySkippedReason: 'reachability'});
|
|
@@ -36,6 +36,7 @@ import { forEach } from 'lodash';
|
|
|
36
36
|
import PasswordError from '@webex/plugin-meetings/src/common/errors/password-error';
|
|
37
37
|
import PermissionError from '@webex/plugin-meetings/src/common/errors/permission';
|
|
38
38
|
import {NoiseReductionEffect,VirtualBackgroundEffect} from '@webex/media-helpers';
|
|
39
|
+
import NoMeetingInfoError from '../../../../src/common/errors/no-meeting-info';
|
|
39
40
|
|
|
40
41
|
describe('plugin-meetings', () => {
|
|
41
42
|
const logger = {
|
|
@@ -662,15 +663,28 @@ describe('plugin-meetings', () => {
|
|
|
662
663
|
});
|
|
663
664
|
});
|
|
664
665
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
666
|
+
const FAKE_USE_RANDOM_DELAY = true;
|
|
667
|
+
const correlationId = 'my-correlationId';
|
|
668
|
+
|
|
669
|
+
const checkCallCreateMeeting = async (createParameters, createMeetingParameters) => {
|
|
670
|
+
const create = webex.meetings.create(...createParameters);
|
|
669
671
|
|
|
670
672
|
assert.exists(create.then);
|
|
671
673
|
await create;
|
|
672
674
|
assert.calledOnce(webex.meetings.createMeeting);
|
|
673
|
-
assert.calledWith(webex.meetings.createMeeting,
|
|
675
|
+
assert.calledWith(webex.meetings.createMeeting, ...createMeetingParameters);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
it('calls createMeeting and returns its promise', async () => {
|
|
679
|
+
checkCallCreateMeeting([test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, true], [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, true]);
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
it('calls createMeeting when failOnMissingMeetinginfo is undefined and returns its promise', async () => {
|
|
683
|
+
checkCallCreateMeeting([test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, undefined], [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, false]);
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
it('calls createMeeting when failOnMissingMeetinginfo is false and returns its promise', async () => {
|
|
687
|
+
checkCallCreateMeeting([test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, false], [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, false]);
|
|
674
688
|
});
|
|
675
689
|
|
|
676
690
|
it('calls createMeeting with extra info params and returns its promise', async () => {
|
|
@@ -1346,37 +1360,63 @@ describe('plugin-meetings', () => {
|
|
|
1346
1360
|
webex.meetings.meetingInfo.fetchMeetingInfo = sinon
|
|
1347
1361
|
.stub()
|
|
1348
1362
|
.returns(Promise.reject(new Error('test')));
|
|
1363
|
+
webex.meetings.destroy = sinon
|
|
1364
|
+
.stub()
|
|
1365
|
+
.returns(Promise.resolve());
|
|
1366
|
+
webex.meetings.createMeeting = sinon.spy(webex.meetings.createMeeting);
|
|
1349
1367
|
});
|
|
1350
|
-
it('creates the meeting from a rejected meeting info fetch', async () => {
|
|
1351
|
-
const meeting = await webex.meetings.createMeeting('test destination', 'test type');
|
|
1352
1368
|
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
webex.meetings.meetingInfo.fetchMeetingInfo
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1369
|
+
const checkCreateMeetingWithNoMeetingInfo = async (failOnMissingMeetingInfo, destroy) => {
|
|
1370
|
+
try {
|
|
1371
|
+
const meeting = await webex.meetings.createMeeting('test destination', 'test type', undefined, undefined, undefined, failOnMissingMeetingInfo);
|
|
1372
|
+
|
|
1373
|
+
assert.instanceOf(
|
|
1374
|
+
meeting,
|
|
1375
|
+
Meeting,
|
|
1376
|
+
'createMeeting should eventually resolve to a Meeting Object'
|
|
1377
|
+
);
|
|
1378
|
+
assert.calledOnce(webex.meetings.meetingInfo.fetchMeetingInfo);
|
|
1379
|
+
assert.calledOnce(MeetingsUtil.getMeetingAddedType);
|
|
1380
|
+
assert.calledThrice(TriggerProxy.trigger);
|
|
1381
|
+
assert.calledWith(
|
|
1382
|
+
webex.meetings.meetingInfo.fetchMeetingInfo,
|
|
1383
|
+
'test destination',
|
|
1384
|
+
'test type'
|
|
1385
|
+
);
|
|
1386
|
+
|
|
1387
|
+
if (destroy) {
|
|
1388
|
+
assert.calledWith(webex.meetings.destroy, sinon.match.instanceOf(Meeting), 'MISSING_MEETING_INFO')
|
|
1389
|
+
assert.notCalled(MeetingsUtil.getMeetingAddedType);
|
|
1390
|
+
assert.notCalled(TriggerProxy.trigger);
|
|
1391
|
+
assert.throw(webex.meetings.createMeeting, 'meeting information not found');
|
|
1392
|
+
} else {
|
|
1393
|
+
assert.notCalled(webex.meetings.destroy);
|
|
1394
|
+
assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
|
|
1395
|
+
assert.calledWith(
|
|
1396
|
+
TriggerProxy.trigger,
|
|
1397
|
+
sinon.match.instanceOf(Meetings),
|
|
1398
|
+
{
|
|
1399
|
+
file: 'meetings',
|
|
1400
|
+
function: 'createMeeting',
|
|
1401
|
+
},
|
|
1402
|
+
'meeting:added',
|
|
1403
|
+
{
|
|
1404
|
+
meeting: sinon.match.instanceOf(Meeting),
|
|
1405
|
+
type: 'test meeting added type',
|
|
1406
|
+
}
|
|
1407
|
+
);
|
|
1378
1408
|
}
|
|
1379
|
-
)
|
|
1409
|
+
} catch (err) {
|
|
1410
|
+
assert.instanceOf(err, NoMeetingInfoError);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
it('creates the meeting from a rejected meeting info fetch', async () => {
|
|
1415
|
+
checkCreateMeetingWithNoMeetingInfo(false, false);
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1418
|
+
it('creates the meeting from a rejected meeting info fetch and destroys it if failOnMissingMeetingInfo', async () => {
|
|
1419
|
+
checkCreateMeetingWithNoMeetingInfo(true, true);
|
|
1380
1420
|
});
|
|
1381
1421
|
});
|
|
1382
1422
|
|