@webex/plugin-meetings 3.12.0-next.3 → 3.12.0-next.31
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/AGENTS.md +9 -0
- package/dist/aiEnableRequest/index.js +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +3 -1
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/constants.js +11 -1
- package/dist/controls-options-manager/constants.js.map +1 -1
- package/dist/controls-options-manager/index.js +23 -21
- package/dist/controls-options-manager/index.js.map +1 -1
- package/dist/controls-options-manager/util.js +91 -0
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/hashTree/constants.js +10 -1
- package/dist/hashTree/constants.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +550 -346
- 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/interceptors/locusRetry.js +23 -8
- package/dist/interceptors/locusRetry.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +222 -61
- package/dist/locus-info/index.js.map +1 -1
- package/dist/meeting/index.js +372 -292
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +1 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +146 -62
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +39 -5
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js +3 -0
- package/dist/member/util.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/constants.d.ts +1 -0
- package/dist/types/controls-options-manager/constants.d.ts +6 -1
- package/dist/types/hashTree/constants.d.ts +1 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +53 -15
- package/dist/types/hashTree/utils.d.ts +11 -0
- package/dist/types/interceptors/locusRetry.d.ts +4 -4
- package/dist/types/locus-info/index.d.ts +38 -5
- package/dist/types/meeting/index.d.ts +11 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/types.d.ts +1 -0
- package/dist/types/member/util.d.ts +1 -0
- package/dist/types/metrics/constants.d.ts +4 -0
- package/dist/types/multistream/sendSlotManager.d.ts +23 -1
- package/dist/webinar/index.js +301 -226
- package/dist/webinar/index.js.map +1 -1
- package/package.json +16 -16
- package/src/constants.ts +1 -0
- package/src/controls-options-manager/constants.ts +14 -1
- package/src/controls-options-manager/index.ts +26 -19
- package/src/controls-options-manager/util.ts +81 -1
- package/src/hashTree/constants.ts +9 -0
- package/src/hashTree/hashTreeParser.ts +273 -154
- package/src/hashTree/utils.ts +17 -0
- package/src/interceptors/locusRetry.ts +25 -4
- package/src/locus-info/index.ts +233 -79
- package/src/meeting/index.ts +98 -11
- package/src/meeting/util.ts +1 -0
- package/src/meetings/index.ts +58 -34
- package/src/meetings/util.ts +44 -1
- package/src/member/index.ts +10 -0
- package/src/member/types.ts +1 -0
- package/src/member/util.ts +3 -0
- package/src/metrics/constants.ts +5 -0
- package/src/multistream/sendSlotManager.ts +97 -3
- package/src/webinar/index.ts +75 -1
- package/test/unit/spec/controls-options-manager/index.js +114 -6
- package/test/unit/spec/controls-options-manager/util.js +165 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +839 -37
- package/test/unit/spec/hashTree/utils.ts +88 -1
- package/test/unit/spec/interceptors/locusRetry.ts +205 -4
- package/test/unit/spec/locus-info/index.js +262 -64
- package/test/unit/spec/meeting/index.js +54 -36
- package/test/unit/spec/meeting/utils.js +4 -0
- package/test/unit/spec/meetings/index.js +190 -8
- package/test/unit/spec/meetings/utils.js +124 -0
- package/test/unit/spec/member/index.js +7 -0
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/multistream/sendSlotManager.ts +135 -36
- package/test/unit/spec/webinar/index.ts +60 -0
|
@@ -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
|
});
|
|
@@ -36,6 +36,27 @@ describe('plugin-meetings', () => {
|
|
|
36
36
|
uri: `https://locus-test.webex.com/locus/api/v1/loci/call`,
|
|
37
37
|
body: 'foo'
|
|
38
38
|
};
|
|
39
|
+
|
|
40
|
+
const hashTreeOptions = {
|
|
41
|
+
method: 'GET',
|
|
42
|
+
headers: {
|
|
43
|
+
trackingid: 'test',
|
|
44
|
+
'retry-after': 1000,
|
|
45
|
+
},
|
|
46
|
+
uri: `https://locus-test.webex.com/locus/api/v1/loci/12345/session/abc/datasets/main/hashtree`,
|
|
47
|
+
body: undefined,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const syncOptions = {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: {
|
|
53
|
+
trackingid: 'test',
|
|
54
|
+
'retry-after': 1000,
|
|
55
|
+
},
|
|
56
|
+
uri: `https://locus-test.webex.com/locus/api/v1/loci/12345/session/abc/datasets/main/sync`,
|
|
57
|
+
body: 'foo',
|
|
58
|
+
};
|
|
59
|
+
|
|
39
60
|
const reason1 = new WebexHttpError.MethodNotAllowed({
|
|
40
61
|
statusCode: 403,
|
|
41
62
|
options: {
|
|
@@ -68,14 +89,194 @@ describe('plugin-meetings', () => {
|
|
|
68
89
|
});
|
|
69
90
|
|
|
70
91
|
it('calls handleRetryRequestLocusServiceError with correct retry time when locus service unavailable error', () => {
|
|
71
|
-
|
|
72
|
-
|
|
92
|
+
interceptor.webex.request = sinon.stub().returns(Promise.resolve());
|
|
93
|
+
const handleRetryStub = sinon.stub(
|
|
94
|
+
interceptor,
|
|
95
|
+
'handleRetryRequestLocusServiceError'
|
|
96
|
+
);
|
|
97
|
+
handleRetryStub.returns(Promise.resolve());
|
|
98
|
+
|
|
99
|
+
return interceptor.onResponseError(options, reason2).then(() => {
|
|
100
|
+
expect(handleRetryStub.calledWith(options, 1000)).to.be.true;
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
[429, 500, 502, 503, 504].forEach((statusCode) => {
|
|
105
|
+
it(`does not retry /hashtree requests on ${statusCode}`, () => {
|
|
106
|
+
const reason = new WebexHttpError.MethodNotAllowed({
|
|
107
|
+
statusCode,
|
|
108
|
+
options: {
|
|
109
|
+
headers: {trackingid: 'test', 'retry-after': 1000},
|
|
110
|
+
uri: hashTreeOptions.uri,
|
|
111
|
+
},
|
|
112
|
+
body: {error: `Fake ${statusCode}`},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const handleRetryStub = sinon.stub(
|
|
116
|
+
interceptor,
|
|
117
|
+
'handleRetryRequestLocusServiceError'
|
|
118
|
+
);
|
|
119
|
+
handleRetryStub.returns(Promise.resolve());
|
|
120
|
+
|
|
121
|
+
return interceptor.onResponseError(hashTreeOptions, reason).then(
|
|
122
|
+
() => assert.fail('Expected promise to be rejected'),
|
|
123
|
+
(err) => {
|
|
124
|
+
expect(err).to.equal(reason);
|
|
125
|
+
expect(handleRetryStub.called).to.be.false;
|
|
126
|
+
handleRetryStub.restore();
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it(`does not retry /sync requests on ${statusCode}`, () => {
|
|
132
|
+
const reason = new WebexHttpError.MethodNotAllowed({
|
|
133
|
+
statusCode,
|
|
134
|
+
options: {
|
|
135
|
+
headers: {trackingid: 'test', 'retry-after': 1000},
|
|
136
|
+
uri: syncOptions.uri,
|
|
137
|
+
},
|
|
138
|
+
body: {error: `Fake ${statusCode}`},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const handleRetryStub = sinon.stub(
|
|
142
|
+
interceptor,
|
|
143
|
+
'handleRetryRequestLocusServiceError'
|
|
144
|
+
);
|
|
73
145
|
handleRetryStub.returns(Promise.resolve());
|
|
74
146
|
|
|
75
|
-
return interceptor.onResponseError(
|
|
76
|
-
|
|
147
|
+
return interceptor.onResponseError(syncOptions, reason).then(
|
|
148
|
+
() => assert.fail('Expected promise to be rejected'),
|
|
149
|
+
(err) => {
|
|
150
|
+
expect(err).to.equal(reason);
|
|
151
|
+
expect(handleRetryStub.called).to.be.false;
|
|
152
|
+
handleRetryStub.restore();
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('still retries other locus requests on 429', () => {
|
|
159
|
+
const reason429 = new WebexHttpError.MethodNotAllowed({
|
|
160
|
+
statusCode: 429,
|
|
161
|
+
options: {
|
|
162
|
+
headers: {trackingid: 'test', 'retry-after': 1000},
|
|
163
|
+
uri: options.uri,
|
|
164
|
+
},
|
|
165
|
+
body: {error: 'Too Many Requests'},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
interceptor.webex.request = sinon.stub().returns(Promise.resolve());
|
|
169
|
+
const handleRetryStub = sinon.stub(
|
|
170
|
+
interceptor,
|
|
171
|
+
'handleRetryRequestLocusServiceError'
|
|
172
|
+
);
|
|
173
|
+
handleRetryStub.returns(Promise.resolve());
|
|
77
174
|
|
|
175
|
+
return interceptor.onResponseError(options, reason429).then(() => {
|
|
176
|
+
expect(handleRetryStub.calledOnce).to.be.true;
|
|
177
|
+
handleRetryStub.restore();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('still retries other locus requests on 503', () => {
|
|
182
|
+
interceptor.webex.request = sinon.stub().returns(Promise.resolve());
|
|
183
|
+
const handleRetryStub = sinon.stub(
|
|
184
|
+
interceptor,
|
|
185
|
+
'handleRetryRequestLocusServiceError'
|
|
186
|
+
);
|
|
187
|
+
handleRetryStub.returns(Promise.resolve());
|
|
188
|
+
|
|
189
|
+
return interceptor.onResponseError(options, reason2).then(() => {
|
|
190
|
+
expect(handleRetryStub.calledOnce).to.be.true;
|
|
191
|
+
handleRetryStub.restore();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe('URI parsing edge cases', () => {
|
|
196
|
+
const make503Reason = (uri) =>
|
|
197
|
+
new WebexHttpError.MethodNotAllowed({
|
|
198
|
+
statusCode: 503,
|
|
199
|
+
options: {headers: {trackingid: 'test', 'retry-after': 1000}, uri},
|
|
200
|
+
body: {error: 'Service Unavailable'},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const makeOptions = (uri) => ({
|
|
204
|
+
method: 'GET',
|
|
205
|
+
headers: {trackingid: 'test', 'retry-after': 1000},
|
|
206
|
+
uri,
|
|
207
|
+
body: undefined,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
[
|
|
211
|
+
'https://locus.webex.com/locus/api/v1/loci/123/session/abc/datasets/main/hashtree?rootHash=xyz',
|
|
212
|
+
'https://locus.webex.com/locus/api/v1/loci/123/session/abc/datasets/main/sync?seq=5',
|
|
213
|
+
].forEach((uri) => {
|
|
214
|
+
it(`skips retry even with query params: ${uri.split('/').pop()}`, () => {
|
|
215
|
+
const opts = makeOptions(uri);
|
|
216
|
+
const reason = make503Reason(uri);
|
|
217
|
+
const stub = sinon
|
|
218
|
+
.stub(interceptor, 'handleRetryRequestLocusServiceError')
|
|
219
|
+
.returns(Promise.resolve());
|
|
220
|
+
|
|
221
|
+
return interceptor.onResponseError(opts, reason).then(
|
|
222
|
+
() => assert.fail('Expected promise to be rejected'),
|
|
223
|
+
(err) => {
|
|
224
|
+
expect(err).to.equal(reason);
|
|
225
|
+
expect(stub.called).to.be.false;
|
|
226
|
+
stub.restore();
|
|
227
|
+
}
|
|
228
|
+
);
|
|
78
229
|
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
[
|
|
233
|
+
'https://locus.webex.com/locus/api/v1/loci/123/hashtree-v2',
|
|
234
|
+
'https://locus.webex.com/locus/api/v1/loci/123/syncData',
|
|
235
|
+
'https://locus.webex.com/locus/api/v1/loci/123/async',
|
|
236
|
+
'https://locus.webex.com/locus/api/v1/loci/123/hashtree/metadata',
|
|
237
|
+
].forEach((uri) => {
|
|
238
|
+
it(`still retries when path only partially matches: ${uri
|
|
239
|
+
.split('/')
|
|
240
|
+
.pop()}`, () => {
|
|
241
|
+
const opts = makeOptions(uri);
|
|
242
|
+
const reason = make503Reason(uri);
|
|
243
|
+
interceptor.webex.request = sinon.stub().returns(Promise.resolve());
|
|
244
|
+
const stub = sinon
|
|
245
|
+
.stub(interceptor, 'handleRetryRequestLocusServiceError')
|
|
246
|
+
.returns(Promise.resolve());
|
|
247
|
+
|
|
248
|
+
return interceptor.onResponseError(opts, reason).then(() => {
|
|
249
|
+
expect(stub.calledOnce).to.be.true;
|
|
250
|
+
stub.restore();
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('still retries when /hashtree is on a non-locus host', () => {
|
|
256
|
+
const uri = 'https://other-service.webex.com/api/v1/hashtree';
|
|
257
|
+
const opts = makeOptions(uri);
|
|
258
|
+
const reason = make503Reason(uri);
|
|
259
|
+
|
|
260
|
+
return interceptor.onResponseError(opts, reason).then(
|
|
261
|
+
() => assert.fail('Expected promise to be rejected'),
|
|
262
|
+
(err) => {
|
|
263
|
+
expect(err).to.equal(reason);
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('still retries when URI is malformed', () => {
|
|
269
|
+
const uri = 'not-a-valid-url';
|
|
270
|
+
const opts = makeOptions(uri);
|
|
271
|
+
const reason = make503Reason(uri);
|
|
272
|
+
|
|
273
|
+
return interceptor.onResponseError(opts, reason).then(
|
|
274
|
+
() => assert.fail('Expected promise to be rejected'),
|
|
275
|
+
(err) => {
|
|
276
|
+
expect(err).to.equal(reason);
|
|
277
|
+
}
|
|
278
|
+
);
|
|
279
|
+
});
|
|
79
280
|
});
|
|
80
281
|
});
|
|
81
282
|
|