@webex/webex-core 3.8.1 → 3.9.0-multi-llms.2

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.
Files changed (89) hide show
  1. package/README.md +87 -27
  2. package/dist/index.js +16 -33
  3. package/dist/index.js.map +1 -1
  4. package/dist/{lib/services/interceptors/hostmap.js → interceptors/proxy.js} +58 -25
  5. package/dist/interceptors/proxy.js.map +1 -0
  6. package/dist/lib/batcher.js +1 -1
  7. package/dist/lib/constants.js +10 -1
  8. package/dist/lib/constants.js.map +1 -1
  9. package/dist/lib/credentials/credentials.js +1 -1
  10. package/dist/lib/credentials/token.js +1 -1
  11. package/dist/lib/{services-v2/interceptors → interceptors}/server-error.js +1 -1
  12. package/dist/lib/interceptors/server-error.js.map +1 -0
  13. package/dist/lib/services/index.js +2 -29
  14. package/dist/lib/services/index.js.map +1 -1
  15. package/dist/lib/services/service-host.js +1 -1
  16. package/dist/lib/services/service-host.js.map +1 -1
  17. package/dist/lib/services/service-registry.js +1 -1
  18. package/dist/lib/services/service-registry.js.map +1 -1
  19. package/dist/lib/services/service-state.js +1 -1
  20. package/dist/lib/services/service-state.js.map +1 -1
  21. package/dist/lib/services/services.js +3 -3
  22. package/dist/lib/services/services.js.map +1 -1
  23. package/dist/lib/services-v2/index.js +0 -29
  24. package/dist/lib/services-v2/index.js.map +1 -1
  25. package/dist/lib/services-v2/metrics.js.map +1 -1
  26. package/dist/lib/services-v2/service-catalog.js +15 -11
  27. package/dist/lib/services-v2/service-catalog.js.map +1 -1
  28. package/dist/lib/services-v2/services-v2.js +160 -111
  29. package/dist/lib/services-v2/services-v2.js.map +1 -1
  30. package/dist/lib/services-v2/types.js.map +1 -1
  31. package/dist/plugins/logger.js +1 -1
  32. package/dist/webex-core.js +53 -60
  33. package/dist/webex-core.js.map +1 -1
  34. package/package.json +14 -14
  35. package/src/index.js +6 -14
  36. package/src/interceptors/proxy.js +70 -0
  37. package/src/lib/constants.js +29 -1
  38. package/src/lib/{services/interceptors → interceptors}/server-error.js +1 -1
  39. package/src/lib/services/index.js +2 -7
  40. package/src/lib/services/service-host.js +1 -1
  41. package/src/lib/services/service-registry.js +1 -1
  42. package/src/lib/services/service-state.js +1 -1
  43. package/src/lib/services/services.js +2 -2
  44. package/src/lib/services-v2/index.ts +0 -16
  45. package/src/lib/services-v2/service-catalog.ts +27 -19
  46. package/src/lib/services-v2/{services-v2.js → services-v2.ts} +188 -104
  47. package/src/lib/services-v2/types.ts +62 -2
  48. package/src/webex-core.js +12 -3
  49. package/test/fixtures/host-catalog-v2.ts +30 -122
  50. package/test/integration/spec/services/services.js +11 -0
  51. package/test/integration/spec/services-v2/service-catalog.js +664 -0
  52. package/test/integration/spec/services-v2/services-v2.js +1136 -0
  53. package/test/unit/spec/interceptors/proxy.js +73 -0
  54. package/test/unit/spec/services-v2/service-detail.ts +1 -1
  55. package/test/unit/spec/services-v2/services-v2.ts +579 -442
  56. package/test/unit/spec/webex-core.js +62 -2
  57. package/dist/lib/services/constants.js +0 -17
  58. package/dist/lib/services/constants.js.map +0 -1
  59. package/dist/lib/services/interceptors/hostmap.js.map +0 -1
  60. package/dist/lib/services/interceptors/server-error.js +0 -77
  61. package/dist/lib/services/interceptors/server-error.js.map +0 -1
  62. package/dist/lib/services/interceptors/service.js +0 -137
  63. package/dist/lib/services/interceptors/service.js.map +0 -1
  64. package/dist/lib/services-v2/constants.js +0 -17
  65. package/dist/lib/services-v2/constants.js.map +0 -1
  66. package/dist/lib/services-v2/interceptors/server-error.js.map +0 -1
  67. package/dist/lib/services-v2/service-host.js +0 -300
  68. package/dist/lib/services-v2/service-host.js.map +0 -1
  69. package/dist/lib/services-v2/service-registry.js +0 -534
  70. package/dist/lib/services-v2/service-registry.js.map +0 -1
  71. package/dist/lib/services-v2/service-state.js +0 -97
  72. package/dist/lib/services-v2/service-state.js.map +0 -1
  73. package/dist/lib/services-v2/service-url.js +0 -119
  74. package/dist/lib/services-v2/service-url.js.map +0 -1
  75. package/src/lib/services/constants.js +0 -21
  76. package/src/lib/services/interceptors/hostmap.js +0 -36
  77. package/src/lib/services/interceptors/service.js +0 -101
  78. package/src/lib/services-v2/constants.ts +0 -21
  79. package/src/lib/services-v2/interceptors/server-error.js +0 -48
  80. /package/dist/lib/{services-v2/interceptors → interceptors}/hostmap.js +0 -0
  81. /package/dist/lib/{services-v2/interceptors → interceptors}/hostmap.js.map +0 -0
  82. /package/dist/lib/{services-v2/interceptors → interceptors}/service.js +0 -0
  83. /package/dist/lib/{services-v2/interceptors → interceptors}/service.js.map +0 -0
  84. /package/dist/lib/{services/metrics.js → metrics.js} +0 -0
  85. /package/dist/lib/{services/metrics.js.map → metrics.js.map} +0 -0
  86. /package/src/lib/{services-v2/interceptors → interceptors}/hostmap.js +0 -0
  87. /package/src/lib/{services-v2/interceptors → interceptors}/service.js +0 -0
  88. /package/src/lib/{services-v2/metrics.js → metrics.js} +0 -0
  89. /package/src/lib/{services/metrics.js → services-v2/metrics.ts} +0 -0
