@webex/plugin-meetings 3.8.1 → 3.9.0-webinar5k.1
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 +16 -3
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/enums.js +1 -0
- package/dist/controls-options-manager/enums.js.map +1 -1
- package/dist/controls-options-manager/types.js.map +1 -1
- package/dist/controls-options-manager/util.js +26 -0
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/hashTree/constants.js +23 -0
- package/dist/hashTree/constants.js.map +1 -0
- package/dist/hashTree/hashTree.js +516 -0
- package/dist/hashTree/hashTree.js.map +1 -0
- package/dist/hashTree/hashTreeParser.js +521 -0
- package/dist/hashTree/hashTreeParser.js.map +1 -0
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +11 -3
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +331 -59
- 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 +17 -14
- package/dist/meeting/brbState.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +5 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +264 -125
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +2 -5
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +19 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +8 -11
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +6 -2
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/members/collection.js +13 -0
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +44 -23
- 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/metrics/constants.js +1 -0
- package/dist/metrics/constants.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 +12 -0
- package/dist/types/controls-options-manager/enums.d.ts +2 -1
- package/dist/types/controls-options-manager/types.d.ts +4 -1
- package/dist/types/hashTree/constants.d.ts +8 -0
- package/dist/types/hashTree/hashTree.d.ts +128 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +152 -0
- package/dist/types/locus-info/index.d.ts +93 -3
- package/dist/types/meeting/brbState.d.ts +0 -1
- package/dist/types/meeting/in-meeting-actions.d.ts +4 -0
- package/dist/types/meeting/index.d.ts +36 -3
- package/dist/types/meeting/request.d.ts +9 -1
- package/dist/types/meeting/request.type.d.ts +74 -0
- package/dist/types/meeting/util.d.ts +3 -3
- package/dist/types/member/types.d.ts +1 -0
- package/dist/types/members/collection.d.ts +6 -0
- package/dist/types/members/index.d.ts +15 -3
- package/dist/types/members/request.d.ts +1 -1
- package/dist/types/members/util.d.ts +5 -2
- package/dist/types/metrics/constants.d.ts +1 -0
- 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 +26 -25
- package/src/constants.ts +16 -0
- package/src/controls-options-manager/enums.ts +1 -0
- package/src/controls-options-manager/types.ts +6 -1
- package/src/controls-options-manager/util.ts +31 -0
- package/src/hashTree/constants.ts +12 -0
- package/src/hashTree/hashTree.ts +460 -0
- package/src/hashTree/hashTreeParser.ts +556 -0
- package/src/locus-info/controlsUtils.ts +15 -0
- package/src/locus-info/index.ts +434 -58
- package/src/media/index.ts +2 -2
- package/src/meeting/brbState.ts +13 -9
- package/src/meeting/in-meeting-actions.ts +8 -0
- package/src/meeting/index.ts +193 -39
- package/src/meeting/muteState.ts +2 -6
- package/src/meeting/request.ts +16 -0
- package/src/meeting/request.type.ts +64 -0
- package/src/meeting/util.ts +17 -20
- package/src/meetings/index.ts +17 -3
- package/src/member/types.ts +1 -0
- package/src/members/collection.ts +11 -0
- package/src/members/index.ts +33 -7
- package/src/members/request.ts +2 -2
- package/src/members/util.ts +14 -3
- package/src/metrics/constants.ts +1 -0
- package/src/multistream/sendSlotManager.ts +34 -2
- package/src/reachability/index.ts +5 -13
- package/test/unit/spec/controls-options-manager/util.js +58 -0
- package/test/unit/spec/hashTree/hashTree.ts +394 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +156 -0
- package/test/unit/spec/locus-info/controlsUtils.js +52 -0
- package/test/unit/spec/locus-info/index.js +547 -54
- package/test/unit/spec/media/index.ts +107 -0
- package/test/unit/spec/meeting/brbState.ts +23 -4
- package/test/unit/spec/meeting/in-meeting-actions.ts +4 -0
- package/test/unit/spec/meeting/index.js +647 -46
- package/test/unit/spec/meeting/request.js +71 -0
- package/test/unit/spec/members/index.js +33 -10
- 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/dist/annotation/annotation.types.d.ts +0 -42
- package/dist/annotation/constants.d.ts +0 -31
- package/dist/annotation/index.d.ts +0 -117
- package/dist/breakouts/breakout.d.ts +0 -8
- package/dist/breakouts/collection.d.ts +0 -5
- package/dist/breakouts/edit-lock-error.d.ts +0 -15
- package/dist/breakouts/events.d.ts +0 -8
- package/dist/breakouts/index.d.ts +0 -5
- package/dist/breakouts/request.d.ts +0 -22
- package/dist/breakouts/utils.d.ts +0 -15
- package/dist/common/browser-detection.d.ts +0 -9
- package/dist/common/collection.d.ts +0 -48
- package/dist/common/config.d.ts +0 -2
- package/dist/common/errors/captcha-error.d.ts +0 -15
- package/dist/common/errors/intent-to-join.d.ts +0 -16
- package/dist/common/errors/join-meeting.d.ts +0 -17
- package/dist/common/errors/media.d.ts +0 -15
- package/dist/common/errors/no-meeting-info.d.ts +0 -14
- package/dist/common/errors/parameter.d.ts +0 -15
- package/dist/common/errors/password-error.d.ts +0 -15
- package/dist/common/errors/permission.d.ts +0 -14
- package/dist/common/errors/reclaim-host-role-error.d.ts +0 -60
- package/dist/common/errors/reclaim-host-role-error.js +0 -158
- package/dist/common/errors/reclaim-host-role-error.js.map +0 -1
- package/dist/common/errors/reclaim-host-role-errors.d.ts +0 -60
- package/dist/common/errors/reconnection-in-progress.d.ts +0 -9
- package/dist/common/errors/reconnection-in-progress.js +0 -35
- package/dist/common/errors/reconnection-in-progress.js.map +0 -1
- package/dist/common/errors/reconnection.d.ts +0 -15
- package/dist/common/errors/stats.d.ts +0 -15
- package/dist/common/errors/webex-errors.d.ts +0 -81
- package/dist/common/errors/webex-meetings-error.d.ts +0 -20
- package/dist/common/events/events-scope.d.ts +0 -17
- package/dist/common/events/events.d.ts +0 -12
- package/dist/common/events/trigger-proxy.d.ts +0 -2
- package/dist/common/events/util.d.ts +0 -2
- package/dist/common/logs/logger-config.d.ts +0 -2
- package/dist/common/logs/logger-proxy.d.ts +0 -2
- package/dist/common/logs/request.d.ts +0 -34
- package/dist/common/queue.d.ts +0 -32
- package/dist/config.d.ts +0 -73
- package/dist/constants.d.ts +0 -952
- package/dist/controls-options-manager/constants.d.ts +0 -4
- package/dist/controls-options-manager/enums.d.ts +0 -5
- package/dist/controls-options-manager/index.d.ts +0 -120
- package/dist/controls-options-manager/types.d.ts +0 -43
- package/dist/controls-options-manager/util.d.ts +0 -7
- package/dist/index.d.ts +0 -4
- package/dist/interceptors/index.d.ts +0 -2
- package/dist/interceptors/locusRetry.d.ts +0 -27
- package/dist/interpretation/collection.d.ts +0 -5
- package/dist/interpretation/index.d.ts +0 -5
- package/dist/interpretation/siLanguage.d.ts +0 -5
- package/dist/locus-info/controlsUtils.d.ts +0 -2
- package/dist/locus-info/embeddedAppsUtils.d.ts +0 -2
- package/dist/locus-info/fullState.d.ts +0 -2
- package/dist/locus-info/hostUtils.d.ts +0 -2
- package/dist/locus-info/index.d.ts +0 -269
- package/dist/locus-info/infoUtils.d.ts +0 -2
- package/dist/locus-info/mediaSharesUtils.d.ts +0 -2
- package/dist/locus-info/parser.d.ts +0 -212
- package/dist/locus-info/selfUtils.d.ts +0 -2
- package/dist/media/index.d.ts +0 -32
- package/dist/media/properties.d.ts +0 -108
- package/dist/media/util.d.ts +0 -2
- package/dist/mediaQualityMetrics/config.d.ts +0 -233
- package/dist/mediaQualityMetrics/config.js +0 -513
- package/dist/mediaQualityMetrics/config.js.map +0 -1
- package/dist/meeting/effectsState.d.ts +0 -42
- package/dist/meeting/effectsState.js +0 -260
- package/dist/meeting/effectsState.js.map +0 -1
- package/dist/meeting/in-meeting-actions.d.ts +0 -79
- package/dist/meeting/index.d.ts +0 -1622
- package/dist/meeting/locusMediaRequest.d.ts +0 -74
- package/dist/meeting/muteState.d.ts +0 -116
- package/dist/meeting/request.d.ts +0 -257
- package/dist/meeting/request.type.d.ts +0 -11
- package/dist/meeting/state.d.ts +0 -9
- package/dist/meeting/util.d.ts +0 -2
- package/dist/meeting/voicea-meeting.d.ts +0 -16
- package/dist/meeting-info/collection.d.ts +0 -20
- package/dist/meeting-info/index.d.ts +0 -57
- package/dist/meeting-info/meeting-info-v2.d.ts +0 -93
- package/dist/meeting-info/request.d.ts +0 -22
- package/dist/meeting-info/util.d.ts +0 -2
- package/dist/meeting-info/utilv2.d.ts +0 -2
- package/dist/meetings/collection.d.ts +0 -23
- package/dist/meetings/index.d.ts +0 -296
- package/dist/meetings/meetings.types.d.ts +0 -4
- package/dist/meetings/request.d.ts +0 -27
- package/dist/meetings/util.d.ts +0 -18
- package/dist/member/index.d.ts +0 -148
- package/dist/member/member.types.d.ts +0 -11
- package/dist/member/member.types.js +0 -18
- package/dist/member/member.types.js.map +0 -1
- package/dist/member/types.d.ts +0 -32
- package/dist/member/util.d.ts +0 -2
- package/dist/members/collection.d.ts +0 -24
- package/dist/members/index.d.ts +0 -308
- package/dist/members/request.d.ts +0 -58
- package/dist/members/types.d.ts +0 -25
- package/dist/members/util.d.ts +0 -2
- package/dist/metrics/config.d.ts +0 -169
- package/dist/metrics/config.js +0 -289
- package/dist/metrics/config.js.map +0 -1
- package/dist/metrics/constants.d.ts +0 -59
- package/dist/metrics/index.d.ts +0 -152
- package/dist/multistream/mediaRequestManager.d.ts +0 -119
- package/dist/multistream/receiveSlot.d.ts +0 -68
- package/dist/multistream/receiveSlotManager.d.ts +0 -56
- package/dist/multistream/remoteMedia.d.ts +0 -72
- package/dist/multistream/remoteMediaGroup.d.ts +0 -49
- package/dist/multistream/remoteMediaManager.d.ts +0 -300
- package/dist/multistream/sendSlotManager.d.ts +0 -69
- package/dist/networkQualityMonitor/index.d.ts +0 -70
- package/dist/networkQualityMonitor/index.js +0 -226
- package/dist/networkQualityMonitor/index.js.map +0 -1
- package/dist/peer-connection-manager/index.d.ts +0 -6
- package/dist/peer-connection-manager/index.js +0 -671
- package/dist/peer-connection-manager/index.js.map +0 -1
- package/dist/peer-connection-manager/util.d.ts +0 -6
- package/dist/peer-connection-manager/util.js +0 -110
- package/dist/peer-connection-manager/util.js.map +0 -1
- package/dist/personal-meeting-room/index.d.ts +0 -47
- package/dist/personal-meeting-room/request.d.ts +0 -14
- package/dist/personal-meeting-room/util.d.ts +0 -2
- package/dist/reachability/clusterReachability.d.ts +0 -109
- package/dist/reachability/index.d.ts +0 -139
- package/dist/reachability/request.d.ts +0 -35
- package/dist/reachability/util.d.ts +0 -8
- package/dist/reactions/constants.d.ts +0 -3
- package/dist/reactions/reactions.d.ts +0 -4
- package/dist/reactions/reactions.type.d.ts +0 -32
- package/dist/reconnection-manager/index.d.ts +0 -112
- package/dist/recording-controller/enums.d.ts +0 -7
- package/dist/recording-controller/index.d.ts +0 -193
- package/dist/recording-controller/util.d.ts +0 -13
- package/dist/roap/collection.d.ts +0 -10
- package/dist/roap/collection.js +0 -63
- package/dist/roap/collection.js.map +0 -1
- package/dist/roap/handler.d.ts +0 -47
- package/dist/roap/handler.js +0 -279
- package/dist/roap/handler.js.map +0 -1
- package/dist/roap/index.d.ts +0 -116
- package/dist/roap/request.d.ts +0 -35
- package/dist/roap/state.d.ts +0 -9
- package/dist/roap/state.js +0 -127
- package/dist/roap/state.js.map +0 -1
- package/dist/roap/turnDiscovery.d.ts +0 -81
- package/dist/roap/util.d.ts +0 -2
- package/dist/roap/util.js +0 -76
- package/dist/roap/util.js.map +0 -1
- package/dist/rtcMetrics/constants.d.ts +0 -4
- package/dist/rtcMetrics/constants.js +0 -11
- package/dist/rtcMetrics/constants.js.map +0 -1
- package/dist/rtcMetrics/index.d.ts +0 -61
- package/dist/rtcMetrics/index.js +0 -197
- package/dist/rtcMetrics/index.js.map +0 -1
- package/dist/statsAnalyzer/global.d.ts +0 -118
- package/dist/statsAnalyzer/global.js +0 -127
- package/dist/statsAnalyzer/global.js.map +0 -1
- package/dist/statsAnalyzer/index.d.ts +0 -193
- package/dist/statsAnalyzer/index.js +0 -1019
- package/dist/statsAnalyzer/index.js.map +0 -1
- package/dist/statsAnalyzer/mqaUtil.d.ts +0 -22
- package/dist/statsAnalyzer/mqaUtil.js +0 -181
- package/dist/statsAnalyzer/mqaUtil.js.map +0 -1
- package/dist/transcription/index.d.ts +0 -64
- package/dist/types/common/errors/reconnection-in-progress.d.ts +0 -9
- package/dist/types/mediaQualityMetrics/config.d.ts +0 -241
- package/dist/types/networkQualityMonitor/index.d.ts +0 -70
- package/dist/types/rtcMetrics/constants.d.ts +0 -4
- package/dist/types/rtcMetrics/index.d.ts +0 -71
- package/dist/types/statsAnalyzer/global.d.ts +0 -36
- package/dist/types/statsAnalyzer/index.d.ts +0 -217
- package/dist/types/statsAnalyzer/mqaUtil.d.ts +0 -48
- package/dist/webinar/collection.d.ts +0 -16
- package/dist/webinar/index.d.ts +0 -5
@@ -0,0 +1,556 @@
|
|
1
|
+
import {zip} from 'lodash';
|
2
|
+
import HashTree, {LeafDataItem} from './hashTree';
|
3
|
+
import LoggerProxy from '../common/logs/logger-proxy';
|
4
|
+
import {Enum, HTTP_VERBS} from '../constants';
|
5
|
+
import {DataSetNames, EMPTY_HASH} from './constants';
|
6
|
+
|
7
|
+
export interface DataSet {
|
8
|
+
url: string;
|
9
|
+
root: string;
|
10
|
+
version: number;
|
11
|
+
leafCount: number;
|
12
|
+
name: string;
|
13
|
+
idleMs: number;
|
14
|
+
backoff: {
|
15
|
+
maxMs: number;
|
16
|
+
exponent: number;
|
17
|
+
};
|
18
|
+
}
|
19
|
+
|
20
|
+
// todo: Locus docs have now more types like CONTROL_ENTRY, EMBEDDED_APP, FULL_STATE, INFO, MEDIA_SHARE - need to add support for them once Locus implements them
|
21
|
+
export const ObjectType = {
|
22
|
+
participant: 'participant',
|
23
|
+
self: 'self',
|
24
|
+
locus: 'locus',
|
25
|
+
} as const;
|
26
|
+
|
27
|
+
export type ObjectType = Enum<typeof ObjectType>;
|
28
|
+
|
29
|
+
export interface HtMeta {
|
30
|
+
elementId: {
|
31
|
+
type: ObjectType;
|
32
|
+
id: number;
|
33
|
+
version: number;
|
34
|
+
};
|
35
|
+
dataSetNames: string[];
|
36
|
+
}
|
37
|
+
export interface HashTreeObject {
|
38
|
+
htMeta: HtMeta;
|
39
|
+
data: Record<string, any>;
|
40
|
+
}
|
41
|
+
|
42
|
+
export interface RootHashMessage {
|
43
|
+
dataSets: Array<DataSet>;
|
44
|
+
}
|
45
|
+
export interface HashTreeMessage {
|
46
|
+
dataSets: Array<DataSet>;
|
47
|
+
locusStateElements?: Array<HashTreeObject>;
|
48
|
+
locusSessionId?: string;
|
49
|
+
locusUrl?: string;
|
50
|
+
}
|
51
|
+
|
52
|
+
interface InternalDataSet extends DataSet {
|
53
|
+
hashTree: HashTree;
|
54
|
+
timer?: ReturnType<typeof setTimeout>;
|
55
|
+
}
|
56
|
+
|
57
|
+
type WebexRequestMethod = (options: Record<string, any>) => Promise<any>;
|
58
|
+
|
59
|
+
export const LocusInfoUpdateType = {
|
60
|
+
OBJECTS_UPDATED: 'OBJECTS_UPDATED',
|
61
|
+
MEETING_ENDED: 'MEETING_ENDED',
|
62
|
+
} as const;
|
63
|
+
|
64
|
+
export type LocusInfoUpdateType = Enum<typeof LocusInfoUpdateType>;
|
65
|
+
export type LocusInfoUpdateCallback = (
|
66
|
+
updateType: LocusInfoUpdateType,
|
67
|
+
data?: {updatedObjects: HashTreeObject[]}
|
68
|
+
) => void;
|
69
|
+
/**
|
70
|
+
* Parses hash tree eventing locus data
|
71
|
+
*/
|
72
|
+
class HashTreeParser {
|
73
|
+
dataSets: Record<string, InternalDataSet> = {};
|
74
|
+
webexRequest: WebexRequestMethod;
|
75
|
+
locusInfoUpdateCallback: LocusInfoUpdateCallback;
|
76
|
+
debugId: string;
|
77
|
+
|
78
|
+
/**
|
79
|
+
* Constructor for HashTreeParser
|
80
|
+
* @param {Object} options
|
81
|
+
* @param {Object} options.initialLocus The initial locus data containing the hash tree information
|
82
|
+
*/
|
83
|
+
constructor(options: {
|
84
|
+
initialLocus: {
|
85
|
+
dataSets: Array<DataSet>;
|
86
|
+
locus: any;
|
87
|
+
};
|
88
|
+
webexRequest: WebexRequestMethod;
|
89
|
+
locusInfoUpdateCallback: LocusInfoUpdateCallback;
|
90
|
+
debugId: string;
|
91
|
+
}) {
|
92
|
+
const {dataSets, locus} = options.initialLocus; // extract dataSets from initialLocus
|
93
|
+
|
94
|
+
this.debugId = options.debugId;
|
95
|
+
this.webexRequest = options.webexRequest;
|
96
|
+
this.locusInfoUpdateCallback = options.locusInfoUpdateCallback;
|
97
|
+
|
98
|
+
// object mapping dataset names to arrays of leaf data
|
99
|
+
const leafData = this.analyzeLocusHtMeta(locus);
|
100
|
+
|
101
|
+
for (const dataSet of dataSets) {
|
102
|
+
const {name, leafCount} = dataSet;
|
103
|
+
|
104
|
+
const hashTree = new HashTree(leafData[name] || [], leafCount);
|
105
|
+
|
106
|
+
this.dataSets[name] = {
|
107
|
+
...dataSet,
|
108
|
+
hashTree,
|
109
|
+
};
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
/**
|
114
|
+
* Each dataset exists at a different place in the dto
|
115
|
+
* iterate recursively over the locus and if it has a htMeta key,
|
116
|
+
* create an object with the type, id and version and add it to the appropriate leafData array
|
117
|
+
*
|
118
|
+
* @param {any} locus - The current part of the locus being processed
|
119
|
+
* @returns {any} - An object mapping dataset names to arrays of leaf data
|
120
|
+
*/
|
121
|
+
private analyzeLocusHtMeta(locus: any) {
|
122
|
+
// object mapping dataset names to arrays of leaf data
|
123
|
+
const leafData: Record<string, Array<{type: string; id: number; version: number}>> = {};
|
124
|
+
|
125
|
+
const findAndStoreMetaData = (currentLocusPart: any) => {
|
126
|
+
if (typeof currentLocusPart !== 'object' || currentLocusPart === null) {
|
127
|
+
return;
|
128
|
+
}
|
129
|
+
|
130
|
+
if (currentLocusPart.htMeta && currentLocusPart.htMeta.dataSetNames) {
|
131
|
+
const {type, id, version} = currentLocusPart.htMeta.elementId;
|
132
|
+
const {dataSetNames} = currentLocusPart.htMeta;
|
133
|
+
const leafInfo = {type, id, version};
|
134
|
+
|
135
|
+
for (const dataSetName of dataSetNames) {
|
136
|
+
if (!leafData[dataSetName]) {
|
137
|
+
leafData[dataSetName] = [];
|
138
|
+
}
|
139
|
+
leafData[dataSetName].push(leafInfo);
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
if (Array.isArray(currentLocusPart)) {
|
144
|
+
for (const item of currentLocusPart) {
|
145
|
+
findAndStoreMetaData(item);
|
146
|
+
}
|
147
|
+
} else {
|
148
|
+
for (const key of Object.keys(currentLocusPart)) {
|
149
|
+
if (Object.prototype.hasOwnProperty.call(currentLocusPart, key)) {
|
150
|
+
findAndStoreMetaData(currentLocusPart[key]);
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
};
|
155
|
+
|
156
|
+
findAndStoreMetaData(locus);
|
157
|
+
|
158
|
+
return leafData;
|
159
|
+
}
|
160
|
+
|
161
|
+
/**
|
162
|
+
* Checks if the provided hash tree message indicates the end of the meeting and that there won't be any more updates.
|
163
|
+
*
|
164
|
+
* @param {HashTreeMessage} message - The hash tree message to check
|
165
|
+
* @returns {boolean} - Returns true if the message indicates the end of the meeting, false otherwise
|
166
|
+
*/
|
167
|
+
private isEndMessage(message: HashTreeMessage) {
|
168
|
+
const mainDataSet = message.dataSets.find(
|
169
|
+
(dataSet) => dataSet.name.toLowerCase() === DataSetNames.MAIN
|
170
|
+
);
|
171
|
+
|
172
|
+
if (
|
173
|
+
mainDataSet &&
|
174
|
+
mainDataSet.leafCount === 1 &&
|
175
|
+
mainDataSet.root === EMPTY_HASH &&
|
176
|
+
this.dataSets[DataSetNames.MAIN].version < mainDataSet.version
|
177
|
+
) {
|
178
|
+
// this is a special way for Locus to indicate that this meeting has ended
|
179
|
+
return true;
|
180
|
+
}
|
181
|
+
|
182
|
+
return false;
|
183
|
+
}
|
184
|
+
|
185
|
+
/**
|
186
|
+
* Handles the root hash heartbeat message
|
187
|
+
*
|
188
|
+
* @param {RootHashMessage} message - The root hash heartbeat message
|
189
|
+
* @returns {void}
|
190
|
+
*/
|
191
|
+
handleRootHashHeartBeatMessage(message: RootHashMessage): void {
|
192
|
+
const {dataSets} = message;
|
193
|
+
|
194
|
+
LoggerProxy.logger.info(
|
195
|
+
`HashTreeParser#handleRootHashMessage --> ${
|
196
|
+
this.debugId
|
197
|
+
} Received heartbeat root hash message with data sets: ${JSON.stringify(
|
198
|
+
dataSets.map(({name, root, leafCount, version}) => ({
|
199
|
+
name,
|
200
|
+
root,
|
201
|
+
leafCount,
|
202
|
+
version,
|
203
|
+
}))
|
204
|
+
)}`
|
205
|
+
);
|
206
|
+
dataSets.forEach((dataSet) => {
|
207
|
+
this.runSyncAlgorithm(dataSet);
|
208
|
+
});
|
209
|
+
}
|
210
|
+
|
211
|
+
/**
|
212
|
+
* This method should be called when we receive a partial locus DTO that contains dataSets and htMeta information
|
213
|
+
* It updates the hash trees with the new leaf data based on the received Locus
|
214
|
+
*
|
215
|
+
* @param {Object} update - The locus update containing data sets and locus information
|
216
|
+
* @returns {void}
|
217
|
+
*/
|
218
|
+
handleLocusUpdate(update: {dataSets?: Array<DataSet>; locus: any}): void {
|
219
|
+
const {dataSets, locus} = update;
|
220
|
+
|
221
|
+
if (!dataSets) {
|
222
|
+
LoggerProxy.logger.warn(
|
223
|
+
`HashTreeParser#handleLocusUpdate --> ${this.debugId} received hash tree update without dataSets, ignoring`
|
224
|
+
);
|
225
|
+
}
|
226
|
+
|
227
|
+
const leafData = this.analyzeLocusHtMeta(locus);
|
228
|
+
|
229
|
+
Object.keys(leafData).forEach((dataSetName) => {
|
230
|
+
this.dataSets[dataSetName].hashTree.putItems(leafData[dataSetName]);
|
231
|
+
});
|
232
|
+
}
|
233
|
+
|
234
|
+
/**
|
235
|
+
* Handles incoming hash tree messages, updates the hash trees and calls locusInfoUpdateCallback
|
236
|
+
*
|
237
|
+
* @param {HashTreeMessage} message - The hash tree message containing data sets and objects to be processed
|
238
|
+
* @returns {void}
|
239
|
+
*/
|
240
|
+
handleMessage(message: HashTreeMessage): void {
|
241
|
+
const {dataSets} = message;
|
242
|
+
|
243
|
+
LoggerProxy.logger.info(
|
244
|
+
`HashTreeParser#handleMessage --> ${this.debugId} received message:`,
|
245
|
+
message
|
246
|
+
);
|
247
|
+
if (this.isEndMessage(message)) {
|
248
|
+
LoggerProxy.logger.info(
|
249
|
+
`HashTreeParser#handleMessage --> ${this.debugId} received END message`
|
250
|
+
);
|
251
|
+
this.stopAllTimers();
|
252
|
+
this.locusInfoUpdateCallback(LocusInfoUpdateType.MEETING_ENDED);
|
253
|
+
} else {
|
254
|
+
const updatedObjects: HashTreeObject[] = [];
|
255
|
+
|
256
|
+
dataSets.forEach((dataSet) => {
|
257
|
+
if (this.dataSets[dataSet.name]) {
|
258
|
+
const {hashTree} = this.dataSets[dataSet.name];
|
259
|
+
|
260
|
+
if (hashTree) {
|
261
|
+
const locusStateElementsForThisSet = message.locusStateElements.filter((object) =>
|
262
|
+
object.htMeta.dataSetNames.includes(dataSet.name)
|
263
|
+
);
|
264
|
+
|
265
|
+
const appliedChangesList = hashTree.updateItems(
|
266
|
+
locusStateElementsForThisSet.map((object) =>
|
267
|
+
object.data
|
268
|
+
? {operation: 'update', item: object.htMeta.elementId}
|
269
|
+
: {operation: 'remove', item: object.htMeta.elementId}
|
270
|
+
)
|
271
|
+
);
|
272
|
+
|
273
|
+
zip(appliedChangesList, locusStateElementsForThisSet).forEach(
|
274
|
+
([changeApplied, object]) => {
|
275
|
+
if (changeApplied) {
|
276
|
+
// update the locus with the new object
|
277
|
+
updatedObjects.push(object);
|
278
|
+
}
|
279
|
+
}
|
280
|
+
);
|
281
|
+
} else {
|
282
|
+
LoggerProxy.logger.warn(
|
283
|
+
`Locus-info:index#handleHashTreeMessage --> ${this.debugId} unsupported dataSet ${dataSet.name} received in hash tree message`
|
284
|
+
);
|
285
|
+
}
|
286
|
+
|
287
|
+
// update our version of the dataSet
|
288
|
+
if (this.dataSets[dataSet.name].version < dataSet.version) {
|
289
|
+
this.dataSets[dataSet.name].version = dataSet.version;
|
290
|
+
this.dataSets[dataSet.name].root = dataSet.root;
|
291
|
+
this.dataSets[dataSet.name].idleMs = dataSet.idleMs;
|
292
|
+
this.dataSets[dataSet.name].backoff = {
|
293
|
+
maxMs: dataSet.backoff.maxMs,
|
294
|
+
exponent: dataSet.backoff.exponent,
|
295
|
+
};
|
296
|
+
LoggerProxy.logger.info(
|
297
|
+
`HashTreeParser#handleMessage --> ${this.debugId} updated "${dataSet.name}" to version=${dataSet.version}, root=${dataSet.root}`
|
298
|
+
);
|
299
|
+
}
|
300
|
+
|
301
|
+
this.runSyncAlgorithm(dataSet);
|
302
|
+
}
|
303
|
+
});
|
304
|
+
|
305
|
+
if (updatedObjects.length > 0) {
|
306
|
+
this.locusInfoUpdateCallback(LocusInfoUpdateType.OBJECTS_UPDATED, {updatedObjects});
|
307
|
+
} else {
|
308
|
+
LoggerProxy.logger.info(
|
309
|
+
`HashTreeParser#handleMessage --> ${this.debugId} No objects updated as a result of received message`
|
310
|
+
);
|
311
|
+
}
|
312
|
+
}
|
313
|
+
}
|
314
|
+
|
315
|
+
/**
|
316
|
+
* Calculates a weighted backoff time that should be used for syncs
|
317
|
+
*
|
318
|
+
* @param {Object} backoff - The backoff configuration containing maxMs and exponent
|
319
|
+
* @returns {number} - A weighted backoff time based on the provided configuration, using algorithm supplied by Locus team
|
320
|
+
*/
|
321
|
+
private getWeightedBackoffTime(backoff: {maxMs: number; exponent: number}): number {
|
322
|
+
const {maxMs, exponent} = backoff;
|
323
|
+
|
324
|
+
const randomValue = Math.random();
|
325
|
+
|
326
|
+
return Math.round(randomValue ** exponent * maxMs);
|
327
|
+
}
|
328
|
+
|
329
|
+
/**
|
330
|
+
* Runs the sync algorithm for the given data set.
|
331
|
+
*
|
332
|
+
* @param {DataSet} receivedDataSet - The data set to run the sync algorithm for.
|
333
|
+
* @returns {void}
|
334
|
+
*/
|
335
|
+
private runSyncAlgorithm(receivedDataSet: DataSet) {
|
336
|
+
const dataSet = this.dataSets[receivedDataSet.name];
|
337
|
+
|
338
|
+
if (!dataSet) {
|
339
|
+
LoggerProxy.logger.warn(
|
340
|
+
`HashTreeParser#runSyncAlgorithm --> ${this.debugId} No data set found for ${receivedDataSet.name}, skipping sync algorithm`
|
341
|
+
);
|
342
|
+
|
343
|
+
return;
|
344
|
+
}
|
345
|
+
|
346
|
+
dataSet.hashTree.resize(receivedDataSet.leafCount);
|
347
|
+
|
348
|
+
// temporary log for the workshop // todo: remove
|
349
|
+
const ourCurrentRootHash = dataSet.hashTree.getRootHash();
|
350
|
+
LoggerProxy.logger.info(
|
351
|
+
`HashTreeParser#runSyncAlgorithm --> ${this.debugId} dataSet="${dataSet.name}" version=${dataSet.version} hashes before starting timer: ours=${ourCurrentRootHash} Locus=${dataSet.root}`
|
352
|
+
);
|
353
|
+
|
354
|
+
const delay = dataSet.idleMs + this.getWeightedBackoffTime(dataSet.backoff);
|
355
|
+
|
356
|
+
if (delay > 0) {
|
357
|
+
if (dataSet.timer) {
|
358
|
+
clearTimeout(dataSet.timer);
|
359
|
+
}
|
360
|
+
|
361
|
+
LoggerProxy.logger.info(
|
362
|
+
`HashTreeParser#runSyncAlgorithm --> ${this.debugId} setting "${dataSet.name}" sync timer for ${delay}`
|
363
|
+
);
|
364
|
+
|
365
|
+
dataSet.timer = setTimeout(async () => {
|
366
|
+
LoggerProxy.logger.info(
|
367
|
+
`HashTreeParser#runSyncAlgorithm --> ${this.debugId} Sync timer fired for "${dataSet.name}" data set`
|
368
|
+
);
|
369
|
+
|
370
|
+
dataSet.timer = undefined;
|
371
|
+
|
372
|
+
const rootHash = dataSet.hashTree.getRootHash();
|
373
|
+
|
374
|
+
if (dataSet.root !== rootHash) {
|
375
|
+
LoggerProxy.logger.info(
|
376
|
+
`HashTreeParser#runSyncAlgorithm --> ${this.debugId} Root hash mismatch: received=${dataSet.root}, ours=${rootHash}, syncing data set "${dataSet.name}"`
|
377
|
+
);
|
378
|
+
|
379
|
+
const mismatchedLeavesData: Record<number, LeafDataItem[]> = {};
|
380
|
+
|
381
|
+
if (dataSet.leafCount !== 1) {
|
382
|
+
let receivedHashes;
|
383
|
+
|
384
|
+
try {
|
385
|
+
// request hashes from sender
|
386
|
+
receivedHashes = await this.getHashesFromLocus(dataSet.name);
|
387
|
+
} catch (error) {
|
388
|
+
if (error.statusCode === 409) {
|
389
|
+
// this is a leaf count mismatch, we should do nothing, just wait for another heartbeat message from Locus
|
390
|
+
LoggerProxy.logger.info(
|
391
|
+
`HashTreeParser#getHashesFromLocus --> ${this.debugId} Got 409 when fetching hashes for data set "${dataSet.name}": ${error.message}`
|
392
|
+
);
|
393
|
+
|
394
|
+
return;
|
395
|
+
}
|
396
|
+
throw error;
|
397
|
+
}
|
398
|
+
|
399
|
+
// identify mismatched leaves
|
400
|
+
const mismatchedLeaveIndexes = dataSet.hashTree.diffHashes(receivedHashes);
|
401
|
+
|
402
|
+
mismatchedLeaveIndexes.forEach((index) => {
|
403
|
+
mismatchedLeavesData[index] = dataSet.hashTree.getLeafData(index);
|
404
|
+
});
|
405
|
+
} else {
|
406
|
+
mismatchedLeavesData[0] = dataSet.hashTree.getLeafData(0);
|
407
|
+
}
|
408
|
+
// request sync for mismatched leaves
|
409
|
+
if (Object.keys(mismatchedLeavesData).length > 0) {
|
410
|
+
const updatedObjects = await this.sendSyncRequestToLocus(dataSet, mismatchedLeavesData);
|
411
|
+
|
412
|
+
if (updatedObjects.length > 0) {
|
413
|
+
this.locusInfoUpdateCallback(LocusInfoUpdateType.OBJECTS_UPDATED, {updatedObjects});
|
414
|
+
}
|
415
|
+
}
|
416
|
+
} else {
|
417
|
+
LoggerProxy.logger.info(
|
418
|
+
`HashTreeParser#runSyncAlgorithm --> ${this.debugId} "${dataSet.name}" root hash matching: ${rootHash}, version=${dataSet.version}`
|
419
|
+
);
|
420
|
+
}
|
421
|
+
}, delay);
|
422
|
+
} else {
|
423
|
+
LoggerProxy.logger.info(
|
424
|
+
`HashTreeParser#runSyncAlgorithm --> ${this.debugId} No delay for "${dataSet.name}" data set, skipping sync timer reset/setup`
|
425
|
+
);
|
426
|
+
}
|
427
|
+
}
|
428
|
+
|
429
|
+
/**
|
430
|
+
* Stops all timers for the data sets to prevent any further sync attempts.
|
431
|
+
* @returns {void}
|
432
|
+
*/
|
433
|
+
stopAllTimers() {
|
434
|
+
Object.values(this.dataSets).forEach((dataSet) => {
|
435
|
+
if (dataSet.timer) {
|
436
|
+
clearTimeout(dataSet.timer);
|
437
|
+
dataSet.timer = undefined;
|
438
|
+
}
|
439
|
+
});
|
440
|
+
}
|
441
|
+
|
442
|
+
/**
|
443
|
+
* Gets the current hashes from the locus for a specific data set.
|
444
|
+
* @param {string} dataSetName
|
445
|
+
* @returns {string[]}
|
446
|
+
*/
|
447
|
+
private getHashesFromLocus(dataSetName: string) {
|
448
|
+
LoggerProxy.logger.info(
|
449
|
+
`HashTreeParser#getHashesFromLocus --> ${this.debugId} Requesting hashes for data set "${dataSetName}"`
|
450
|
+
);
|
451
|
+
|
452
|
+
const dataSet = this.dataSets[dataSetName];
|
453
|
+
|
454
|
+
const url = `${dataSet.url}/hashtree`;
|
455
|
+
|
456
|
+
return this.webexRequest({
|
457
|
+
method: HTTP_VERBS.GET,
|
458
|
+
uri: url,
|
459
|
+
})
|
460
|
+
.then((response) => {
|
461
|
+
const hashes = response.body?.hashes;
|
462
|
+
|
463
|
+
if (!hashes || !Array.isArray(hashes)) {
|
464
|
+
LoggerProxy.logger.warn(
|
465
|
+
`HashTreeParser#getHashesFromLocus --> ${this.debugId} Locus returned invalid hashes, response body=`,
|
466
|
+
response.body
|
467
|
+
);
|
468
|
+
throw new Error(`Locus returned invalid hashes: ${hashes}`);
|
469
|
+
}
|
470
|
+
|
471
|
+
LoggerProxy.logger.info(
|
472
|
+
`HashTreeParser#getHashesFromLocus --> ${
|
473
|
+
this.debugId
|
474
|
+
} Received hashes for data set "${dataSetName}": ${JSON.stringify(hashes)}`
|
475
|
+
);
|
476
|
+
|
477
|
+
return hashes;
|
478
|
+
})
|
479
|
+
.catch((error) => {
|
480
|
+
LoggerProxy.logger.error(
|
481
|
+
`HashTreeParser#getHashesFromLocus --> ${this.debugId} Error ${error.statusCode} fetching hashes for data set "${dataSetName}":`,
|
482
|
+
error
|
483
|
+
);
|
484
|
+
throw error;
|
485
|
+
});
|
486
|
+
}
|
487
|
+
|
488
|
+
/**
|
489
|
+
* Sends a sync request to Locus for the specified data set.
|
490
|
+
*
|
491
|
+
* @param {InternalDataSet} dataSet The data set to sync.
|
492
|
+
* @param {Record<number, LeafDataItem[]>} mismatchedLeavesData The mismatched leaves data to include in the sync request.
|
493
|
+
* @returns {Promise<HashTreeObject[]>}
|
494
|
+
*/
|
495
|
+
private sendSyncRequestToLocus(
|
496
|
+
dataSet: InternalDataSet,
|
497
|
+
mismatchedLeavesData: Record<number, LeafDataItem[]>
|
498
|
+
): Promise<HashTreeObject[]> {
|
499
|
+
LoggerProxy.logger.info(
|
500
|
+
`HashTreeParser#sendSyncRequestToLocus --> ${this.debugId} Sending sync request for data set "${dataSet.name}"`
|
501
|
+
);
|
502
|
+
|
503
|
+
const url = `${dataSet.url}/sync`;
|
504
|
+
const body = {
|
505
|
+
dataSet: {
|
506
|
+
name: dataSet.name,
|
507
|
+
leafCount: dataSet.leafCount,
|
508
|
+
root: dataSet.hashTree.getRootHash(), // todo: avoid recalculation
|
509
|
+
},
|
510
|
+
leafDataEntries: [],
|
511
|
+
};
|
512
|
+
|
513
|
+
Object.keys(mismatchedLeavesData).forEach((index) => {
|
514
|
+
body.leafDataEntries.push({
|
515
|
+
leafIndex: parseInt(index, 10),
|
516
|
+
elementIds: mismatchedLeavesData[index],
|
517
|
+
});
|
518
|
+
});
|
519
|
+
|
520
|
+
return this.webexRequest({
|
521
|
+
method: HTTP_VERBS.POST,
|
522
|
+
uri: url,
|
523
|
+
body,
|
524
|
+
})
|
525
|
+
.then((resp) => {
|
526
|
+
LoggerProxy.logger.info(
|
527
|
+
`HashTreeParser#sendSyncRequestToLocus --> ${this.debugId} Sync request succeeded for "${dataSet.name}"`
|
528
|
+
);
|
529
|
+
|
530
|
+
// todo: handle response body (it may be there or not)
|
531
|
+
if (resp.statusCode === 202) {
|
532
|
+
LoggerProxy.logger.info(
|
533
|
+
`HashTreeParser#sendSyncRequestToLocus --> ${this.debugId} Got 202 for sync request for data set "${dataSet.name}", data should arrive via messages`
|
534
|
+
);
|
535
|
+
}
|
536
|
+
const updatedObjects = resp.body?.objects || [];
|
537
|
+
|
538
|
+
if (updatedObjects.length !== body.leafDataEntries.length) {
|
539
|
+
LoggerProxy.logger.info(
|
540
|
+
`HashTreeParser#sendSyncRequestToLocus --> ${this.debugId} Sync request sent for "${dataSet.name}" with ${body.leafDataEntries.length} entries, but got ${updatedObjects.length} objects in response (statusCode=${resp.statusCode})`
|
541
|
+
);
|
542
|
+
}
|
543
|
+
|
544
|
+
return updatedObjects;
|
545
|
+
})
|
546
|
+
.catch((error) => {
|
547
|
+
LoggerProxy.logger.error(
|
548
|
+
`HashTreeParser#sendSyncRequestToLocus --> ${this.debugId} Error ${error.statusCode} sending sync request for data set "${dataSet.name}":`,
|
549
|
+
error
|
550
|
+
);
|
551
|
+
throw error;
|
552
|
+
});
|
553
|
+
}
|
554
|
+
}
|
555
|
+
|
556
|
+
export default HashTreeParser;
|
@@ -40,6 +40,7 @@ ControlsUtils.parse = (controls: any) => {
|
|
40
40
|
parsedControls.transcribe = {
|
41
41
|
transcribing: controls.transcribe.transcribing,
|
42
42
|
caption: controls.transcribe.caption,
|
43
|
+
spokenLanguage: controls.transcribe.spokenLanguage,
|
43
44
|
};
|
44
45
|
}
|
45
46
|
|
@@ -123,6 +124,12 @@ ControlsUtils.parse = (controls: any) => {
|
|
123
124
|
};
|
124
125
|
}
|
125
126
|
|
127
|
+
if (controls?.pollingQAControl) {
|
128
|
+
parsedControls.pollingQAControl = {
|
129
|
+
enabled: controls.pollingQAControl.enabled,
|
130
|
+
};
|
131
|
+
}
|
132
|
+
|
126
133
|
return parsedControls;
|
127
134
|
};
|
128
135
|
|
@@ -186,6 +193,11 @@ ControlsUtils.getControls = (oldControls: any, newControls: any) => {
|
|
186
193
|
!isEqual(previous?.transcribe?.transcribing, current?.transcribe?.transcribing) && // upon first join, previous?.record?.recording = undefined; thus, never going to be equal and will always return true
|
187
194
|
(previous?.transcribe?.transcribing || current?.transcribe?.transcribing), // therefore, condition added to prevent false firings of #meeting:recording:stopped upon first joining a meeting
|
188
195
|
|
196
|
+
hasTranscribeSpokenLanguageChanged:
|
197
|
+
current?.transcribe &&
|
198
|
+
!isEqual(previous?.transcribe?.spokenLanguage, current?.transcribe?.spokenLanguage) &&
|
199
|
+
!!(previous?.transcribe?.spokenLanguage || current?.transcribe?.spokenLanguage),
|
200
|
+
|
189
201
|
hasManualCaptionChanged:
|
190
202
|
current?.manualCaptionControl &&
|
191
203
|
!isEqual(previous?.manualCaptionControl?.enabled, current?.manualCaptionControl?.enabled) &&
|
@@ -229,6 +241,9 @@ ControlsUtils.getControls = (oldControls: any, newControls: any) => {
|
|
229
241
|
|
230
242
|
hasRemoteDesktopControlChanged:
|
231
243
|
current?.rdcControl?.enabled !== previous?.rdcControl?.enabled,
|
244
|
+
|
245
|
+
hasPollingQAControlChanged:
|
246
|
+
current?.pollingQAControl?.enabled !== previous?.pollingQAControl?.enabled,
|
232
247
|
},
|
233
248
|
};
|
234
249
|
};
|