@webex/internal-plugin-metrics 3.0.0-beta.22 → 3.0.0-beta.220

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 (67) hide show
  1. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js +56 -0
  2. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js.map +1 -0
  3. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +451 -0
  4. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -0
  5. package/dist/call-diagnostic/call-diagnostic-metrics.js +645 -0
  6. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -0
  7. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +276 -0
  8. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -0
  9. package/dist/call-diagnostic/config.js +580 -0
  10. package/dist/call-diagnostic/config.js.map +1 -0
  11. package/dist/call-diagnostic/generated-types-temp/ClientEvent.js +7 -0
  12. package/dist/call-diagnostic/generated-types-temp/ClientEvent.js.map +1 -0
  13. package/dist/call-diagnostic/generated-types-temp/Event.js +7 -0
  14. package/dist/call-diagnostic/generated-types-temp/Event.js.map +1 -0
  15. package/dist/call-diagnostic/generated-types-temp/MediaQualityEvent.js +7 -0
  16. package/dist/call-diagnostic/generated-types-temp/MediaQualityEvent.js.map +1 -0
  17. package/dist/config.js +20 -1
  18. package/dist/config.js.map +1 -1
  19. package/dist/index.js +25 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/metrics.js +30 -30
  22. package/dist/metrics.js.map +1 -1
  23. package/dist/metrics.types.js +7 -0
  24. package/dist/metrics.types.js.map +1 -0
  25. package/dist/new-metrics.js +249 -0
  26. package/dist/new-metrics.js.map +1 -0
  27. package/dist/types/batcher.d.ts +2 -0
  28. package/dist/types/call-diagnostic/call-diagnostic-metrics-batcher.d.ts +2 -0
  29. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +189 -0
  30. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +356 -0
  31. package/dist/types/call-diagnostic/call-diagnostic-metrics.util.d.ts +73 -0
  32. package/dist/types/call-diagnostic/config.d.ts +86 -0
  33. package/dist/types/call-diagnostic/generated-types-temp/ClientEvent.d.ts +1112 -0
  34. package/dist/types/call-diagnostic/generated-types-temp/Event.d.ts +4851 -0
  35. package/dist/types/call-diagnostic/generated-types-temp/MediaQualityEvent.d.ts +2121 -0
  36. package/dist/types/client-metrics-batcher.d.ts +2 -0
  37. package/dist/types/config.d.ts +35 -0
  38. package/dist/types/index.d.ts +11 -0
  39. package/dist/types/metrics.d.ts +3 -0
  40. package/dist/types/metrics.types.d.ts +95 -0
  41. package/dist/types/new-metrics.d.ts +119 -0
  42. package/package.json +12 -8
  43. package/src/call-diagnostic/call-diagnostic-metrics-batcher.ts +51 -0
  44. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +408 -0
  45. package/src/call-diagnostic/call-diagnostic-metrics.ts +655 -0
  46. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +280 -0
  47. package/src/call-diagnostic/config.ts +578 -0
  48. package/src/call-diagnostic/generated-types-temp/ClientEvent.ts +2395 -0
  49. package/src/call-diagnostic/generated-types-temp/Event.ts +7762 -0
  50. package/src/call-diagnostic/generated-types-temp/MediaQualityEvent.ts +2321 -0
  51. package/src/config.js +19 -0
  52. package/src/index.ts +41 -0
  53. package/src/metrics.js +25 -27
  54. package/src/metrics.types.ts +140 -0
  55. package/src/new-metrics.ts +223 -0
  56. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +243 -0
  57. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +474 -0
  58. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +1015 -0
  59. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +454 -0
  60. package/test/unit/spec/metrics.js +65 -97
  61. package/test/unit/spec/new-metrics.ts +155 -0
  62. package/tsconfig.json +6 -0
  63. package/dist/call-diagnostic-events-batcher.js +0 -60
  64. package/dist/call-diagnostic-events-batcher.js.map +0 -1
  65. package/src/call-diagnostic-events-batcher.js +0 -62
  66. package/src/index.js +0 -17
  67. package/test/unit/spec/call-diagnostic-events-batcher.js +0 -195
