@webex/internal-plugin-metrics 3.12.0-auth-prejoin-fetch.1 → 3.12.0-llmrefactor.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.
@@ -841,6 +841,60 @@ describe('internal-plugin-metrics', () => {
841
841
  userId: 'preLoginId',
842
842
  });
843
843
  });
844
+
845
+ it('should use the userId from credentials when no device userId is available', () => {
846
+ cd.device.userId = undefined;
847
+ webex.credentials.getUserId = sinon.stub().returns('credentials-user-id');
848
+
849
+ const res = cd.getIdentifiers({
850
+ correlationId: 'correlationId',
851
+ });
852
+
853
+ assert.deepEqual(res, {
854
+ correlationId: 'correlationId',
855
+ locusUrl: 'locus-url',
856
+ deviceId: 'deviceUrl',
857
+ orgId: 'orgId',
858
+ userId: 'credentials-user-id',
859
+ });
860
+ });
861
+
862
+ it('should prefer the device userId over the credentials userId', () => {
863
+ webex.credentials.getUserId = sinon.stub().returns('credentials-user-id');
864
+
865
+ const res = cd.getIdentifiers({
866
+ correlationId: 'correlationId',
867
+ });
868
+
869
+ assert.equal(res.userId, 'userId');
870
+ assert.notCalled(webex.credentials.getUserId);
871
+ });
872
+
873
+ it('should fall back to preLoginId when neither the device nor credentials provide a userId', () => {
874
+ cd.device.userId = undefined;
875
+ webex.credentials.getUserId = sinon.stub().throws(new Error('no user token available'));
876
+
877
+ const res = cd.getIdentifiers({
878
+ correlationId: 'correlationId',
879
+ preLoginId: 'preLoginId',
880
+ });
881
+
882
+ assert.equal(res.userId, 'preLoginId');
883
+ });
884
+ });
885
+
886
+ describe('#getUserIdFromCredentials', () => {
887
+ it('should return the userId from credentials', () => {
888
+ webex.credentials.getUserId = sinon.stub().returns('credentials-user-id');
889
+
890
+ assert.equal(cd.getUserIdFromCredentials(), 'credentials-user-id');
891
+ });
892
+
893
+ it('should return undefined when credentials cannot provide a userId', () => {
894
+ webex.credentials.getUserId = sinon.stub().throws(new Error('no user token available'));
895
+
896
+ assert.isUndefined(cd.getUserIdFromCredentials());
897
+ });
844
898
  });
845
899
 
846
900
  it('should prepare diagnostic event successfully', () => {
@@ -3730,7 +3784,7 @@ describe('internal-plugin-metrics', () => {
3730
3784
  serviceErrorCode: undefined,
3731
3785
  errorCode: 1026,
3732
3786
  rawErrorMessage: '{}\nundefined https://example.com\nWEBEX_TRACKING_ID: undefined\n',
3733
- httpStatusCode: 0,
3787
+ httpCode: 0,
3734
3788
  });
3735
3789
  });
3736
3790
 
@@ -3847,7 +3901,7 @@ describe('internal-plugin-metrics', () => {
3847
3901
  serviceErrorCode: undefined,
3848
3902
  errorCode: 1026,
3849
3903
  rawErrorMessage: '{}\nundefined https://example.com\nWEBEX_TRACKING_ID: undefined\n',
3850
- httpStatusCode: 0,
3904
+ httpCode: 0,
3851
3905
  });
3852
3906
  });
3853
3907
 
@@ -3869,7 +3923,7 @@ describe('internal-plugin-metrics', () => {
3869
3923
  serviceErrorCode: undefined,
3870
3924
  errorCode: 1010,
3871
3925
  rawErrorMessage: '{}\nundefined https://example.com\nWEBEX_TRACKING_ID: undefined\n',
3872
- httpStatusCode: 0,
3926
+ httpCode: 0,
3873
3927
  });
3874
3928
  });
3875
3929
 
@@ -3895,7 +3949,7 @@ describe('internal-plugin-metrics', () => {
3895
3949
  serviceErrorCode: undefined,
3896
3950
  errorCode: 1010,
3897
3951
  rawErrorMessage: '{}\nundefined https://example.com\nWEBEX_TRACKING_ID: undefined\n',
3898
- httpStatusCode: 0,
3952
+ httpCode: 0,
3899
3953
  });
3900
3954
  });
3901
3955
 
@@ -4000,8 +4054,144 @@ describe('internal-plugin-metrics', () => {
4000
4054
  });
4001
4055
  });
4002
4056
 
