@webex/plugin-meetings 3.11.0-next.32 → 3.11.0-next.34
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/aiEnableRequest/index.js +5 -2
- package/dist/aiEnableRequest/index.js.map +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +0 -2
- package/dist/constants.js.map +1 -1
- package/dist/hashTree/constants.js +3 -1
- package/dist/hashTree/constants.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +148 -94
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +48 -26
- package/dist/locus-info/index.js.map +1 -1
- package/dist/types/constants.d.ts +0 -1
- package/dist/types/hashTree/constants.d.ts +1 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +14 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +1 -1
- package/src/aiEnableRequest/index.ts +7 -1
- package/src/constants.ts +0 -1
- package/src/hashTree/constants.ts +1 -0
- package/src/hashTree/hashTreeParser.ts +140 -73
- package/src/locus-info/index.ts +19 -25
- package/test/unit/spec/aiEnableRequest/index.ts +28 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +307 -0
- package/test/unit/spec/locus-info/index.js +31 -0
|
@@ -310,7 +310,6 @@ export declare const HEADERS: {
|
|
|
310
310
|
};
|
|
311
311
|
export declare const MEETING_REMOVED_REASON: {
|
|
312
312
|
SELF_REMOVED: string;
|
|
313
|
-
FULLSTATE_REMOVED: string;
|
|
314
313
|
MEETING_INACTIVE_TERMINATING: string;
|
|
315
314
|
CLIENT_LEAVE_REQUEST: string;
|
|
316
315
|
CLIENT_LEAVE_REQUEST_TAB_CLOSED: string;
|
|
@@ -48,6 +48,12 @@ export type LocusInfoUpdateType = Enum<typeof LocusInfoUpdateType>;
|
|
|
48
48
|
export type LocusInfoUpdateCallback = (updateType: LocusInfoUpdateType, data?: {
|
|
49
49
|
updatedObjects: HashTreeObject[];
|
|
50
50
|
}) => void;
|
|
51
|
+
/**
|
|
52
|
+
* This error is thrown if we receive information that the meeting has ended while we're processing some hash messages.
|
|
53
|
+
* It's handled internally by HashTreeParser and results in MEETING_ENDED being sent up.
|
|
54
|
+
*/
|
|
55
|
+
export declare class MeetingEndedError extends Error {
|
|
56
|
+
}
|
|
51
57
|
/**
|
|
52
58
|
* Parses hash tree eventing locus data
|
|
53
59
|
*/
|
|
@@ -177,6 +183,13 @@ declare class HashTreeParser {
|
|
|
177
183
|
* @returns {void}
|
|
178
184
|
*/
|
|
179
185
|
private handleRootHashHeartBeatMessage;
|
|
186
|
+
/**
|
|
187
|
+
* Asynchronously initializes new visible data sets
|
|
188
|
+
*
|
|
189
|
+
* @param {VisibleDataSetInfo[]} dataSetsRequiringInitialization list of datasets to initialize
|
|
190
|
+
* @returns {void}
|
|
191
|
+
*/
|
|
192
|
+
private queueInitForNewVisibleDataSets;
|
|
180
193
|
/**
|
|
181
194
|
* Handles updates to Metadata object that we receive from Locus via other means than messages. Right now
|
|
182
195
|
* that means only in the API response alongside locus object.
|
|
@@ -302,6 +315,7 @@ declare class HashTreeParser {
|
|
|
302
315
|
* @returns {void}
|
|
303
316
|
*/
|
|
304
317
|
private stopAllTimers;
|
|
318
|
+
private checkForSentinelHttpResponse;
|
|
305
319
|
/**
|
|
306
320
|
* Gets the current hashes from the locus for a specific data set.
|
|
307
321
|
* @param {string} dataSetName
|
package/dist/webinar/index.js
CHANGED
package/package.json
CHANGED
|
@@ -51,7 +51,13 @@ const AIEnableRequest = WebexPlugin.extend({
|
|
|
51
51
|
const isApprover = !!approverId && approverId === this.selfParticipantId;
|
|
52
52
|
const initiatorId = initiator?.participantId;
|
|
53
53
|
const isInitiator = !!initiatorId && initiatorId === this.selfParticipantId;
|
|
54
|
-
if (
|
|
54
|
+
if (
|
|
55
|
+
!isApprover &&
|
|
56
|
+
!isInitiator &&
|
|
57
|
+
// Not just the initiator needs to know about declined all because
|
|
58
|
+
// all future requests will be rejected if the meeting is in the declined all state
|
|
59
|
+
actionType !== AI_ENABLE_REQUEST.ACTION_TYPE.DECLINED_ALL
|
|
60
|
+
) {
|
|
55
61
|
return;
|
|
56
62
|
}
|
|
57
63
|
this.trigger(AI_ENABLE_REQUEST.EVENTS.APPROVAL_REQUEST_ARRIVED, {
|
package/src/constants.ts
CHANGED
|
@@ -414,7 +414,6 @@ export const HEADERS = {
|
|
|
414
414
|
// Meeting actually ended
|
|
415
415
|
export const MEETING_REMOVED_REASON = {
|
|
416
416
|
SELF_REMOVED: 'SELF_REMOVED', // server or host removed you from the meeting
|
|
417
|
-
FULLSTATE_REMOVED: 'FULLSTATE_REMOVED', // meeting got dropped ? not sure
|
|
418
417
|
MEETING_INACTIVE_TERMINATING: 'MEETING_INACTIVE_TERMINATING', // Meeting got ended or everyone left the meeting
|
|
419
418
|
CLIENT_LEAVE_REQUEST: 'CLIENT_LEAVE_REQUEST', // You triggered leave meeting
|
|
420
419
|
CLIENT_LEAVE_REQUEST_TAB_CLOSED: 'CLIENT_LEAVE_REQUEST_TAB_CLOSED', // You triggered leave meeting, such as closing the browser tab directly
|
|
@@ -6,4 +6,5 @@ export const DataSetNames = {
|
|
|
6
6
|
ATD_ACTIVE: 'atd-active', // only sent to panelists, over LLM; the attendees that have their hands raised or are allowed to unmute themselves
|
|
7
7
|
ATD_UNMUTED: 'atd-unmuted', // sent to web client, over LLM, not sent to panelists; the attendees that are unmuted
|
|
8
8
|
SELF: 'self', // sent to web client, over Mercury
|
|
9
|
+
UNJOINED: 'unjoined', // sent when you are not joined, but can still see some stuff from the meeting (mutually exclusive with "main")
|
|
9
10
|
};
|
|
@@ -73,13 +73,19 @@ interface LeafInfo {
|
|
|
73
73
|
* This error is thrown if we receive information that the meeting has ended while we're processing some hash messages.
|
|
74
74
|
* It's handled internally by HashTreeParser and results in MEETING_ENDED being sent up.
|
|
75
75
|
*/
|
|
76
|
-
class MeetingEndedError extends Error {}
|
|
76
|
+
export class MeetingEndedError extends Error {}
|
|
77
77
|
|
|
78
78
|
/* Currently Locus always sends Metadata objects only in the "self" dataset.
|
|
79
79
|
* If this ever changes, update all the code that relies on this constant.
|
|
80
80
|
*/
|
|
81
81
|
const MetadataDataSetName = DataSetNames.SELF;
|
|
82
82
|
|
|
83
|
+
const PossibleSentinelMessageDataSetNames = [
|
|
84
|
+
DataSetNames.MAIN,
|
|
85
|
+
DataSetNames.SELF,
|
|
86
|
+
DataSetNames.UNJOINED,
|
|
87
|
+
];
|
|
88
|
+
|
|
83
89
|
/**
|
|
84
90
|
* Parses hash tree eventing locus data
|
|
85
91
|
*/
|
|
@@ -285,9 +291,15 @@ class HashTreeParser {
|
|
|
285
291
|
return this.webexRequest({
|
|
286
292
|
method: HTTP_VERBS.GET,
|
|
287
293
|
uri: this.visibleDataSetsUrl,
|
|
288
|
-
})
|
|
289
|
-
|
|
290
|
-
|
|
294
|
+
})
|
|
295
|
+
.then((response) => {
|
|
296
|
+
return response.body.dataSets as Array<DataSet>;
|
|
297
|
+
})
|
|
298
|
+
.catch((error) => {
|
|
299
|
+
this.checkForSentinelHttpResponse(error);
|
|
300
|
+
|
|
301
|
+
throw error;
|
|
302
|
+
});
|
|
291
303
|
}
|
|
292
304
|
|
|
293
305
|
/**
|
|
@@ -511,21 +523,19 @@ class HashTreeParser {
|
|
|
511
523
|
* @returns {boolean} - Returns true if the message indicates the end of the meeting, false otherwise
|
|
512
524
|
*/
|
|
513
525
|
private isEndMessage(message: HashTreeMessage) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
// this is a special way for Locus to indicate that this meeting has ended
|
|
525
|
-
return true;
|
|
526
|
-
}
|
|
526
|
+
return message.dataSets.some((dataSet) => {
|
|
527
|
+
if (
|
|
528
|
+
dataSet.leafCount === 1 &&
|
|
529
|
+
dataSet.root === EMPTY_HASH &&
|
|
530
|
+
(!this.dataSets[dataSet.name] || this.dataSets[dataSet.name].version < dataSet.version) &&
|
|
531
|
+
PossibleSentinelMessageDataSetNames.includes(dataSet.name.toLowerCase())
|
|
532
|
+
) {
|
|
533
|
+
// this is a special way for Locus to indicate that this meeting has ended
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
527
536
|
|
|
528
|
-
|
|
537
|
+
return false;
|
|
538
|
+
});
|
|
529
539
|
}
|
|
530
540
|
|
|
531
541
|
/**
|
|
@@ -556,6 +566,33 @@ class HashTreeParser {
|
|
|
556
566
|
});
|
|
557
567
|
}
|
|
558
568
|
|
|
569
|
+
/**
|
|
570
|
+
* Asynchronously initializes new visible data sets
|
|
571
|
+
*
|
|
572
|
+
* @param {VisibleDataSetInfo[]} dataSetsRequiringInitialization list of datasets to initialize
|
|
573
|
+
* @returns {void}
|
|
574
|
+
*/
|
|
575
|
+
private queueInitForNewVisibleDataSets(dataSetsRequiringInitialization: VisibleDataSetInfo[]) {
|
|
576
|
+
queueMicrotask(() => {
|
|
577
|
+
this.initializeNewVisibleDataSets(dataSetsRequiringInitialization).catch((error) => {
|
|
578
|
+
if (error instanceof MeetingEndedError) {
|
|
579
|
+
this.callLocusInfoUpdateCallback({
|
|
580
|
+
updateType: LocusInfoUpdateType.MEETING_ENDED,
|
|
581
|
+
});
|
|
582
|
+
} else {
|
|
583
|
+
LoggerProxy.logger.warn(
|
|
584
|
+
`HashTreeParser#queueInitForNewVisibleDataSets --> ${
|
|
585
|
+
this.debugId
|
|
586
|
+
} error while initializing new visible datasets: ${dataSetsRequiringInitialization
|
|
587
|
+
.map((ds) => ds.name)
|
|
588
|
+
.join(', ')}: `,
|
|
589
|
+
error
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
|
|
559
596
|
/**
|
|
560
597
|
* Handles updates to Metadata object that we receive from Locus via other means than messages. Right now
|
|
561
598
|
* that means only in the API response alongside locus object.
|
|
@@ -600,9 +637,7 @@ class HashTreeParser {
|
|
|
600
637
|
|
|
601
638
|
if (dataSetsRequiringInitialization.length > 0) {
|
|
602
639
|
// there are some data sets that we need to initialize asynchronously
|
|
603
|
-
|
|
604
|
-
this.initializeNewVisibleDataSets(dataSetsRequiringInitialization);
|
|
605
|
-
});
|
|
640
|
+
this.queueInitForNewVisibleDataSets(dataSetsRequiringInitialization);
|
|
606
641
|
}
|
|
607
642
|
}
|
|
608
643
|
}
|
|
@@ -931,15 +966,6 @@ class HashTreeParser {
|
|
|
931
966
|
this.visibleDataSetsUrl = visibleDataSetsUrl;
|
|
932
967
|
dataSets.forEach((dataSet) => this.updateDataSetInfo(dataSet));
|
|
933
968
|
|
|
934
|
-
if (this.isEndMessage(message)) {
|
|
935
|
-
LoggerProxy.logger.info(
|
|
936
|
-
`HashTreeParser#parseMessage --> ${this.debugId} received END message`
|
|
937
|
-
);
|
|
938
|
-
this.stopAllTimers();
|
|
939
|
-
|
|
940
|
-
return {updateType: LocusInfoUpdateType.MEETING_ENDED};
|
|
941
|
-
}
|
|
942
|
-
|
|
943
969
|
let isRosterDropped = false;
|
|
944
970
|
const updatedObjects: HashTreeObject[] = [];
|
|
945
971
|
|
|
@@ -1039,9 +1065,7 @@ class HashTreeParser {
|
|
|
1039
1065
|
|
|
1040
1066
|
if (dataSetsRequiringInitialization.length > 0) {
|
|
1041
1067
|
// there are some data sets that we need to initialize asynchronously
|
|
1042
|
-
|
|
1043
|
-
this.initializeNewVisibleDataSets(dataSetsRequiringInitialization);
|
|
1044
|
-
});
|
|
1068
|
+
this.queueInitForNewVisibleDataSets(dataSetsRequiringInitialization);
|
|
1045
1069
|
}
|
|
1046
1070
|
|
|
1047
1071
|
if (updatedObjects.length === 0) {
|
|
@@ -1064,7 +1088,14 @@ class HashTreeParser {
|
|
|
1064
1088
|
if (message.heartbeatIntervalMs) {
|
|
1065
1089
|
this.heartbeatIntervalMs = message.heartbeatIntervalMs;
|
|
1066
1090
|
}
|
|
1067
|
-
if (message
|
|
1091
|
+
if (this.isEndMessage(message)) {
|
|
1092
|
+
LoggerProxy.logger.info(
|
|
1093
|
+
`HashTreeParser#parseMessage --> ${this.debugId} received sentinel END MEETING message`
|
|
1094
|
+
);
|
|
1095
|
+
this.stopAllTimers();
|
|
1096
|
+
|
|
1097
|
+
this.callLocusInfoUpdateCallback({updateType: LocusInfoUpdateType.MEETING_ENDED});
|
|
1098
|
+
} else if (message.locusStateElements === undefined) {
|
|
1068
1099
|
this.handleRootHashHeartBeatMessage(message);
|
|
1069
1100
|
this.resetHeartbeatWatchdogs(message.dataSets);
|
|
1070
1101
|
} else {
|
|
@@ -1169,54 +1200,67 @@ class HashTreeParser {
|
|
|
1169
1200
|
return;
|
|
1170
1201
|
}
|
|
1171
1202
|
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1203
|
+
try {
|
|
1204
|
+
LoggerProxy.logger.info(
|
|
1205
|
+
`HashTreeParser#performSync --> ${this.debugId} ${reason}, syncing data set "${dataSet.name}"`
|
|
1206
|
+
);
|
|
1175
1207
|
|
|
1176
|
-
|
|
1208
|
+
const mismatchedLeavesData: Record<number, LeafDataItem[]> = {};
|
|
1177
1209
|
|
|
1178
|
-
|
|
1179
|
-
|
|
1210
|
+
if (dataSet.leafCount !== 1) {
|
|
1211
|
+
let receivedHashes;
|
|
1180
1212
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1213
|
+
try {
|
|
1214
|
+
// request hashes from sender
|
|
1215
|
+
const {hashes, dataSet: latestDataSetInfo} = await this.getHashesFromLocus(
|
|
1216
|
+
dataSet.name,
|
|
1217
|
+
rootHash
|
|
1218
|
+
);
|
|
1187
1219
|
|
|
1188
|
-
|
|
1220
|
+
receivedHashes = hashes;
|
|
1189
1221
|
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1222
|
+
dataSet.hashTree.resize(latestDataSetInfo.leafCount);
|
|
1223
|
+
} catch (error) {
|
|
1224
|
+
if (error.statusCode === 409) {
|
|
1225
|
+
// this is a leaf count mismatch, we should do nothing, just wait for another heartbeat message from Locus
|
|
1226
|
+
LoggerProxy.logger.info(
|
|
1227
|
+
`HashTreeParser#getHashesFromLocus --> ${this.debugId} Got 409 when fetching hashes for data set "${dataSet.name}": ${error.message}`
|
|
1228
|
+
);
|
|
1197
1229
|
|
|
1198
|
-
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
throw error;
|
|
1199
1233
|
}
|
|
1200
|
-
throw error;
|
|
1201
|
-
}
|
|
1202
1234
|
|
|
1203
|
-
|
|
1204
|
-
|
|
1235
|
+
// identify mismatched leaves
|
|
1236
|
+
const mismatchedLeaveIndexes = dataSet.hashTree.diffHashes(receivedHashes);
|
|
1205
1237
|
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1238
|
+
mismatchedLeaveIndexes.forEach((index) => {
|
|
1239
|
+
mismatchedLeavesData[index] = dataSet.hashTree.getLeafData(index);
|
|
1240
|
+
});
|
|
1241
|
+
} else {
|
|
1242
|
+
mismatchedLeavesData[0] = dataSet.hashTree.getLeafData(0);
|
|
1243
|
+
}
|
|
1244
|
+
// request sync for mismatched leaves
|
|
1245
|
+
if (Object.keys(mismatchedLeavesData).length > 0) {
|
|
1246
|
+
const syncResponse = await this.sendSyncRequestToLocus(dataSet, mismatchedLeavesData);
|
|
1247
|
+
|
|
1248
|
+
// sync API may return nothing (in that case data will arrive via messages)
|
|
1249
|
+
// or it may return a response in the same format as messages
|
|
1250
|
+
if (syncResponse) {
|
|
1251
|
+
this.handleMessage(syncResponse, 'via sync API');
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
} catch (error) {
|
|
1255
|
+
if (error instanceof MeetingEndedError) {
|
|
1256
|
+
this.callLocusInfoUpdateCallback({
|
|
1257
|
+
updateType: LocusInfoUpdateType.MEETING_ENDED,
|
|
1258
|
+
});
|
|
1259
|
+
} else {
|
|
1260
|
+
LoggerProxy.logger.warn(
|
|
1261
|
+
`HashTreeParser#performSync --> ${this.debugId} error during sync for data set "${dataSet.name}":`,
|
|
1262
|
+
error
|
|
1263
|
+
);
|
|
1220
1264
|
}
|
|
1221
1265
|
}
|
|
1222
1266
|
}
|
|
@@ -1360,6 +1404,25 @@ class HashTreeParser {
|
|
|
1360
1404
|
});
|
|
1361
1405
|
}
|
|
1362
1406
|
|
|
1407
|
+
private checkForSentinelHttpResponse(error: any, dataSetName?: string) {
|
|
1408
|
+
const isValidDataSetForSentinel =
|
|
1409
|
+
dataSetName === undefined ||
|
|
1410
|
+
PossibleSentinelMessageDataSetNames.includes(dataSetName.toLowerCase());
|
|
1411
|
+
|
|
1412
|
+
if (
|
|
1413
|
+
((error.statusCode === 409 && error.body?.errorCode === 2403004) ||
|
|
1414
|
+
error.statusCode === 404) &&
|
|
1415
|
+
isValidDataSetForSentinel
|
|
1416
|
+
) {
|
|
1417
|
+
LoggerProxy.logger.info(
|
|
1418
|
+
`HashTreeParser#checkForSentinelHttpResponse --> ${this.debugId} Received ${error.statusCode} for data set "${dataSetName}", indicating that the meeting has ended`
|
|
1419
|
+
);
|
|
1420
|
+
this.stopAllTimers();
|
|
1421
|
+
|
|
1422
|
+
throw new MeetingEndedError();
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1363
1426
|
/**
|
|
1364
1427
|
* Gets the current hashes from the locus for a specific data set.
|
|
1365
1428
|
* @param {string} dataSetName
|
|
@@ -1410,6 +1473,8 @@ class HashTreeParser {
|
|
|
1410
1473
|
`HashTreeParser#getHashesFromLocus --> ${this.debugId} Error ${error.statusCode} fetching hashes for data set "${dataSetName}":`,
|
|
1411
1474
|
error
|
|
1412
1475
|
);
|
|
1476
|
+
this.checkForSentinelHttpResponse(error, dataSet.name);
|
|
1477
|
+
|
|
1413
1478
|
throw error;
|
|
1414
1479
|
});
|
|
1415
1480
|
}
|
|
@@ -1472,6 +1537,8 @@ class HashTreeParser {
|
|
|
1472
1537
|
`HashTreeParser#sendSyncRequestToLocus --> ${this.debugId} Error ${error.statusCode} sending sync request for data set "${dataSet.name}":`,
|
|
1473
1538
|
error
|
|
1474
1539
|
);
|
|
1540
|
+
this.checkForSentinelHttpResponse(error, dataSet.name);
|
|
1541
|
+
|
|
1475
1542
|
throw error;
|
|
1476
1543
|
});
|
|
1477
1544
|
}
|
package/src/locus-info/index.ts
CHANGED
|
@@ -35,6 +35,7 @@ import HashTreeParser, {
|
|
|
35
35
|
DataSet,
|
|
36
36
|
HashTreeMessage,
|
|
37
37
|
LocusInfoUpdateType,
|
|
38
|
+
MeetingEndedError,
|
|
38
39
|
Metadata,
|
|
39
40
|
} from '../hashTree/hashTreeParser';
|
|
40
41
|
import {HashTreeObject, ObjectType, ObjectTypeToLocusKeyMap} from '../hashTree/types';
|
|
@@ -416,10 +417,13 @@ export default class LocusInfo extends EventsScope {
|
|
|
416
417
|
);
|
|
417
418
|
|
|
418
419
|
if (!metadataObject?.data?.visibleDataSets) {
|
|
419
|
-
|
|
420
|
+
// this is a common case (not an error)
|
|
421
|
+
// it happens for example after we leave the meeting and still get some heartbeats or delayed messages
|
|
422
|
+
LoggerProxy.logger.info(
|
|
420
423
|
`Locus-info:index#initialSetup --> cannot initialize HashTreeParser, Metadata object with visibleDataSets is missing in the message`
|
|
421
424
|
);
|
|
422
425
|
|
|
426
|
+
// throw so that handleLocusEvent() catches it and destroys the partially created meeting object
|
|
423
427
|
throw new Error('Metadata object with visibleDataSets is missing in the message');
|
|
424
428
|
}
|
|
425
429
|
|
|
@@ -727,7 +731,11 @@ export default class LocusInfo extends EventsScope {
|
|
|
727
731
|
* @param {HashTreeMessage} message incoming hash tree message
|
|
728
732
|
* @returns {void}
|
|
729
733
|
*/
|
|
730
|
-
private handleHashTreeMessage(
|
|
734
|
+
private async handleHashTreeMessage(
|
|
735
|
+
meeting: any,
|
|
736
|
+
eventType: LOCUSEVENT,
|
|
737
|
+
message: HashTreeMessage
|
|
738
|
+
) {
|
|
731
739
|
if (eventType !== LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
|
|
732
740
|
this.sendClassicVsHashTreeMismatchMetric(
|
|
733
741
|
meeting,
|
|
@@ -736,8 +744,15 @@ export default class LocusInfo extends EventsScope {
|
|
|
736
744
|
|
|
737
745
|
return;
|
|
738
746
|
}
|
|
739
|
-
|
|
740
|
-
|
|
747
|
+
try {
|
|
748
|
+
await this.hashTreeParser.handleMessage(message);
|
|
749
|
+
} catch (error) {
|
|
750
|
+
if (error instanceof MeetingEndedError) {
|
|
751
|
+
this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.SELF_REMOVED);
|
|
752
|
+
} else {
|
|
753
|
+
throw error;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
741
756
|
}
|
|
742
757
|
|
|
743
758
|
/**
|
|
@@ -1292,27 +1307,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
1292
1307
|
shouldLeave: false,
|
|
1293
1308
|
}
|
|
1294
1309
|
);
|
|
1295
|
-
} else if (this.fullState && this.fullState.removed) {
|
|
1296
|
-
// user has been dropped from a meeting
|
|
1297
|
-
|
|
1298
|
-
// @ts-ignore
|
|
1299
|
-
this.webex.internal.newMetrics.submitClientEvent({
|
|
1300
|
-
name: 'client.call.remote-ended',
|
|
1301
|
-
options: {
|
|
1302
|
-
meetingId: this.meetingId,
|
|
1303
|
-
},
|
|
1304
|
-
});
|
|
1305
|
-
this.emitScoped(
|
|
1306
|
-
{
|
|
1307
|
-
file: 'locus-info',
|
|
1308
|
-
function: 'isMeetingActive',
|
|
1309
|
-
},
|
|
1310
|
-
EVENTS.DESTROY_MEETING,
|
|
1311
|
-
{
|
|
1312
|
-
reason: MEETING_REMOVED_REASON.FULLSTATE_REMOVED,
|
|
1313
|
-
shouldLeave: false,
|
|
1314
|
-
}
|
|
1315
|
-
);
|
|
1316
1310
|
}
|
|
1317
1311
|
// If you are guest and you are removed from the meeting
|
|
1318
1312
|
// You wont get any further events
|
|
@@ -206,6 +206,34 @@ describe('plugin-meetings', () => {
|
|
|
206
206
|
sinon.assert.notCalled(triggerSpy);
|
|
207
207
|
});
|
|
208
208
|
|
|
209
|
+
it('should trigger DECLINED_ALL event even when user is neither approver nor initiator', () => {
|
|
210
|
+
aiEnableRequest.listenToApprovalRequests();
|
|
211
|
+
|
|
212
|
+
const event = {
|
|
213
|
+
data: {
|
|
214
|
+
approval: {
|
|
215
|
+
resourceType: AI_ENABLE_REQUEST.RESOURCE_TYPE,
|
|
216
|
+
receivers: [{participantId: testApproverId}],
|
|
217
|
+
initiator: {participantId: testInitiatorId},
|
|
218
|
+
actionType: AI_ENABLE_REQUEST.ACTION_TYPE.DECLINED_ALL,
|
|
219
|
+
url: testUrl,
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
webex.internal.mercury.emit(`event:${LOCUSEVENT.APPROVAL_REQUEST}`, event);
|
|
225
|
+
|
|
226
|
+
sinon.assert.calledOnce(triggerSpy);
|
|
227
|
+
sinon.assert.calledWith(triggerSpy, AI_ENABLE_REQUEST.EVENTS.APPROVAL_REQUEST_ARRIVED, {
|
|
228
|
+
actionType: AI_ENABLE_REQUEST.ACTION_TYPE.DECLINED_ALL,
|
|
229
|
+
isApprover: false,
|
|
230
|
+
isInitiator: false,
|
|
231
|
+
initiatorId: testInitiatorId,
|
|
232
|
+
approverId: testApproverId,
|
|
233
|
+
url: testUrl,
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
209
237
|
it('should not trigger event when resourceType does not match', () => {
|
|
210
238
|
aiEnableRequest.listenToApprovalRequests();
|
|
211
239
|
|