@@ -0,0 +1,655 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ /* eslint-disable class-methods-use-this */
3
+ /* eslint-disable valid-jsdoc */
4
+ import {getOSNameInternal} from '@webex/internal-plugin-metrics';
5
+ import {BrowserDetection} from '@webex/common';
6
+ import uuid from 'uuid';
7
+ import {merge} from 'lodash';
8
+ import {StatelessWebexPlugin} from '@webex/webex-core';
9
+
10
+ import {
11
+ anonymizeIPAddress,
12
+ clearEmptyKeysRecursively,
13
+ isLocusServiceErrorCode,
14
+ prepareDiagnosticMetricItem,
15
+ userAgentToString,
16
+ extractVersionMetadata,
17
+ isMeetingInfoServiceError,
18
+ isBrowserMediaErrorName,
19
+ } from './call-diagnostic-metrics.util';
20
+ import {CLIENT_NAME} from '../config';
21
+ import {
22
+ RecursivePartial,
23
+ Event,
24
+ ClientType,
25
+ SubClientType,
26
+ NetworkType,
27
+ ClientEvent,
28
+ SubmitClientEventOptions,
29
+ MediaQualityEvent,
30
+ SubmitMQEOptions,
31
+ SubmitMQEPayload,
32
+ ClientEventError,
33
+ ClientEventPayload,
34
+ ClientInfo,
35
+ ClientEventPayloadError,
36
+ } from '../metrics.types';
37
+ import CallDiagnosticEventsBatcher from './call-diagnostic-metrics-batcher';
38
+ import {
39
+ CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD,
40
+ CALL_DIAGNOSTIC_EVENT_FAILED_TO_SEND,
41
+ NEW_LOCUS_ERROR_CLIENT_CODE,
42
+ SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP,
43
+ UNKNOWN_ERROR,
44
+ BROWSER_MEDIA_ERROR_NAME_TO_CLIENT_ERROR_CODES_MAP,
45
+ MEETING_INFO_LOOKUP_ERROR_CLIENT_CODE,
46
+ } from './config';
47
+
48
+ const {getOSVersion, getBrowserName, getBrowserVersion} = BrowserDetection();
49
+
50
+ type GetOriginOptions = {
51
+ clientType: ClientType;
52
+ subClientType: SubClientType;
53
+ networkType?: NetworkType;
54
+ };
55
+
56
+ type GetIdentifiersOptions = {
57
+ meeting?: any;
58
+ mediaConnections?: any[];
59
+ correlationId?: string;
60
+ };
61
+
62
+ /**
63
+ * @description Util class to handle Call Analyzer Metrics
64
+ * @export
65
+ * @class CallDiagnosticMetrics
66
+ */
67
+ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
68
+ // @ts-ignore
69
+ private callDiagnosticEventsBatcher: CallDiagnosticEventsBatcher;
70
+
71
+ /**
72
+ * Constructor
73
+ * @param args
74
+ */
75
+ constructor(...args) {
76
+ super(...args);
77
+ // @ts-ignore
78
+ this.callDiagnosticEventsBatcher = new CallDiagnosticEventsBatcher({}, {parent: this.webex});
79
+ }
80
+
81
+ /**
82
+ * Returns the login type of the current user
83
+ * @returns one of 'login-ci','unverified-guest', null
84
+ */
85
+ getCurLoginType() {
86
+ // @ts-ignore
87
+ if (this.webex.canAuthorize) {
88
+ // @ts-ignore
89
+ return this.webex.credentials.isUnverifiedGuest ? 'unverified-guest' : 'login-ci';
90
+ }
91
+
92
+ return null;
93
+ }
94
+
95
+ /**
96
+ * Returns if the meeting has converged architecture enabled
97
+ * @param options.meetingId
98
+ */
99
+ getIsConvergedArchitectureEnabled({meetingId}: {meetingId?: string}): boolean {
100
+ if (meetingId) {
101
+ // @ts-ignore
102
+ const meeting = this.webex.meetings.meetingCollection.get(meetingId);
103
+
104
+ return meeting?.meetingInfo?.enableConvergedArchitecture;
105
+ }
106
+
107
+ return undefined;
108
+ }
109
+
110
+ /**
111
+ * Get origin object for Call Diagnostic Event payload.
112
+ * @param options
113
+ * @param meetingId
114
+ * @returns
115
+ */
116
+ getOrigin(options: GetOriginOptions, meetingId?: string) {
117
+ const defaultClientType: ClientType =
118
+ // @ts-ignore
119
+ this.webex.meetings.config?.metrics?.clientType;
120
+ const defaultSubClientType: SubClientType =
121
+ // @ts-ignore
122
+ this.webex.meetings.config?.metrics?.subClientType;
123
+ // @ts-ignore
124
+ const providedClientVersion: string = this.webex.meetings.config?.metrics?.clientVersion;
125
+ // @ts-ignore
126
+ const defaultSDKClientVersion = `${CLIENT_NAME}/${this.webex.version}`;
127
+
128
+ let versionMetadata: Pick<ClientInfo, 'majorVersion' | 'minorVersion'> = {};
129
+
130
+ // sdk version split doesn't really make sense for now...
131
+ if (providedClientVersion) {
132
+ versionMetadata = extractVersionMetadata(providedClientVersion);
133
+ }
134
+
135
+ if (
136
+ (defaultClientType && defaultSubClientType) ||
137
+ (options.clientType && options.subClientType)
138
+ ) {
139
+ const origin: Event['origin'] = {
140
+ name: 'endpoint',
141
+ networkType: options?.networkType || 'unknown',
142
+ userAgent: userAgentToString({
143
+ // @ts-ignore
144
+ clientName: this.webex.meetings?.metrics?.clientName,
145
+ // @ts-ignore
146
+ webexVersion: this.webex.version,
147
+ }),
148
+ clientInfo: {
149
+ clientType: options?.clientType || defaultClientType,
150
+ clientVersion: providedClientVersion || defaultSDKClientVersion,
151
+ ...versionMetadata,
152
+ localNetworkPrefix:
153
+ // @ts-ignore
154
+ anonymizeIPAddress(this.webex.meetings.geoHintInfo?.clientAddress) || undefined,
155
+ osVersion: getOSVersion() || 'unknown',
156
+ subClientType: options?.subClientType || defaultSubClientType,
157
+ os: getOSNameInternal(),
158
+ browser: getBrowserName(),
159
+ browserVersion: getBrowserVersion(),
160
+ },
161
+ };
162
+
163
+ if (meetingId) {
164
+ // @ts-ignore
165
+ const meeting = this.webex.meetings.meetingCollection.get(meetingId);
166
+ if (meeting?.environment) {
167
+ origin.environment = meeting.environment;
168
+ }
169
+ }
170
+
171
+ return origin;
172
+ }
173
+
174
+ throw new Error("ClientType and SubClientType can't be undefined");
175
+ }
176
+
177
+ /**
178
+ * Gather identifier details for call diagnostic payload.
179
+ * @throws Error if initialization fails.
180
+ * @param options
181
+ */
182
+ getIdentifiers(options: GetIdentifiersOptions) {
183
+ const {meeting, mediaConnections, correlationId} = options;
184
+ const identifiers: Event['event']['identifiers'] = {correlationId: 'unknown'};
185
+
186
+ if (meeting) {
187
+ identifiers.correlationId = meeting.correlationId;
188
+ }
189
+
190
+ if (correlationId) {
191
+ identifiers.correlationId = correlationId;
192
+ }
193
+ // @ts-ignore
194
+ if (this.webex.internal) {
195
+ // @ts-ignore
196
+ const {device} = this.webex.internal;
197
+ identifiers.userId = device.userId;
198
+ identifiers.deviceId = device.url;
199
+ identifiers.orgId = device.orgId;
200
+ // @ts-ignore
201
+ identifiers.locusUrl = this.webex.internal.services.get('locus');
202
+ }
203
+
204
+ if (meeting?.locusInfo?.fullState) {
205
+ identifiers.locusUrl = meeting.locusUrl;
206
+ identifiers.locusId = meeting.locusUrl && meeting.locusUrl.split('/').pop();
207
+ identifiers.locusStartTime =
208
+ meeting.locusInfo.fullState && meeting.locusInfo.fullState.lastActive;
209
+ }
210
+
211
+ if (mediaConnections) {
212
+ identifiers.mediaAgentAlias = mediaConnections?.[0]?.mediaAgentAlias;
213
+ identifiers.mediaAgentGroupId = mediaConnections?.[0]?.mediaAgentGroupId;
214
+ }
215
+
216
+ if (identifiers.correlationId === undefined) {
217
+ throw new Error('Identifiers initialization failed.');
218
+ }
219
+
220
+ return identifiers;
221
+ }
222
+
223
+ /**
224
+ * Create diagnostic event, which can hold client event, feature event or MQE event data.
225
+ * This just initiates the shared properties that are required for all the 3 event categories.
226
+ * @param eventData
227
+ * @param options
228
+ * @returns
229
+ */
230
+ prepareDiagnosticEvent(eventData: Event['event'], options: any) {
231
+ const {meetingId} = options;
232
+ const origin = this.getOrigin(options, meetingId);
233
+
234
+ const event: Event = {
235
+ eventId: uuid.v4(),
236
+ version: 1,
237
+ origin,
238
+ originTime: {
239
+ triggered: new Date().toISOString(),
240
+ // is overridden in prepareRequest batcher
241
+ sent: 'not_defined_yet',
242
+ },
243
+ // @ts-ignore
244
+ senderCountryCode: this.webex.meetings.geoHintInfo?.countryCode,
245
+ event: eventData,
246
+ };
247
+
248
+ // sanitize (remove empty properties, CA requires it)
249
+ // but we don't want to sanitize MQE as most of the times
250
+ // values will be 0, [] etc, and they are required.
251
+ if (eventData.name !== 'client.mediaquality.event') {
252
+ clearEmptyKeysRecursively(event);
253
+ }
254
+
255
+ return event;
256
+ }
257
+
258
+ /**
259
+ * TODO: NOT IMPLEMENTED
260
+ * Submit Feature Event
261
+ * @returns
262
+ */
263
+ public submitFeatureEvent() {
264
+ throw Error('Not implemented');
265
+ }
266
+
267
+ /**
268
+ * Submit Media Quality Event
269
+ * @param args - submit params
270
+ * @param arg.name - event key
271
+ * @param arg.payload - additional payload to be merge with the default payload
272
+ * @param arg.options - options
273
+ */
274
+ submitMQE({
275
+ name,
276
+ payload,
277
+ options,
278
+ }: {
279
+ name: MediaQualityEvent['name'];
280
+ payload: SubmitMQEPayload;
281
+ options: SubmitMQEOptions;
282
+ }) {
283
+ const {meetingId, mediaConnections} = options;
284
+
285
+ // events that will most likely happen in join phase
286
+ if (meetingId) {
287
+ // @ts-ignore
288
+ const meeting = this.webex.meetings.meetingCollection.get(meetingId);
289
+
290
+ if (!meeting) {
291
+ console.warn(
292
+ 'Attempt to send MQE but no meeting was found...',
293
+ `event: ${name}, meetingId: ${meetingId}`
294
+ );
295
+ // @ts-ignore
296
+ this.webex.internal.metrics.submitClientMetrics(CALL_DIAGNOSTIC_EVENT_FAILED_TO_SEND, {
297
+ fields: {
298
+ meetingId,
299
+ name,
300
+ },
301
+ });
302
+
303
+ return;
304
+ }
305
+
306
+ // merge identifiers
307
+ const identifiers = this.getIdentifiers({
308
+ meeting,
309
+ mediaConnections: meeting.mediaConnections || mediaConnections,
310
+ });
311
+
312
+ // create media quality event object
313
+ let clientEventObject: MediaQualityEvent['payload'] = {
314
+ name,
315
+ canProceed: true,
316
+ identifiers,
317
+ eventData: {
318
+ webClientDomain: window.location.hostname,
319
+ },
320
+ intervals: payload.intervals,
321
+ sourceMetadata: {
322
+ applicationSoftwareType: CLIENT_NAME,
323
+ // @ts-ignore
324
+ applicationSoftwareVersion: this.webex.version,
325
+ mediaEngineSoftwareType: getBrowserName() || 'browser',
326
+ mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
327
+ startTime: new Date().toISOString(),
328
+ },
329
+ };
330
+
331
+ // merge any new properties, or override existing ones
332
+ clientEventObject = merge(clientEventObject, payload);
333
+
334
+ // append media quality event data to the call diagnostic event
335
+ const diagnosticEvent = this.prepareDiagnosticEvent(clientEventObject, options);
336
+ this.submitToCallDiagnostics(diagnosticEvent);
337
+ } else {
338
+ throw new Error(
339
+ 'Media quality events cant be sent outside the context of a meeting. Meeting id is required.'
340
+ );
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Return Client Event payload by client error code
346
+ * @param arg - get error arg
347
+ * @param arg.clientErrorCode
348
+ * @param arg.serviceErrorCode
349
+ * @returns
350
+ */
351
+ public getErrorPayloadForClientErrorCode({
352
+ clientErrorCode,
353
+ serviceErrorCode,
354
+ serviceErrorName,
355
+ }: {
356
+ clientErrorCode: number;
357
+ serviceErrorCode: any;
358
+ serviceErrorName?: any;
359
+ }): ClientEventError {
360
+ let error: ClientEventError;
361
+
362
+ if (clientErrorCode) {
363
+ const partialParsedError = CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD[clientErrorCode];
364
+
365
+ if (partialParsedError) {
366
+ error = merge(
367
+ {fatal: true, shownToUser: false, name: 'other', category: 'other'}, // default values
368
+ {errorCode: clientErrorCode},
369
+ serviceErrorName ? {errorData: {errorName: serviceErrorName}} : {},
370
+ {serviceErrorCode},
371
+ partialParsedError
372
+ );
373
+
374
+ return error;
375
+ }
376
+ }
377
+
378
+ return undefined;
379
+ }
380
+
381
+ /**
382
+ * Generate error payload for Client Event
383
+ * @param rawError
384
+ */
385
+ generateClientEventErrorPayload(rawError: any) {
386
+ if (rawError.name) {
387
+ if (isBrowserMediaErrorName(rawError.name)) {
388
+ return this.getErrorPayloadForClientErrorCode({
389
+ serviceErrorCode: undefined,
390
+ clientErrorCode: BROWSER_MEDIA_ERROR_NAME_TO_CLIENT_ERROR_CODES_MAP[rawError.name],
391
+ serviceErrorName: rawError.name,
392
+ });
393
+ }
394
+ }
395
+
396
+ const serviceErrorCode =
397
+ rawError?.error?.body?.errorCode ||
398
+ rawError?.body?.errorCode ||
399
+ rawError?.body?.code ||
400
+ rawError?.body?.reason?.reasonCode;
401
+
402
+ if (serviceErrorCode) {
403
+ const clientErrorCode = SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP[serviceErrorCode];
404
+ if (clientErrorCode) {
405
+ return this.getErrorPayloadForClientErrorCode({clientErrorCode, serviceErrorCode});
406
+ }
407
+
408
+ // by default, if it is locus error, return new locus err
409
+ if (isLocusServiceErrorCode(serviceErrorCode)) {
410
+ return this.getErrorPayloadForClientErrorCode({
411
+ clientErrorCode: NEW_LOCUS_ERROR_CLIENT_CODE,
412
+ serviceErrorCode,
413
+ });
414
+ }
415
+ }
416
+
417
+ if (isMeetingInfoServiceError(rawError)) {
418
+ return this.getErrorPayloadForClientErrorCode({
419
+ clientErrorCode: MEETING_INFO_LOOKUP_ERROR_CLIENT_CODE,
420
+ serviceErrorCode,
421
+ });
422
+ }
423
+
424
+ // otherwise return unkown error
425
+ return this.getErrorPayloadForClientErrorCode({
426
+ clientErrorCode: UNKNOWN_ERROR,
427
+ serviceErrorCode: UNKNOWN_ERROR,
428
+ });
429
+ }
430
+
431
+ /**
432
+ * Create client event object for in meeting events
433
+ * @param arg - create args
434
+ * @param arg.event - event key
435
+ * @param arg.options - options
436
+ * @returns object
437
+ */
438
+ private createClientEventObjectInMeeting({
439
+ name,
440
+ options,
441
+ errors,
442
+ }: {
443
+ name: ClientEvent['name'];
444
+ options?: SubmitClientEventOptions;
445
+ errors?: ClientEventPayloadError;
446
+ }) {
447
+ const {meetingId, mediaConnections} = options;
448
+
449
+ // @ts-ignore
450
+ const meeting = this.webex.meetings.meetingCollection.get(meetingId);
451
+
452
+ if (!meeting) {
453
+ console.warn(
454
+ 'Attempt to send client event but no meeting was found...',
455
+ `event: ${name}, meetingId: ${meetingId}`
456
+ );
457
+ // @ts-ignore
458
+ this.webex.internal.metrics.submitClientMetrics(CALL_DIAGNOSTIC_EVENT_FAILED_TO_SEND, {
459
+ fields: {
460
+ meetingId,
461
+ name,
462
+ },
463
+ });
464
+
465
+ return undefined;
466
+ }
467
+
468
+ // grab identifiers
469
+ const identifiers = this.getIdentifiers({
470
+ meeting,
471
+ mediaConnections: meeting?.mediaConnections || mediaConnections,
472
+ });
473
+
474
+ // create client event object
475
+ const clientEventObject: ClientEvent['payload'] = {
476
+ name,
477
+ canProceed: true,
478
+ identifiers,
479
+ errors,
480
+ eventData: {
481
+ webClientDomain: window.location.hostname,
482
+ },
483
+ userType: meeting.getCurUserType(),
484
+ loginType: this.getCurLoginType(),
485
+ isConvergedArchitectureEnabled: this.getIsConvergedArchitectureEnabled({
486
+ meetingId,
487
+ }),
488
+ };
489
+
490
+ return clientEventObject;
491
+ }
492
+
493
+ /**
494
+ * Create client event object for pre meeting events
495
+ * @param arg - create args
496
+ * @param arg.event - event key
497
+ * @param arg.options - payload
498
+ * @returns object
499
+ */
500
+ private createClientEventObjectPreMeeting({
501
+ name,
502
+ options,
503
+ errors,
504
+ }: {
505
+ name: ClientEvent['name'];
506
+ options?: SubmitClientEventOptions;
507
+ errors?: ClientEventPayloadError;
508
+ }) {
509
+ const {correlationId} = options;
510
+
511
+ // grab identifiers
512
+ const identifiers = this.getIdentifiers({
513
+ correlationId,
514
+ });
515
+
516
+ // create client event object
517
+ const clientEventObject: ClientEvent['payload'] = {
518
+ name,
519
+ errors,
520
+ canProceed: true,
521
+ identifiers,
522
+ eventData: {
523
+ webClientDomain: window.location.hostname,
524
+ },
525
+ loginType: this.getCurLoginType(),
526
+ };
527
+
528
+ return clientEventObject;
529
+ }
530
+
531
+ /**
532
+ * Prepare Client Event CA event.
533
+ * @param arg - submit params
534
+ * @param arg.event - event key
535
+ * @param arg.payload - additional payload to be merged with default payload
536
+ * @param arg.options - payload
537
+ * @returns {any} options to be with fetch
538
+ * @throws
539
+ */
540
+ private prepareClientEvent({
541
+ name,
542
+ payload,
543
+ options,
544
+ }: {
545
+ name: ClientEvent['name'];
546
+ payload?: ClientEventPayload;
547
+ options?: SubmitClientEventOptions;
548
+ }) {
549
+ const {meetingId, correlationId, rawError} = options;
550
+ let clientEventObject: ClientEvent['payload'];
551
+
552
+ // check if we need to generate errors
553
+ const errors: ClientEventPayloadError = [];
554
+
555
+ if (rawError) {
556
+ const generatedError = this.generateClientEventErrorPayload(rawError);
557
+ if (generatedError) {
558
+ errors.push(generatedError);
559
+ }
560
+ }
561
+
562
+ // events that will most likely happen in join phase
563
+ if (meetingId) {
564
+ clientEventObject = this.createClientEventObjectInMeeting({name, options, errors});
565
+ } else if (correlationId) {
566
+ // any pre join events or events that are outside the meeting.
567
+ clientEventObject = this.createClientEventObjectPreMeeting({name, options, errors});
568
+ } else {
569
+ throw new Error('Not implemented');
570
+ }
571
+
572
+ // merge any new properties, or override existing ones
573
+ clientEventObject = merge(clientEventObject, payload);
574
+
575
+ // append client event data to the call diagnostic event
576
+ const diagnosticEvent = this.prepareDiagnosticEvent(clientEventObject, options);
577
+
578
+ return diagnosticEvent;
579
+ }
580
+
581
+ /**
582
+ * Submit Client Event CA event.
583
+ * @param arg - submit params
584
+ * @param arg.event - event key
585
+ * @param arg.payload - additional payload to be merged with default payload
586
+ * @param arg.options - payload
587
+ * @throws
588
+ */
589
+ public submitClientEvent({
590
+ name,
591
+ payload,
592
+ options,
593
+ }: {
594
+ name: ClientEvent['name'];
595
+ payload?: ClientEventPayload;
596
+ options?: SubmitClientEventOptions;
597
+ }) {
598
+ const diagnosticEvent = this.prepareClientEvent({name, payload, options});
599
+
600
+ return this.submitToCallDiagnostics(diagnosticEvent);
601
+ }
602
+
603
+ /**
604
+ * Prepare the event and send the request to metrics-a service.
605
+ * @param event
606
+ * @returns promise
607
+ */
608
+ submitToCallDiagnostics(event: Event): Promise<any> {
609
+ // build metrics-a event type
610
+ const finalEvent = {
611
+ eventPayload: event,
612
+ type: ['diagnostic-event'],
613
+ };
614
+
615
+ return this.callDiagnosticEventsBatcher.request(finalEvent);
616
+ }
617
+
618
+ /**
619
+ * Builds a request options object to later be passed to fetch().
620
+ * @param arg - submit params
621
+ * @param arg.event - event key
622
+ * @param arg.payload - additional payload to be merged with default payload
623
+ * @param arg.options - client event options
624
+ * @returns {Promise<any>}
625
+ * @throws
626
+ */
627
+ public async buildClientEventFetchRequestOptions({
628
+ name,
629
+ payload,
630
+ options,
631
+ }: {
632
+ name: ClientEvent['name'];
633
+ payload?: ClientEventPayload;
634
+ options?: SubmitClientEventOptions;
635
+ }): Promise<any> {
636
+ const clientEvent = this.prepareClientEvent({name, payload, options});
637
+
638
+ // build metrics-a event type
639
+ // @ts-ignore
640
+ const diagnosticEvent = prepareDiagnosticMetricItem(this.webex, {
641
+ eventPayload: clientEvent,
642
+ type: ['diagnostic-event'],
643
+ });
644
+
645
+ // @ts-ignore
646
+ return this.webex.prepareFetchOptions({
647
+ method: 'POST',
648
+ service: 'metrics',
649
+ resource: 'clientmetrics',
650
+ body: {
651
+ metrics: [diagnosticEvent],
652
+ },
653
+ });
654
+ }
655
+ }