4003
- describe('httpStatusCode', () => {
4004
- it('should include httpStatusCode for browser media errors', () => {
4057
+ describe('httpCode', () => {
4058
+ it('should identify an outgoing Locus 429', () => {
4059
+ const rawErrorMessage =
4060
+ 'partition 371 on PARALLEL_EXECUTOR_PARTITION_KEY_QUEUE_FULL state';
4061
+ const [res] = cd.generateClientEventErrorPayload({
4062
+ message: rawErrorMessage,
4063
+ statusCode: 429,
4064
+ options: {uri: 'https://locus.example.com/locus/api/v1/loci/call'},
4065
+ });
4066
+
4067
+ assert.deepEqual(res, {
4068
+ category: 'signaling',
4069
+ errorCode: 1002,
4070
+ errorDescription: 'LocusRateLimitedOutgoing',
4071
+ fatal: true,
4072
+ name: 'locus.response',
4073
+ rawErrorMessage,
4074
+ serviceErrorCode: undefined,
4075
+ shownToUser: false,
4076
+ httpCode: 429,
4077
+ });
4078
+ });
4079
+
4080
+ [
4081
+ ['media', 'https://example.com/locus/api/v1/loci/123/media'],
4082
+ ['hashtree', 'https://example.com/locus/api/v1/loci/123/hashtree'],
4083
+ ['host-only Locus URL', 'https://locus.example.com/api/v1/resource'],
4084
+ ['query-only Locus URL', 'https://example.com/api/v1/resource?service=locus'],
4085
+ ].forEach(([operation, uri]) => {
4086
+ it(`should identify a Locus 429 for ${operation}`, () => {
4087
+ const [res] = cd.generateClientEventErrorPayload({
4088
+ message: 'Locus rate limited',
4089
+ statusCode: 429,
4090
+ options: {uri},
4091
+ });
4092
+
4093
+ assert.equal(res.errorCode, 1002);
4094
+ assert.equal(res.errorDescription, 'LocusRateLimitedOutgoing');
4095
+ assert.equal(res.httpCode, 429);
4096
+ });
4097
+ });
4098
+
4099
+ it('should identify an unavailable Locus 503', () => {
4100
+ const rawErrorMessage = 'Service unavailable';
4101
+ const [res] = cd.generateClientEventErrorPayload({
4102
+ message: rawErrorMessage,
4103
+ statusCode: 503,
4104
+ options: {uri: 'https://locus.example.com/locus/api/v1/loci/call'},
4105
+ });
4106
+
4107
+ assert.deepEqual(res, {
4108
+ category: 'signaling',
4109
+ errorCode: 1003,
4110
+ errorDescription: 'LocusUnavailable',
4111
+ fatal: true,
4112
+ name: 'locus.response',
4113
+ rawErrorMessage,
4114
+ serviceErrorCode: undefined,
4115
+ shownToUser: false,
4116
+ httpCode: 503,
4117
+ });
4118
+ });
4119
+
4120
+ it('should prefer a specific service error mapping over the Locus HTTP status', () => {
4121
+ const rawErrorMessage = 'Fraud detected';
4122
+ const [res] = cd.generateClientEventErrorPayload({
4123
+ body: {errorCode: 2423012},
4124
+ message: rawErrorMessage,
4125
+ statusCode: 429,
4126
+ options: {uri: 'https://locus.example.com/locus/api/v1/loci/call'},
4127
+ });
4128
+
4129
+ assert.deepEqual(res, {
4130
+ category: 'expected',
4131
+ errorCode: 12000,
4132
+ errorDescription: 'FraudDetection',
4133
+ fatal: true,
4134
+ name: 'locus.response',
4135
+ rawErrorMessage,
4136
+ serviceErrorCode: 2423012,
4137
+ shownToUser: true,
4138
+ httpCode: 429,
4139
+ });
4140
+ });
4141
+
4142
+ it('should retain an unmapped service error code on the Locus HTTP status payload', () => {
4143
+ const rawErrorMessage = 'Locus rate limited';
4144
+ const [res] = cd.generateClientEventErrorPayload({
4145
+ body: {errorCode: 2429999},
4146
+ message: rawErrorMessage,
4147
+ statusCode: 429,
4148
+ options: {uri: 'https://locus.example.com/locus/api/v1/loci/call'},
4149
+ });
4150
+
4151
+ assert.deepEqual(res, {
4152
+ category: 'signaling',
4153
+ errorCode: 1002,
4154
+ errorDescription: 'LocusRateLimitedOutgoing',
4155
+ fatal: true,
4156
+ name: 'locus.response',
4157
+ rawErrorMessage,
4158
+ serviceErrorCode: 2429999,
4159
+ shownToUser: false,
4160
+ httpCode: 429,
4161
+ });
4162
+ });
4163
+
4164
+ it('should not identify a non-Locus 429 as a Locus rate limit', () => {
4165
+ const [res] = cd.generateClientEventErrorPayload({
4166
+ message: 'Too many requests',
4167
+ statusCode: 429,
4168
+ options: {uri: 'https://example.com/api/v1/resource'},
4169
+ });
4170
+
4171
+ assert.deepEqual(res, {
4172
+ category: 'other',
4173
+ errorCode: 9999,
4174
+ errorDescription: 'UnknownError',
4175
+ fatal: true,
4176
+ name: 'other',
4177
+ rawErrorMessage: 'Too many requests',
4178
+ serviceErrorCode: 9999,
4179
+ shownToUser: false,
4180
+ httpCode: 429,
4181
+ });
4182
+ });
4183
+
4184
+ it('should keep the fallback classification when a 429 has no request URL', () => {
4185
+ const [res] = cd.generateClientEventErrorPayload({
4186
+ message: 'Too many requests',
4187
+ statusCode: 429,
4188
+ });
4189
+
4190
+ assert.equal(res.errorCode, 9999);
4191
+ assert.equal(res.httpCode, 429);
4192
+ });
4193
+
4194
+ it('should include httpCode for browser media errors', () => {
4005
4195
  const [res, cached] = cd.generateClientEventErrorPayload({
4006
4196
  name: 'PermissionDeniedError',
4007
4197
  message: 'bad times',
@@ -4019,11 +4209,11 @@ describe('internal-plugin-metrics', () => {
4019
4209
  rawErrorMessage: 'bad times',
4020
4210
  serviceErrorCode: undefined,
4021
4211
  shownToUser: false,
4022
- httpStatusCode: 401,
4212
+ httpCode: 401,
4023
4213
  });
4024
4214
  });
4025
4215
 
4026
- it('should include httpStatusCode for SdpOfferCreationErrors', () => {
4216
+ it('should include httpCode for SdpOfferCreationErrors', () => {
4027
4217
  const [res, cached] = cd.generateClientEventErrorPayload({
4028
4218
  name: 'SdpOfferCreationError',
4029
4219
  message: 'bad times',
@@ -4041,11 +4231,11 @@ describe('internal-plugin-metrics', () => {
4041
4231
  rawErrorMessage: 'bad times',
4042
4232
  serviceErrorCode: undefined,
4043
4233
  shownToUser: true,
4044
- httpStatusCode: 404,
4234
+ httpCode: 404,
4045
4235
  });
4046
4236
  });
4047
4237
 
4048
- it('should include httpStatusCode for service error codes', () => {
4238
+ it('should include httpCode for service error codes', () => {
4049
4239
  const [res, cached] = cd.generateClientEventErrorPayload({
4050
4240
  body: {errorCode: 58400},
4051
4241
  message: 'bad times',
@@ -4060,11 +4250,11 @@ describe('internal-plugin-metrics', () => {
4060
4250
  rawErrorMessage: 'bad times',
4061
4251
  serviceErrorCode: 58400,
4062
4252
  shownToUser: false,
4063
- httpStatusCode: 400,
4253
+ httpCode: 400,
4064
4254
  });
4065
4255
  });
4066
4256
 
4067
- it('should include httpStatusCode for locus service error codes', () => {
4257
+ it('should include httpCode for locus service error codes', () => {
4068
4258
  const [res, cached] = cd.generateClientEventErrorPayload({
4069
4259
  body: {errorCode: 2403001},
4070
4260
  message: 'bad times',
@@ -4079,11 +4269,11 @@ describe('internal-plugin-metrics', () => {
4079
4269
  rawErrorMessage: 'bad times',
4080
4270
  serviceErrorCode: 2403001,
4081
4271
  shownToUser: false,
4082
- httpStatusCode: 400,
4272
+ httpCode: 400,
4083
4273
  });
4084
4274
  });
4085
4275
 
4086
- it('should include httpStatusCode for meetingInfo service error codes', () => {
4276
+ it('should include httpCode for meetingInfo service error codes', () => {
4087
4277
  const [res, cached] = cd.generateClientEventErrorPayload({
4088
4278
  body: {data: {meetingInfo: {}}},
4089
4279
  message: 'bad times',
@@ -4098,11 +4288,11 @@ describe('internal-plugin-metrics', () => {
4098
4288
  rawErrorMessage: 'bad times',
4099
4289
  serviceErrorCode: undefined,
4100
4290
  shownToUser: false,
4101
- httpStatusCode: 400,
4291
+ httpCode: 400,
4102
4292
  });
4103
4293
  });
4104
4294
 
4105
- it('should include httpStatusCode for network errors', () => {
4295
+ it('should include httpCode for network errors', () => {
4106
4296
  const error = new WebexHttpError.NetworkOrCORSError({
4107
4297
  statusCode: 400,
4108
4298
  options: {service: '', headers: {}},
@@ -4117,11 +4307,11 @@ describe('internal-plugin-metrics', () => {
4117
4307
  rawErrorMessage: 'undefined\nundefined /undefined\nWEBEX_TRACKING_ID: undefined\n',
4118
4308
  serviceErrorCode: undefined,
4119
4309
  shownToUser: false,
4120
- httpStatusCode: 400,
4310
+ httpCode: 400,
4121
4311
  });
4122
4312
  });
4123
4313
 
4124
- it('should include httpStatusCode for unauthorized errors', () => {
4314
+ it('should include httpCode for unauthorized errors', () => {
4125
4315
  const error = new WebexHttpError.Unauthorized({
4126
4316
  statusCode: 401,
4127
4317
  options: {service: '', headers: {}},
@@ -4136,11 +4326,11 @@ describe('internal-plugin-metrics', () => {
4136
4326
  rawErrorMessage: 'undefined\nundefined /undefined\nWEBEX_TRACKING_ID: undefined\n',
4137
4327
  serviceErrorCode: undefined,
4138
4328
  shownToUser: false,
4139
- httpStatusCode: 401,
4329
+ httpCode: 401,
4140
4330
  });
4141
4331
  });
4142
4332
 
4143
- it('should include httpStatusCode for unknown errors', () => {
4333
+ it('should include httpCode for unknown errors', () => {
4144
4334
  const [res, cached] = cd.generateClientEventErrorPayload({
4145
4335
  message: 'bad times',
4146
4336
  statusCode: 404,
@@ -4154,7 +4344,7 @@ describe('internal-plugin-metrics', () => {
4154
4344
  serviceErrorCode: 9999,
4155
4345
  errorDescription: 'UnknownError',
4156
4346
  rawErrorMessage: 'bad times',
4157
- httpStatusCode: 404,
4347
+ httpCode: 404,
4158
4348
  });
4159
4349
  });
4160
4350
  });
@@ -13,7 +13,7 @@ import {BrowserDetection} from '@webex/common';
13
13
  const {getOSVersion} = BrowserDetection();
14
14
 
15
15
  //@ts-ignore
16
- global.window = {location: {hostname: 'whatever'}};
16
+ global.window = {location: {hostname: 'whatever', origin: 'https://whatever'}};
17
17
  function promiseTick(count) {
18
18
  let promise = Promise.resolve();
19
19
 
@@ -30,6 +30,7 @@ describe('plugin-metrics', () => {
30
30
  let webex;
31
31
  let metrics;
32
32
  let clock;
33
+ let previousWindow;
33
34
 
34
35
  const eventName = 'test_event';
35
36
  const mockPayload = {
@@ -67,10 +68,15 @@ describe('plugin-metrics', () => {
67
68
 
68
69
  beforeEach(() => {
69
70
  clock = FakeTimers.install({now: 0});
71
+ previousWindow = global.window;
72
+ // Other test files also replace the shared browser mock. Restore the
73
+ // location needed to verify that app_url prefers the browser origin.
74
+ global.window = {location: {hostname: 'whatever', origin: 'https://whatever'}};
70
75
  });
71
76
 
72
77
  afterEach(() => {
73
78
  clock.uninstall();
79
+ global.window = previousWindow;
74
80
  });
75
81
 
76
82
  beforeEach(() => {
@@ -107,10 +113,10 @@ describe('plugin-metrics', () => {
107
113
  webex.meetings = {
108
114
  config: {
109
115
  metrics: {
110
- clientVersion: '43.0.105'
111
- }
112
- }
113
- }
116
+ clientVersion: '43.0.105',
117
+ },
118
+ },
119
+ };
114
120
 
115
121
  sinon.spy(webex, 'request');
116
122
  sinon.spy(metrics, 'aliasUser');
@@ -186,6 +192,9 @@ describe('plugin-metrics', () => {
186
192
  },
187
193
  metricName: 'test',
188
194
  tags: {
195
+ app_name: 'appName',
196
+ app_url: 'https://whatever',
197
+ app_version: 'appVersion',
189
198
  appVersion: '43.0.105',
190
199
  browser: '',
191
200
  domain: 'whatever',