@webex/plugin-meetings 3.12.0-next.1 → 3.12.0-next.10
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 +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/hashTree/constants.js +10 -1
- package/dist/hashTree/constants.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +20 -11
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/hashTree/utils.js +22 -0
- package/dist/hashTree/utils.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/meeting/index.js +427 -323
- package/dist/meeting/index.js.map +1 -1
- package/dist/metrics/constants.js +5 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +116 -2
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/types/hashTree/constants.d.ts +1 -0
- package/dist/types/hashTree/utils.d.ts +11 -0
- package/dist/types/meeting/index.d.ts +24 -1
- package/dist/types/metrics/constants.d.ts +4 -0
- package/dist/types/multistream/sendSlotManager.d.ts +23 -1
- package/dist/webinar/index.js +325 -220
- package/dist/webinar/index.js.map +1 -1
- package/package.json +15 -15
- package/src/hashTree/constants.ts +9 -0
- package/src/hashTree/hashTreeParser.ts +21 -14
- package/src/hashTree/utils.ts +17 -0
- package/src/meeting/index.ts +165 -57
- package/src/metrics/constants.ts +5 -0
- package/src/multistream/sendSlotManager.ts +97 -3
- package/src/webinar/index.ts +120 -18
- package/test/unit/spec/hashTree/hashTreeParser.ts +238 -0
- package/test/unit/spec/hashTree/utils.ts +88 -1
- package/test/unit/spec/meeting/index.js +179 -48
- package/test/unit/spec/meetings/index.js +3 -3
- package/test/unit/spec/multistream/sendSlotManager.ts +135 -36
- package/test/unit/spec/webinar/index.ts +193 -8
|
@@ -553,6 +553,7 @@ describe('HashTreeParser', () => {
|
|
|
553
553
|
);
|
|
554
554
|
|
|
555
555
|
// Verify callback was called with OBJECTS_UPDATED and correct updatedObjects list
|
|
556
|
+
// Note: main is initialized before self due to sortByInitPriority
|
|
556
557
|
assert.calledWith(callback, LocusInfoUpdateType.OBJECTS_UPDATED, {
|
|
557
558
|
updatedObjects: [
|
|
558
559
|
{
|
|
@@ -596,6 +597,41 @@ describe('HashTreeParser', () => {
|
|
|
596
597
|
});
|
|
597
598
|
});
|
|
598
599
|
|
|
600
|
+
it('initializes "main" before "self" regardless of order from Locus', async () => {
|
|
601
|
+
const parser = createHashTreeParser({dataSets: [], locus: null}, null);
|
|
602
|
+
|
|
603
|
+
// Locus returns datasets in non-priority order: atd-active, main, self
|
|
604
|
+
const atdActiveDataSet = createDataSet('atd-active', 4, 500);
|
|
605
|
+
const mainDataSet = createDataSet('main', 16, 1100);
|
|
606
|
+
const selfDataSet = createDataSet('self', 1, 2100);
|
|
607
|
+
|
|
608
|
+
mockGetAllDataSetsMetadata(webexRequest, visibleDataSetsUrl, [
|
|
609
|
+
atdActiveDataSet,
|
|
610
|
+
mainDataSet,
|
|
611
|
+
selfDataSet,
|
|
612
|
+
]);
|
|
613
|
+
|
|
614
|
+
mockSyncRequest(webexRequest, selfDataSet.url);
|
|
615
|
+
mockSyncRequest(webexRequest, mainDataSet.url);
|
|
616
|
+
mockSyncRequest(webexRequest, atdActiveDataSet.url);
|
|
617
|
+
|
|
618
|
+
await parser.initializeFromMessage({
|
|
619
|
+
dataSets: [],
|
|
620
|
+
visibleDataSetsUrl,
|
|
621
|
+
locusUrl,
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
// Verify sync requests were sent in priority order: main, self, then atd-active
|
|
625
|
+
const syncCalls = webexRequest
|
|
626
|
+
.getCalls()
|
|
627
|
+
.filter((call) => call.args[0]?.method === 'POST' && call.args[0]?.uri?.endsWith('/sync'));
|
|
628
|
+
|
|
629
|
+
expect(syncCalls).to.have.lengthOf(3);
|
|
630
|
+
expect(syncCalls[0].args[0].uri).to.equal(`${mainDataSet.url}/sync`);
|
|
631
|
+
expect(syncCalls[1].args[0].uri).to.equal(`${selfDataSet.url}/sync`);
|
|
632
|
+
expect(syncCalls[2].args[0].uri).to.equal(`${atdActiveDataSet.url}/sync`);
|
|
633
|
+
});
|
|
634
|
+
|
|
599
635
|
it('handles sync response that has locusStateElements undefined', async () => {
|
|
600
636
|
const minimalInitialLocus = {
|
|
601
637
|
dataSets: [],
|
|
@@ -861,6 +897,116 @@ describe('HashTreeParser', () => {
|
|
|
861
897
|
});
|
|
862
898
|
});
|
|
863
899
|
|
|
900
|
+
it('handles updates to control entries correctly', () => {
|
|
901
|
+
const parser = createHashTreeParser();
|
|
902
|
+
|
|
903
|
+
const mainPutItemsSpy = sinon.spy(parser.dataSets.main.hashTree, 'putItems');
|
|
904
|
+
|
|
905
|
+
// Create a locus update with new htMeta information for some things
|
|
906
|
+
const locusUpdate = {
|
|
907
|
+
dataSets: [
|
|
908
|
+
createDataSet('main', 16, 1100),
|
|
909
|
+
],
|
|
910
|
+
locus: {
|
|
911
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f',
|
|
912
|
+
htMeta: {
|
|
913
|
+
elementId: {
|
|
914
|
+
type: 'locus',
|
|
915
|
+
id: 0,
|
|
916
|
+
version: 200, // same version
|
|
917
|
+
},
|
|
918
|
+
dataSetNames: ['main'],
|
|
919
|
+
},
|
|
920
|
+
participants: [],
|
|
921
|
+
controls: {
|
|
922
|
+
lock: {
|
|
923
|
+
locked: true,
|
|
924
|
+
htMeta: {
|
|
925
|
+
elementId: {
|
|
926
|
+
type: 'ControlEntry',
|
|
927
|
+
id: 10100,
|
|
928
|
+
version: 100,
|
|
929
|
+
},
|
|
930
|
+
dataSetNames: ['main'],
|
|
931
|
+
},
|
|
932
|
+
},
|
|
933
|
+
stream: {
|
|
934
|
+
streaming: true,
|
|
935
|
+
htMeta: {
|
|
936
|
+
elementId: {
|
|
937
|
+
type: 'ControlEntry',
|
|
938
|
+
id: 10101,
|
|
939
|
+
version: 100,
|
|
940
|
+
},
|
|
941
|
+
dataSetNames: ['main'],
|
|
942
|
+
},
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
},
|
|
946
|
+
};
|
|
947
|
+
|
|
948
|
+
// Call handleLocusUpdate
|
|
949
|
+
parser.handleLocusUpdate(locusUpdate);
|
|
950
|
+
|
|
951
|
+
// Verify putItems was called on main hash tree with correct data
|
|
952
|
+
assert.calledOnceWithExactly(mainPutItemsSpy, [
|
|
953
|
+
{type: 'locus', id: 0, version: 200},
|
|
954
|
+
{type: 'ControlEntry', id: 10100, version: 100},
|
|
955
|
+
{type: 'ControlEntry', id: 10101, version: 100}
|
|
956
|
+
]);
|
|
957
|
+
|
|
958
|
+
assert.calledOnceWithExactly(callback, LocusInfoUpdateType.OBJECTS_UPDATED, {
|
|
959
|
+
updatedObjects: [
|
|
960
|
+
{
|
|
961
|
+
htMeta: {
|
|
962
|
+
elementId: {
|
|
963
|
+
type: 'ControlEntry',
|
|
964
|
+
id: 10100,
|
|
965
|
+
version: 100,
|
|
966
|
+
},
|
|
967
|
+
dataSetNames: ['main'],
|
|
968
|
+
},
|
|
969
|
+
data: {
|
|
970
|
+
lock: {
|
|
971
|
+
locked: true,
|
|
972
|
+
htMeta: {
|
|
973
|
+
elementId: {
|
|
974
|
+
type: 'ControlEntry',
|
|
975
|
+
id: 10100,
|
|
976
|
+
version: 100,
|
|
977
|
+
},
|
|
978
|
+
dataSetNames: ['main'],
|
|
979
|
+
},
|
|
980
|
+
},
|
|
981
|
+
},
|
|
982
|
+
},
|
|
983
|
+
{
|
|
984
|
+
htMeta: {
|
|
985
|
+
elementId: {
|
|
986
|
+
type: 'ControlEntry',
|
|
987
|
+
id: 10101,
|
|
988
|
+
version: 100,
|
|
989
|
+
},
|
|
990
|
+
dataSetNames: ['main'],
|
|
991
|
+
},
|
|
992
|
+
data: {
|
|
993
|
+
stream: {
|
|
994
|
+
streaming: true,
|
|
995
|
+
htMeta: {
|
|
996
|
+
elementId: {
|
|
997
|
+
type: 'ControlEntry',
|
|
998
|
+
id: 10101,
|
|
999
|
+
version: 100,
|
|
1000
|
+
},
|
|
1001
|
+
dataSetNames: ['main'],
|
|
1002
|
+
},
|
|
1003
|
+
},
|
|
1004
|
+
},
|
|
1005
|
+
}
|
|
1006
|
+
],
|
|
1007
|
+
});
|
|
1008
|
+
});
|
|
1009
|
+
|
|
864
1010
|
it('handles unknown datasets gracefully', () => {
|
|
865
1011
|
const parser = createHashTreeParser();
|
|
866
1012
|
|
|
@@ -2062,6 +2208,98 @@ describe('HashTreeParser', () => {
|
|
|
2062
2208
|
await checkAsyncDatasetInitialization(parser, newDataSet);
|
|
2063
2209
|
});
|
|
2064
2210
|
|
|
2211
|
+
it('initializes new visible data sets in priority order', async () => {
|
|
2212
|
+
// Create a parser that only has "self" as visible (no "main")
|
|
2213
|
+
const initialLocusWithoutMain = {
|
|
2214
|
+
dataSets: [createDataSet('self', 1, 2000)],
|
|
2215
|
+
locus: {
|
|
2216
|
+
...exampleInitialLocus.locus,
|
|
2217
|
+
},
|
|
2218
|
+
};
|
|
2219
|
+
const metadataWithoutMain = {
|
|
2220
|
+
...exampleMetadata,
|
|
2221
|
+
visibleDataSets: [
|
|
2222
|
+
{
|
|
2223
|
+
name: 'self',
|
|
2224
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
2225
|
+
},
|
|
2226
|
+
],
|
|
2227
|
+
};
|
|
2228
|
+
const parser = createHashTreeParser(initialLocusWithoutMain, metadataWithoutMain);
|
|
2229
|
+
|
|
2230
|
+
// Verify "main" is not visible initially
|
|
2231
|
+
expect(parser.visibleDataSets.some((vds) => vds.name === 'main')).to.be.false;
|
|
2232
|
+
|
|
2233
|
+
// Stub updateItems on self hash tree to return true
|
|
2234
|
+
sinon.stub(parser.dataSets.self.hashTree, 'updateItems').returns([true]);
|
|
2235
|
+
|
|
2236
|
+
// Send a message that adds "main" and "atd-active" as new visible datasets.
|
|
2237
|
+
// Neither has info in dataSets, so both require async initialization.
|
|
2238
|
+
const newMainDataSet = createDataSet('main', 16, 6000);
|
|
2239
|
+
const newAtdActiveDataSet = createDataSet('atd-active', 4, 7000);
|
|
2240
|
+
|
|
2241
|
+
const message = {
|
|
2242
|
+
dataSets: [createDataSet('self', 1, 2100)],
|
|
2243
|
+
visibleDataSetsUrl,
|
|
2244
|
+
locusUrl,
|
|
2245
|
+
locusStateElements: [
|
|
2246
|
+
{
|
|
2247
|
+
htMeta: {
|
|
2248
|
+
elementId: {
|
|
2249
|
+
type: 'metadata' as const,
|
|
2250
|
+
id: 5,
|
|
2251
|
+
version: 51,
|
|
2252
|
+
},
|
|
2253
|
+
dataSetNames: ['self'],
|
|
2254
|
+
},
|
|
2255
|
+
data: {
|
|
2256
|
+
visibleDataSets: [
|
|
2257
|
+
{
|
|
2258
|
+
name: 'self',
|
|
2259
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
2260
|
+
},
|
|
2261
|
+
// listed in non-priority order: atd-active before main
|
|
2262
|
+
{name: 'atd-active', url: newAtdActiveDataSet.url},
|
|
2263
|
+
{name: 'main', url: newMainDataSet.url},
|
|
2264
|
+
],
|
|
2265
|
+
},
|
|
2266
|
+
},
|
|
2267
|
+
],
|
|
2268
|
+
};
|
|
2269
|
+
|
|
2270
|
+
// Mock getAllVisibleDataSetsFromLocus to return both new datasets (in non-priority order)
|
|
2271
|
+
mockGetAllDataSetsMetadata(webexRequest, visibleDataSetsUrl, [
|
|
2272
|
+
newAtdActiveDataSet,
|
|
2273
|
+
newMainDataSet,
|
|
2274
|
+
]);
|
|
2275
|
+
mockSyncRequest(webexRequest, newMainDataSet.url);
|
|
2276
|
+
mockSyncRequest(webexRequest, newAtdActiveDataSet.url);
|
|
2277
|
+
|
|
2278
|
+
parser.handleMessage(message, 'add main and atd-active datasets');
|
|
2279
|
+
|
|
2280
|
+
// Wait for the async initialization (queueMicrotask) to complete
|
|
2281
|
+
await clock.tickAsync(0);
|
|
2282
|
+
|
|
2283
|
+
// Verify both datasets are initialized
|
|
2284
|
+
expect(parser.dataSets.main?.hashTree).to.exist;
|
|
2285
|
+
expect(parser.dataSets['atd-active']?.hashTree).to.exist;
|
|
2286
|
+
|
|
2287
|
+
// Verify sync requests were sent in priority order: "main" before "atd-active",
|
|
2288
|
+
// even though atd-active was listed first in both the message and the Locus response
|
|
2289
|
+
const syncCalls = webexRequest
|
|
2290
|
+
.getCalls()
|
|
2291
|
+
.filter(
|
|
2292
|
+
(call) =>
|
|
2293
|
+
call.args[0]?.method === 'POST' &&
|
|
2294
|
+
call.args[0]?.uri?.endsWith('/sync') &&
|
|
2295
|
+
(call.args[0]?.uri?.includes('/main/') || call.args[0]?.uri?.includes('/atd-active/'))
|
|
2296
|
+
);
|
|
2297
|
+
|
|
2298
|
+
expect(syncCalls).to.have.lengthOf(2);
|
|
2299
|
+
expect(syncCalls[0].args[0].uri).to.equal(`${newMainDataSet.url}/sync`);
|
|
2300
|
+
expect(syncCalls[1].args[0].uri).to.equal(`${newAtdActiveDataSet.url}/sync`);
|
|
2301
|
+
});
|
|
2302
|
+
|
|
2065
2303
|
it('emits MEETING_ENDED if async init of a new visible dataset fails with 404', async () => {
|
|
2066
2304
|
const parser = createHashTreeParser();
|
|
2067
2305
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import {HashTreeObject, ObjectType} from '../../../../src/hashTree/types';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
deleteNestedObjectsWithHtMeta,
|
|
4
|
+
isSelf,
|
|
5
|
+
sortByInitPriority,
|
|
6
|
+
} from '../../../../src/hashTree/utils';
|
|
7
|
+
import {DataSetNames, DATA_SET_INIT_PRIORITY} from '../../../../src/hashTree/constants';
|
|
3
8
|
|
|
4
9
|
import {assert} from '@webex/test-helper-chai';
|
|
5
10
|
|
|
@@ -137,4 +142,86 @@ describe('Hash Tree Utils', () => {
|
|
|
137
142
|
assert.isFalse(isSelf(participantObject));
|
|
138
143
|
});
|
|
139
144
|
});
|
|
145
|
+
|
|
146
|
+
describe('#sortByInitPriority', () => {
|
|
147
|
+
[
|
|
148
|
+
{
|
|
149
|
+
description: 'places "main" and "self" first when both appear',
|
|
150
|
+
input: ['atd-active', 'main', 'atd-unmuted', 'self'],
|
|
151
|
+
expected: ['main', 'self', 'atd-active', 'atd-unmuted'],
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
description: 'preserves original order of non-priority items',
|
|
155
|
+
input: ['atd-unmuted', 'atd-active', 'self'],
|
|
156
|
+
expected: ['self', 'atd-unmuted', 'atd-active'],
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
description: 'returns items unchanged when no priority items present',
|
|
160
|
+
input: ['atd-active', 'atd-unmuted'],
|
|
161
|
+
expected: ['atd-active', 'atd-unmuted'],
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
description: 'reorders when only priority items present',
|
|
165
|
+
input: ['self', 'main'],
|
|
166
|
+
expected: ['main', 'self'],
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
description: 'handles empty list',
|
|
170
|
+
input: [],
|
|
171
|
+
expected: [],
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
description: 'handles only some priority items present',
|
|
175
|
+
input: ['atd-active', 'main'],
|
|
176
|
+
expected: ['main', 'atd-active'],
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
description: 'handles single non-priority item',
|
|
180
|
+
input: ['atd-active'],
|
|
181
|
+
expected: ['atd-active'],
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
description: 'handles single priority item',
|
|
185
|
+
input: ['self'],
|
|
186
|
+
expected: ['self'],
|
|
187
|
+
},
|
|
188
|
+
].forEach(({description, input, expected}) => {
|
|
189
|
+
it(description, () => {
|
|
190
|
+
const items = input.map((name) => ({name}));
|
|
191
|
+
|
|
192
|
+
const result = sortByInitPriority(items, DATA_SET_INIT_PRIORITY);
|
|
193
|
+
|
|
194
|
+
assert.deepEqual(
|
|
195
|
+
result.map((i) => i.name),
|
|
196
|
+
expected
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('should not mutate the original array', () => {
|
|
202
|
+
const items = [{name: DataSetNames.ATD_ACTIVE}, {name: DataSetNames.SELF}];
|
|
203
|
+
const originalOrder = items.map((i) => i.name);
|
|
204
|
+
|
|
205
|
+
sortByInitPriority(items, DATA_SET_INIT_PRIORITY);
|
|
206
|
+
|
|
207
|
+
assert.deepEqual(
|
|
208
|
+
items.map((i) => i.name),
|
|
209
|
+
originalOrder
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should preserve extra properties on items', () => {
|
|
214
|
+
const items = [
|
|
215
|
+
{name: DataSetNames.ATD_ACTIVE, url: 'url1'},
|
|
216
|
+
{name: DataSetNames.SELF, url: 'url2'},
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
const result = sortByInitPriority(items, DATA_SET_INIT_PRIORITY);
|
|
220
|
+
|
|
221
|
+
assert.deepEqual(result, [
|
|
222
|
+
{name: DataSetNames.SELF, url: 'url2'},
|
|
223
|
+
{name: DataSetNames.ATD_ACTIVE, url: 'url1'},
|
|
224
|
+
]);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
140
227
|
});
|