@webex/internal-plugin-metrics 3.8.1 → 3.9.0-multipleLLM.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +92 -14
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.js +351 -48
- package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js +21 -0
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
- package/dist/call-diagnostic/config.js +3 -1
- package/dist/call-diagnostic/config.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/metrics.js +1 -1
- package/dist/metrics.types.js.map +1 -1
- package/dist/new-metrics.js +43 -1
- package/dist/new-metrics.js.map +1 -1
- package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +23 -1
- package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +177 -10
- package/dist/types/call-diagnostic/config.d.ts +2 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/metrics.types.d.ts +19 -7
- package/dist/types/new-metrics.d.ts +19 -2
- package/package.json +11 -12
- package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +104 -14
- package/src/call-diagnostic/call-diagnostic-metrics.ts +368 -25
- package/src/call-diagnostic/call-diagnostic-metrics.util.ts +20 -0
- package/src/call-diagnostic/config.ts +3 -0
- package/src/index.ts +2 -0
- package/src/metrics.types.ts +26 -6
- package/src/new-metrics.ts +52 -1
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +20 -1
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +255 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +864 -39
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +6 -0
- package/test/unit/spec/new-metrics.ts +67 -2
- package/test/unit/spec/prelogin-metrics-batcher.ts +72 -3
- package/dist/call-diagnostic-events-batcher.js +0 -60
- package/dist/call-diagnostic-events-batcher.js.map +0 -1
|
@@ -41,6 +41,9 @@ import {
|
|
|
41
41
|
ClientSubServiceType,
|
|
42
42
|
BrowserLaunchMethodType,
|
|
43
43
|
DelayedClientEvent,
|
|
44
|
+
DelayedClientFeatureEvent,
|
|
45
|
+
FeatureEvent,
|
|
46
|
+
ClientFeatureEventPayload,
|
|
44
47
|
} from '../metrics.types';
|
|
45
48
|
import CallDiagnosticEventsBatcher from './call-diagnostic-metrics-batcher';
|
|
46
49
|
import PreLoginMetricsBatcher from '../prelogin-metrics-batcher';
|
|
@@ -58,6 +61,8 @@ import {
|
|
|
58
61
|
AUTHENTICATION_FAILED_CODE,
|
|
59
62
|
WEBEX_SUB_SERVICE_TYPES,
|
|
60
63
|
SDP_OFFER_CREATION_ERROR_MAP,
|
|
64
|
+
CALL_FEATURE_LOG_IDENTIFIER,
|
|
65
|
+
CALL_FEATURE_EVENT_FAILED_TO_SEND,
|
|
61
66
|
} from './config';
|
|
62
67
|
|
|
63
68
|
const {getOSVersion, getBrowserName, getBrowserVersion} = BrowserDetection();
|
|
@@ -70,6 +75,7 @@ type GetOriginOptions = {
|
|
|
70
75
|
browserLaunchMethod?: BrowserLaunchMethodType;
|
|
71
76
|
environment?: EnvironmentType;
|
|
72
77
|
newEnvironment?: NewEnvironmentType;
|
|
78
|
+
vendorId?: string;
|
|
73
79
|
};
|
|
74
80
|
|
|
75
81
|
type GetIdentifiersOptions = {
|
|
@@ -97,7 +103,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
97
103
|
private hasLoggedBrowserSerial: boolean;
|
|
98
104
|
private device: any;
|
|
99
105
|
private delayedClientEvents: DelayedClientEvent[] = [];
|
|
106
|
+
private delayedClientFeatureEvents: DelayedClientFeatureEvent[] = [];
|
|
100
107
|
private eventErrorCache: WeakMap<any, any> = new WeakMap();
|
|
108
|
+
private isMercuryConnected = false;
|
|
109
|
+
private eventLimitTracker: Map<string, number> = new Map();
|
|
110
|
+
private eventLimitWarningsLogged: Set<string> = new Set();
|
|
101
111
|
|
|
102
112
|
// the default validator before piping an event to the batcher
|
|
103
113
|
// this function can be overridden by the user
|
|
@@ -150,6 +160,16 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
150
160
|
return undefined;
|
|
151
161
|
}
|
|
152
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Sets mercury connected status for event data object in CA events
|
|
165
|
+
* @public
|
|
166
|
+
* @param status - boolean value indicating mercury connection status
|
|
167
|
+
* @return {void}
|
|
168
|
+
*/
|
|
169
|
+
public setMercuryConnectedStatus(status: boolean): void {
|
|
170
|
+
this.isMercuryConnected = status;
|
|
171
|
+
}
|
|
172
|
+
|
|
153
173
|
/**
|
|
154
174
|
* Returns meeting's subServiceType
|
|
155
175
|
* @param meeting
|
|
@@ -278,6 +298,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
278
298
|
origin.clientInfo.browserLaunchMethod = options.browserLaunchMethod;
|
|
279
299
|
}
|
|
280
300
|
|
|
301
|
+
if (options?.vendorId) {
|
|
302
|
+
origin.clientInfo.vendorId = options.vendorId;
|
|
303
|
+
}
|
|
304
|
+
|
|
281
305
|
return origin;
|
|
282
306
|
}
|
|
283
307
|
|
|
@@ -416,12 +440,97 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
416
440
|
}
|
|
417
441
|
|
|
418
442
|
/**
|
|
419
|
-
*
|
|
443
|
+
* Create feature event
|
|
444
|
+
* @param name
|
|
445
|
+
* @param payload
|
|
446
|
+
* @param options
|
|
447
|
+
* @returns
|
|
448
|
+
*/
|
|
449
|
+
private prepareClientFeatureEvent({
|
|
450
|
+
name,
|
|
451
|
+
payload,
|
|
452
|
+
options,
|
|
453
|
+
}: {
|
|
454
|
+
name: FeatureEvent['name'];
|
|
455
|
+
payload?: ClientFeatureEventPayload;
|
|
456
|
+
options?: SubmitClientEventOptions;
|
|
457
|
+
}) {
|
|
458
|
+
const {meetingId, correlationId} = options;
|
|
459
|
+
let featureEventObject: FeatureEvent['payload'];
|
|
460
|
+
|
|
461
|
+
// events that will most likely happen in join phase
|
|
462
|
+
if (meetingId) {
|
|
463
|
+
featureEventObject = this.createFeatureEventObjectInMeeting({name, options});
|
|
464
|
+
} else {
|
|
465
|
+
throw new Error('Not implemented');
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// merge any new properties, or override existing ones
|
|
469
|
+
featureEventObject = merge(featureEventObject, payload);
|
|
470
|
+
|
|
471
|
+
// append client event data to the call diagnostic event
|
|
472
|
+
const featureEvent = this.prepareDiagnosticEvent(featureEventObject, options);
|
|
473
|
+
|
|
474
|
+
return featureEvent;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
420
478
|
* Submit Feature Event
|
|
479
|
+
* submit to business_ucf
|
|
421
480
|
* @returns
|
|
422
481
|
*/
|
|
423
|
-
public submitFeatureEvent(
|
|
424
|
-
|
|
482
|
+
public submitFeatureEvent({
|
|
483
|
+
name,
|
|
484
|
+
payload,
|
|
485
|
+
options,
|
|
486
|
+
delaySubmitEvent,
|
|
487
|
+
}: {
|
|
488
|
+
name: FeatureEvent['name'];
|
|
489
|
+
payload?: ClientFeatureEventPayload;
|
|
490
|
+
options?: SubmitClientEventOptions;
|
|
491
|
+
delaySubmitEvent?: boolean;
|
|
492
|
+
}) {
|
|
493
|
+
if (delaySubmitEvent) {
|
|
494
|
+
// Preserve the time when the event was triggered if delaying the submission to Call Features
|
|
495
|
+
const delayedOptions = {
|
|
496
|
+
...options,
|
|
497
|
+
triggeredTime: new Date().toISOString(),
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
this.delayedClientFeatureEvents.push({
|
|
501
|
+
name,
|
|
502
|
+
payload,
|
|
503
|
+
options: delayedOptions,
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
return Promise.resolve();
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
this.logger.log(
|
|
510
|
+
CALL_FEATURE_LOG_IDENTIFIER,
|
|
511
|
+
'CallFeatureMetrics: @submitFeatureEvent. Submit Client Feature Event CA event.',
|
|
512
|
+
`name: ${name}`
|
|
513
|
+
);
|
|
514
|
+
const featureEvent = this.prepareClientFeatureEvent({name, payload, options});
|
|
515
|
+
|
|
516
|
+
this.validator({type: 'ce', event: featureEvent});
|
|
517
|
+
|
|
518
|
+
return this.submitToCallFeatures(featureEvent);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Submit Feature Event
|
|
523
|
+
* type is business
|
|
524
|
+
* @param event
|
|
525
|
+
*/
|
|
526
|
+
submitToCallFeatures(event: Event): Promise<any> {
|
|
527
|
+
// build metrics-a event type
|
|
528
|
+
const finalEvent = {
|
|
529
|
+
eventPayload: event,
|
|
530
|
+
type: ['business'],
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
return this.callDiagnosticEventsBatcher.request(finalEvent);
|
|
425
534
|
}
|
|
426
535
|
|
|
427
536
|
/**
|
|
@@ -563,6 +672,144 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
563
672
|
this.eventErrorCache = new WeakMap();
|
|
564
673
|
}
|
|
565
674
|
|
|
675
|
+
/**
|
|
676
|
+
* Checks if an event should be limited based on criteria defined in the event dictionary.
|
|
677
|
+
* Returns true if the event should be sent, false if it has reached its limit.
|
|
678
|
+
* @param event - The diagnostic event object
|
|
679
|
+
* @returns boolean indicating whether the event should be sent
|
|
680
|
+
*/
|
|
681
|
+
private shouldSendEvent({event}: Event): boolean {
|
|
682
|
+
const eventName = event?.name as string;
|
|
683
|
+
const correlationId = event?.identifiers?.correlationId;
|
|
684
|
+
|
|
685
|
+
if (!correlationId || correlationId === 'unknown') {
|
|
686
|
+
return true;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
const limitKeyPrefix = `${eventName}:${correlationId}`;
|
|
690
|
+
|
|
691
|
+
switch (eventName) {
|
|
692
|
+
case 'client.media.render.start':
|
|
693
|
+
case 'client.media.render.stop':
|
|
694
|
+
case 'client.media.rx.start':
|
|
695
|
+
case 'client.media.rx.stop':
|
|
696
|
+
case 'client.media.tx.start':
|
|
697
|
+
case 'client.media.tx.stop': {
|
|
698
|
+
// Send only once per mediaType-correlationId pair (or mediaType-correlationId-shareInstanceId for share/share_audio)
|
|
699
|
+
const mediaType = event?.mediaType;
|
|
700
|
+
if (mediaType) {
|
|
701
|
+
if (mediaType === 'share' || mediaType === 'share_audio') {
|
|
702
|
+
const shareInstanceId = event?.shareInstanceId;
|
|
703
|
+
if (shareInstanceId) {
|
|
704
|
+
const limitKey = `${limitKeyPrefix}:${mediaType}:${shareInstanceId}`;
|
|
705
|
+
|
|
706
|
+
return this.checkAndIncrementEventCount(
|
|
707
|
+
limitKey,
|
|
708
|
+
1,
|
|
709
|
+
`${eventName} for ${mediaType} instance ${shareInstanceId}`
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
} else {
|
|
713
|
+
const limitKey = `${limitKeyPrefix}:${mediaType}`;
|
|
714
|
+
|
|
715
|
+
return this.checkAndIncrementEventCount(
|
|
716
|
+
limitKey,
|
|
717
|
+
1,
|
|
718
|
+
`${eventName} for mediaType ${mediaType}`
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
case 'client.roap-message.received':
|
|
726
|
+
case 'client.roap-message.sent': {
|
|
727
|
+
// Send only once per correlationId and roap.messageType/roap.type
|
|
728
|
+
const roapMessageType = event?.roap?.messageType || event?.roap?.type;
|
|
729
|
+
if (roapMessageType) {
|
|
730
|
+
const limitKey = `${limitKeyPrefix}:${roapMessageType}`;
|
|
731
|
+
|
|
732
|
+
return this.checkAndIncrementEventCount(
|
|
733
|
+
limitKey,
|
|
734
|
+
1,
|
|
735
|
+
`${eventName} for ROAP type ${roapMessageType}`
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
default:
|
|
742
|
+
return true;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
return true;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Checks the current count for a limit key and increments if under limit.
|
|
750
|
+
* @param limitKey - The unique key for this limit combination
|
|
751
|
+
* @param maxCount - Maximum allowed count
|
|
752
|
+
* @param eventDescription - Description for logging
|
|
753
|
+
* @returns true if under limit and incremented, false if at/over limit
|
|
754
|
+
*/
|
|
755
|
+
private checkAndIncrementEventCount(
|
|
756
|
+
limitKey: string,
|
|
757
|
+
maxCount: number,
|
|
758
|
+
eventDescription: string
|
|
759
|
+
): boolean {
|
|
760
|
+
const currentCount = this.eventLimitTracker.get(limitKey) || 0;
|
|
761
|
+
|
|
762
|
+
if (currentCount >= maxCount) {
|
|
763
|
+
// Log warning only once per limit key
|
|
764
|
+
if (!this.eventLimitWarningsLogged.has(limitKey)) {
|
|
765
|
+
this.logger.log(
|
|
766
|
+
CALL_DIAGNOSTIC_LOG_IDENTIFIER,
|
|
767
|
+
`CallDiagnosticMetrics: Event limit reached for ${eventDescription}. ` +
|
|
768
|
+
`Max count ${maxCount} exceeded. Event will not be sent.`,
|
|
769
|
+
`limitKey: ${limitKey}`
|
|
770
|
+
);
|
|
771
|
+
this.eventLimitWarningsLogged.add(limitKey);
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// Increment count and allow event
|
|
778
|
+
this.eventLimitTracker.set(limitKey, currentCount + 1);
|
|
779
|
+
|
|
780
|
+
return true;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Clears event limit tracking
|
|
785
|
+
*/
|
|
786
|
+
public clearEventLimits(): void {
|
|
787
|
+
this.eventLimitTracker.clear();
|
|
788
|
+
this.eventLimitWarningsLogged.clear();
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Clears event limit tracking for a specific correlationId only.
|
|
793
|
+
* Keeps limits for other meetings intact.
|
|
794
|
+
*/
|
|
795
|
+
public clearEventLimitsForCorrelationId(correlationId: string): void {
|
|
796
|
+
if (!correlationId) {
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
// Keys are formatted as "eventName:correlationId:..." across all limiters.
|
|
800
|
+
const hasCorrIdAtSecondToken = (key: string) => key.split(':')[1] === correlationId;
|
|
801
|
+
for (const key of Array.from(this.eventLimitTracker.keys())) {
|
|
802
|
+
if (hasCorrIdAtSecondToken(key)) {
|
|
803
|
+
this.eventLimitTracker.delete(key);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
for (const key of Array.from(this.eventLimitWarningsLogged.values())) {
|
|
807
|
+
if (hasCorrIdAtSecondToken(key)) {
|
|
808
|
+
this.eventLimitWarningsLogged.delete(key);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
566
813
|
/**
|
|
567
814
|
* Generate error payload for Client Event
|
|
568
815
|
* @param rawError
|
|
@@ -680,20 +927,20 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
680
927
|
}
|
|
681
928
|
|
|
682
929
|
/**
|
|
683
|
-
* Create
|
|
684
|
-
* @param
|
|
685
|
-
* @param
|
|
686
|
-
* @param
|
|
930
|
+
* Create common object for in meeting events
|
|
931
|
+
* @param name
|
|
932
|
+
* @param options
|
|
933
|
+
* @param eventType - 'client' | 'feature'
|
|
687
934
|
* @returns object
|
|
688
935
|
*/
|
|
689
|
-
private
|
|
936
|
+
private createCommonEventObjectInMeeting({
|
|
690
937
|
name,
|
|
691
938
|
options,
|
|
692
|
-
|
|
939
|
+
eventType = 'client',
|
|
693
940
|
}: {
|
|
694
|
-
name:
|
|
941
|
+
name: string;
|
|
695
942
|
options?: SubmitClientEventOptions;
|
|
696
|
-
|
|
943
|
+
eventType?: 'client' | 'feature';
|
|
697
944
|
}) {
|
|
698
945
|
const {
|
|
699
946
|
meetingId,
|
|
@@ -708,16 +955,21 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
708
955
|
|
|
709
956
|
if (!meeting) {
|
|
710
957
|
console.warn(
|
|
711
|
-
'Attempt to send
|
|
958
|
+
'Attempt to send common event but no meeting was found...',
|
|
712
959
|
`name: ${name}, meetingId: ${meetingId}`
|
|
713
960
|
);
|
|
714
961
|
// @ts-ignore
|
|
715
|
-
this.webex.internal.metrics.submitClientMetrics(
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
962
|
+
this.webex.internal.metrics.submitClientMetrics(
|
|
963
|
+
eventType === 'feature'
|
|
964
|
+
? CALL_FEATURE_EVENT_FAILED_TO_SEND
|
|
965
|
+
: CALL_DIAGNOSTIC_EVENT_FAILED_TO_SEND,
|
|
966
|
+
{
|
|
967
|
+
fields: {
|
|
968
|
+
meetingId,
|
|
969
|
+
name,
|
|
970
|
+
},
|
|
971
|
+
}
|
|
972
|
+
);
|
|
721
973
|
|
|
722
974
|
return undefined;
|
|
723
975
|
}
|
|
@@ -731,12 +983,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
731
983
|
sessionCorrelationId,
|
|
732
984
|
});
|
|
733
985
|
|
|
734
|
-
// create
|
|
735
|
-
const
|
|
986
|
+
// create common event object structur
|
|
987
|
+
const commonEventObject = {
|
|
736
988
|
name,
|
|
737
989
|
canProceed: true,
|
|
738
990
|
identifiers,
|
|
739
|
-
errors,
|
|
740
991
|
eventData: {
|
|
741
992
|
webClientDomain: window.location.hostname,
|
|
742
993
|
},
|
|
@@ -757,18 +1008,80 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
757
1008
|
|
|
758
1009
|
const joinFlowVersion = options.joinFlowVersion ?? meeting.callStateForMetrics?.joinFlowVersion;
|
|
759
1010
|
if (joinFlowVersion) {
|
|
760
|
-
|
|
1011
|
+
// @ts-ignore
|
|
1012
|
+
commonEventObject.joinFlowVersion = joinFlowVersion;
|
|
761
1013
|
}
|
|
762
1014
|
const meetingJoinedTime = meeting.isoLocalClientMeetingJoinTime;
|
|
763
1015
|
if (meetingJoinedTime) {
|
|
764
|
-
|
|
1016
|
+
// @ts-ignore
|
|
1017
|
+
commonEventObject.meetingJoinedTime = meetingJoinedTime;
|
|
765
1018
|
}
|
|
766
1019
|
|
|
767
1020
|
if (options.meetingJoinPhase) {
|
|
768
|
-
|
|
1021
|
+
// @ts-ignore
|
|
1022
|
+
commonEventObject.meetingJoinPhase = options.meetingJoinPhase;
|
|
769
1023
|
}
|
|
770
1024
|
|
|
771
|
-
return
|
|
1025
|
+
return commonEventObject;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* Create client event object for in meeting events
|
|
1030
|
+
* @param arg - create args
|
|
1031
|
+
* @param arg.event - event key
|
|
1032
|
+
* @param arg.options - options
|
|
1033
|
+
* @returns object
|
|
1034
|
+
*/
|
|
1035
|
+
private createClientEventObjectInMeeting({
|
|
1036
|
+
name,
|
|
1037
|
+
options,
|
|
1038
|
+
errors,
|
|
1039
|
+
}: {
|
|
1040
|
+
name: ClientEvent['name'];
|
|
1041
|
+
options?: SubmitClientEventOptions;
|
|
1042
|
+
errors?: ClientEventPayloadError;
|
|
1043
|
+
}) {
|
|
1044
|
+
const commonObject = this.createCommonEventObjectInMeeting({
|
|
1045
|
+
name,
|
|
1046
|
+
options,
|
|
1047
|
+
eventType: 'client',
|
|
1048
|
+
});
|
|
1049
|
+
if (!commonObject) return undefined;
|
|
1050
|
+
|
|
1051
|
+
return {
|
|
1052
|
+
...commonObject,
|
|
1053
|
+
errors,
|
|
1054
|
+
eventData: {
|
|
1055
|
+
...commonObject.eventData,
|
|
1056
|
+
isMercuryConnected: this.isMercuryConnected,
|
|
1057
|
+
},
|
|
1058
|
+
} as ClientEvent['payload'];
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* Create feature event object for in meeting function event
|
|
1063
|
+
* @param name
|
|
1064
|
+
* @param options
|
|
1065
|
+
* @returns object
|
|
1066
|
+
*/
|
|
1067
|
+
private createFeatureEventObjectInMeeting({
|
|
1068
|
+
name,
|
|
1069
|
+
options,
|
|
1070
|
+
}: {
|
|
1071
|
+
name: FeatureEvent['name'];
|
|
1072
|
+
options?: SubmitClientEventOptions;
|
|
1073
|
+
}) {
|
|
1074
|
+
const commonObject = this.createCommonEventObjectInMeeting({
|
|
1075
|
+
name,
|
|
1076
|
+
options,
|
|
1077
|
+
eventType: 'feature',
|
|
1078
|
+
});
|
|
1079
|
+
if (!commonObject) return undefined;
|
|
1080
|
+
|
|
1081
|
+
return {
|
|
1082
|
+
...commonObject,
|
|
1083
|
+
key: 'UcfFeatureUsage',
|
|
1084
|
+
} as FeatureEvent['payload'];
|
|
772
1085
|
}
|
|
773
1086
|
|
|
774
1087
|
/**
|
|
@@ -807,6 +1120,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
807
1120
|
identifiers,
|
|
808
1121
|
eventData: {
|
|
809
1122
|
webClientDomain: window.location.hostname,
|
|
1123
|
+
isMercuryConnected: this.isMercuryConnected,
|
|
810
1124
|
},
|
|
811
1125
|
loginType: this.getCurLoginType(),
|
|
812
1126
|
// @ts-ignore
|
|
@@ -930,6 +1244,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
930
1244
|
);
|
|
931
1245
|
const diagnosticEvent = this.prepareClientEvent({name, payload, options});
|
|
932
1246
|
|
|
1247
|
+
if (!this.shouldSendEvent(diagnosticEvent)) {
|
|
1248
|
+
return Promise.resolve();
|
|
1249
|
+
}
|
|
1250
|
+
|
|
933
1251
|
if (options?.preLoginId) {
|
|
934
1252
|
return this.submitToCallDiagnosticsPreLogin(diagnosticEvent, options?.preLoginId);
|
|
935
1253
|
}
|
|
@@ -964,6 +1282,31 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
964
1282
|
return Promise.all(promises);
|
|
965
1283
|
}
|
|
966
1284
|
|
|
1285
|
+
/**
|
|
1286
|
+
* Submit Delayed feature Event CA events. Clears submitDelayedClientFeatureEvents array after submission.
|
|
1287
|
+
*/
|
|
1288
|
+
public submitDelayedClientFeatureEvents(overrides?: Partial<DelayedClientEvent['options']>) {
|
|
1289
|
+
this.logger.log(
|
|
1290
|
+
CALL_FEATURE_LOG_IDENTIFIER,
|
|
1291
|
+
'CallDiagnosticMetrics: @submitDelayedClientFeatureEvents. Submitting delayed feature events.'
|
|
1292
|
+
);
|
|
1293
|
+
|
|
1294
|
+
if (this.delayedClientFeatureEvents.length === 0) {
|
|
1295
|
+
return Promise.resolve();
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
const promises = this.delayedClientFeatureEvents.map((delayedSubmitClientEventParams) => {
|
|
1299
|
+
const {name, payload, options} = delayedSubmitClientEventParams;
|
|
1300
|
+
const optionsWithOverrides: DelayedClientEvent['options'] = {...options, ...overrides};
|
|
1301
|
+
|
|
1302
|
+
return this.submitFeatureEvent({name, payload, options: optionsWithOverrides});
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
this.delayedClientFeatureEvents = [];
|
|
1306
|
+
|
|
1307
|
+
return Promise.all(promises);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
967
1310
|
/**
|
|
968
1311
|
* Prepare the event and send the request to metrics-a service.
|
|
969
1312
|
* @param event
|
|
@@ -239,6 +239,20 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
239
239
|
|
|
240
240
|
// Set upgradeChannel to 'gold' if buildType is 'prod', otherwise to the buildType value
|
|
241
241
|
const upgradeChannel = buildType === 'prod' ? 'gold' : buildType;
|
|
242
|
+
if (webex.devicemanager) {
|
|
243
|
+
const pairedDevice = webex.devicemanager.getPairedDevice();
|
|
244
|
+
if (pairedDevice) {
|
|
245
|
+
const devicePayload = {
|
|
246
|
+
deviceId: pairedDevice.deviceInfo?.id,
|
|
247
|
+
devicePairingType: webex.devicemanager.getPairedMethod(),
|
|
248
|
+
deviceURL: pairedDevice.url,
|
|
249
|
+
isPersonalDevice: pairedDevice.mode === 'personal',
|
|
250
|
+
productName: pairedDevice.devices[0]?.productName,
|
|
251
|
+
};
|
|
252
|
+
item.eventPayload.event.pairingState = 'paired';
|
|
253
|
+
item.eventPayload.event.pairedDevice = devicePayload;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
242
256
|
|
|
243
257
|
const origin: Partial<Event['origin']> = {
|
|
244
258
|
buildType,
|
|
@@ -257,6 +271,7 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
257
271
|
switch (eventName) {
|
|
258
272
|
case 'client.webexapp.launched':
|
|
259
273
|
joinTimes.downloadTime = cdl.getDownloadTimeJMT();
|
|
274
|
+
joinTimes.pageJmt = cdl.getPageJMT();
|
|
260
275
|
break;
|
|
261
276
|
case 'client.login.end':
|
|
262
277
|
joinTimes.otherAppApiReqResp = cdl.getOtherAppApiReqResp();
|
|
@@ -267,6 +282,7 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
267
282
|
joinTimes.clickToInterstitial = cdl.getClickToInterstitial();
|
|
268
283
|
joinTimes.refreshCaptchaServiceReqResp = cdl.getRefreshCaptchaReqResp();
|
|
269
284
|
joinTimes.downloadIntelligenceModelsReqResp = cdl.getDownloadIntelligenceModelsReqResp();
|
|
285
|
+
joinTimes.clickToInterstitialWithUserDelay = cdl.getClickToInterstitialWithUserDelay();
|
|
270
286
|
break;
|
|
271
287
|
|
|
272
288
|
case 'client.call.initiated':
|
|
@@ -287,6 +303,8 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
287
303
|
joinTimes.totalJmt = cdl.getTotalJMT();
|
|
288
304
|
joinTimes.clientJmt = cdl.getClientJMT();
|
|
289
305
|
joinTimes.downloadTime = cdl.getDownloadTimeJMT();
|
|
306
|
+
joinTimes.clickToInterstitialWithUserDelay = cdl.getClickToInterstitialWithUserDelay();
|
|
307
|
+
joinTimes.totalJMTWithUserDelay = cdl.getTotalJMTWithUserDelay();
|
|
290
308
|
break;
|
|
291
309
|
|
|
292
310
|
case 'client.ice.end':
|
|
@@ -307,6 +325,8 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
307
325
|
joinTimes.interstitialToMediaOKJMT = cdl.getInterstitialToMediaOKJMT();
|
|
308
326
|
joinTimes.callInitMediaEngineReady = cdl.getCallInitMediaEngineReady();
|
|
309
327
|
joinTimes.stayLobbyTime = cdl.getStayLobbyTime();
|
|
328
|
+
joinTimes.totalMediaJMTWithUserDelay = cdl.getTotalMediaJMTWithUserDelay();
|
|
329
|
+
joinTimes.totalJMTWithUserDelay = cdl.getTotalJMTWithUserDelay();
|
|
310
330
|
break;
|
|
311
331
|
|
|
312
332
|
case 'client.media.tx.start':
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import {ClientEventError, ClientSubServiceType} from '../metrics.types';
|
|
6
6
|
|
|
7
7
|
export const CALL_DIAGNOSTIC_LOG_IDENTIFIER = 'call-diagnostic-events -> ';
|
|
8
|
+
export const CALL_FEATURE_LOG_IDENTIFIER = 'call-diagnostic-events-feature -> ';
|
|
8
9
|
|
|
9
10
|
export const AUTHENTICATION_FAILED_CODE = 1010;
|
|
10
11
|
export const NETWORK_ERROR = 1026;
|
|
@@ -738,3 +739,5 @@ export const CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD: Record<number, Partial<ClientEv
|
|
|
738
739
|
};
|
|
739
740
|
|
|
740
741
|
export const CALL_DIAGNOSTIC_EVENT_FAILED_TO_SEND = 'js_sdk_call_diagnostic_event_failed_to_send';
|
|
742
|
+
|
|
743
|
+
export const CALL_FEATURE_EVENT_FAILED_TO_SEND = 'js_sdk_call_feature_event_failed_to_send';
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
SubmitBusinessEvent,
|
|
19
19
|
SubmitMQE,
|
|
20
20
|
PreComputedLatencies,
|
|
21
|
+
SubmitFeatureEvent,
|
|
21
22
|
} from './metrics.types';
|
|
22
23
|
import * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';
|
|
23
24
|
import * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';
|
|
@@ -61,4 +62,5 @@ export type {
|
|
|
61
62
|
SubmitOperationalEvent,
|
|
62
63
|
SubmitBusinessEvent,
|
|
63
64
|
PreComputedLatencies,
|
|
65
|
+
SubmitFeatureEvent,
|
|
64
66
|
};
|
package/src/metrics.types.ts
CHANGED
|
@@ -2,9 +2,12 @@ import {
|
|
|
2
2
|
ClientEvent as RawClientEvent,
|
|
3
3
|
Event as RawEvent,
|
|
4
4
|
MediaQualityEvent as RawMediaQualityEvent,
|
|
5
|
+
FeatureEvent as RawFeatureEvent,
|
|
5
6
|
} from '@webex/event-dictionary-ts';
|
|
6
7
|
|
|
7
|
-
export type Event = Omit<RawEvent, 'event'> & {
|
|
8
|
+
export type Event = Omit<RawEvent, 'event'> & {
|
|
9
|
+
event: RawClientEvent | RawMediaQualityEvent | RawFeatureEvent;
|
|
10
|
+
};
|
|
8
11
|
|
|
9
12
|
export type ClientEventError = NonNullable<RawClientEvent['errors']>[0];
|
|
10
13
|
|
|
@@ -135,6 +138,7 @@ export type SubmitClientEventOptions = {
|
|
|
135
138
|
triggeredTime?: string;
|
|
136
139
|
emailInput?: ClientEmailInput;
|
|
137
140
|
userNameInput?: ClientUserNameInput;
|
|
141
|
+
vendorId?: string;
|
|
138
142
|
};
|
|
139
143
|
|
|
140
144
|
export type SubmitMQEOptions = {
|
|
@@ -157,7 +161,9 @@ export type InternalEvent = {
|
|
|
157
161
|
| 'internal.client.meeting.interstitial-window.showed'
|
|
158
162
|
| 'internal.client.interstitial-window.click.joinbutton'
|
|
159
163
|
| 'internal.client.add-media.turn-discovery.start'
|
|
160
|
-
| 'internal.client.add-media.turn-discovery.end'
|
|
164
|
+
| 'internal.client.add-media.turn-discovery.end'
|
|
165
|
+
| 'internal.client.share.initiated'
|
|
166
|
+
| 'internal.client.share.stopped';
|
|
161
167
|
|
|
162
168
|
payload?: never;
|
|
163
169
|
options?: never;
|
|
@@ -212,10 +218,9 @@ export type BehavioralEvent = TaggedEvent;
|
|
|
212
218
|
export type OperationalEvent = TaggedEvent;
|
|
213
219
|
|
|
214
220
|
export interface FeatureEvent {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
options?: never;
|
|
221
|
+
name: RawFeatureEvent['name'];
|
|
222
|
+
payload?: RawFeatureEvent;
|
|
223
|
+
options?: SubmitClientEventOptions;
|
|
219
224
|
}
|
|
220
225
|
|
|
221
226
|
export interface MediaQualityEvent {
|
|
@@ -251,6 +256,8 @@ export type ClientEventPayload = RecursivePartial<ClientEvent['payload']>;
|
|
|
251
256
|
export type ClientEventLeaveReason = ClientEvent['payload']['leaveReason'];
|
|
252
257
|
export type ClientEventPayloadError = ClientEvent['payload']['errors'];
|
|
253
258
|
|
|
259
|
+
export type ClientFeatureEventPayload = RecursivePartial<FeatureEvent['payload']>;
|
|
260
|
+
|
|
254
261
|
export type MediaQualityEventAudioSetupDelayPayload = NonNullable<
|
|
255
262
|
MediaQualityEvent['payload']
|
|
256
263
|
>['audioSetupDelay'];
|
|
@@ -311,6 +318,7 @@ export type PreComputedLatencies =
|
|
|
311
318
|
| 'internal.download.time'
|
|
312
319
|
| 'internal.get.cluster.time'
|
|
313
320
|
| 'internal.click.to.interstitial'
|
|
321
|
+
| 'internal.click.to.interstitial.with.user.delay'
|
|
314
322
|
| 'internal.refresh.captcha.time'
|
|
315
323
|
| 'internal.exchange.ci.token.time'
|
|
316
324
|
| 'internal.get.u2c.time'
|
|
@@ -339,3 +347,15 @@ export interface DelayedClientEvent {
|
|
|
339
347
|
payload?: RecursivePartial<ClientEvent['payload']>;
|
|
340
348
|
options?: SubmitClientEventOptions;
|
|
341
349
|
}
|
|
350
|
+
|
|
351
|
+
export type SubmitFeatureEvent = (args: {
|
|
352
|
+
name: FeatureEvent['name'];
|
|
353
|
+
payload?: RecursivePartial<FeatureEvent['payload']>;
|
|
354
|
+
options?: SubmitClientEventOptions;
|
|
355
|
+
}) => Promise<any>;
|
|
356
|
+
|
|
357
|
+
export interface DelayedClientFeatureEvent {
|
|
358
|
+
name: FeatureEvent['name'];
|
|
359
|
+
payload?: RecursivePartial<FeatureEvent['payload']>;
|
|
360
|
+
options?: SubmitClientEventOptions;
|
|
361
|
+
}
|