@webex/plugin-meetings 3.11.0 → 3.12.0
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 +184 -0
- package/dist/aiEnableRequest/index.js.map +1 -0
- package/dist/aiEnableRequest/utils.js +36 -0
- package/dist/aiEnableRequest/utils.js.map +1 -0
- package/dist/annotation/index.js +14 -5
- package/dist/annotation/index.js.map +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/config.js +5 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +28 -6
- 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/hashTree.js +18 -0
- package/dist/hashTree/hashTree.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +709 -380
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/hashTree/types.js +4 -2
- package/dist/hashTree/types.js.map +1 -1
- package/dist/hashTree/utils.js +10 -0
- package/dist/hashTree/utils.js.map +1 -1
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/interceptors/constant.js +12 -0
- package/dist/interceptors/constant.js.map +1 -0
- package/dist/interceptors/dataChannelAuthToken.js +290 -0
- package/dist/interceptors/dataChannelAuthToken.js.map +1 -0
- package/dist/interceptors/index.js +7 -0
- package/dist/interceptors/index.js.map +1 -1
- package/dist/interceptors/utils.js +27 -0
- package/dist/interceptors/utils.js.map +1 -0
- package/dist/interpretation/index.js +2 -2
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +5 -3
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +217 -79
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +1 -0
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/locus-info/types.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +57 -1
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/properties.js +4 -2
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +7 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +1082 -861
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +50 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +133 -3
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +100 -45
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/index.js +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +10 -0
- package/dist/member/util.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +9 -60
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +11 -0
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/reachability/index.js +18 -10
- package/dist/reachability/index.js.map +1 -1
- package/dist/reactions/reactions.type.js.map +1 -1
- package/dist/reconnection-manager/index.js +0 -1
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/types/aiEnableRequest/index.d.ts +5 -0
- package/dist/types/aiEnableRequest/utils.d.ts +2 -0
- package/dist/types/config.d.ts +3 -0
- package/dist/types/constants.d.ts +23 -1
- package/dist/types/hashTree/constants.d.ts +1 -0
- package/dist/types/hashTree/hashTree.d.ts +7 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +99 -14
- package/dist/types/hashTree/types.d.ts +3 -0
- package/dist/types/hashTree/utils.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/interceptors/constant.d.ts +5 -0
- package/dist/types/interceptors/dataChannelAuthToken.d.ts +43 -0
- package/dist/types/interceptors/index.d.ts +2 -1
- package/dist/types/interceptors/utils.d.ts +1 -0
- package/dist/types/locus-info/index.d.ts +21 -2
- package/dist/types/locus-info/types.d.ts +1 -0
- package/dist/types/media/MediaConnectionAwaiter.d.ts +10 -1
- package/dist/types/media/properties.d.ts +2 -1
- package/dist/types/meeting/in-meeting-actions.d.ts +6 -0
- package/dist/types/meeting/index.d.ts +38 -6
- package/dist/types/meeting/request.d.ts +16 -1
- package/dist/types/meeting/request.type.d.ts +5 -0
- package/dist/types/meeting/util.d.ts +31 -0
- package/dist/types/meetings/index.d.ts +4 -2
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/util.d.ts +5 -0
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/multistream/mediaRequestManager.d.ts +0 -23
- package/dist/types/reactions/reactions.type.d.ts +1 -0
- package/dist/types/webinar/utils.d.ts +6 -0
- package/dist/webinar/index.js +260 -90
- package/dist/webinar/index.js.map +1 -1
- package/dist/webinar/utils.js +25 -0
- package/dist/webinar/utils.js.map +1 -0
- package/package.json +24 -23
- package/src/aiEnableRequest/README.md +84 -0
- package/src/aiEnableRequest/index.ts +170 -0
- package/src/aiEnableRequest/utils.ts +25 -0
- package/src/annotation/index.ts +27 -7
- package/src/config.ts +3 -0
- package/src/constants.ts +29 -1
- package/src/hashTree/constants.ts +1 -0
- package/src/hashTree/hashTree.ts +17 -0
- package/src/hashTree/hashTreeParser.ts +627 -249
- package/src/hashTree/types.ts +4 -0
- package/src/hashTree/utils.ts +9 -0
- package/src/index.ts +8 -1
- package/src/interceptors/constant.ts +6 -0
- package/src/interceptors/dataChannelAuthToken.ts +170 -0
- package/src/interceptors/index.ts +2 -1
- package/src/interceptors/utils.ts +16 -0
- package/src/interpretation/index.ts +2 -2
- package/src/locus-info/controlsUtils.ts +11 -0
- package/src/locus-info/index.ts +231 -61
- package/src/locus-info/selfUtils.ts +1 -0
- package/src/locus-info/types.ts +1 -0
- package/src/media/MediaConnectionAwaiter.ts +41 -1
- package/src/media/properties.ts +3 -1
- package/src/meeting/in-meeting-actions.ts +12 -0
- package/src/meeting/index.ts +205 -44
- package/src/meeting/request.ts +42 -0
- package/src/meeting/request.type.ts +6 -0
- package/src/meeting/util.ts +160 -2
- package/src/meetings/index.ts +135 -41
- package/src/member/index.ts +10 -0
- package/src/member/util.ts +12 -0
- package/src/metrics/constants.ts +1 -0
- package/src/multistream/mediaRequestManager.ts +4 -54
- package/src/multistream/remoteMediaManager.ts +13 -0
- package/src/reachability/index.ts +9 -0
- package/src/reactions/reactions.type.ts +1 -0
- package/src/reconnection-manager/index.ts +0 -1
- package/src/webinar/index.ts +162 -5
- package/src/webinar/utils.ts +16 -0
- package/test/unit/spec/aiEnableRequest/index.ts +981 -0
- package/test/unit/spec/aiEnableRequest/utils.ts +130 -0
- package/test/unit/spec/annotation/index.ts +69 -7
- package/test/unit/spec/hashTree/hashTree.ts +66 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +1869 -189
- package/test/unit/spec/interceptors/dataChannelAuthToken.ts +210 -0
- package/test/unit/spec/interceptors/utils.ts +75 -0
- package/test/unit/spec/locus-info/controlsUtils.js +29 -0
- package/test/unit/spec/locus-info/index.js +383 -46
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +41 -1
- package/test/unit/spec/media/properties.ts +12 -3
- package/test/unit/spec/meeting/in-meeting-actions.ts +8 -2
- package/test/unit/spec/meeting/index.js +716 -115
- package/test/unit/spec/meeting/request.js +70 -0
- package/test/unit/spec/meeting/utils.js +438 -26
- package/test/unit/spec/meetings/index.js +652 -31
- package/test/unit/spec/member/index.js +28 -4
- package/test/unit/spec/member/util.js +65 -27
- package/test/unit/spec/multistream/mediaRequestManager.ts +2 -85
- package/test/unit/spec/multistream/remoteMediaManager.ts +30 -0
- package/test/unit/spec/reachability/index.ts +23 -0
- package/test/unit/spec/reconnection-manager/index.js +4 -8
- package/test/unit/spec/webinar/index.ts +348 -36
- package/test/unit/spec/webinar/utils.ts +39 -0
package/src/locus-info/index.ts
CHANGED
|
@@ -35,10 +35,11 @@ import HashTreeParser, {
|
|
|
35
35
|
DataSet,
|
|
36
36
|
HashTreeMessage,
|
|
37
37
|
LocusInfoUpdateType,
|
|
38
|
+
Metadata,
|
|
38
39
|
} from '../hashTree/hashTreeParser';
|
|
39
40
|
import {HashTreeObject, ObjectType, ObjectTypeToLocusKeyMap} from '../hashTree/types';
|
|
40
|
-
import {
|
|
41
|
-
import {Links, LocusDTO
|
|
41
|
+
import {isMetadata} from '../hashTree/utils';
|
|
42
|
+
import {Links, LocusDTO} from './types';
|
|
42
43
|
|
|
43
44
|
export type LocusLLMEvent = {
|
|
44
45
|
data: {
|
|
@@ -52,6 +53,7 @@ export type LocusLLMEvent = {
|
|
|
52
53
|
const LocusDtoTopLevelKeys = [
|
|
53
54
|
'controls',
|
|
54
55
|
'fullState',
|
|
56
|
+
'embeddedApps',
|
|
55
57
|
'host',
|
|
56
58
|
'info',
|
|
57
59
|
'links',
|
|
@@ -69,6 +71,7 @@ const LocusDtoTopLevelKeys = [
|
|
|
69
71
|
export type LocusApiResponseBody = {
|
|
70
72
|
dataSets?: DataSet[];
|
|
71
73
|
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)
|
|
74
|
+
metadata?: Metadata;
|
|
72
75
|
};
|
|
73
76
|
|
|
74
77
|
const LocusObjectStateAfterUpdates = {
|
|
@@ -79,6 +82,89 @@ const LocusObjectStateAfterUpdates = {
|
|
|
79
82
|
|
|
80
83
|
type LocusObjectStateAfterUpdates = Enum<typeof LocusObjectStateAfterUpdates>;
|
|
81
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Creates a locus object from the objects received in a hash tree message. It usually will be
|
|
87
|
+
* incomplete, because hash tree messages only contain the parts of locus that have changed,
|
|
88
|
+
* and some updates come separately over Mercury or LLM in separate messages.
|
|
89
|
+
*
|
|
90
|
+
* @param {HashTreeMessage} message hash tree message to created the locus from
|
|
91
|
+
* @returns {Object} the created locus object and metadata if present
|
|
92
|
+
*/
|
|
93
|
+
export function createLocusFromHashTreeMessage(message: HashTreeMessage): {
|
|
94
|
+
locus: LocusDTO;
|
|
95
|
+
metadata?: Metadata;
|
|
96
|
+
} {
|
|
97
|
+
const locus: LocusDTO = {
|
|
98
|
+
participants: [],
|
|
99
|
+
url: message.locusUrl,
|
|
100
|
+
};
|
|
101
|
+
let metadata: Metadata | undefined;
|
|
102
|
+
|
|
103
|
+
if (!message.locusStateElements) {
|
|
104
|
+
return {locus, metadata};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (const element of message.locusStateElements) {
|
|
108
|
+
if (!element.data) {
|
|
109
|
+
// eslint-disable-next-line no-continue
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const type = element.htMeta.elementId.type.toLowerCase();
|
|
114
|
+
|
|
115
|
+
switch (type) {
|
|
116
|
+
case ObjectType.locus: {
|
|
117
|
+
// spread locus object data onto the top level, but remove keys managed by other ObjectTypes
|
|
118
|
+
const locusObjectData = {...element.data};
|
|
119
|
+
|
|
120
|
+
Object.values(ObjectTypeToLocusKeyMap).forEach((locusDtoKey) => {
|
|
121
|
+
delete locusObjectData[locusDtoKey];
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
Object.assign(locus, locusObjectData);
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case ObjectType.participant:
|
|
128
|
+
locus.participants.push(element.data);
|
|
129
|
+
break;
|
|
130
|
+
case ObjectType.mediaShare:
|
|
131
|
+
if (!locus.mediaShares) {
|
|
132
|
+
locus.mediaShares = [];
|
|
133
|
+
}
|
|
134
|
+
locus.mediaShares.push(element.data);
|
|
135
|
+
break;
|
|
136
|
+
case ObjectType.embeddedApp:
|
|
137
|
+
if (!locus.embeddedApps) {
|
|
138
|
+
locus.embeddedApps = [];
|
|
139
|
+
}
|
|
140
|
+
locus.embeddedApps.push(element.data);
|
|
141
|
+
break;
|
|
142
|
+
case ObjectType.control:
|
|
143
|
+
if (!locus.controls) {
|
|
144
|
+
locus.controls = {};
|
|
145
|
+
}
|
|
146
|
+
Object.assign(locus.controls, element.data);
|
|
147
|
+
break;
|
|
148
|
+
case ObjectType.links:
|
|
149
|
+
case ObjectType.info:
|
|
150
|
+
case ObjectType.fullState:
|
|
151
|
+
case ObjectType.self: {
|
|
152
|
+
const locusDtoKey = ObjectTypeToLocusKeyMap[type];
|
|
153
|
+
locus[locusDtoKey] = element.data;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case ObjectType.metadata:
|
|
157
|
+
// metadata is not part of Locus DTO
|
|
158
|
+
metadata = {...element.data, htMeta: element.htMeta} as Metadata;
|
|
159
|
+
break;
|
|
160
|
+
default:
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {locus, metadata};
|
|
166
|
+
}
|
|
167
|
+
|
|
82
168
|
/**
|
|
83
169
|
* @description LocusInfo extends ChildEmitter to convert locusInfo info a private emitter to parent object
|
|
84
170
|
* @export
|
|
@@ -239,7 +325,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
239
325
|
'Locus-info:index#doLocusSync --> got full DTO when we asked for delta'
|
|
240
326
|
);
|
|
241
327
|
}
|
|
242
|
-
meeting.locusInfo.onFullLocus(res.body);
|
|
328
|
+
meeting.locusInfo.onFullLocus('classic Locus sync', res.body);
|
|
243
329
|
})
|
|
244
330
|
.catch((e) => {
|
|
245
331
|
LoggerProxy.logger.info(
|
|
@@ -362,17 +448,21 @@ export default class LocusInfo extends EventsScope {
|
|
|
362
448
|
*/
|
|
363
449
|
private createHashTreeParser({
|
|
364
450
|
initialLocus,
|
|
451
|
+
metadata,
|
|
365
452
|
}: {
|
|
366
453
|
initialLocus: {
|
|
367
454
|
dataSets: Array<DataSet>;
|
|
368
455
|
locus: any;
|
|
369
456
|
};
|
|
457
|
+
metadata: Metadata;
|
|
370
458
|
}) {
|
|
371
459
|
return new HashTreeParser({
|
|
372
460
|
initialLocus,
|
|
461
|
+
metadata,
|
|
373
462
|
webexRequest: this.webex.request.bind(this.webex),
|
|
374
463
|
locusInfoUpdateCallback: this.updateFromHashTree.bind(this),
|
|
375
464
|
debugId: `HT-${this.meetingId.substring(0, 4)}`,
|
|
465
|
+
excludedDataSets: this.webex.config.meetings.locus?.excludedDataSets,
|
|
376
466
|
});
|
|
377
467
|
}
|
|
378
468
|
|
|
@@ -387,6 +477,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
387
477
|
trigger: 'join-response';
|
|
388
478
|
locus: LocusDTO;
|
|
389
479
|
dataSets?: DataSet[];
|
|
480
|
+
metadata?: Metadata;
|
|
390
481
|
}
|
|
391
482
|
| {
|
|
392
483
|
trigger: 'locus-message';
|
|
@@ -401,28 +492,36 @@ export default class LocusInfo extends EventsScope {
|
|
|
401
492
|
switch (data.trigger) {
|
|
402
493
|
case 'locus-message':
|
|
403
494
|
if (data.hashTreeMessage) {
|
|
404
|
-
// we need the
|
|
495
|
+
// we need the Metadata object to be in the received message, because it contains visibleDataSets
|
|
405
496
|
// and these are needed to initialize all the hash trees
|
|
406
|
-
const
|
|
497
|
+
const metadataObject = data.hashTreeMessage.locusStateElements?.find((el) =>
|
|
498
|
+
isMetadata(el)
|
|
499
|
+
);
|
|
407
500
|
|
|
408
|
-
if (!
|
|
409
|
-
|
|
410
|
-
|
|
501
|
+
if (!metadataObject?.data?.visibleDataSets) {
|
|
502
|
+
// this is a common case (not an error)
|
|
503
|
+
// it happens for example after we leave the meeting and still get some heartbeats or delayed messages
|
|
504
|
+
LoggerProxy.logger.info(
|
|
505
|
+
`Locus-info:index#initialSetup --> cannot initialize HashTreeParser, Metadata object with visibleDataSets is missing in the message`
|
|
411
506
|
);
|
|
412
507
|
|
|
413
|
-
throw
|
|
508
|
+
// throw so that handleLocusEvent() catches it and destroys the partially created meeting object
|
|
509
|
+
throw new Error('Metadata object with visibleDataSets is missing in the message');
|
|
414
510
|
}
|
|
415
511
|
|
|
416
512
|
LoggerProxy.logger.info(
|
|
417
513
|
'Locus-info:index#initialSetup --> creating HashTreeParser from message'
|
|
418
514
|
);
|
|
419
515
|
// first create the HashTreeParser, but don't initialize it with any data yet
|
|
420
|
-
// pass just a fake locus that contains only the visibleDataSets
|
|
421
516
|
this.hashTreeParser = this.createHashTreeParser({
|
|
422
517
|
initialLocus: {
|
|
423
|
-
locus:
|
|
518
|
+
locus: null,
|
|
424
519
|
dataSets: [], // empty, because they will be populated in initializeFromMessage() call // dataSets: data.hashTreeMessage.dataSets,
|
|
425
520
|
},
|
|
521
|
+
metadata: {
|
|
522
|
+
htMeta: metadataObject.htMeta,
|
|
523
|
+
visibleDataSets: metadataObject.data.visibleDataSets,
|
|
524
|
+
},
|
|
426
525
|
});
|
|
427
526
|
|
|
428
527
|
// now handle the message - that should populate all the visible datasets
|
|
@@ -430,12 +529,12 @@ export default class LocusInfo extends EventsScope {
|
|
|
430
529
|
} else {
|
|
431
530
|
// "classic" Locus case, no hash trees involved
|
|
432
531
|
this.updateLocusCache(data.locus);
|
|
433
|
-
this.onFullLocus(data.locus, undefined);
|
|
532
|
+
this.onFullLocus('classic locus message', data.locus, undefined);
|
|
434
533
|
}
|
|
435
534
|
break;
|
|
436
535
|
case 'join-response':
|
|
437
536
|
this.updateLocusCache(data.locus);
|
|
438
|
-
this.onFullLocus(data.locus, undefined, data.dataSets);
|
|
537
|
+
this.onFullLocus('join response', data.locus, undefined, data.dataSets, data.metadata);
|
|
439
538
|
break;
|
|
440
539
|
case 'get-loci-response':
|
|
441
540
|
if (data.locus?.links?.resources?.visibleDataSets?.url) {
|
|
@@ -443,12 +542,12 @@ export default class LocusInfo extends EventsScope {
|
|
|
443
542
|
'Locus-info:index#initialSetup --> creating HashTreeParser from get-loci-response'
|
|
444
543
|
);
|
|
445
544
|
// first create the HashTreeParser, but don't initialize it with any data yet
|
|
446
|
-
// pass just a fake locus that contains only the visibleDataSets
|
|
447
545
|
this.hashTreeParser = this.createHashTreeParser({
|
|
448
546
|
initialLocus: {
|
|
449
|
-
locus:
|
|
547
|
+
locus: null,
|
|
450
548
|
dataSets: [], // empty, because we don't have them yet
|
|
451
549
|
},
|
|
550
|
+
metadata: null, // get-loci-response doesn't contain Metadata object
|
|
452
551
|
});
|
|
453
552
|
|
|
454
553
|
// now initialize all the data
|
|
@@ -456,7 +555,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
456
555
|
} else {
|
|
457
556
|
// "classic" Locus case, no hash trees involved
|
|
458
557
|
this.updateLocusCache(data.locus);
|
|
459
|
-
this.onFullLocus(data.locus, undefined);
|
|
558
|
+
this.onFullLocus('classic get-loci-response', data.locus, undefined);
|
|
460
559
|
}
|
|
461
560
|
}
|
|
462
561
|
// Change it to true after it receives it first locus object
|
|
@@ -571,6 +670,31 @@ export default class LocusInfo extends EventsScope {
|
|
|
571
670
|
);
|
|
572
671
|
}
|
|
573
672
|
break;
|
|
673
|
+
case ObjectType.embeddedApp:
|
|
674
|
+
if (object.data) {
|
|
675
|
+
LoggerProxy.logger.info(
|
|
676
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> embeddedApp id=${object.htMeta.elementId.id} url='${object.data.url}' updated version=${object.htMeta.elementId.version}:`,
|
|
677
|
+
object.data
|
|
678
|
+
);
|
|
679
|
+
const existingEmbeddedApp = locus.embeddedApps?.find(
|
|
680
|
+
(ms) => ms.htMeta.elementId.id === object.htMeta.elementId.id
|
|
681
|
+
);
|
|
682
|
+
|
|
683
|
+
if (existingEmbeddedApp) {
|
|
684
|
+
Object.assign(existingEmbeddedApp, object.data);
|
|
685
|
+
} else {
|
|
686
|
+
locus.embeddedApps = locus.embeddedApps || [];
|
|
687
|
+
locus.embeddedApps.push(object.data);
|
|
688
|
+
}
|
|
689
|
+
} else {
|
|
690
|
+
LoggerProxy.logger.info(
|
|
691
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> embeddedApp id=${object.htMeta.elementId.id} removed, version=${object.htMeta.elementId.version}`
|
|
692
|
+
);
|
|
693
|
+
locus.embeddedApps = locus.embeddedApps?.filter(
|
|
694
|
+
(ms) => ms.htMeta.elementId.id !== object.htMeta.elementId.id
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
break;
|
|
574
698
|
case ObjectType.participant:
|
|
575
699
|
LoggerProxy.logger.info(
|
|
576
700
|
`Locus-info:index#updateLocusFromHashTreeObject --> participant id=${
|
|
@@ -643,6 +767,12 @@ export default class LocusInfo extends EventsScope {
|
|
|
643
767
|
}
|
|
644
768
|
}
|
|
645
769
|
break;
|
|
770
|
+
case ObjectType.metadata:
|
|
771
|
+
LoggerProxy.logger.info(
|
|
772
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> metadata object updated to version ${object.htMeta.elementId.version}`
|
|
773
|
+
);
|
|
774
|
+
// we don't use hash tree metadata right now for anything, it's mainly used internally by HashTreeParser
|
|
775
|
+
break;
|
|
646
776
|
default:
|
|
647
777
|
LoggerProxy.logger.warn(
|
|
648
778
|
`Locus-info:index#updateLocusFromHashTreeObject --> received unsupported object type ${type}`
|
|
@@ -792,11 +922,14 @@ export default class LocusInfo extends EventsScope {
|
|
|
792
922
|
}
|
|
793
923
|
|
|
794
924
|
case LocusInfoUpdateType.MEETING_ENDED: {
|
|
795
|
-
LoggerProxy.logger.info(
|
|
796
|
-
`Locus-info:index#updateFromHashTree --> received signal that meeting ended, destroying meeting ${this.meetingId}`
|
|
797
|
-
);
|
|
798
925
|
const meeting = this.webex.meetings.meetingCollection.get(this.meetingId);
|
|
799
|
-
|
|
926
|
+
|
|
927
|
+
if (meeting) {
|
|
928
|
+
LoggerProxy.logger.info(
|
|
929
|
+
`Locus-info:index#updateFromHashTree --> received signal that meeting ended, destroying meeting ${this.meetingId}`
|
|
930
|
+
);
|
|
931
|
+
this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.SELF_REMOVED);
|
|
932
|
+
}
|
|
800
933
|
}
|
|
801
934
|
}
|
|
802
935
|
}
|
|
@@ -815,8 +948,18 @@ export default class LocusInfo extends EventsScope {
|
|
|
815
948
|
data.stateElementsMessage as HashTreeMessage
|
|
816
949
|
);
|
|
817
950
|
} else {
|
|
818
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
819
951
|
const {eventType} = data;
|
|
952
|
+
|
|
953
|
+
if (eventType === LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
|
|
954
|
+
// this can happen when we get an event before join http response
|
|
955
|
+
// it's OK to just ignore it
|
|
956
|
+
LoggerProxy.logger.info(
|
|
957
|
+
`Locus-info:index#parse --> received locus hash tree event before hashTreeParser is created`
|
|
958
|
+
);
|
|
959
|
+
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
|
|
820
963
|
const locus = this.getTheLocusToUpdate(data.locus);
|
|
821
964
|
LoggerProxy.logger.info(`Locus-info:index#parse --> received locus data: ${eventType}`);
|
|
822
965
|
|
|
@@ -837,17 +980,11 @@ export default class LocusInfo extends EventsScope {
|
|
|
837
980
|
case LOCUSEVENT.PARTICIPANT_DECLINED:
|
|
838
981
|
case LOCUSEVENT.FLOOR_GRANTED:
|
|
839
982
|
case LOCUSEVENT.FLOOR_RELEASED:
|
|
840
|
-
this.onFullLocus(locus, eventType);
|
|
983
|
+
this.onFullLocus(`classic locus event ${eventType}`, locus, eventType);
|
|
841
984
|
break;
|
|
842
985
|
case LOCUSEVENT.DIFFERENCE:
|
|
843
986
|
this.handleLocusDelta(locus, meeting);
|
|
844
987
|
break;
|
|
845
|
-
case LOCUSEVENT.HASH_TREE_DATA_UPDATED:
|
|
846
|
-
this.sendClassicVsHashTreeMismatchMetric(
|
|
847
|
-
meeting,
|
|
848
|
-
`got ${eventType}, expected classic events`
|
|
849
|
-
);
|
|
850
|
-
break;
|
|
851
988
|
|
|
852
989
|
default:
|
|
853
990
|
// Why will there be a event with no eventType ????
|
|
@@ -871,22 +1008,35 @@ export default class LocusInfo extends EventsScope {
|
|
|
871
1008
|
/**
|
|
872
1009
|
* Function for handling full locus when it's using hash trees (so not the "classic" one).
|
|
873
1010
|
*
|
|
1011
|
+
* @param {string} debugText string explaining the trigger for this call, added to logs for debugging purposes
|
|
874
1012
|
* @param {object} locus locus object
|
|
1013
|
+
* @param {object} metadata locus hash trees metadata
|
|
875
1014
|
* @param {string} eventType locus event
|
|
876
1015
|
* @param {DataSet[]} dataSets
|
|
877
1016
|
* @returns {void}
|
|
878
1017
|
*/
|
|
879
|
-
private onFullLocusWithHashTrees(
|
|
1018
|
+
private onFullLocusWithHashTrees(
|
|
1019
|
+
debugText: string,
|
|
1020
|
+
locus: any,
|
|
1021
|
+
metadata: Metadata,
|
|
1022
|
+
eventType?: string,
|
|
1023
|
+
dataSets?: Array<DataSet>
|
|
1024
|
+
) {
|
|
880
1025
|
if (!this.hashTreeParser) {
|
|
881
|
-
LoggerProxy.logger.info(`Locus-info:index#onFullLocus --> creating hash tree parser`);
|
|
882
1026
|
LoggerProxy.logger.info(
|
|
883
|
-
|
|
1027
|
+
`Locus-info:index#onFullLocus (${debugText}) --> creating hash tree parser`
|
|
1028
|
+
);
|
|
1029
|
+
LoggerProxy.logger.info(
|
|
1030
|
+
`Locus-info:index#onFullLocus (${debugText}) --> dataSets:`,
|
|
884
1031
|
dataSets,
|
|
885
1032
|
' and locus:',
|
|
886
|
-
locus
|
|
1033
|
+
locus,
|
|
1034
|
+
' and metadata:',
|
|
1035
|
+
metadata
|
|
887
1036
|
);
|
|
888
1037
|
this.hashTreeParser = this.createHashTreeParser({
|
|
889
1038
|
initialLocus: {locus, dataSets},
|
|
1039
|
+
metadata,
|
|
890
1040
|
});
|
|
891
1041
|
this.onFullLocusCommon(locus, eventType);
|
|
892
1042
|
} else {
|
|
@@ -894,23 +1044,24 @@ export default class LocusInfo extends EventsScope {
|
|
|
894
1044
|
// so treat it like if we just got it in any api response
|
|
895
1045
|
|
|
896
1046
|
LoggerProxy.logger.info(
|
|
897
|
-
|
|
1047
|
+
`Locus-info:index#onFullLocus (${debugText}) --> hash tree parser already exists, handling it like a normal API response`
|
|
898
1048
|
);
|
|
899
|
-
this.handleLocusAPIResponse(undefined, {dataSets, locus});
|
|
1049
|
+
this.handleLocusAPIResponse(undefined, {dataSets, locus, metadata});
|
|
900
1050
|
}
|
|
901
1051
|
}
|
|
902
1052
|
|
|
903
1053
|
/**
|
|
904
1054
|
* Function for handling full locus when it's the "classic" one (not hash trees)
|
|
905
1055
|
*
|
|
1056
|
+
* @param {string} debugText string explaining the trigger for this call, added to logs for debugging purposes
|
|
906
1057
|
* @param {object} locus locus object
|
|
907
1058
|
* @param {string} eventType locus event
|
|
908
1059
|
* @returns {void}
|
|
909
1060
|
*/
|
|
910
|
-
private onFullLocusClassic(locus: any, eventType?: string) {
|
|
1061
|
+
private onFullLocusClassic(debugText: string, locus: any, eventType?: string) {
|
|
911
1062
|
if (!this.locusParser.isNewFullLocus(locus)) {
|
|
912
1063
|
LoggerProxy.logger.info(
|
|
913
|
-
`Locus-info:index#onFullLocus --> ignoring old full locus DTO, eventType=${eventType}`
|
|
1064
|
+
`Locus-info:index#onFullLocus (${debugText}) --> ignoring old full locus DTO, eventType=${eventType}`
|
|
914
1065
|
);
|
|
915
1066
|
|
|
916
1067
|
return;
|
|
@@ -920,24 +1071,37 @@ export default class LocusInfo extends EventsScope {
|
|
|
920
1071
|
|
|
921
1072
|
/**
|
|
922
1073
|
* updates the locus with full locus object
|
|
1074
|
+
* @param {string} debugText string explaining the trigger for this call, added to logs for debugging purposes
|
|
923
1075
|
* @param {object} locus locus object
|
|
924
1076
|
* @param {string} eventType locus event
|
|
925
1077
|
* @param {DataSet[]} dataSets
|
|
1078
|
+
* @param {object} metadata locus hash trees metadata
|
|
926
1079
|
* @returns {object} null
|
|
927
1080
|
* @memberof LocusInfo
|
|
928
1081
|
*/
|
|
929
|
-
onFullLocus(
|
|
1082
|
+
onFullLocus(
|
|
1083
|
+
debugText: string,
|
|
1084
|
+
locus: any,
|
|
1085
|
+
eventType?: string,
|
|
1086
|
+
dataSets?: Array<DataSet>,
|
|
1087
|
+
metadata?: Metadata
|
|
1088
|
+
) {
|
|
930
1089
|
if (!locus) {
|
|
931
1090
|
LoggerProxy.logger.error(
|
|
932
|
-
|
|
1091
|
+
`Locus-info:index#onFullLocus (${debugText}) --> object passed as argument was invalid, continuing.`
|
|
933
1092
|
);
|
|
934
1093
|
}
|
|
935
1094
|
|
|
936
1095
|
if (dataSets) {
|
|
1096
|
+
if (!metadata) {
|
|
1097
|
+
throw new Error(
|
|
1098
|
+
`Locus-info:index#onFullLocus (${debugText}) --> hash tree metadata is missing with full Locus`
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
937
1101
|
// this is the new hashmap Locus DTO format (only applicable to webinars for now)
|
|
938
|
-
this.onFullLocusWithHashTrees(locus, eventType, dataSets);
|
|
1102
|
+
this.onFullLocusWithHashTrees(debugText, locus, metadata, eventType, dataSets);
|
|
939
1103
|
} else {
|
|
940
|
-
this.onFullLocusClassic(locus, eventType);
|
|
1104
|
+
this.onFullLocusClassic(debugText, locus, eventType);
|
|
941
1105
|
}
|
|
942
1106
|
}
|
|
943
1107
|
|
|
@@ -1217,27 +1381,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
1217
1381
|
shouldLeave: false,
|
|
1218
1382
|
}
|
|
1219
1383
|
);
|
|
1220
|
-
} else if (this.fullState && this.fullState.removed) {
|
|
1221
|
-
// user has been dropped from a meeting
|
|
1222
|
-
|
|
1223
|
-
// @ts-ignore
|
|
1224
|
-
this.webex.internal.newMetrics.submitClientEvent({
|
|
1225
|
-
name: 'client.call.remote-ended',
|
|
1226
|
-
options: {
|
|
1227
|
-
meetingId: this.meetingId,
|
|
1228
|
-
},
|
|
1229
|
-
});
|
|
1230
|
-
this.emitScoped(
|
|
1231
|
-
{
|
|
1232
|
-
file: 'locus-info',
|
|
1233
|
-
function: 'isMeetingActive',
|
|
1234
|
-
},
|
|
1235
|
-
EVENTS.DESTROY_MEETING,
|
|
1236
|
-
{
|
|
1237
|
-
reason: MEETING_REMOVED_REASON.FULLSTATE_REMOVED,
|
|
1238
|
-
shouldLeave: false,
|
|
1239
|
-
}
|
|
1240
|
-
);
|
|
1241
1384
|
}
|
|
1242
1385
|
// If you are guest and you are removed from the meeting
|
|
1243
1386
|
// You wont get any further events
|
|
@@ -1373,6 +1516,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
1373
1516
|
hasMeetingContainerChanged,
|
|
1374
1517
|
hasTranscribeChanged,
|
|
1375
1518
|
hasHesiodLLMIdChanged,
|
|
1519
|
+
hasAiSummaryNotificationChanged,
|
|
1376
1520
|
hasTranscribeSpokenLanguageChanged,
|
|
1377
1521
|
hasManualCaptionChanged,
|
|
1378
1522
|
hasEntryExitToneChanged,
|
|
@@ -1529,6 +1673,19 @@ export default class LocusInfo extends EventsScope {
|
|
|
1529
1673
|
);
|
|
1530
1674
|
}
|
|
1531
1675
|
|
|
1676
|
+
if (hasAiSummaryNotificationChanged) {
|
|
1677
|
+
this.emitScoped(
|
|
1678
|
+
{
|
|
1679
|
+
file: 'locus-info',
|
|
1680
|
+
function: 'updateControls',
|
|
1681
|
+
},
|
|
1682
|
+
LOCUSINFO.EVENTS.CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED,
|
|
1683
|
+
{
|
|
1684
|
+
aiSummaryNotification: current.transcribe.aiSummaryNotification,
|
|
1685
|
+
}
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1532
1689
|
if (hasTranscribeSpokenLanguageChanged) {
|
|
1533
1690
|
const {spokenLanguage} = current.transcribe;
|
|
1534
1691
|
|
|
@@ -2038,6 +2195,19 @@ export default class LocusInfo extends EventsScope {
|
|
|
2038
2195
|
);
|
|
2039
2196
|
}
|
|
2040
2197
|
|
|
2198
|
+
if (parsedSelves.updates.selfIdChanged) {
|
|
2199
|
+
this.emitScoped(
|
|
2200
|
+
{
|
|
2201
|
+
file: 'locus-info',
|
|
2202
|
+
function: 'updateSelf',
|
|
2203
|
+
},
|
|
2204
|
+
LOCUSINFO.EVENTS.SELF_ID_CHANGED,
|
|
2205
|
+
{
|
|
2206
|
+
selfId: parsedSelves.current.selfId,
|
|
2207
|
+
}
|
|
2208
|
+
);
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2041
2211
|
if (parsedSelves.updates.interpretationChanged) {
|
|
2042
2212
|
this.emitScoped(
|
|
2043
2213
|
{
|
|
@@ -138,6 +138,7 @@ const SelfUtils = {
|
|
|
138
138
|
updates.breakoutsChanged = SelfUtils.breakoutsChanged(previous, current);
|
|
139
139
|
updates.interpretationChanged = SelfUtils.interpretationChanged(previous, current);
|
|
140
140
|
updates.brbChanged = SelfUtils.brbChanged(previous, current);
|
|
141
|
+
updates.selfIdChanged = previous?.selfId !== current.selfId;
|
|
141
142
|
|
|
142
143
|
return {
|
|
143
144
|
previous,
|
package/src/locus-info/types.ts
CHANGED
|
@@ -2,9 +2,12 @@ import {Defer} from '@webex/common';
|
|
|
2
2
|
import {ConnectionState, MediaConnectionEventNames} from '@webex/internal-media-core';
|
|
3
3
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
4
4
|
import {ICE_AND_DTLS_CONNECTION_TIMEOUT} from '../constants';
|
|
5
|
+
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
6
|
+
import Metrics from '../metrics';
|
|
5
7
|
|
|
6
8
|
export interface MediaConnectionAwaiterProps {
|
|
7
9
|
webrtcMediaConnection: any;
|
|
10
|
+
correlationId: string;
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
/**
|
|
@@ -16,6 +19,7 @@ export default class MediaConnectionAwaiter {
|
|
|
16
19
|
private defer: Defer;
|
|
17
20
|
private retried: boolean;
|
|
18
21
|
private iceConnected: boolean;
|
|
22
|
+
private correlationId: string;
|
|
19
23
|
private onTimeoutCallback: () => void;
|
|
20
24
|
private peerConnectionStateCallback: () => void;
|
|
21
25
|
private iceConnectionStateCallback: () => void;
|
|
@@ -24,11 +28,12 @@ export default class MediaConnectionAwaiter {
|
|
|
24
28
|
/**
|
|
25
29
|
* @param {MediaConnectionAwaiterProps} mediaConnectionAwaiterProps
|
|
26
30
|
*/
|
|
27
|
-
constructor({webrtcMediaConnection}: MediaConnectionAwaiterProps) {
|
|
31
|
+
constructor({webrtcMediaConnection, correlationId}: MediaConnectionAwaiterProps) {
|
|
28
32
|
this.webrtcMediaConnection = webrtcMediaConnection;
|
|
29
33
|
this.defer = new Defer();
|
|
30
34
|
this.retried = false;
|
|
31
35
|
this.iceConnected = false;
|
|
36
|
+
this.correlationId = correlationId;
|
|
32
37
|
this.onTimeoutCallback = this.onTimeout.bind(this);
|
|
33
38
|
this.peerConnectionStateCallback = this.peerConnectionStateHandler.bind(this);
|
|
34
39
|
this.iceConnectionStateCallback = this.iceConnectionStateHandler.bind(this);
|
|
@@ -175,6 +180,32 @@ export default class MediaConnectionAwaiter {
|
|
|
175
180
|
this.timer = setTimeout(this.onTimeoutCallback, ICE_AND_DTLS_CONNECTION_TIMEOUT);
|
|
176
181
|
}
|
|
177
182
|
|
|
183
|
+
/**
|
|
184
|
+
* sends a metric with some additional info that might help debugging
|
|
185
|
+
* issues where browser doesn't update the RTCPeerConnection's iceConnectionState or connectionState
|
|
186
|
+
*
|
|
187
|
+
* @returns {void}
|
|
188
|
+
*/
|
|
189
|
+
async sendMetric() {
|
|
190
|
+
const stats = await this.webrtcMediaConnection.getStats();
|
|
191
|
+
|
|
192
|
+
// in theory we can end up with more than one transport report in the stats,
|
|
193
|
+
// but for the purpose of this metric it's fine to just use the first one
|
|
194
|
+
const transportReports = Array.from(
|
|
195
|
+
stats.values().filter((report) => report.type === 'transport')
|
|
196
|
+
) as Record<string, number | string>[];
|
|
197
|
+
|
|
198
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEDIA_STILL_NOT_CONNECTED, {
|
|
199
|
+
correlation_id: this.correlationId,
|
|
200
|
+
numTransports: transportReports.length,
|
|
201
|
+
dtlsState: transportReports[0]?.dtlsState,
|
|
202
|
+
iceState: transportReports[0]?.iceState,
|
|
203
|
+
packetsSent: transportReports[0]?.packetsSent,
|
|
204
|
+
packetsReceived: transportReports[0]?.packetsReceived,
|
|
205
|
+
dataChannelState: this.webrtcMediaConnection.multistreamConnection?.dataChannel?.readyState,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
178
209
|
/**
|
|
179
210
|
* Function called when the timeout is reached.
|
|
180
211
|
*
|
|
@@ -189,6 +220,8 @@ export default class MediaConnectionAwaiter {
|
|
|
189
220
|
return;
|
|
190
221
|
}
|
|
191
222
|
|
|
223
|
+
this.sendMetric();
|
|
224
|
+
|
|
192
225
|
if (!this.isIceGatheringCompleted()) {
|
|
193
226
|
if (!this.retried) {
|
|
194
227
|
LoggerProxy.logger.warn(
|
|
@@ -226,8 +259,15 @@ export default class MediaConnectionAwaiter {
|
|
|
226
259
|
*/
|
|
227
260
|
waitForMediaConnectionConnected(): Promise<void> {
|
|
228
261
|
if (this.isConnected()) {
|
|
262
|
+
LoggerProxy.logger.log(
|
|
263
|
+
'Media:MediaConnectionAwaiter#waitForMediaConnectionConnected --> Already connected'
|
|
264
|
+
);
|
|
265
|
+
|
|
229
266
|
return Promise.resolve();
|
|
230
267
|
}
|
|
268
|
+
LoggerProxy.logger.log(
|
|
269
|
+
'Media:MediaConnectionAwaiter#waitForMediaConnectionConnected --> Waiting for media connection to be connected'
|
|
270
|
+
);
|
|
231
271
|
|
|
232
272
|
this.webrtcMediaConnection.on(
|
|
233
273
|
MediaConnectionEventNames.PEER_CONNECTION_STATE_CHANGED,
|
package/src/media/properties.ts
CHANGED
|
@@ -196,11 +196,13 @@ export default class MediaProperties {
|
|
|
196
196
|
/**
|
|
197
197
|
* Waits for the webrtc media connection to be connected.
|
|
198
198
|
*
|
|
199
|
+
* @param {string} correlationId
|
|
199
200
|
* @returns {Promise<void>}
|
|
200
201
|
*/
|
|
201
|
-
waitForMediaConnectionConnected(): Promise<void> {
|
|
202
|
+
waitForMediaConnectionConnected(correlationId: string): Promise<void> {
|
|
202
203
|
const mediaConnectionAwaiter = new MediaConnectionAwaiter({
|
|
203
204
|
webrtcMediaConnection: this.webrtcMediaConnection,
|
|
205
|
+
correlationId,
|
|
204
206
|
});
|
|
205
207
|
|
|
206
208
|
return mediaConnectionAwaiter.waitForMediaConnectionConnected();
|