@webex/plugin-meetings 3.10.0-next.8 → 3.10.0-set-bitrate.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/dist/annotation/annotation.types.js.map +1 -1
- package/dist/annotation/constants.js.map +1 -1
- package/dist/annotation/index.js +19 -22
- package/dist/annotation/index.js.map +1 -1
- package/dist/breakouts/breakout.js +6 -6
- package/dist/breakouts/breakout.js.map +1 -1
- package/dist/breakouts/collection.js.map +1 -1
- package/dist/breakouts/edit-lock-error.js +9 -11
- package/dist/breakouts/edit-lock-error.js.map +1 -1
- package/dist/breakouts/events.js.map +1 -1
- package/dist/breakouts/index.js +126 -127
- package/dist/breakouts/index.js.map +1 -1
- package/dist/breakouts/request.js +6 -8
- package/dist/breakouts/request.js.map +1 -1
- package/dist/breakouts/utils.js.map +1 -1
- package/dist/common/browser-detection.js.map +1 -1
- package/dist/common/collection.js +1 -2
- package/dist/common/collection.js.map +1 -1
- package/dist/common/config.js.map +1 -1
- package/dist/common/errors/captcha-error.js +9 -11
- package/dist/common/errors/captcha-error.js.map +1 -1
- package/dist/common/errors/intent-to-join.js +10 -12
- package/dist/common/errors/intent-to-join.js.map +1 -1
- package/dist/common/errors/join-forbidden-error.js +10 -12
- package/dist/common/errors/join-forbidden-error.js.map +1 -1
- package/dist/common/errors/join-meeting.js +10 -12
- package/dist/common/errors/join-meeting.js.map +1 -1
- package/dist/common/errors/join-webinar-error.js +9 -11
- package/dist/common/errors/join-webinar-error.js.map +1 -1
- package/dist/common/errors/media.js +9 -11
- package/dist/common/errors/media.js.map +1 -1
- package/dist/common/errors/multistream-not-supported-error.js +9 -11
- package/dist/common/errors/multistream-not-supported-error.js.map +1 -1
- package/dist/common/errors/no-meeting-info.js +9 -11
- package/dist/common/errors/no-meeting-info.js.map +1 -1
- package/dist/common/errors/parameter.js +11 -14
- package/dist/common/errors/parameter.js.map +1 -1
- package/dist/common/errors/password-error.js +9 -11
- package/dist/common/errors/password-error.js.map +1 -1
- package/dist/common/errors/permission.js +9 -11
- package/dist/common/errors/permission.js.map +1 -1
- package/dist/common/errors/reclaim-host-role-errors.js +32 -38
- package/dist/common/errors/reclaim-host-role-errors.js.map +1 -1
- package/dist/common/errors/reconnection-not-started.js +5 -6
- package/dist/common/errors/reconnection-not-started.js.map +1 -1
- package/dist/common/errors/reconnection.js +9 -11
- package/dist/common/errors/reconnection.js.map +1 -1
- package/dist/common/errors/stats.js +9 -11
- package/dist/common/errors/stats.js.map +1 -1
- package/dist/common/errors/webex-errors.js +20 -29
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/common/errors/webex-meetings-error.js +9 -12
- package/dist/common/errors/webex-meetings-error.js.map +1 -1
- package/dist/common/events/events-scope.js +9 -10
- package/dist/common/events/events-scope.js.map +1 -1
- package/dist/common/events/events.js +9 -10
- package/dist/common/events/events.js.map +1 -1
- package/dist/common/events/trigger-proxy.js.map +1 -1
- package/dist/common/events/util.js.map +1 -1
- package/dist/common/logs/logger-config.js.map +1 -1
- package/dist/common/logs/logger-proxy.js.map +1 -1
- package/dist/common/logs/request.js +17 -17
- package/dist/common/logs/request.js.map +1 -1
- package/dist/common/queue.js +1 -2
- package/dist/common/queue.js.map +1 -1
- package/dist/config.js +0 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +11 -8
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/constants.js.map +1 -1
- package/dist/controls-options-manager/enums.js.map +1 -1
- package/dist/controls-options-manager/index.js +1 -2
- package/dist/controls-options-manager/index.js.map +1 -1
- package/dist/controls-options-manager/types.js.map +1 -1
- package/dist/controls-options-manager/util.js +1 -2
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/hashTree/constants.js +20 -0
- package/dist/hashTree/constants.js.map +1 -0
- package/dist/hashTree/hashTree.js +515 -0
- package/dist/hashTree/hashTree.js.map +1 -0
- package/dist/hashTree/hashTreeParser.js +1266 -0
- package/dist/hashTree/hashTreeParser.js.map +1 -0
- package/dist/hashTree/types.js +22 -0
- package/dist/hashTree/types.js.map +1 -0
- package/dist/hashTree/utils.js +48 -0
- package/dist/hashTree/utils.js.map +1 -0
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/interceptors/index.js.map +1 -1
- package/dist/interceptors/locusRetry.js +6 -8
- package/dist/interceptors/locusRetry.js.map +1 -1
- package/dist/interceptors/locusRouteToken.js +6 -8
- package/dist/interceptors/locusRouteToken.js.map +1 -1
- package/dist/interpretation/collection.js.map +1 -1
- package/dist/interpretation/index.js +1 -2
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/interpretation/siLanguage.js.map +1 -1
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
- package/dist/locus-info/fullState.js.map +1 -1
- package/dist/locus-info/hostUtils.js.map +1 -1
- package/dist/locus-info/index.js +584 -170
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/infoUtils.js.map +1 -1
- package/dist/locus-info/mediaSharesUtils.js.map +1 -1
- package/dist/locus-info/parser.js +3 -4
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/locus-info/types.js +7 -0
- package/dist/locus-info/types.js.map +1 -0
- package/dist/media/MediaConnectionAwaiter.js +1 -2
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/index.js +0 -2
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +15 -17
- package/dist/media/properties.js.map +1 -1
- package/dist/media/util.js.map +1 -1
- package/dist/meeting/brbState.js +8 -9
- package/dist/meeting/brbState.js.map +1 -1
- package/dist/meeting/connectionStateHandler.js +10 -13
- package/dist/meeting/connectionStateHandler.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +1576 -1533
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +13 -17
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +11 -12
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +101 -104
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/state.js.map +1 -1
- package/dist/meeting/type.js.map +1 -1
- package/dist/meeting/util.js +24 -23
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting/voicea-meeting.js +3 -3
- package/dist/meeting/voicea-meeting.js.map +1 -1
- package/dist/meeting-info/collection.js +7 -10
- package/dist/meeting-info/collection.js.map +1 -1
- package/dist/meeting-info/index.js +1 -2
- package/dist/meeting-info/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +135 -146
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/request.js +1 -2
- package/dist/meeting-info/request.js.map +1 -1
- package/dist/meeting-info/util.js +36 -37
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js +30 -31
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/collection.js +6 -8
- package/dist/meetings/collection.js.map +1 -1
- package/dist/meetings/index.js +179 -141
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.js.map +1 -1
- package/dist/meetings/request.js +6 -8
- package/dist/meetings/request.js.map +1 -1
- package/dist/meetings/util.js +25 -23
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +1 -2
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js +6 -3
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js.map +1 -1
- package/dist/members/collection.js +1 -2
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +18 -21
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +8 -11
- package/dist/members/request.js.map +1 -1
- package/dist/members/types.js.map +1 -1
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +3 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/metrics/index.js +3 -4
- package/dist/metrics/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +1 -2
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/receiveSlot.js +34 -45
- package/dist/multistream/receiveSlot.js.map +1 -1
- package/dist/multistream/receiveSlotManager.js +8 -9
- package/dist/multistream/receiveSlotManager.js.map +1 -1
- package/dist/multistream/remoteMedia.js +12 -15
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js +1 -2
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +122 -123
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +29 -30
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/personal-meeting-room/index.js +16 -19
- package/dist/personal-meeting-room/index.js.map +1 -1
- package/dist/personal-meeting-room/request.js +7 -10
- package/dist/personal-meeting-room/request.js.map +1 -1
- package/dist/personal-meeting-room/util.js.map +1 -1
- package/dist/reachability/clusterReachability.js +56 -373
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +203 -205
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/reachability.types.js +14 -1
- package/dist/reachability/reachability.types.js.map +1 -1
- package/dist/reachability/reachabilityPeerConnection.js +445 -0
- package/dist/reachability/reachabilityPeerConnection.js.map +1 -0
- package/dist/reachability/request.js.map +1 -1
- package/dist/reachability/util.js.map +1 -1
- package/dist/reactions/constants.js.map +1 -1
- package/dist/reactions/reactions.js.map +1 -1
- package/dist/reactions/reactions.type.js.map +1 -1
- package/dist/reconnection-manager/index.js +178 -176
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/recording-controller/enums.js.map +1 -1
- package/dist/recording-controller/index.js +1 -2
- package/dist/recording-controller/index.js.map +1 -1
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/roap/index.js +12 -15
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +24 -26
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +75 -76
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/roap/types.js.map +1 -1
- package/dist/transcription/index.js +4 -5
- package/dist/transcription/index.js.map +1 -1
- package/dist/types/constants.d.ts +26 -21
- package/dist/types/hashTree/constants.d.ts +8 -0
- package/dist/types/hashTree/hashTree.d.ts +129 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +260 -0
- package/dist/types/hashTree/types.d.ts +27 -0
- package/dist/types/hashTree/utils.d.ts +9 -0
- package/dist/types/locus-info/index.d.ts +97 -80
- package/dist/types/locus-info/types.d.ts +54 -0
- package/dist/types/meeting/index.d.ts +22 -9
- package/dist/types/meetings/index.d.ts +9 -2
- package/dist/types/metrics/constants.d.ts +2 -0
- package/dist/types/reachability/clusterReachability.d.ts +10 -88
- package/dist/types/reachability/reachability.types.d.ts +12 -1
- package/dist/types/reachability/reachabilityPeerConnection.d.ts +111 -0
- package/dist/webinar/collection.js +1 -2
- package/dist/webinar/collection.js.map +1 -1
- package/dist/webinar/index.js +148 -158
- package/dist/webinar/index.js.map +1 -1
- package/package.json +22 -21
- package/src/constants.ts +13 -1
- package/src/hashTree/constants.ts +9 -0
- package/src/hashTree/hashTree.ts +463 -0
- package/src/hashTree/hashTreeParser.ts +1161 -0
- package/src/hashTree/types.ts +32 -0
- package/src/hashTree/utils.ts +42 -0
- package/src/locus-info/index.ts +597 -154
- package/src/locus-info/types.ts +53 -0
- package/src/meeting/index.ts +78 -27
- package/src/meeting/util.ts +1 -0
- package/src/meetings/index.ts +104 -51
- package/src/metrics/constants.ts +2 -0
- package/src/reachability/clusterReachability.ts +50 -347
- package/src/reachability/reachability.types.ts +15 -1
- package/src/reachability/reachabilityPeerConnection.ts +416 -0
- package/test/unit/spec/hashTree/hashTree.ts +655 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +1532 -0
- package/test/unit/spec/hashTree/utils.ts +103 -0
- package/test/unit/spec/locus-info/index.js +795 -16
- package/test/unit/spec/meeting/index.js +120 -20
- package/test/unit/spec/meeting/utils.js +77 -0
- package/test/unit/spec/meetings/index.js +71 -26
- package/test/unit/spec/reachability/clusterReachability.ts +281 -138
package/src/locus-info/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {isEqual, assignWith, cloneDeep, isEmpty
|
|
1
|
+
import {isEqual, assignWith, cloneDeep, isEmpty} from 'lodash';
|
|
2
2
|
|
|
3
3
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
4
4
|
import EventsScope from '../common/events/events-scope';
|
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
MEETING_REMOVED_REASON,
|
|
18
18
|
CALL_REMOVED_REASON,
|
|
19
19
|
RECORDING_STATE,
|
|
20
|
-
|
|
20
|
+
Enum,
|
|
21
|
+
SELF_ROLES,
|
|
21
22
|
} from '../constants';
|
|
22
23
|
|
|
23
24
|
import InfoUtils from './infoUtils';
|
|
@@ -30,52 +31,55 @@ import MediaSharesUtils from './mediaSharesUtils';
|
|
|
30
31
|
import LocusDeltaParser from './parser';
|
|
31
32
|
import Metrics from '../metrics';
|
|
32
33
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
host?: {
|
|
48
|
-
id: string;
|
|
49
|
-
incomingCallProtocols: any[];
|
|
50
|
-
isExternal: boolean;
|
|
51
|
-
name: string;
|
|
52
|
-
orgId: string;
|
|
34
|
+
import HashTreeParser, {
|
|
35
|
+
DataSet,
|
|
36
|
+
HashTreeMessage,
|
|
37
|
+
HashTreeObject,
|
|
38
|
+
isSelf,
|
|
39
|
+
LocusInfoUpdateType,
|
|
40
|
+
} from '../hashTree/hashTreeParser';
|
|
41
|
+
import {ObjectType, ObjectTypeToLocusKeyMap} from '../hashTree/types';
|
|
42
|
+
import {Links, LocusDTO, LocusFullState} from './types';
|
|
43
|
+
|
|
44
|
+
export type LocusLLMEvent = {
|
|
45
|
+
data: {
|
|
46
|
+
eventType: typeof LOCUSEVENT.HASH_TREE_DATA_UPDATED;
|
|
47
|
+
stateElementsMessage: HashTreeMessage;
|
|
53
48
|
};
|
|
54
|
-
info?: any;
|
|
55
|
-
links?: any;
|
|
56
|
-
mediaShares?: any[];
|
|
57
|
-
meetings?: any[];
|
|
58
|
-
participants: any[];
|
|
59
|
-
replaces?: any[];
|
|
60
|
-
self?: any;
|
|
61
|
-
sequence?: {
|
|
62
|
-
dirtyParticipants: number;
|
|
63
|
-
entries: number[];
|
|
64
|
-
rangeEnd: number;
|
|
65
|
-
rangeStart: number;
|
|
66
|
-
sequenceHash: number;
|
|
67
|
-
sessionToken: string;
|
|
68
|
-
since: string;
|
|
69
|
-
totalParticipants: number;
|
|
70
|
-
};
|
|
71
|
-
syncUrl?: string;
|
|
72
|
-
url?: string;
|
|
73
49
|
};
|
|
74
50
|
|
|
51
|
+
// list of top level keys in Locus DTO relevant for Hash Tree DTOs processing
|
|
52
|
+
// it does not contain fields specific to classic Locus DTOs like sequence or baseSequence
|
|
53
|
+
const LocusDtoTopLevelKeys = [
|
|
54
|
+
'controls',
|
|
55
|
+
'fullState',
|
|
56
|
+
'host',
|
|
57
|
+
'info',
|
|
58
|
+
'links',
|
|
59
|
+
'mediaShares',
|
|
60
|
+
'meetings',
|
|
61
|
+
'participants',
|
|
62
|
+
'replaces',
|
|
63
|
+
'self',
|
|
64
|
+
'sequence',
|
|
65
|
+
'syncUrl',
|
|
66
|
+
'url',
|
|
67
|
+
'htMeta', // only exists when hash trees are used
|
|
68
|
+
];
|
|
69
|
+
|
|
75
70
|
export type LocusApiResponseBody = {
|
|
71
|
+
dataSets?: DataSet[];
|
|
76
72
|
locus: LocusDTO; // this LocusDTO here might not be the full one (for example it won't have all the participants, but it should have self)
|
|
77
73
|
};
|
|
78
74
|
|
|
75
|
+
const LocusObjectStateAfterUpdates = {
|
|
76
|
+
unchanged: 'unchanged',
|
|
77
|
+
removed: 'removed',
|
|
78
|
+
updated: 'updated',
|
|
79
|
+
} as const;
|
|
80
|
+
|
|
81
|
+
type LocusObjectStateAfterUpdates = Enum<typeof LocusObjectStateAfterUpdates>;
|
|
82
|
+
|
|
79
83
|
/**
|
|
80
84
|
* @description LocusInfo extends ChildEmitter to convert locusInfo info a private emitter to parent object
|
|
81
85
|
* @export
|
|
@@ -93,10 +97,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
93
97
|
aclUrl: any;
|
|
94
98
|
baseSequence: any;
|
|
95
99
|
created: any;
|
|
96
|
-
identities: any;
|
|
97
|
-
membership: any;
|
|
98
100
|
participants: any;
|
|
99
|
-
participantsUrl: any;
|
|
100
101
|
replaces: any;
|
|
101
102
|
scheduledMeeting: any;
|
|
102
103
|
sequence: any;
|
|
@@ -108,12 +109,14 @@ export default class LocusInfo extends EventsScope {
|
|
|
108
109
|
info: any;
|
|
109
110
|
roles: any;
|
|
110
111
|
mediaShares: any;
|
|
111
|
-
replace: any;
|
|
112
112
|
url: any;
|
|
113
|
-
|
|
114
|
-
resources: any;
|
|
113
|
+
links?: Links;
|
|
115
114
|
mainSessionLocusCache: any;
|
|
116
115
|
self: any;
|
|
116
|
+
hashTreeParser?: HashTreeParser;
|
|
117
|
+
hashTreeObjectId2ParticipantId: Map<number, string>; // mapping of hash tree object ids to participant ids
|
|
118
|
+
classicVsHashTreeMismatchMetricCounter = 0;
|
|
119
|
+
|
|
117
120
|
/**
|
|
118
121
|
* Constructor
|
|
119
122
|
* @param {function} updateMeeting callback to update the meeting object from an object
|
|
@@ -132,10 +135,12 @@ export default class LocusInfo extends EventsScope {
|
|
|
132
135
|
this.meetingId = meetingId;
|
|
133
136
|
this.updateMeeting = updateMeeting;
|
|
134
137
|
this.locusParser = new LocusDeltaParser();
|
|
138
|
+
this.hashTreeObjectId2ParticipantId = new Map();
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
/**
|
|
138
142
|
* Does a Locus sync. It tries to get the latest delta DTO or if it can't, it falls back to getting the full Locus DTO.
|
|
143
|
+
* WARNING: This function must not be used for hash tree based Locus meetings.
|
|
139
144
|
*
|
|
140
145
|
* @param {Meeting} meeting
|
|
141
146
|
* @param {boolean} isLocusUrlChanged
|
|
@@ -319,13 +324,10 @@ export default class LocusInfo extends EventsScope {
|
|
|
319
324
|
init(locus: any = {}) {
|
|
320
325
|
this.created = locus.created || null;
|
|
321
326
|
this.scheduledMeeting = locus.meeting || null;
|
|
322
|
-
this.participantsUrl = locus.participantsUrl || null;
|
|
323
327
|
this.replaces = locus.replaces || null;
|
|
324
328
|
this.aclUrl = locus.aclUrl || null;
|
|
325
329
|
this.baseSequence = locus.baseSequence || null;
|
|
326
330
|
this.sequence = locus.sequence || null;
|
|
327
|
-
this.membership = locus.membership || null;
|
|
328
|
-
this.identities = locus.identities || null;
|
|
329
331
|
this.participants = locus.participants || null;
|
|
330
332
|
|
|
331
333
|
/**
|
|
@@ -351,19 +353,113 @@ export default class LocusInfo extends EventsScope {
|
|
|
351
353
|
this.updateSelf(locus.self);
|
|
352
354
|
this.updateHostInfo(locus.host);
|
|
353
355
|
this.updateMediaShares(locus.mediaShares);
|
|
354
|
-
this.
|
|
355
|
-
this.updateResources(locus.links?.resources);
|
|
356
|
+
this.updateLinks(locus.links);
|
|
356
357
|
}
|
|
357
358
|
|
|
358
359
|
/**
|
|
359
|
-
*
|
|
360
|
+
* Creates the HashTreeParser instance.
|
|
361
|
+
* @param {Object} initial locus data
|
|
362
|
+
* @returns {void}
|
|
363
|
+
*/
|
|
364
|
+
private createHashTreeParser({
|
|
365
|
+
initialLocus,
|
|
366
|
+
}: {
|
|
367
|
+
initialLocus: {
|
|
368
|
+
dataSets: Array<DataSet>;
|
|
369
|
+
locus: any;
|
|
370
|
+
};
|
|
371
|
+
}) {
|
|
372
|
+
return new HashTreeParser({
|
|
373
|
+
initialLocus,
|
|
374
|
+
webexRequest: this.webex.request.bind(this.webex),
|
|
375
|
+
locusInfoUpdateCallback: this.updateFromHashTree.bind(this),
|
|
376
|
+
debugId: `HT-${this.meetingId.substring(0, 4)}`,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* @param {Object} data - data to initialize locus info with. It may be from a join or GET /loci response or from a Mercury event that triggers a creation of meeting object
|
|
360
382
|
* @returns {undefined}
|
|
361
383
|
* @memberof LocusInfo
|
|
362
384
|
*/
|
|
363
|
-
initialSetup(
|
|
364
|
-
|
|
365
|
-
|
|
385
|
+
async initialSetup(
|
|
386
|
+
data:
|
|
387
|
+
| {
|
|
388
|
+
trigger: 'join-response';
|
|
389
|
+
locus: LocusDTO;
|
|
390
|
+
dataSets?: DataSet[];
|
|
391
|
+
}
|
|
392
|
+
| {
|
|
393
|
+
trigger: 'locus-message';
|
|
394
|
+
locus?: LocusDTO;
|
|
395
|
+
hashTreeMessage?: HashTreeMessage;
|
|
396
|
+
}
|
|
397
|
+
| {
|
|
398
|
+
trigger: 'get-loci-response';
|
|
399
|
+
locus?: LocusDTO;
|
|
400
|
+
}
|
|
401
|
+
) {
|
|
402
|
+
switch (data.trigger) {
|
|
403
|
+
case 'locus-message':
|
|
404
|
+
if (data.hashTreeMessage) {
|
|
405
|
+
// we need the SELF object to be in the received message, because it contains visibleDataSets
|
|
406
|
+
// and these are needed to initialize all the hash trees
|
|
407
|
+
const selfObject = data.hashTreeMessage.locusStateElements?.find((el) => isSelf(el));
|
|
408
|
+
|
|
409
|
+
if (!selfObject?.data?.visibleDataSets) {
|
|
410
|
+
LoggerProxy.logger.warn(
|
|
411
|
+
`Locus-info:index#initialSetup --> cannot initialize HashTreeParser, SELF object with visibleDataSets is missing in the message`
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
throw new Error('SELF object with visibleDataSets is missing in the message');
|
|
415
|
+
}
|
|
366
416
|
|
|
417
|
+
LoggerProxy.logger.info(
|
|
418
|
+
'Locus-info:index#initialSetup --> creating HashTreeParser from message'
|
|
419
|
+
);
|
|
420
|
+
// first create the HashTreeParser, but don't initialize it with any data yet
|
|
421
|
+
// pass just a fake locus that contains only the visibleDataSets
|
|
422
|
+
this.hashTreeParser = this.createHashTreeParser({
|
|
423
|
+
initialLocus: {
|
|
424
|
+
locus: {self: {visibleDataSets: selfObject.data.visibleDataSets}},
|
|
425
|
+
dataSets: [], // empty, because they will be populated in initializeFromMessage() call // dataSets: data.hashTreeMessage.dataSets,
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// now handle the message - that should populate all the visible datasets
|
|
430
|
+
await this.hashTreeParser.initializeFromMessage(data.hashTreeMessage);
|
|
431
|
+
} else {
|
|
432
|
+
// "classic" Locus case, no hash trees involved
|
|
433
|
+
this.updateLocusCache(data.locus);
|
|
434
|
+
this.onFullLocus(data.locus, undefined);
|
|
435
|
+
}
|
|
436
|
+
break;
|
|
437
|
+
case 'join-response':
|
|
438
|
+
this.updateLocusCache(data.locus);
|
|
439
|
+
this.onFullLocus(data.locus, undefined, data.dataSets);
|
|
440
|
+
break;
|
|
441
|
+
case 'get-loci-response':
|
|
442
|
+
if (data.locus?.links?.resources?.visibleDataSets?.url) {
|
|
443
|
+
LoggerProxy.logger.info(
|
|
444
|
+
'Locus-info:index#initialSetup --> creating HashTreeParser from get-loci-response'
|
|
445
|
+
);
|
|
446
|
+
// first create the HashTreeParser, but don't initialize it with any data yet
|
|
447
|
+
// pass just a fake locus that contains only the visibleDataSets
|
|
448
|
+
this.hashTreeParser = this.createHashTreeParser({
|
|
449
|
+
initialLocus: {
|
|
450
|
+
locus: {self: {visibleDataSets: data.locus?.self?.visibleDataSets}},
|
|
451
|
+
dataSets: [], // empty, because we don't have them yet
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// now initialize all the data
|
|
456
|
+
await this.hashTreeParser.initializeFromGetLociResponse(data.locus);
|
|
457
|
+
} else {
|
|
458
|
+
// "classic" Locus case, no hash trees involved
|
|
459
|
+
this.updateLocusCache(data.locus);
|
|
460
|
+
this.onFullLocus(data.locus, undefined);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
367
463
|
// Change it to true after it receives it first locus object
|
|
368
464
|
this.emitChange = true;
|
|
369
465
|
}
|
|
@@ -375,7 +471,317 @@ export default class LocusInfo extends EventsScope {
|
|
|
375
471
|
* @returns {void}
|
|
376
472
|
*/
|
|
377
473
|
handleLocusAPIResponse(meeting, responseBody: LocusApiResponseBody): void {
|
|
378
|
-
this.
|
|
474
|
+
if (this.hashTreeParser) {
|
|
475
|
+
if (!responseBody.dataSets) {
|
|
476
|
+
this.sendClassicVsHashTreeMismatchMetric(
|
|
477
|
+
meeting,
|
|
478
|
+
`expected hash tree dataSets in API response but they are missing`
|
|
479
|
+
);
|
|
480
|
+
// continuing as we can still manage without responseBody.dataSets, but this is very suspicious
|
|
481
|
+
}
|
|
482
|
+
LoggerProxy.logger.info(
|
|
483
|
+
'Locus-info:index#handleLocusAPIResponse --> passing Locus API response to HashTreeParser: ',
|
|
484
|
+
responseBody
|
|
485
|
+
);
|
|
486
|
+
// update the data in our hash trees
|
|
487
|
+
this.hashTreeParser.handleLocusUpdate(responseBody);
|
|
488
|
+
} else {
|
|
489
|
+
if (responseBody.dataSets) {
|
|
490
|
+
this.sendClassicVsHashTreeMismatchMetric(
|
|
491
|
+
meeting,
|
|
492
|
+
`unexpected hash tree dataSets in API response`
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
// classic Locus delta
|
|
496
|
+
this.handleLocusDelta(responseBody.locus, meeting);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
*
|
|
502
|
+
* @param {HashTreeObject} object data set object
|
|
503
|
+
* @param {any} locus
|
|
504
|
+
* @returns {void}
|
|
505
|
+
*/
|
|
506
|
+
updateLocusFromHashTreeObject(object: HashTreeObject, locus: LocusDTO): LocusDTO {
|
|
507
|
+
const type = object.htMeta.elementId.type.toLowerCase();
|
|
508
|
+
|
|
509
|
+
const addParticipantObject = (obj: HashTreeObject) => {
|
|
510
|
+
if (!locus.participants) {
|
|
511
|
+
locus.participants = [];
|
|
512
|
+
}
|
|
513
|
+
locus.participants.push(obj.data);
|
|
514
|
+
this.hashTreeObjectId2ParticipantId.set(obj.htMeta.elementId.id, obj.data.id);
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
switch (type) {
|
|
518
|
+
case ObjectType.locus: {
|
|
519
|
+
if (!object.data) {
|
|
520
|
+
// not doing anything here, as we need Locus to always be there (at least some fields)
|
|
521
|
+
// and that's already taken care of in updateFromHashTree()
|
|
522
|
+
LoggerProxy.logger.info(
|
|
523
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object removed, version=${object.htMeta.elementId.version}`
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
return locus;
|
|
527
|
+
}
|
|
528
|
+
// replace the main locus
|
|
529
|
+
|
|
530
|
+
// The Locus object we receive from backend has empty participants array,
|
|
531
|
+
// and may have (although it shouldn't) other fields that are managed by other ObjectTypes
|
|
532
|
+
// like "fullState" or "info", so we're making sure to delete them here
|
|
533
|
+
const locusObjectFromData = object.data;
|
|
534
|
+
|
|
535
|
+
Object.values(ObjectTypeToLocusKeyMap).forEach((locusDtoKey) => {
|
|
536
|
+
delete locusObjectFromData[locusDtoKey];
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
locus = {...locus, ...locusObjectFromData};
|
|
540
|
+
LoggerProxy.logger.info(
|
|
541
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object updated to version=${object.htMeta.elementId.version}`
|
|
542
|
+
);
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
case ObjectType.mediaShare:
|
|
546
|
+
if (object.data) {
|
|
547
|
+
LoggerProxy.logger.info(
|
|
548
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> mediaShare id=${
|
|
549
|
+
object.htMeta.elementId.id
|
|
550
|
+
} name='${object.data.name}' updated ${
|
|
551
|
+
object.data.name === 'content'
|
|
552
|
+
? `floor=${object.data.floor?.disposition}, ${object.data.floor?.beneficiary?.id}`
|
|
553
|
+
: ''
|
|
554
|
+
} version=${object.htMeta.elementId.version}`
|
|
555
|
+
);
|
|
556
|
+
const existingMediaShare = locus.mediaShares?.find(
|
|
557
|
+
(ms) => ms.htMeta.elementId.id === object.htMeta.elementId.id
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
if (existingMediaShare) {
|
|
561
|
+
Object.assign(existingMediaShare, object.data);
|
|
562
|
+
} else {
|
|
563
|
+
locus.mediaShares = locus.mediaShares || [];
|
|
564
|
+
locus.mediaShares.push(object.data);
|
|
565
|
+
}
|
|
566
|
+
} else {
|
|
567
|
+
LoggerProxy.logger.info(
|
|
568
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> mediaShare id=${object.htMeta.elementId.id} removed, version=${object.htMeta.elementId.version}`
|
|
569
|
+
);
|
|
570
|
+
locus.mediaShares = locus.mediaShares?.filter(
|
|
571
|
+
(ms) => ms.htMeta.elementId.id !== object.htMeta.elementId.id
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
break;
|
|
575
|
+
case ObjectType.participant:
|
|
576
|
+
LoggerProxy.logger.info(
|
|
577
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> participant id=${
|
|
578
|
+
object.htMeta.elementId.id
|
|
579
|
+
} ${object.data ? 'updated' : 'removed'} version=${object.htMeta.elementId.version}`
|
|
580
|
+
);
|
|
581
|
+
if (object.data) {
|
|
582
|
+
addParticipantObject(object);
|
|
583
|
+
} else {
|
|
584
|
+
const participantId = this.hashTreeObjectId2ParticipantId.get(object.htMeta.elementId.id);
|
|
585
|
+
|
|
586
|
+
if (!locus.jsSdkMeta) {
|
|
587
|
+
locus.jsSdkMeta = {removedParticipantIds: []};
|
|
588
|
+
}
|
|
589
|
+
locus.jsSdkMeta.removedParticipantIds.push(participantId);
|
|
590
|
+
this.hashTreeObjectId2ParticipantId.delete(object.htMeta.elementId.id);
|
|
591
|
+
}
|
|
592
|
+
break;
|
|
593
|
+
case ObjectType.links:
|
|
594
|
+
case ObjectType.info:
|
|
595
|
+
case ObjectType.fullState:
|
|
596
|
+
case ObjectType.self:
|
|
597
|
+
if (!object.data) {
|
|
598
|
+
// self without data is handled inside HashTreeParser and results in LocusInfoUpdateType.MEETING_ENDED, so we should never get here
|
|
599
|
+
// all other types info, fullstate, etc - Locus should never send them without data
|
|
600
|
+
LoggerProxy.logger.warn(
|
|
601
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> received ${type} object without data, this is not expected! version=${object.htMeta.elementId.version}`
|
|
602
|
+
);
|
|
603
|
+
} else {
|
|
604
|
+
LoggerProxy.logger.info(
|
|
605
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> ${type} object updated to version ${object.htMeta.elementId.version}`
|
|
606
|
+
);
|
|
607
|
+
const locusDtoKey = ObjectTypeToLocusKeyMap[type];
|
|
608
|
+
locus[locusDtoKey] = object.data;
|
|
609
|
+
|
|
610
|
+
/* Hash tree based webinar attendees don't receive a Participant object for themselves from Locus,
|
|
611
|
+
but a lot of existing code in SDK and web app expects a member object for self to exist,
|
|
612
|
+
so whenever SELF changes for a webinar attendee, we copy it into a participant object.
|
|
613
|
+
We can do it, because SELF has always all the same properties as a participant object.
|
|
614
|
+
*/
|
|
615
|
+
if (
|
|
616
|
+
type === ObjectType.self &&
|
|
617
|
+
locus.info?.isWebinar &&
|
|
618
|
+
object.data.controls?.role?.roles?.find(
|
|
619
|
+
(r) => r.type === SELF_ROLES.ATTENDEE && r.hasRole
|
|
620
|
+
)
|
|
621
|
+
) {
|
|
622
|
+
LoggerProxy.logger.info(
|
|
623
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> webinar attendee: creating participant object from self`
|
|
624
|
+
);
|
|
625
|
+
addParticipantObject(object);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
break;
|
|
629
|
+
default:
|
|
630
|
+
LoggerProxy.logger.warn(
|
|
631
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> received unsupported object type ${type}`
|
|
632
|
+
);
|
|
633
|
+
break;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
return locus;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Sends a metric when we receive something from Locus that uses hash trees while we
|
|
641
|
+
* expect classic deltas or the other way around.
|
|
642
|
+
* @param {Meeting} meeting
|
|
643
|
+
* @param {string} message
|
|
644
|
+
* @returns {void}
|
|
645
|
+
*/
|
|
646
|
+
sendClassicVsHashTreeMismatchMetric(meeting: any, message: string) {
|
|
647
|
+
LoggerProxy.logger.warn(
|
|
648
|
+
`Locus-info:index#sendClassicVsHashTreeMismatchMetric --> classic vs hash tree mismatch! ${message}`
|
|
649
|
+
);
|
|
650
|
+
|
|
651
|
+
// we don't want to flood the metrics system
|
|
652
|
+
if (this.classicVsHashTreeMismatchMetricCounter < 5) {
|
|
653
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.LOCUS_CLASSIC_VS_HASH_TREE_MISMATCH, {
|
|
654
|
+
correlationId: meeting.correlationId,
|
|
655
|
+
message,
|
|
656
|
+
});
|
|
657
|
+
this.classicVsHashTreeMismatchMetricCounter += 1;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Handles a hash tree message received from Locus.
|
|
663
|
+
*
|
|
664
|
+
* @param {Meeting} meeting - The meeting object
|
|
665
|
+
* @param {eventType} eventType - The event type
|
|
666
|
+
* @param {HashTreeMessage} message incoming hash tree message
|
|
667
|
+
* @returns {void}
|
|
668
|
+
*/
|
|
669
|
+
private handleHashTreeMessage(meeting: any, eventType: LOCUSEVENT, message: HashTreeMessage) {
|
|
670
|
+
if (eventType !== LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
|
|
671
|
+
this.sendClassicVsHashTreeMismatchMetric(
|
|
672
|
+
meeting,
|
|
673
|
+
`got ${eventType}, expected ${LOCUSEVENT.HASH_TREE_DATA_UPDATED}`
|
|
674
|
+
);
|
|
675
|
+
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
this.hashTreeParser.handleMessage(message);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Callback registered with HashTreeParser to receive locus info updates.
|
|
684
|
+
* Updates our locus info based on the data parsed by the hash tree parser.
|
|
685
|
+
*
|
|
686
|
+
* @param {LocusInfoUpdateType} updateType - The type of update received.
|
|
687
|
+
* @param {Object} [data] - Additional data for the update, if applicable.
|
|
688
|
+
* @returns {void}
|
|
689
|
+
*/
|
|
690
|
+
private updateFromHashTree(
|
|
691
|
+
updateType: LocusInfoUpdateType,
|
|
692
|
+
data?: {updatedObjects: HashTreeObject[]}
|
|
693
|
+
) {
|
|
694
|
+
switch (updateType) {
|
|
695
|
+
case LocusInfoUpdateType.OBJECTS_UPDATED: {
|
|
696
|
+
// initialize our new locus
|
|
697
|
+
let locus: LocusDTO = {
|
|
698
|
+
participants: [],
|
|
699
|
+
jsSdkMeta: {removedParticipantIds: []},
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
// first go over all the updates and check what happens with the main locus object
|
|
703
|
+
let locusObjectStateAfterUpdates: LocusObjectStateAfterUpdates =
|
|
704
|
+
LocusObjectStateAfterUpdates.unchanged;
|
|
705
|
+
data.updatedObjects.forEach((object) => {
|
|
706
|
+
if (object.htMeta.elementId.type.toLowerCase() === ObjectType.locus) {
|
|
707
|
+
if (locusObjectStateAfterUpdates === LocusObjectStateAfterUpdates.updated) {
|
|
708
|
+
// this code doesn't supported it right now,
|
|
709
|
+
// cases for "updated" followed by "removed", or multiple "updated" would need more handling
|
|
710
|
+
// but these should never happen
|
|
711
|
+
LoggerProxy.logger.warn(
|
|
712
|
+
`Locus-info:index#updateFromHashTree --> received multiple LOCUS objects in one update, this is unexpected!`
|
|
713
|
+
);
|
|
714
|
+
Metrics.sendBehavioralMetric(
|
|
715
|
+
BEHAVIORAL_METRICS.LOCUS_HASH_TREE_UNSUPPORTED_OPERATION,
|
|
716
|
+
{
|
|
717
|
+
locusUrl: object.data?.url || this.url,
|
|
718
|
+
message: object.data
|
|
719
|
+
? 'multiple LOCUS object updates'
|
|
720
|
+
: 'LOCUS object update followed by removal',
|
|
721
|
+
}
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
if (object.data) {
|
|
725
|
+
locusObjectStateAfterUpdates = LocusObjectStateAfterUpdates.updated;
|
|
726
|
+
} else {
|
|
727
|
+
locusObjectStateAfterUpdates = LocusObjectStateAfterUpdates.removed;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
// if Locus object is unchanged or removed, we need to keep using the existing locus
|
|
733
|
+
// because the rest of the locusInfo code expects locus to always be present (with at least some of the fields)
|
|
734
|
+
// if it gets updated, we only need to have the fields that are not part of "locus" object (like "info" or "mediaShares")
|
|
735
|
+
// so that when Locus object gets updated, if the new one is missing some field, that field will
|
|
736
|
+
// be removed from our locusInfo
|
|
737
|
+
if (
|
|
738
|
+
locusObjectStateAfterUpdates === LocusObjectStateAfterUpdates.unchanged ||
|
|
739
|
+
locusObjectStateAfterUpdates === LocusObjectStateAfterUpdates.removed
|
|
740
|
+
) {
|
|
741
|
+
// copy over all of existing locus except participants
|
|
742
|
+
LocusDtoTopLevelKeys.forEach((key) => {
|
|
743
|
+
if (key !== 'participants') {
|
|
744
|
+
locus[key] = cloneDeep(this[key]);
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
} else {
|
|
748
|
+
// initialize only the fields that are not part of main "Locus" object
|
|
749
|
+
// (except participants, which need to stay empty - that means "no participant changes")
|
|
750
|
+
Object.values(ObjectTypeToLocusKeyMap).forEach((locusDtoKey) => {
|
|
751
|
+
if (locusDtoKey !== 'participants') {
|
|
752
|
+
locus[locusDtoKey] = cloneDeep(this[locusDtoKey]);
|
|
753
|
+
}
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
LoggerProxy.logger.info(
|
|
758
|
+
`Locus-info:index#updateFromHashTree --> LOCUS object is ${locusObjectStateAfterUpdates}, all updates: ${JSON.stringify(
|
|
759
|
+
data.updatedObjects.map((o) => ({
|
|
760
|
+
type: o.htMeta.elementId.type,
|
|
761
|
+
id: o.htMeta.elementId.id,
|
|
762
|
+
hasData: !!o.data,
|
|
763
|
+
}))
|
|
764
|
+
)}`
|
|
765
|
+
);
|
|
766
|
+
// now apply all the updates from the hash tree onto the locus
|
|
767
|
+
data.updatedObjects.forEach((object) => {
|
|
768
|
+
locus = this.updateLocusFromHashTreeObject(object, locus);
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
// update our locus info with the new locus
|
|
772
|
+
this.onDeltaLocus(locus);
|
|
773
|
+
|
|
774
|
+
break;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
case LocusInfoUpdateType.MEETING_ENDED: {
|
|
778
|
+
LoggerProxy.logger.info(
|
|
779
|
+
`Locus-info:index#updateFromHashTree --> received signal that meeting ended, destroying meeting ${this.meetingId}`
|
|
780
|
+
);
|
|
781
|
+
const meeting = this.webex.meetings.meetingCollection.get(this.meetingId);
|
|
782
|
+
this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.SELF_REMOVED);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
379
785
|
}
|
|
380
786
|
|
|
381
787
|
/**
|
|
@@ -385,38 +791,52 @@ export default class LocusInfo extends EventsScope {
|
|
|
385
791
|
* @memberof LocusInfo
|
|
386
792
|
*/
|
|
387
793
|
parse(meeting: any, data: any) {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
794
|
+
if (this.hashTreeParser) {
|
|
795
|
+
this.handleHashTreeMessage(
|
|
796
|
+
meeting,
|
|
797
|
+
data.eventType,
|
|
798
|
+
data.stateElementsMessage as HashTreeMessage
|
|
799
|
+
);
|
|
800
|
+
} else {
|
|
801
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
802
|
+
const {eventType} = data;
|
|
803
|
+
const locus = this.getTheLocusToUpdate(data.locus);
|
|
804
|
+
LoggerProxy.logger.info(`Locus-info:index#parse --> received locus data: ${eventType}`);
|
|
805
|
+
|
|
806
|
+
locus.jsSdkMeta = {removedParticipantIds: []};
|
|
807
|
+
|
|
808
|
+
switch (eventType) {
|
|
809
|
+
case LOCUSEVENT.PARTICIPANT_JOIN:
|
|
810
|
+
case LOCUSEVENT.PARTICIPANT_LEFT:
|
|
811
|
+
case LOCUSEVENT.CONTROLS_UPDATED:
|
|
812
|
+
case LOCUSEVENT.PARTICIPANT_AUDIO_MUTED:
|
|
813
|
+
case LOCUSEVENT.PARTICIPANT_AUDIO_UNMUTED:
|
|
814
|
+
case LOCUSEVENT.PARTICIPANT_VIDEO_MUTED:
|
|
815
|
+
case LOCUSEVENT.PARTICIPANT_VIDEO_UNMUTED:
|
|
816
|
+
case LOCUSEVENT.SELF_CHANGED:
|
|
817
|
+
case LOCUSEVENT.PARTICIPANT_UPDATED:
|
|
818
|
+
case LOCUSEVENT.PARTICIPANT_CONTROLS_UPDATED:
|
|
819
|
+
case LOCUSEVENT.PARTICIPANT_ROLES_UPDATED:
|
|
820
|
+
case LOCUSEVENT.PARTICIPANT_DECLINED:
|
|
821
|
+
case LOCUSEVENT.FLOOR_GRANTED:
|
|
822
|
+
case LOCUSEVENT.FLOOR_RELEASED:
|
|
823
|
+
this.onFullLocus(locus, eventType);
|
|
824
|
+
break;
|
|
825
|
+
case LOCUSEVENT.DIFFERENCE:
|
|
826
|
+
this.handleLocusDelta(locus, meeting);
|
|
827
|
+
break;
|
|
828
|
+
case LOCUSEVENT.HASH_TREE_DATA_UPDATED:
|
|
829
|
+
this.sendClassicVsHashTreeMismatchMetric(
|
|
830
|
+
meeting,
|
|
831
|
+
`got ${eventType}, expected classic events`
|
|
832
|
+
);
|
|
833
|
+
break;
|
|
415
834
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
835
|
+
default:
|
|
836
|
+
// Why will there be a event with no eventType ????
|
|
837
|
+
// we may not need this, we can get full locus
|
|
838
|
+
this.handleLocusDelta(locus, meeting);
|
|
839
|
+
}
|
|
420
840
|
}
|
|
421
841
|
}
|
|
422
842
|
|
|
@@ -432,19 +852,45 @@ export default class LocusInfo extends EventsScope {
|
|
|
432
852
|
}
|
|
433
853
|
|
|
434
854
|
/**
|
|
435
|
-
*
|
|
855
|
+
* Function for handling full locus when it's using hash trees (so not the "classic" one).
|
|
856
|
+
*
|
|
436
857
|
* @param {object} locus locus object
|
|
437
|
-
* @param {string} eventType
|
|
438
|
-
* @
|
|
439
|
-
* @
|
|
858
|
+
* @param {string} eventType locus event
|
|
859
|
+
* @param {DataSet[]} dataSets
|
|
860
|
+
* @returns {void}
|
|
440
861
|
*/
|
|
441
|
-
|
|
442
|
-
if (!
|
|
443
|
-
LoggerProxy.logger.
|
|
444
|
-
|
|
862
|
+
private onFullLocusWithHashTrees(locus: any, eventType?: string, dataSets?: Array<DataSet>) {
|
|
863
|
+
if (!this.hashTreeParser) {
|
|
864
|
+
LoggerProxy.logger.info(`Locus-info:index#onFullLocus --> creating hash tree parser`);
|
|
865
|
+
LoggerProxy.logger.info(
|
|
866
|
+
'Locus-info:index#onFullLocus --> dataSets:',
|
|
867
|
+
dataSets,
|
|
868
|
+
' and locus:',
|
|
869
|
+
locus
|
|
445
870
|
);
|
|
871
|
+
this.hashTreeParser = this.createHashTreeParser({
|
|
872
|
+
initialLocus: {locus, dataSets},
|
|
873
|
+
});
|
|
874
|
+
this.onFullLocusCommon(locus, eventType);
|
|
875
|
+
} else {
|
|
876
|
+
// in this case the Locus we're getting is not necessarily the full one
|
|
877
|
+
// so treat it like if we just got it in any api response
|
|
878
|
+
|
|
879
|
+
LoggerProxy.logger.info(
|
|
880
|
+
'Locus-info:index#onFullLocus --> hash tree parser already exists, handling it like a normal API response'
|
|
881
|
+
);
|
|
882
|
+
this.handleLocusAPIResponse(undefined, {dataSets, locus});
|
|
446
883
|
}
|
|
884
|
+
}
|
|
447
885
|
|
|
886
|
+
/**
|
|
887
|
+
* Function for handling full locus when it's the "classic" one (not hash trees)
|
|
888
|
+
*
|
|
889
|
+
* @param {object} locus locus object
|
|
890
|
+
* @param {string} eventType locus event
|
|
891
|
+
* @returns {void}
|
|
892
|
+
*/
|
|
893
|
+
private onFullLocusClassic(locus: any, eventType?: string) {
|
|
448
894
|
if (!this.locusParser.isNewFullLocus(locus)) {
|
|
449
895
|
LoggerProxy.logger.info(
|
|
450
896
|
`Locus-info:index#onFullLocus --> ignoring old full locus DTO, eventType=${eventType}`
|
|
@@ -452,9 +898,47 @@ export default class LocusInfo extends EventsScope {
|
|
|
452
898
|
|
|
453
899
|
return;
|
|
454
900
|
}
|
|
901
|
+
this.onFullLocusCommon(locus, eventType);
|
|
902
|
+
}
|
|
455
903
|
|
|
904
|
+
/**
|
|
905
|
+
* updates the locus with full locus object
|
|
906
|
+
* @param {object} locus locus object
|
|
907
|
+
* @param {string} eventType locus event
|
|
908
|
+
* @param {DataSet[]} dataSets
|
|
909
|
+
* @returns {object} null
|
|
910
|
+
* @memberof LocusInfo
|
|
911
|
+
*/
|
|
912
|
+
onFullLocus(locus: any, eventType?: string, dataSets?: Array<DataSet>) {
|
|
913
|
+
if (!locus) {
|
|
914
|
+
LoggerProxy.logger.error(
|
|
915
|
+
'Locus-info:index#onFullLocus --> object passed as argument was invalid, continuing.'
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
if (dataSets) {
|
|
920
|
+
// this is the new hashmap Locus DTO format (only applicable to webinars for now)
|
|
921
|
+
this.onFullLocusWithHashTrees(locus, eventType, dataSets);
|
|
922
|
+
} else {
|
|
923
|
+
this.onFullLocusClassic(locus, eventType);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* Common part of handling full locus, used by both classic and hash tree based locus handling
|
|
929
|
+
* @param {object} locus locus object
|
|
930
|
+
* @param {string} eventType locus event
|
|
931
|
+
* @returns {void}
|
|
932
|
+
*/
|
|
933
|
+
private onFullLocusCommon(locus: any, eventType?: string) {
|
|
456
934
|
this.scheduledMeeting = locus.meeting || null;
|
|
457
935
|
this.participants = locus.participants;
|
|
936
|
+
this.participants?.forEach((participant) => {
|
|
937
|
+
// participant.htMeta is set only for hash tree based locus
|
|
938
|
+
if (participant.htMeta?.elementId.id) {
|
|
939
|
+
this.hashTreeObjectId2ParticipantId.set(participant.htMeta.elementId.id, participant.id);
|
|
940
|
+
}
|
|
941
|
+
});
|
|
458
942
|
const isReplaceMembers = ControlsUtils.isNeedReplaceMembers(this.controls, locus.controls);
|
|
459
943
|
this.updateLocusInfo(locus);
|
|
460
944
|
this.updateParticipants(
|
|
@@ -552,17 +1036,13 @@ export default class LocusInfo extends EventsScope {
|
|
|
552
1036
|
this.updateLocusUrl(locus.url, ControlsUtils.isMainSessionDTO(locus));
|
|
553
1037
|
this.updateMeetingInfo(locus.info, locus.self);
|
|
554
1038
|
this.updateMediaShares(locus.mediaShares);
|
|
555
|
-
this.
|
|
556
|
-
this.updateReplace(locus.replace);
|
|
1039
|
+
this.updateReplaces(locus.replaces);
|
|
557
1040
|
this.updateSelf(locus.self);
|
|
558
1041
|
this.updateAclUrl(locus.aclUrl);
|
|
559
1042
|
this.updateBasequence(locus.baseSequence);
|
|
560
1043
|
this.updateSequence(locus.sequence);
|
|
561
|
-
this.updateMemberShip(locus.membership);
|
|
562
|
-
this.updateIdentifiers(locus.identities);
|
|
563
1044
|
this.updateEmbeddedApps(locus.embeddedApps);
|
|
564
|
-
this.
|
|
565
|
-
this.updateResources(locus.links?.resources);
|
|
1045
|
+
this.updateLinks(locus.links);
|
|
566
1046
|
this.compareAndUpdate();
|
|
567
1047
|
// update which required to compare different objects from locus
|
|
568
1048
|
}
|
|
@@ -1214,17 +1694,19 @@ export default class LocusInfo extends EventsScope {
|
|
|
1214
1694
|
}
|
|
1215
1695
|
|
|
1216
1696
|
/**
|
|
1217
|
-
*
|
|
1697
|
+
* Updates links and emits appropriate events if services or resources have changed
|
|
1698
|
+
* @param {Object} links
|
|
1218
1699
|
* @returns {undefined}
|
|
1219
1700
|
* @memberof LocusInfo
|
|
1220
1701
|
*/
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1702
|
+
updateLinks(links?: Links) {
|
|
1703
|
+
const {services, resources} = links || {};
|
|
1704
|
+
|
|
1705
|
+
if (services && !isEqual(this.links?.services, services)) {
|
|
1224
1706
|
this.emitScoped(
|
|
1225
1707
|
{
|
|
1226
1708
|
file: 'locus-info',
|
|
1227
|
-
function: '
|
|
1709
|
+
function: 'updateLinks',
|
|
1228
1710
|
},
|
|
1229
1711
|
LOCUSINFO.EVENTS.LINKS_SERVICES,
|
|
1230
1712
|
{
|
|
@@ -1232,20 +1714,12 @@ export default class LocusInfo extends EventsScope {
|
|
|
1232
1714
|
}
|
|
1233
1715
|
);
|
|
1234
1716
|
}
|
|
1235
|
-
}
|
|
1236
1717
|
|
|
1237
|
-
|
|
1238
|
-
* @param {Object} resources
|
|
1239
|
-
* @returns {undefined}
|
|
1240
|
-
* @memberof LocusInfo
|
|
1241
|
-
*/
|
|
1242
|
-
updateResources(resources: Record<'webcastInstance', {url: string}>) {
|
|
1243
|
-
if (resources && !isEqual(this.resources, resources)) {
|
|
1244
|
-
this.resources = resources;
|
|
1718
|
+
if (resources && !isEqual(this.links?.resources, resources)) {
|
|
1245
1719
|
this.emitScoped(
|
|
1246
1720
|
{
|
|
1247
1721
|
file: 'locus-info',
|
|
1248
|
-
function: '
|
|
1722
|
+
function: 'updateLinks',
|
|
1249
1723
|
},
|
|
1250
1724
|
LOCUSINFO.EVENTS.LINKS_RESOURCES,
|
|
1251
1725
|
{
|
|
@@ -1253,6 +1727,8 @@ export default class LocusInfo extends EventsScope {
|
|
|
1253
1727
|
}
|
|
1254
1728
|
);
|
|
1255
1729
|
}
|
|
1730
|
+
|
|
1731
|
+
this.links = links;
|
|
1256
1732
|
}
|
|
1257
1733
|
|
|
1258
1734
|
/**
|
|
@@ -1439,24 +1915,13 @@ export default class LocusInfo extends EventsScope {
|
|
|
1439
1915
|
}
|
|
1440
1916
|
|
|
1441
1917
|
/**
|
|
1442
|
-
* @param {
|
|
1443
|
-
* @returns {undefined}
|
|
1444
|
-
* @memberof LocusInfo
|
|
1445
|
-
*/
|
|
1446
|
-
updateParticipantsUrl(participantsUrl: string) {
|
|
1447
|
-
if (participantsUrl && !isEqual(this.participantsUrl, participantsUrl)) {
|
|
1448
|
-
this.participantsUrl = participantsUrl;
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
/**
|
|
1453
|
-
* @param {Object} replace
|
|
1918
|
+
* @param {Object} replaces
|
|
1454
1919
|
* @returns {undefined}
|
|
1455
1920
|
* @memberof LocusInfo
|
|
1456
1921
|
*/
|
|
1457
|
-
|
|
1458
|
-
if (
|
|
1459
|
-
this.
|
|
1922
|
+
updateReplaces(replaces: object) {
|
|
1923
|
+
if (replaces && !isEqual(this.replaces, replaces)) {
|
|
1924
|
+
this.replaces = replaces;
|
|
1460
1925
|
}
|
|
1461
1926
|
}
|
|
1462
1927
|
|
|
@@ -1793,28 +2258,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
1793
2258
|
}
|
|
1794
2259
|
}
|
|
1795
2260
|
|
|
1796
|
-
/**
|
|
1797
|
-
* @param {Object} membership
|
|
1798
|
-
* @returns {undefined}
|
|
1799
|
-
* @memberof LocusInfo
|
|
1800
|
-
*/
|
|
1801
|
-
updateMemberShip(membership: object) {
|
|
1802
|
-
if (membership && !isEqual(this.membership, membership)) {
|
|
1803
|
-
this.membership = membership;
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
/**
|
|
1808
|
-
* @param {Array} identities
|
|
1809
|
-
* @returns {undefined}
|
|
1810
|
-
* @memberof LocusInfo
|
|
1811
|
-
*/
|
|
1812
|
-
updateIdentifiers(identities: Array<any>) {
|
|
1813
|
-
if (identities && !isEqual(this.identities, identities)) {
|
|
1814
|
-
this.identities = identities;
|
|
1815
|
-
}
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
2261
|
/**
|
|
1819
2262
|
* check the locus is main session's one or not, if is main session's, update main session cache
|
|
1820
2263
|
* @param {Object} locus
|