@@ -23,7 +23,7 @@ describe('webex-core', () => {
23
23
  let catalog;
24
24
 
25
25
  beforeEach(() => {
26
- webex = new MockWebex({
26
+ webex = MockWebex({
27
27
  children: {
28
28
  services: ServicesV2,
29
29
  newMetrics: NewMetrics,
@@ -33,294 +33,293 @@ describe('webex-core', () => {
33
33
  catalog = services._getCatalog();
34
34
  });
35
35
 
36
- // describe('#initialize', () => {
37
- // it('initFailed is false when initialization succeeds and credentials are available', async () => {
38
- // services.listenToOnce = sinon.stub();
39
- // services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
40
- // services.webex.credentials = {
41
- // supertoken: {
42
- // access_token: 'token',
43
- // },
44
- // };
45
-
46
- // services.initialize();
47
-
48
- // // call the onReady callback
49
- // services.listenToOnce.getCall(1).args[2]();
50
- // await waitForAsync();
51
-
52
- // assert.isFalse(services.initFailed);
53
- // });
54
-
55
- // it('initFailed is false when initialization succeeds no credentials are available', async () => {
56
- // services.listenToOnce = sinon.stub();
57
- // services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
58
-
59
- // services.initialize();
60
-
61
- // // call the onReady callback
62
- // services.listenToOnce.getCall(1).args[2]();
63
- // await waitForAsync();
64
-
65
- // assert.isFalse(services.initFailed);
66
- // });
67
-
68
- // it.each([
69
- // {error: new Error('failed'), expectedMessage: 'failed'},
70
- // {error: undefined, expectedMessage: undefined},
71
- // ])(
72
- // 'sets initFailed to true when collectPreauthCatalog errors',
73
- // async ({error, expectedMessage}) => {
74
- // services.collectPreauthCatalog = sinon.stub().callsFake(() => {
75
- // return Promise.reject(error);
76
- // });
36
+ describe('#initialize', () => {
37
+ it('initFailed is false when initialization succeeds and credentials are available', async () => {
38
+ services.listenToOnce = sinon.stub();
39
+ services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
40
+ services.webex.credentials = {
41
+ supertoken: {
42
+ access_token: 'token',
43
+ },
44
+ };
77
45
 
78
- // services.listenToOnce = sinon.stub();
79
- // services.logger.error = sinon.stub();
80
-
81
- // services.initialize();
82
-
83
- // // call the onReady callback
84
- // services.listenToOnce.getCall(1).args[2]();
85
-
86
- // await waitForAsync();
87
-
88
- // assert.isTrue(services.initFailed);
89
- // sinon.assert.calledWith(
90
- // services.logger.error,
91
- // `services: failed to init initial services when no credentials available, ${expectedMessage}`
92
- // );
93
- // }
94
- // );
95
-
96
- // it.each([
97
- // {error: new Error('failed'), expectedMessage: 'failed'},
98
- // {error: undefined, expectedMessage: undefined},
99
- // ])(
100
- // 'sets initFailed to true when initServiceCatalogs errors',
101
- // async ({error, expectedMessage}) => {
102
- // services.initServiceCatalogs = sinon.stub().callsFake(() => {
103
- // return Promise.reject(error);
104
- // });
105
- // services.webex.credentials = {
106
- // supertoken: {
107
- // access_token: 'token',
108
- // },
109
- // };
110
-
111
- // services.listenToOnce = sinon.stub();
112
- // services.logger.error = sinon.stub();
113
-
114
- // services.initialize();
115
-
116
- // // call the onReady callback
117
- // services.listenToOnce.getCall(1).args[2]();
118
-
119
- // await waitForAsync();
120
-
121
- // assert.isTrue(services.initFailed);
122
- // sinon.assert.calledWith(
123
- // services.logger.error,
124
- // `services: failed to init initial services when credentials available, ${expectedMessage}`
125
- // );
126
- // }
127
- // );
128
- // });
129
-
130
- // describe('#initServiceCatalogs', () => {
131
- // it('does not set initFailed to true when updateServices succeeds', async () => {
132
- // services.webex.credentials = {
133
- // getOrgId: sinon.stub().returns('orgId'),
134
- // canAuthorize: true,
135
- // };
136
-
137
- // services.collectPreauthCatalog = sinon.stub().callsFake(() => {
138
- // return Promise.resolve();
139
- // });
140
-
141
- // services.updateServices = sinon.stub().callsFake(() => {
142
- // return Promise.resolve();
143
- // });
144
-
145
- // services.logger.error = sinon.stub();
146
-
147
- // await services.initServiceCatalogs();
148
-
149
- // assert.isFalse(services.initFailed);
150
-
151
- // sinon.assert.calledWith(services.collectPreauthCatalog, {orgId: 'orgId'});
152
- // sinon.assert.notCalled(services.logger.warn);
153
- // });
154
-
155
- // it('sets initFailed to true when updateServices errors', async () => {
156
- // const error = new Error('failed');
157
-
158
- // services.webex.credentials = {
159
- // getOrgId: sinon.stub().returns('orgId'),
160
- // canAuthorize: true,
161
- // };
162
-
163
- // services.collectPreauthCatalog = sinon.stub().callsFake(() => {
164
- // return Promise.resolve();
165
- // });
166
-
167
- // services.updateServices = sinon.stub().callsFake(() => {
168
- // return Promise.reject(error);
169
- // });
170
-
171
- // services.logger.error = sinon.stub();
172
-
173
- // await services.initServiceCatalogs();
174
-
175
- // assert.isTrue(services.initFailed);
176
-
177
- // sinon.assert.calledWith(services.collectPreauthCatalog, {orgId: 'orgId'});
178
- // sinon.assert.calledWith(services.logger.warn, 'services: cannot retrieve postauth catalog');
179
- // });
180
- // });
181
-
182
- // describe('class members', () => {
183
- // describe('#registries', () => {
184
- // it('should be a weakmap', () => {
185
- // assert.instanceOf(services.registries, WeakMap);
186
- // });
187
- // });
188
-
189
- // describe('#states', () => {
190
- // it('should be a weakmap', () => {
191
- // assert.instanceOf(services.states, WeakMap);
192
- // });
193
- // });
194
- // });
195
-
196
- // describe('class methods', () => {
197
- // describe('#getRegistry', () => {
198
- // it('should be a service registry', () => {
199
- // assert.instanceOf(services.getRegistry(), ServiceRegistry);
200
- // });
201
- // });
202
-
203
- // describe('#getState', () => {
204
- // it('should be a service state', () => {
205
- // assert.instanceOf(services.getState(), ServiceState);
206
- // });
207
- // });
208
- // });
209
-
210
- // describe('#namespace', () => {
211
- // it('is accurate to plugin name', () => {
212
- // assert.equal(services.namespace, 'Services');
213
- // });
214
- // });
215
-
216
- // describe('#_catalogs', () => {
217
- // it('is a weakmap', () => {
218
- // assert.typeOf(services._catalogs, 'weakmap');
219
- // });
220
- // });
221
-
222
- // describe('#validateDomains', () => {
223
- // it('is a boolean', () => {
224
- // assert.isBoolean(services.validateDomains);
225
- // });
226
- // });
227
-
228
- // describe('#initFailed', () => {
229
- // it('is a boolean', () => {
230
- // assert.isFalse(services.initFailed);
231
- // });
232
- // });
233
-
234
- // describe('#list()', () => {
235
- // let serviceList;
236
-
237
- // beforeEach(() => {
238
- // serviceList = services.list();
239
- // });
240
-
241
- // it('must return an object', () => {
242
- // assert.typeOf(serviceList, 'object');
243
- // });
244
-
245
- // it('returned list must be of shape {Record<string, string>}', () => {
246
- // Object.keys(serviceList).forEach((key) => {
247
- // assert.typeOf(key, 'string');
248
- // assert.typeOf(serviceList[key], 'string');
249
- // });
250
- // });
251
- // });
252
-
253
- // describe('#fetchClientRegionInfo', () => {
254
- // beforeEach(() => {
255
- // services.webex.config = {
256
- // services: {
257
- // discovery: {
258
- // sqdiscovery: 'https://test.ciscospark.com/v1/region',
259
- // },
260
- // },
261
- // };
262
- // });
263
-
264
- // it('successfully resolves with undefined if fetch request failed', () => {
265
- // webex.request = sinon.stub().returns(Promise.reject());
266
-
267
- // return services.fetchClientRegionInfo().then((r) => {
268
- // assert.isUndefined(r);
269
- // });
270
- // });
271
-
272
- // it('successfully resolves with true if fetch request succeeds', () => {
273
- // webex.request = sinon.stub().returns(Promise.resolve({body: true}));
274
-
275
- // return services.fetchClientRegionInfo().then((r) => {
276
- // assert.equal(r, true);
277
- // assert.calledWith(webex.request, {
278
- // uri: 'https://test.ciscospark.com/v1/region',
279
- // addAuthHeader: false,
280
- // headers: {'spark-user-agent': null},
281
- // timeout: 5000,
282
- // });
283
- // });
284
- // });
285
- // });
286
-
287
- // describe('#getMeetingPreferences', () => {
288
- // it('Fetch login users information ', async () => {
289
- // const userPreferences = {userPreferences: 'userPreferences'};
290
-
291
- // webex.request = sinon.stub().returns(Promise.resolve({body: userPreferences}));
292
-
293
- // const res = await services.getMeetingPreferences();
294
-
295
- // assert.calledWith(webex.request, {
296
- // method: 'GET',
297
- // service: 'hydra',
298
- // resource: 'meetingPreferences',
299
- // });
300
- // assert.isDefined(res);
301
- // assert.equal(res, userPreferences);
302
- // });
303
-
304
- // it('Resolve getMeetingPreferences if the api request fails ', async () => {
305
- // webex.request = sinon.stub().returns(Promise.reject());
306
-
307
- // const res = await services.getMeetingPreferences();
308
-
309
- // assert.calledWith(webex.request, {
310
- // method: 'GET',
311
- // service: 'hydra',
312
- // resource: 'meetingPreferences',
313
- // });
314
- // assert.isUndefined(res);
315
- // });
316
- // });
46
+ services.initialize();
47
+
48
+ // call the onReady callback
49
+ services.listenToOnce.getCall(1).args[2]();
50
+ await waitForAsync();
51
+
52
+ assert.isFalse(services.initFailed);
53
+ });
54
+
55
+ it('initFailed is false when initialization succeeds no credentials are available', async () => {
56
+ services.listenToOnce = sinon.stub();
57
+ services.collectPreauthCatalog = sinon.stub().returns(Promise.resolve());
58
+
59
+ services.initialize();
60
+
61
+ // call the onReady callback
62
+ services.listenToOnce.getCall(1).args[2]();
63
+ await waitForAsync();
64
+
65
+ assert.isFalse(services.initFailed);
66
+ });
67
+
68
+ it.each([
69
+ {error: new Error('failed'), expectedMessage: 'failed'},
70
+ {error: undefined, expectedMessage: undefined},
71
+ ])(
72
+ 'sets initFailed to true when collectPreauthCatalog errors',
73
+ async ({error, expectedMessage}) => {
74
+ services.collectPreauthCatalog = sinon.stub().callsFake(() => {
75
+ return Promise.reject(error);
76
+ });
77
+
78
+ services.listenToOnce = sinon.stub();
79
+ services.logger.error = sinon.stub();
80
+
81
+ services.initialize();
82
+
83
+ // call the onReady callback
84
+ services.listenToOnce.getCall(1).args[2]();
85
+
86
+ await waitForAsync();
87
+
88
+ assert.isTrue(services.initFailed);
89
+ sinon.assert.calledWith(
90
+ services.logger.error,
91
+ `services: failed to init initial services when no credentials available, ${expectedMessage}`
92
+ );
93
+ }
94
+ );
95
+
96
+ it.each([
97
+ {error: new Error('failed'), expectedMessage: 'failed'},
98
+ {error: undefined, expectedMessage: undefined},
99
+ ])(
100
+ 'sets initFailed to true when initServiceCatalogs errors',
101
+ async ({error, expectedMessage}) => {
102
+ services.initServiceCatalogs = sinon.stub().callsFake(() => {
103
+ return Promise.reject(error);
104
+ });
105
+ services.webex.credentials = {
106
+ supertoken: {
107
+ access_token: 'token',
108
+ },
109
+ };
110
+
111
+ services.listenToOnce = sinon.stub();
112
+ services.logger.error = sinon.stub();
113
+
114
+ services.initialize();
115
+
116
+ // call the onReady callback
117
+ services.listenToOnce.getCall(1).args[2]();
118
+
119
+ await waitForAsync();
120
+
121
+ assert.isTrue(services.initFailed);
122
+ sinon.assert.calledWith(
123
+ services.logger.error,
124
+ `services: failed to init initial services when credentials available, ${expectedMessage}`
125
+ );
126
+ }
127
+ );
128
+ });
129
+
130
+ describe('#initServiceCatalogs', () => {
131
+ it('does not set initFailed to true when updateServices succeeds', async () => {
132
+ services.webex.credentials = {
133
+ getOrgId: sinon.stub().returns('orgId'),
134
+ canAuthorize: true,
135
+ };
136
+
137
+ services.collectPreauthCatalog = sinon.stub().callsFake(() => {
138
+ return Promise.resolve();
139
+ });
140
+
141
+ services.updateServices = sinon.stub().callsFake(() => {
142
+ return Promise.resolve();
143
+ });
144
+
145
+ services.logger.error = sinon.stub();
146
+
147
+ await services.initServiceCatalogs();
148
+
149
+ assert.isFalse(services.initFailed);
150
+
151
+ sinon.assert.calledWith(services.collectPreauthCatalog, {orgId: 'orgId'});
152
+ sinon.assert.notCalled(services.logger.warn);
153
+ });
154
+
155
+ it('sets initFailed to true when updateServices errors', async () => {
156
+ const error = new Error('failed');
157
+
158
+ services.webex.credentials = {
159
+ getOrgId: sinon.stub().returns('orgId'),
160
+ canAuthorize: true,
161
+ };
162
+
163
+ services.collectPreauthCatalog = sinon.stub().callsFake(() => {
164
+ return Promise.resolve();
165
+ });
166
+
167
+ services.updateServices = sinon.stub().callsFake(() => {
168
+ return Promise.reject(error);
169
+ });
170
+
171
+ services.logger.error = sinon.stub();
172
+
173
+ await services.initServiceCatalogs();
174
+
175
+ assert.isTrue(services.initFailed);
176
+
177
+ sinon.assert.calledWith(services.collectPreauthCatalog, {orgId: 'orgId'});
178
+ sinon.assert.calledWith(services.logger.warn, 'services: cannot retrieve postauth catalog');
179
+ });
180
+ });
181
+
182
+ describe('#namespace', () => {
183
+ it('is accurate to plugin name', () => {
184
+ assert.equal(services.namespace, 'Services');
185
+ });
186
+ });
187
+
188
+ describe('#_catalogs', () => {
189
+ it('is a weakmap', () => {
190
+ assert.typeOf(services._catalogs, 'weakmap');
191
+ });
192
+ });
193
+
194
+ describe('#validateDomains', () => {
195
+ it('is a boolean', () => {
196
+ assert.isBoolean(services.validateDomains);
197
+ });
198
+ });
199
+
200
+ describe('#initFailed', () => {
201
+ it('is a boolean', () => {
202
+ assert.isFalse(services.initFailed);
203
+ });
204
+ });
205
+
206
+ describe('#fetchClientRegionInfo', () => {
207
+ beforeEach(() => {
208
+ services.webex.config = {
209
+ services: {
210
+ discovery: {
211
+ sqdiscovery: 'https://test.ciscospark.com/v1/region',
212
+ },
213
+ },
214
+ };
215
+ });
216
+
217
+ it('successfully resolves with undefined if fetch request failed', () => {
218
+ webex.request = sinon.stub().returns(Promise.reject());
219
+
220
+ return services.fetchClientRegionInfo().then((r) => {
221
+ assert.isUndefined(r);
222
+ });
223
+ });
224
+
225
+ it('successfully resolves with true if fetch request succeeds', () => {
226
+ webex.request = sinon.stub().returns(Promise.resolve({body: true}));
227
+
228
+ return services.fetchClientRegionInfo().then((r) => {
229
+ assert.equal(r, true);
230
+ assert.calledWith(webex.request, {
231
+ uri: 'https://test.ciscospark.com/v1/region',
232
+ addAuthHeader: false,
233
+ headers: {'spark-user-agent': null},
234
+ timeout: 5000,
235
+ });
236
+ });
237
+ });
238
+ });
239
+
240
+ describe('#getMeetingPreferences', () => {
241
+ it('Fetch login users information ', async () => {
242
+ const userPreferences = {userPreferences: 'userPreferences'};
243
+
244
+ webex.request = sinon.stub().returns(Promise.resolve({body: userPreferences}));
245
+
246
+ const res = await services.getMeetingPreferences();
247
+
248
+ assert.calledWith(webex.request, {
249
+ method: 'GET',
250
+ service: 'hydra',
251
+ resource: 'meetingPreferences',
252
+ });
253
+ assert.isDefined(res);
254
+ assert.equal(res, userPreferences);
255
+ });
256
+
257
+ it('Resolve getMeetingPreferences if the api request fails ', async () => {
258
+ webex.request = sinon.stub().returns(Promise.reject());
259
+
260
+ const res = await services.getMeetingPreferences();
261
+
262
+ assert.calledWith(webex.request, {
263
+ method: 'GET',
264
+ service: 'hydra',
265
+ resource: 'meetingPreferences',
266
+ });
267
+ assert.isUndefined(res);
268
+ });
269
+ });
270
+
271
+ describe('#switchActiveClusterIds', () => {
272
+ let serviceHostmap;
273
+ let formattedHM;
274
+
275
+ beforeEach(() => {
276
+ serviceHostmap = serviceHostmapV2;
277
+ formattedHM = services._formatReceivedHostmap(serviceHostmap);
278
+
279
+ services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
280
+ services.webex.credentials = {
281
+ getOrgId: sinon.stub().returns('')
282
+ };
283
+ catalog.status = {};
284
+ });
285
+
286
+ it('switches properly when id exists', async () => {
287
+ services._updateActiveServices = sinon.stub().callsFake((data) => {
288
+ Object.assign(services._activeServices, data);
289
+ });
290
+
291
+ await services.switchActiveClusterIds({
292
+ conversation: 'urn:TEAM:me-central-1_d:conversation',
293
+ });
294
+
295
+ assert.notCalled(services.initServiceCatalogs);
296
+
297
+ assert.calledWith(services._updateActiveServices, {
298
+ conversation: 'urn:TEAM:me-central-1_d:conversation',
299
+ });
300
+
301
+ assert.equal(services._activeServices.conversation, 'urn:TEAM:me-central-1_d:conversation');
302
+ });
303
+
304
+ it('makes request to fetch when id does not exist', async () => {
305
+ services._updateActiveServices = sinon.stub().callsFake((data) => {
306
+ Object.assign(services._activeServices, data);
307
+ });
308
+
309
+ await services.switchActiveClusterIds({
310
+ conversation: 'urn:TEAM:me-central-1_asdf:conversation',
311
+ });
312
+
313
+ assert.calledOnce(services.initServiceCatalogs);
314
+ });
315
+ });
317
316
 
318
317
  describe('#updateCatalog', () => {
319
318
  it('updates the catalog', async () => {
320
319
  const serviceGroup = 'postauth';
321
- const hostmap = [{hostmap: 'hostmap'}];
320
+ const hostmap = {services: [{hostmap: 'hostmap'}]};
322
321
 
323
- services._formatReceivedHostmap = sinon.stub().returns([{some: 'hostmap'}]);
322
+ services._formatReceivedHostmap = sinon.stub().returns({services : [{some: 'hostmap'}]});
324
323
 
325
324
  catalog.updateServiceGroups = sinon.stub().returns(Promise.resolve([{some: 'value'}]));
326
325
 
@@ -334,118 +333,107 @@ describe('webex-core', () => {
334
333
  });
335
334
  });
336
335
 
337
- // describe('#_fetchNewServiceHostmap()', () => {
338
- // beforeEach(() => {
339
- // sinon.spy(webex.internal.newMetrics.callDiagnosticLatencies, 'measureLatency');
340
- // });
341
-
342
- // afterEach(() => {
343
- // sinon.restore();
344
- // });
345
-
346
- // it('checks service request resolves', async () => {
347
- // const mapResponse = 'map response';
348
-
349
- // sinon.stub(services, '_formatReceivedHostmap').resolves(mapResponse);
350
- // sinon.stub(services, 'request').resolves({});
351
-
352
- // const mapResult = await services._fetchNewServiceHostmap({from: 'limited'});
353
-
354
- // assert.deepEqual(mapResult, mapResponse);
355
-
356
- // assert.calledOnceWithExactly(services.request, {
357
- // method: 'GET',
358
- // service: 'u2c',
359
- // resource: '/limited/catalog',
360
- // qs: {format: 'hostmap'},
361
- // });
362
- // assert.calledOnceWithExactly(
363
- // webex.internal.newMetrics.callDiagnosticLatencies.measureLatency,
364
- // sinon.match.func,
365
- // 'internal.get.u2c.time'
366
- // );
367
- // });
368
-
369
- // it('checks service request rejects', async () => {
370
- // const error = new Error('some error');
371
-
372
- // sinon.spy(services, '_formatReceivedHostmap');
373
- // sinon.stub(services, 'request').rejects(error);
374
-
375
- // const promise = services._fetchNewServiceHostmap({from: 'limited'});
376
- // const rejectedValue = await assert.isRejected(promise);
377
-
378
- // assert.deepEqual(rejectedValue, error);
379
-
380
- // assert.notCalled(services._formatReceivedHostmap);
381
-
382
- // assert.calledOnceWithExactly(services.request, {
383
- // method: 'GET',
384
- // service: 'u2c',
385
- // resource: '/limited/catalog',
386
- // qs: {format: 'hostmap'},
387
- // });
388
- // assert.calledOnceWithExactly(
389
- // webex.internal.newMetrics.callDiagnosticLatencies.measureLatency,
390
- // sinon.match.func,
391
- // 'internal.get.u2c.time'
392
- // );
393
- // });
394
- // });
395
-
396
- // describe('replaceHostFromHostmap', () => {
397
- // it('returns the same uri if the hostmap is not set', () => {
398
- // services._hostCatalog = null;
399
-
400
- // const uri = 'http://example.com';
401
-
402
- // assert.equal(services.replaceHostFromHostmap(uri), uri);
403
- // });
404
-
405
- // it('returns the same uri if the hostmap does not contain the host', () => {
406
- // services._hostCatalog = {
407
- // 'not-example.com': [
408
- // {
409
- // host: 'example-1.com',
410
- // ttl: -1,
411
- // priority: 5,
412
- // id: '0:0:0:example',
413
- // },
414
- // ],
415
- // };
416
-
417
- // const uri = 'http://example.com';
418
-
419
- // assert.equal(services.replaceHostFromHostmap(uri), uri);
420
- // });
421
-
422
- // it('returns the original uri if the hostmap has no hosts for the host', () => {
423
- // services._hostCatalog = {
424
- // 'example.com': [],
425
- // };
426
-
427
- // const uri = 'http://example.com';
428
-
429
- // assert.equal(services.replaceHostFromHostmap(uri), uri);
430
- // });
431
-
432
- // it('returns the replaces the host in the uri with the host from the hostmap', () => {
433
- // services._hostCatalog = {
434
- // 'example.com': [
435
- // {
436
- // host: 'example-1.com',
437
- // ttl: -1,
438
- // priority: 5,
439
- // id: '0:0:0:example',
440
- // },
441
- // ],
442
- // };
443
-
444
- // const uri = 'http://example.com/somepath';
445
-
446
- // assert.equal(services.replaceHostFromHostmap(uri), 'http://example-1.com/somepath');
447
- // });
448
- // });
336
+ describe('#_fetchNewServiceHostmap()', () => {
337
+ beforeEach(() => {
338
+ sinon.spy(webex.internal.newMetrics.callDiagnosticLatencies, 'measureLatency');
339
+ });
340
+
341
+ afterEach(() => {
342
+ sinon.restore();
343
+ });
344
+
345
+ it('checks service request resolves', async () => {
346
+ const mapResponse = 'map response';
347
+
348
+ sinon.stub(services, '_formatReceivedHostmap').resolves(mapResponse);
349
+ sinon.stub(services, 'request').resolves({});
350
+
351
+ const mapResult = await services._fetchNewServiceHostmap({from: 'limited'});
352
+
353
+ assert.deepEqual(mapResult, mapResponse);
354
+
355
+ assert.calledOnceWithExactly(services.request, {
356
+ method: 'GET',
357
+ service: 'u2c',
358
+ resource: '/limited/catalog',
359
+ qs: {format: 'U2CV2'},
360
+ headers: {},
361
+ });
362
+ assert.calledOnceWithExactly(
363
+ webex.internal.newMetrics.callDiagnosticLatencies.measureLatency,
364
+ sinon.match.func,
365
+ 'internal.get.u2c.time'
366
+ );
367
+ });
368
+
369
+ it('checks service request rejects', async () => {
370
+ const error = new Error('some error');
371
+
372
+ sinon.spy(services, '_formatReceivedHostmap');
373
+ sinon.stub(services, 'request').rejects(error);
374
+
375
+ const promise = services._fetchNewServiceHostmap({from: 'limited'});
376
+ const rejectedValue = await assert.isRejected(promise);
377
+
378
+ assert.deepEqual(rejectedValue, error);
379
+
380
+ assert.notCalled(services._formatReceivedHostmap);
381
+
382
+ assert.calledOnceWithExactly(services.request, {
383
+ method: 'GET',
384
+ service: 'u2c',
385
+ resource: '/limited/catalog',
386
+ qs: {format: 'U2CV2'},
387
+ headers: {},
388
+ });
389
+ assert.calledOnceWithExactly(
390
+ webex.internal.newMetrics.callDiagnosticLatencies.measureLatency,
391
+ sinon.match.func,
392
+ 'internal.get.u2c.time'
393
+ );
394
+ });
395
+ });
396
+
397
+ describe('replaceHostFromHostmap', () => {
398
+ it('returns the same uri if the hostmap is not set', () => {
399
+ services._hostCatalog = null;
400
+
401
+ const uri = 'http://example.com';
402
+
403
+ assert.equal(services.replaceHostFromHostmap(uri), uri);
404
+ });
405
+
406
+ it('returns the same uri if the hostmap does not contain the host', () => {
407
+ catalog.updateServiceGroups('preauth', [
408
+ {
409
+ id: 'example-1',
410
+ serviceName: 'example-1',
411
+ serviceUrls: [{host: 'example-1.com', baseUrl: 'http://example-1.com', priority: 1}],
412
+ },
413
+ ]);
414
+
415
+ const uri = 'http://example.com';
416
+
417
+ assert.equal(services.replaceHostFromHostmap(uri), uri);
418
+ });
419
+
420
+ it('returns the replaces the host in the uri with the host from the hostmap', () => {
421
+ catalog.updateServiceGroups('preauth', [
422
+ {
423
+ id: 'example-1',
424
+ serviceName: 'example-1',
425
+ serviceUrls: [
426
+ {host: 'example-1.com', baseUrl: 'http://example-1.com', priority: 1},
427
+ {host: 'example.com', baseUrl: 'http://example.com', priority: 2},
428
+ ],
429
+ },
430
+ ]);
431
+
432
+ const uri = 'http://example.com/somepath';
433
+
434
+ assert.equal(services.replaceHostFromHostmap(uri), 'http://example-1.com/somepath');
435
+ });
436
+ });
449
437
 
450
438
  describe('#_formatReceivedHostmap()', () => {
451
439
  let serviceHostmap;
@@ -459,7 +447,7 @@ describe('webex-core', () => {
459
447
  formattedHM = services._formatReceivedHostmap(serviceHostmap);
460
448
 
461
449
  assert(
462
- serviceHostmap.services.length >= formattedHM.length,
450
+ serviceHostmap.services.length >= formattedHM.services.length,
463
451
  'length is not equal or less than'
464
452
  );
465
453
  });
@@ -467,7 +455,7 @@ describe('webex-core', () => {
467
455
  it('has all keys in host map hosts', () => {
468
456
  formattedHM = services._formatReceivedHostmap(serviceHostmap);
469
457
 
470
- formattedHM.forEach((service) => {
458
+ formattedHM.services.forEach((service) => {
471
459
  assert.hasAllKeys(
472
460
  service,
473
461
  ['id', 'serviceName', 'serviceUrls'],
@@ -486,7 +474,7 @@ describe('webex-core', () => {
486
474
  it('creates a formmated host map containing all received host map service entries', () => {
487
475
  formattedHM = services._formatReceivedHostmap(serviceHostmap);
488
476
 
489
- formattedHM.forEach((service) => {
477
+ formattedHM.services.forEach((service) => {
490
478
  const foundServiceKey = Object.keys(serviceHostmap.activeServices).find(
491
479
  (key) => service.serviceName === key
492
480
  );
@@ -498,7 +486,7 @@ describe('webex-core', () => {
498
486
  it('creates the expected formatted host map', () => {
499
487
  formattedHM = services._formatReceivedHostmap(serviceHostmap);
500
488
 
501
- assert.deepEqual(formattedHM, formattedServiceHostmapV2);
489
+ assert.deepEqual(formattedHM.services, formattedServiceHostmapV2);
502
490
  });
503
491
 
504
492
  it('has hostCatalog updated', () => {
@@ -515,48 +503,197 @@ describe('webex-core', () => {
515
503
  });
516
504
  });
517
505
 
518
- // describe('#updateCredentialsConfig()', () => {
519
- // // updateCredentialsConfig must remove `/` if exist. so expected serviceList must be.
520
- // const expectedServiceList = {
521
- // idbroker: 'https://idbroker.webex.com',
522
- // identity: 'https://identity.webex.com',
523
- // };
524
-
525
- // beforeEach(async () => {
526
- // const servicesList = {
527
- // idbroker: 'https://idbroker.webex.com',
528
- // identity: 'https://identity.webex.com/',
529
- // };
530
-
531
- // catalog.list = sinon.stub().returns(servicesList);
532
- // await services.updateCredentialsConfig();
533
- // });
534
-
535
- // it('sets the idbroker url properly when trailing slash is not present', () => {
536
- // assert.equal(webex.config.credentials.idbroker.url, expectedServiceList.idbroker);
537
- // });
538
-
539
- // it('sets the identity url properly when a trailing slash is present', () => {
540
- // assert.equal(webex.config.credentials.identity.url, expectedServiceList.identity);
541
- // });
542
-
543
- // it('sets the authorize url properly when authorization string is not provided', () => {
544
- // assert.equal(
545
- // webex.config.credentials.authorizeUrl,
546
- // `${expectedServiceList.idbroker}/idb/oauth2/v1/authorize`
547
- // );
548
- // });
549
-
550
- // it('should retain the authorize url property when authorization string is provided', () => {
551
- // const authUrl = 'http://example-auth-url.com/resource';
552
-
553
- // webex.config.credentials.authorizationString = authUrl;
554
- // webex.config.credentials.authorizeUrl = authUrl;
555
-
556
- // services.updateCredentialsConfig();
557
-
558
- // assert.equal(webex.config.credentials.authorizeUrl, authUrl);
559
- // });
560
- // });
506
+ describe('#updateCredentialsConfig()', () => {
507
+ // updateCredentialsConfig must remove `/` if exist. so expected serviceList must be.
508
+ const expectedServiceList = {
509
+ idbroker: 'https://idbroker.webex.com',
510
+ identity: 'https://identity.webex.com',
511
+ };
512
+
513
+ beforeEach(async () => {
514
+ const servicesList = [
515
+ {
516
+ id: 'idbroker',
517
+ name: 'idbroker',
518
+ serviceUrls: [
519
+ {baseUrl: 'https://idbroker.webex.com/', host: 'idbroker.webex.com', priority: 1},
520
+ ],
521
+ },
522
+ {
523
+ id: 'identity',
524
+ name: 'identity',
525
+ serviceUrls: [
526
+ {baseUrl: 'https://identity.webex.com/', host: 'identity.webex.com', priority: 1},
527
+ ],
528
+ },
529
+ ];
530
+
531
+ catalog.updateServiceGroups('preauth', servicesList);
532
+ await services.updateCredentialsConfig();
533
+ });
534
+
535
+ it('sets the idbroker url properly when trailing slash is not present', () => {
536
+ assert.equal(webex.config.credentials.idbroker.url, expectedServiceList.idbroker);
537
+ });
538
+
539
+ it('sets the identity url properly when a trailing slash is present', () => {
540
+ assert.equal(webex.config.credentials.identity.url, expectedServiceList.identity);
541
+ });
542
+
543
+ it('sets the authorize url properly when authorization string is not provided', () => {
544
+ assert.equal(
545
+ webex.config.credentials.authorizeUrl,
546
+ `${expectedServiceList.idbroker}/idb/oauth2/v1/authorize`
547
+ );
548
+ });
549
+
550
+ it('should retain the authorize url property when authorization string is provided', () => {
551
+ const authUrl = 'http://example-auth-url.com/resource';
552
+
553
+ webex.config.credentials.authorizationString = authUrl;
554
+ webex.config.credentials.authorizeUrl = authUrl;
555
+
556
+ services.updateCredentialsConfig();
557
+
558
+ assert.equal(webex.config.credentials.authorizeUrl, authUrl);
559
+ });
560
+ });
561
+
562
+ describe('#invalidateCache', () => {
563
+ beforeEach( () => {
564
+ services.initServiceCatalogs = sinon.stub().returns(Promise.resolve());
565
+ services.webex.credentials = {
566
+ getOrgId: sinon.stub().returns('')
567
+ };
568
+ catalog.status = {};
569
+ })
570
+ it('should log the timestamp parameter', async () => {
571
+ const timestamp = '1234567890';
572
+ services.logger.info = sinon.stub();
573
+ services._getCatalog = sinon.stub().returns({timestamp: '1234567880'});
574
+
575
+ await services.invalidateCache(timestamp);
576
+
577
+ assert.calledWith(services.logger.info, 'services: invalidate cache, timestamp:', timestamp);
578
+ });
579
+
580
+ it('should call initServiceCatalogs when invalidate timestamp is newer than catalog timestamp', async () => {
581
+ const newTimestamp = '1234567890';
582
+ const oldTimestamp = '1234567880';
583
+ services.logger.info = sinon.stub();
584
+ services._getCatalog = sinon.stub().returns({timestamp: oldTimestamp});
585
+
586
+ await services.invalidateCache(newTimestamp);
587
+
588
+ assert.calledOnce(services.initServiceCatalogs);
589
+ assert.calledWith(services.logger.info, 'services: invalidateCache, refresh services');
590
+ });
591
+
592
+ it('should not call initServiceCatalogs when invalidate timestamp is older than catalog timestamp', async () => {
593
+ const oldTimestamp = '1234567880';
594
+ const newTimestamp = '1234567890';
595
+ services._getCatalog = sinon.stub().returns({timestamp: newTimestamp});
596
+ await services.invalidateCache(oldTimestamp);
597
+
598
+ assert.notCalled(services.initServiceCatalogs);
599
+ });
600
+
601
+ it('should not call initServiceCatalogs when invalidate timestamp equals catalog timestamp', async () => {
602
+ const timestamp = '1234567890';
603
+ services._getCatalog = sinon.stub().returns({timestamp: timestamp});
604
+
605
+ await services.invalidateCache(timestamp);
606
+
607
+ assert.notCalled(services.initServiceCatalogs);
608
+ });
609
+
610
+ it('should handle numeric timestamp strings correctly', async () => {
611
+ const newTimestamp = '1700000000';
612
+ const oldTimestamp = '1600000000';
613
+ services._getCatalog = sinon.stub().returns({timestamp: oldTimestamp});
614
+
615
+ await services.invalidateCache(newTimestamp);
616
+
617
+ assert.calledOnce(services.initServiceCatalogs);
618
+ });
619
+
620
+ it('should handle undefined catalog gracefully', async () => {
621
+ const timestamp = '1234567890';
622
+ services._getCatalog = sinon.stub().returns(undefined);
623
+
624
+ await services.invalidateCache(timestamp);
625
+
626
+ assert.calledOnce(services.initServiceCatalogs);
627
+ });
628
+
629
+ it('should handle catalog without timestamp gracefully', async () => {
630
+ const timestamp = '1234567890';
631
+ services._getCatalog = sinon.stub().returns({});
632
+
633
+ await services.invalidateCache(timestamp);
634
+
635
+ assert.calledOnce(services.initServiceCatalogs);
636
+ });
637
+
638
+ it('should handle null catalog timestamp gracefully', async () => {
639
+ const timestamp = '1234567890';
640
+ services._getCatalog = sinon.stub().returns({timestamp: null});
641
+
642
+ await services.invalidateCache(timestamp);
643
+
644
+ assert.calledOnce(services.initServiceCatalogs);
645
+ });
646
+
647
+ it('should handle undefined timestamp parameter gracefully', async () => {
648
+ services._getCatalog = sinon.stub().returns({timestamp: '1234567890'});
649
+
650
+ await services.invalidateCache(undefined);
651
+
652
+ assert.notCalled(services.initServiceCatalogs);
653
+ });
654
+
655
+ it('should handle null timestamp parameter gracefully', async () => {
656
+ services._getCatalog = sinon.stub().returns({timestamp: '1234567890'});
657
+
658
+ await services.invalidateCache(null);
659
+
660
+ assert.notCalled(services.initServiceCatalogs);
661
+ });
662
+
663
+ it('should handle empty string timestamp parameter gracefully', async () => {
664
+ services._getCatalog = sinon.stub().returns({timestamp: '1234567890'});
665
+
666
+ await services.invalidateCache('');
667
+
668
+ assert.notCalled(services.initServiceCatalogs);
669
+ });
670
+
671
+ it('should handle non-numeric timestamp strings gracefully', async () => {
672
+ const invalidTimestamp = 'not-a-number';
673
+ services._getCatalog = sinon.stub().returns({timestamp: '1234567890'});
674
+
675
+ await services.invalidateCache(invalidTimestamp);
676
+
677
+ assert.notCalled(services.initServiceCatalogs);
678
+ });
679
+
680
+ it('should handle non-numeric catalog timestamp gracefully', async () => {
681
+ const timestamp = '1234567890';
682
+ services._getCatalog = sinon.stub().returns({timestamp: 'not-a-number'});
683
+
684
+ await services.invalidateCache(timestamp);
685
+
686
+ assert.calledOnce(services.initServiceCatalogs);
687
+ });
688
+
689
+ it('should return a resolved Promise', async () => {
690
+ const timestamp = '1234567890';
691
+ services._getCatalog = sinon.stub().returns({timestamp: '1234567880'});
692
+
693
+ const result = await services.invalidateCache(timestamp);
694
+
695
+ assert.isUndefined(result);
696
+ });
697
+ });
561
698
  });
562
699
  });