@webex/plugin-meetings 3.10.0-next.13 → 3.10.0-next.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/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 +1115 -14
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/hashTree/types.js +9 -3
- package/dist/hashTree/types.js.map +1 -1
- package/dist/hashTree/utils.js +48 -0
- package/dist/hashTree/utils.js.map +1 -0
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +41 -22
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/types.js.map +1 -1
- 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 +151 -0
- package/dist/types/hashTree/types.d.ts +9 -0
- package/dist/types/hashTree/utils.d.ts +9 -0
- package/dist/types/locus-info/types.d.ts +12 -11
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -21
- package/src/hashTree/constants.ts +9 -0
- package/src/hashTree/hashTree.ts +463 -0
- package/src/hashTree/hashTreeParser.ts +1022 -7
- package/src/hashTree/types.ts +11 -1
- package/src/hashTree/utils.ts +42 -0
- package/src/locus-info/index.ts +49 -27
- package/src/locus-info/types.ts +13 -11
- 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 +95 -4
package/src/hashTree/types.ts
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
import {Enum} from '../constants';
|
|
2
2
|
|
|
3
|
-
// todo: Locus docs have now more types like CONTROL_ENTRY, EMBEDDED_APP
|
|
3
|
+
// todo: Locus docs have now more types like CONTROL_ENTRY, EMBEDDED_APP - need to add support for them once Locus implements them
|
|
4
4
|
export const ObjectType = {
|
|
5
5
|
participant: 'participant',
|
|
6
6
|
self: 'self',
|
|
7
7
|
locus: 'locus',
|
|
8
8
|
mediaShare: 'mediashare',
|
|
9
|
+
info: 'info',
|
|
10
|
+
fullState: 'fullstate',
|
|
9
11
|
} as const;
|
|
10
12
|
|
|
11
13
|
export type ObjectType = Enum<typeof ObjectType>;
|
|
12
14
|
|
|
15
|
+
// mapping from ObjectType to top level LocusDTO keys
|
|
16
|
+
export const ObjectTypeToLocusKeyMap = {
|
|
17
|
+
[ObjectType.info]: 'info',
|
|
18
|
+
[ObjectType.fullState]: 'fullState',
|
|
19
|
+
[ObjectType.self]: 'self',
|
|
20
|
+
[ObjectType.participant]: 'participants', // note: each object is a single participant in participants array
|
|
21
|
+
[ObjectType.mediaShare]: 'mediaShares', // note: each object is a single mediaShare in mediaShares array
|
|
22
|
+
};
|
|
13
23
|
export interface HtMeta {
|
|
14
24
|
elementId: {
|
|
15
25
|
type: ObjectType;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* eslint-disable import/prefer-default-export */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Analyzes given part of Locus DTO recursively and delete any nested objects that have their own htMeta
|
|
5
|
+
*
|
|
6
|
+
* @param {Object} currentLocusPart part of locus DTO to analyze
|
|
7
|
+
* @param {Object} parent parent object
|
|
8
|
+
* @param {string|number} currentKey key of the parent object that currentLocusPart is
|
|
9
|
+
* @returns {void}
|
|
10
|
+
*/
|
|
11
|
+
export const deleteNestedObjectsWithHtMeta = (
|
|
12
|
+
currentLocusPart: any,
|
|
13
|
+
parent?: any,
|
|
14
|
+
currentKey?: string | number
|
|
15
|
+
) => {
|
|
16
|
+
if (typeof currentLocusPart !== 'object' || currentLocusPart === null) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (parent && currentKey !== undefined && currentLocusPart.htMeta) {
|
|
21
|
+
if (Array.isArray(parent)) {
|
|
22
|
+
parent.splice(Number(currentKey), 1);
|
|
23
|
+
} else {
|
|
24
|
+
delete parent[currentKey];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (Array.isArray(currentLocusPart)) {
|
|
31
|
+
// iterate array in reverse, so that indexes remain valid when deleting elements
|
|
32
|
+
for (let i = currentLocusPart.length - 1; i >= 0; i -= 1) {
|
|
33
|
+
deleteNestedObjectsWithHtMeta(currentLocusPart[i], currentLocusPart, i);
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
for (const key of Object.keys(currentLocusPart)) {
|
|
37
|
+
if (Object.prototype.hasOwnProperty.call(currentLocusPart, key)) {
|
|
38
|
+
deleteNestedObjectsWithHtMeta(currentLocusPart[key], currentLocusPart, key);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
package/src/locus-info/index.ts
CHANGED
|
@@ -37,8 +37,8 @@ import HashTreeParser, {
|
|
|
37
37
|
isSelf,
|
|
38
38
|
LocusInfoUpdateType,
|
|
39
39
|
} from '../hashTree/hashTreeParser';
|
|
40
|
-
import {ObjectType} from '../hashTree/types';
|
|
41
|
-
import {LocusDTO} from './types';
|
|
40
|
+
import {ObjectType, ObjectTypeToLocusKeyMap} from '../hashTree/types';
|
|
41
|
+
import {LocusDTO, LocusFullState} from './types';
|
|
42
42
|
|
|
43
43
|
export type LocusLLMEvent = {
|
|
44
44
|
data: {
|
|
@@ -47,6 +47,8 @@ export type LocusLLMEvent = {
|
|
|
47
47
|
};
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
+
// list of top level keys in Locus DTO relevant for Hash Tree DTOs processing
|
|
51
|
+
// it does not contain fields specific to classic Locus DTOs like sequence or baseSequence
|
|
50
52
|
const LocusDtoTopLevelKeys = [
|
|
51
53
|
'controls',
|
|
52
54
|
'fullState',
|
|
@@ -478,12 +480,19 @@ export default class LocusInfo extends EventsScope {
|
|
|
478
480
|
*/
|
|
479
481
|
handleLocusAPIResponse(meeting, responseBody: LocusApiResponseBody): void {
|
|
480
482
|
if (this.hashTreeParser) {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
483
|
+
if (!responseBody.dataSets) {
|
|
484
|
+
this.sendClassicVsHashTreeMismatchMetric(
|
|
485
|
+
meeting,
|
|
486
|
+
`expected hash tree dataSets in API response but they are missing`
|
|
487
|
+
);
|
|
488
|
+
// continuing as we can still manage without responseBody.dataSets, but this is very suspicious
|
|
489
|
+
}
|
|
484
490
|
LoggerProxy.logger.info(
|
|
485
|
-
'Locus-info:index#handleLocusAPIResponse
|
|
491
|
+
'Locus-info:index#handleLocusAPIResponse --> passing Locus API response to HashTreeParser: ',
|
|
492
|
+
responseBody
|
|
486
493
|
);
|
|
494
|
+
// update the data in our hash trees
|
|
495
|
+
this.hashTreeParser.handleLocusUpdate(responseBody);
|
|
487
496
|
} else {
|
|
488
497
|
if (responseBody.dataSets) {
|
|
489
498
|
this.sendClassicVsHashTreeMismatchMetric(
|
|
@@ -511,23 +520,25 @@ export default class LocusInfo extends EventsScope {
|
|
|
511
520
|
// not doing anything here, as we need Locus to always be there (at least some fields)
|
|
512
521
|
// and that's already taken care of in updateFromHashTree()
|
|
513
522
|
LoggerProxy.logger.info(
|
|
514
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object removed`
|
|
523
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object removed, version=${object.htMeta.elementId.version}`
|
|
515
524
|
);
|
|
516
525
|
|
|
517
526
|
return locus;
|
|
518
527
|
}
|
|
519
528
|
// replace the main locus
|
|
520
529
|
|
|
521
|
-
// The Locus object we receive from backend has empty participants,
|
|
522
|
-
//
|
|
523
|
-
//
|
|
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
|
|
524
533
|
const locusObjectFromData = object.data;
|
|
525
|
-
|
|
526
|
-
|
|
534
|
+
|
|
535
|
+
Object.values(ObjectTypeToLocusKeyMap).forEach((locusDtoKey) => {
|
|
536
|
+
delete locusObjectFromData[locusDtoKey];
|
|
537
|
+
});
|
|
527
538
|
|
|
528
539
|
locus = {...locus, ...locusObjectFromData};
|
|
529
540
|
LoggerProxy.logger.info(
|
|
530
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object updated`
|
|
541
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> LOCUS object updated to version=${object.htMeta.elementId.version}`
|
|
531
542
|
);
|
|
532
543
|
break;
|
|
533
544
|
}
|
|
@@ -540,7 +551,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
540
551
|
object.data.name === 'content'
|
|
541
552
|
? `floor=${object.data.floor?.disposition}, ${object.data.floor?.beneficiary?.id}`
|
|
542
553
|
: ''
|
|
543
|
-
}`
|
|
554
|
+
} version=${object.htMeta.elementId.version}`
|
|
544
555
|
);
|
|
545
556
|
const existingMediaShare = locus.mediaShares?.find(
|
|
546
557
|
(ms) => ms.htMeta.elementId.id === object.htMeta.elementId.id
|
|
@@ -554,7 +565,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
554
565
|
}
|
|
555
566
|
} else {
|
|
556
567
|
LoggerProxy.logger.info(
|
|
557
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> mediaShare id=${object.htMeta.elementId.id} removed`
|
|
568
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> mediaShare id=${object.htMeta.elementId.id} removed, version=${object.htMeta.elementId.version}`
|
|
558
569
|
);
|
|
559
570
|
locus.mediaShares = locus.mediaShares?.filter(
|
|
560
571
|
(ms) => ms.htMeta.elementId.id !== object.htMeta.elementId.id
|
|
@@ -565,7 +576,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
565
576
|
LoggerProxy.logger.info(
|
|
566
577
|
`Locus-info:index#updateLocusFromHashTreeObject --> participant id=${
|
|
567
578
|
object.htMeta.elementId.id
|
|
568
|
-
} ${object.data ? 'updated' : 'removed'}`
|
|
579
|
+
} ${object.data ? 'updated' : 'removed'} version=${object.htMeta.elementId.version}`
|
|
569
580
|
);
|
|
570
581
|
if (object.data) {
|
|
571
582
|
if (!locus.participants) {
|
|
@@ -585,19 +596,22 @@ export default class LocusInfo extends EventsScope {
|
|
|
585
596
|
this.hashTreeObjectId2ParticipantId.delete(object.htMeta.elementId.id);
|
|
586
597
|
}
|
|
587
598
|
break;
|
|
599
|
+
case ObjectType.info:
|
|
600
|
+
case ObjectType.fullState:
|
|
588
601
|
case ObjectType.self:
|
|
589
602
|
if (!object.data) {
|
|
590
603
|
// self without data is handled inside HashTreeParser and results in LocusInfoUpdateType.MEETING_ENDED, so we should never get here
|
|
604
|
+
// other types like info or fullstate - Locus should never send them without data
|
|
591
605
|
LoggerProxy.logger.warn(
|
|
592
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> received
|
|
606
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> received ${type} object without data, this is not expected! version=${object.htMeta.elementId.version}`
|
|
593
607
|
);
|
|
594
|
-
|
|
595
|
-
|
|
608
|
+
} else {
|
|
609
|
+
LoggerProxy.logger.info(
|
|
610
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> ${type} object updated to version ${object.htMeta.elementId.version}`
|
|
611
|
+
);
|
|
612
|
+
const locusDtoKey = ObjectTypeToLocusKeyMap[type];
|
|
613
|
+
locus[locusDtoKey] = object.data;
|
|
596
614
|
}
|
|
597
|
-
LoggerProxy.logger.info(
|
|
598
|
-
`Locus-info:index#updateLocusFromHashTreeObject --> SELF object updated`
|
|
599
|
-
);
|
|
600
|
-
locus.self = object.data;
|
|
601
615
|
break;
|
|
602
616
|
default:
|
|
603
617
|
LoggerProxy.logger.warn(
|
|
@@ -704,19 +718,27 @@ export default class LocusInfo extends EventsScope {
|
|
|
704
718
|
|
|
705
719
|
// if Locus object is unchanged or removed, we need to keep using the existing locus
|
|
706
720
|
// because the rest of the locusInfo code expects locus to always be present (with at least some of the fields)
|
|
707
|
-
// if it gets updated, we
|
|
708
|
-
// so that when
|
|
721
|
+
// if it gets updated, we only need to have the fields that are not part of "locus" object (like "info" or "mediaShares")
|
|
722
|
+
// so that when Locus object gets updated, if the new one is missing some field, that field will
|
|
709
723
|
// be removed from our locusInfo
|
|
710
724
|
if (
|
|
711
725
|
locusObjectStateAfterUpdates === LocusObjectStateAfterUpdates.unchanged ||
|
|
712
726
|
locusObjectStateAfterUpdates === LocusObjectStateAfterUpdates.removed
|
|
713
727
|
) {
|
|
714
|
-
// copy over existing locus
|
|
728
|
+
// copy over all of existing locus except participants
|
|
715
729
|
LocusDtoTopLevelKeys.forEach((key) => {
|
|
716
730
|
if (key !== 'participants') {
|
|
717
731
|
locus[key] = cloneDeep(this[key]);
|
|
718
732
|
}
|
|
719
733
|
});
|
|
734
|
+
} else {
|
|
735
|
+
// initialize only the fields that are not part of main "Locus" object
|
|
736
|
+
// (except participants, which need to stay empty - that means "no participant changes")
|
|
737
|
+
Object.values(ObjectTypeToLocusKeyMap).forEach((locusDtoKey) => {
|
|
738
|
+
if (locusDtoKey !== 'participants') {
|
|
739
|
+
locus[locusDtoKey] = cloneDeep(this[locusDtoKey]);
|
|
740
|
+
}
|
|
741
|
+
});
|
|
720
742
|
}
|
|
721
743
|
|
|
722
744
|
LoggerProxy.logger.info(
|
|
@@ -724,7 +746,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
724
746
|
data.updatedObjects.map((o) => ({
|
|
725
747
|
type: o.htMeta.elementId.type,
|
|
726
748
|
id: o.htMeta.elementId.id,
|
|
727
|
-
hasData: o.data
|
|
749
|
+
hasData: !!o.data,
|
|
728
750
|
}))
|
|
729
751
|
)}`
|
|
730
752
|
);
|
package/src/locus-info/types.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import {HtMeta} from '../hashTree/types';
|
|
2
2
|
|
|
3
|
+
export type LocusFullState = {
|
|
4
|
+
active: boolean;
|
|
5
|
+
count: number;
|
|
6
|
+
lastActive: string;
|
|
7
|
+
locked: boolean;
|
|
8
|
+
sessionId: string;
|
|
9
|
+
seessionIds: string[];
|
|
10
|
+
startTime: number;
|
|
11
|
+
state: string;
|
|
12
|
+
type: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
3
15
|
export type LocusDTO = {
|
|
4
16
|
controls?: any;
|
|
5
|
-
fullState?:
|
|
6
|
-
active: boolean;
|
|
7
|
-
count: number;
|
|
8
|
-
lastActive: string;
|
|
9
|
-
locked: boolean;
|
|
10
|
-
sessionId: string;
|
|
11
|
-
seessionIds: string[];
|
|
12
|
-
startTime: number;
|
|
13
|
-
state: string;
|
|
14
|
-
type: string;
|
|
15
|
-
};
|
|
17
|
+
fullState?: LocusFullState;
|
|
16
18
|
host?: {
|
|
17
19
|
id: string;
|
|
18
20
|
incomingCallProtocols: any[];
|