@webex/internal-plugin-metrics 3.8.1-web-workers-keepalive.1 → 3.9.0
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 +44 -15
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.js +157 -27
- package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js +15 -0
- package/dist/call-diagnostic/call-diagnostic-metrics.util.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/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +8 -1
- package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +32 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/metrics.types.d.ts +1 -1
- package/package.json +10 -11
- package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +47 -15
- package/src/call-diagnostic/call-diagnostic-metrics.ts +144 -0
- package/src/call-diagnostic/call-diagnostic-metrics.util.ts +14 -0
- package/src/index.ts +2 -0
- package/src/metrics.types.ts +3 -1
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +88 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +404 -1
- package/test/unit/spec/prelogin-metrics-batcher.ts +71 -3
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_webexCore","require","_metrics","_interopRequireWildcard","_config","_interopRequireDefault","_newMetrics","Utils","exports","CALL_DIAGNOSTIC_CONFIG","CallDiagnosticUtils","_callDiagnosticMetrics2","_callDiagnosticMetricsLatencies","_behavioralMetrics","_operationalMetrics","_businessMetrics","_rtcMetrics","_getRequireWildcardCache","e","_WeakMap","r","t","__esModule","_typeof","default","has","get","n","__proto__","a","_Object$defineProperty","_Object$getOwnPropertyDescriptor","u","Object","prototype","hasOwnProperty","call","i","set","registerInternalPlugin","Metrics","config","NewMetrics"],"sources":["index.ts"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nimport {registerInternalPlugin} from '@webex/webex-core';\n\nimport Metrics from './metrics';\nimport config from './config';\nimport NewMetrics from './new-metrics';\nimport * as Utils from './utils';\nimport {\n ClientEvent,\n ClientEventLeaveReason,\n SubmitBehavioralEvent,\n SubmitClientEvent,\n SubmitInternalEvent,\n SubmitOperationalEvent,\n SubmitBusinessEvent,\n SubmitMQE,\n PreComputedLatencies,\n} from './metrics.types';\nimport * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';\nimport * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';\nimport CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';\nimport CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';\nimport BehavioralMetrics from './behavioral-metrics';\nimport OperationalMetrics from './operational-metrics';\nimport BusinessMetrics from './business-metrics';\nimport RtcMetrics from './rtcMetrics';\n\nregisterInternalPlugin('metrics', Metrics, {\n config,\n});\n\nregisterInternalPlugin('newMetrics', NewMetrics, {\n config,\n});\n\nexport {default, getOSNameInternal} from './metrics';\n\nexport {\n config,\n CALL_DIAGNOSTIC_CONFIG,\n NewMetrics,\n Utils,\n CallDiagnosticUtils,\n CallDiagnosticLatencies,\n CallDiagnosticMetrics,\n BehavioralMetrics,\n OperationalMetrics,\n BusinessMetrics,\n RtcMetrics,\n};\nexport type {\n ClientEvent,\n ClientEventLeaveReason,\n SubmitBehavioralEvent,\n SubmitClientEvent,\n SubmitInternalEvent,\n SubmitMQE,\n SubmitOperationalEvent,\n SubmitBusinessEvent,\n PreComputedLatencies,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,IAAAA,UAAA,GAAAC,OAAA;AAEA,IAAAC,QAAA,GAAAC,uBAAA,CAAAF,OAAA;AACA,IAAAG,OAAA,GAAAC,sBAAA,CAAAJ,OAAA;AACA,IAAAK,WAAA,GAAAD,sBAAA,CAAAJ,OAAA;AACA,IAAAM,KAAA,GAAAJ,uBAAA,CAAAF,OAAA;AAAiCO,OAAA,CAAAD,KAAA,GAAAA,KAAA;
|
|
1
|
+
{"version":3,"names":["_webexCore","require","_metrics","_interopRequireWildcard","_config","_interopRequireDefault","_newMetrics","Utils","exports","CALL_DIAGNOSTIC_CONFIG","CallDiagnosticUtils","_callDiagnosticMetrics2","_callDiagnosticMetricsLatencies","_behavioralMetrics","_operationalMetrics","_businessMetrics","_rtcMetrics","_getRequireWildcardCache","e","_WeakMap","r","t","__esModule","_typeof","default","has","get","n","__proto__","a","_Object$defineProperty","_Object$getOwnPropertyDescriptor","u","Object","prototype","hasOwnProperty","call","i","set","registerInternalPlugin","Metrics","config","NewMetrics"],"sources":["index.ts"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nimport {registerInternalPlugin} from '@webex/webex-core';\n\nimport Metrics from './metrics';\nimport config from './config';\nimport NewMetrics from './new-metrics';\nimport * as Utils from './utils';\nimport {\n ClientEvent,\n ClientEventLeaveReason,\n SubmitBehavioralEvent,\n SubmitClientEvent,\n SubmitInternalEvent,\n SubmitOperationalEvent,\n SubmitBusinessEvent,\n SubmitMQE,\n PreComputedLatencies,\n SubmitFeatureEvent,\n} from './metrics.types';\nimport * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';\nimport * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';\nimport CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';\nimport CallDiagnosticLatencies from './call-diagnostic/call-diagnostic-metrics-latencies';\nimport BehavioralMetrics from './behavioral-metrics';\nimport OperationalMetrics from './operational-metrics';\nimport BusinessMetrics from './business-metrics';\nimport RtcMetrics from './rtcMetrics';\n\nregisterInternalPlugin('metrics', Metrics, {\n config,\n});\n\nregisterInternalPlugin('newMetrics', NewMetrics, {\n config,\n});\n\nexport {default, getOSNameInternal} from './metrics';\n\nexport {\n config,\n CALL_DIAGNOSTIC_CONFIG,\n NewMetrics,\n Utils,\n CallDiagnosticUtils,\n CallDiagnosticLatencies,\n CallDiagnosticMetrics,\n BehavioralMetrics,\n OperationalMetrics,\n BusinessMetrics,\n RtcMetrics,\n};\nexport type {\n ClientEvent,\n ClientEventLeaveReason,\n SubmitBehavioralEvent,\n SubmitClientEvent,\n SubmitInternalEvent,\n SubmitMQE,\n SubmitOperationalEvent,\n SubmitBusinessEvent,\n PreComputedLatencies,\n SubmitFeatureEvent,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,IAAAA,UAAA,GAAAC,OAAA;AAEA,IAAAC,QAAA,GAAAC,uBAAA,CAAAF,OAAA;AACA,IAAAG,OAAA,GAAAC,sBAAA,CAAAJ,OAAA;AACA,IAAAK,WAAA,GAAAD,sBAAA,CAAAJ,OAAA;AACA,IAAAM,KAAA,GAAAJ,uBAAA,CAAAF,OAAA;AAAiCO,OAAA,CAAAD,KAAA,GAAAA,KAAA;AAajC,IAAAE,sBAAA,GAAAN,uBAAA,CAAAF,OAAA;AAAmEO,OAAA,CAAAC,sBAAA,GAAAA,sBAAA;AACnE,IAAAC,mBAAA,GAAAP,uBAAA,CAAAF,OAAA;AAAsFO,OAAA,CAAAE,mBAAA,GAAAA,mBAAA;AACtF,IAAAC,uBAAA,GAAAN,sBAAA,CAAAJ,OAAA;AACA,IAAAW,+BAAA,GAAAP,sBAAA,CAAAJ,OAAA;AACA,IAAAY,kBAAA,GAAAR,sBAAA,CAAAJ,OAAA;AACA,IAAAa,mBAAA,GAAAT,sBAAA,CAAAJ,OAAA;AACA,IAAAc,gBAAA,GAAAV,sBAAA,CAAAJ,OAAA;AACA,IAAAe,WAAA,GAAAX,sBAAA,CAAAJ,OAAA;AAAsC,SAAAgB,yBAAAC,CAAA,6BAAAC,QAAA,mBAAAC,CAAA,OAAAD,QAAA,IAAAE,CAAA,OAAAF,QAAA,YAAAF,wBAAA,YAAAA,yBAAAC,CAAA,WAAAA,CAAA,GAAAG,CAAA,GAAAD,CAAA,KAAAF,CAAA;AAAA,SAAAf,wBAAAe,CAAA,EAAAE,CAAA,SAAAA,CAAA,IAAAF,CAAA,IAAAA,CAAA,CAAAI,UAAA,SAAAJ,CAAA,eAAAA,CAAA,gBAAAK,OAAA,CAAAL,CAAA,0BAAAA,CAAA,WAAAM,OAAA,EAAAN,CAAA,QAAAG,CAAA,GAAAJ,wBAAA,CAAAG,CAAA,OAAAC,CAAA,IAAAA,CAAA,CAAAI,GAAA,CAAAP,CAAA,UAAAG,CAAA,CAAAK,GAAA,CAAAR,CAAA,OAAAS,CAAA,KAAAC,SAAA,UAAAC,CAAA,GAAAC,sBAAA,IAAAC,gCAAA,WAAAC,CAAA,IAAAd,CAAA,oBAAAc,CAAA,IAAAC,MAAA,CAAAC,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAlB,CAAA,EAAAc,CAAA,SAAAK,CAAA,GAAAR,CAAA,GAAAE,gCAAA,CAAAb,CAAA,EAAAc,CAAA,UAAAK,CAAA,KAAAA,CAAA,CAAAX,GAAA,IAAAW,CAAA,CAAAC,GAAA,IAAAR,sBAAA,CAAAH,CAAA,EAAAK,CAAA,EAAAK,CAAA,IAAAV,CAAA,CAAAK,CAAA,IAAAd,CAAA,CAAAc,CAAA,YAAAL,CAAA,CAAAH,OAAA,GAAAN,CAAA,EAAAG,CAAA,IAAAA,CAAA,CAAAiB,GAAA,CAAApB,CAAA,EAAAS,CAAA,GAAAA,CAAA;AA7BtC;AACA;AACA;;AA6BA,IAAAY,iCAAsB,EAAC,SAAS,EAAEC,gBAAO,EAAE;EACzCC,MAAM,EAANA;AACF,CAAC,CAAC;AAEF,IAAAF,iCAAsB,EAAC,YAAY,EAAEG,mBAAU,EAAE;EAC/CD,MAAM,EAANA;AACF,CAAC,CAAC"}
|
package/dist/metrics.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["metrics.types.ts"],"sourcesContent":["import {\n ClientEvent as RawClientEvent,\n Event as RawEvent,\n MediaQualityEvent as RawMediaQualityEvent,\n FeatureEvent as RawFeatureEvent,\n} from '@webex/event-dictionary-ts';\n\nexport type Event = Omit<RawEvent, 'event'> & {\n event: RawClientEvent | RawMediaQualityEvent | RawFeatureEvent;\n};\n\nexport type ClientEventError = NonNullable<RawClientEvent['errors']>[0];\n\nexport type EnvironmentType = NonNullable<RawEvent['origin']['environment']>;\n\nexport type NewEnvironmentType = NonNullable<RawEvent['origin']['newEnvironment']>;\n\nexport type ClientLaunchMethodType = NonNullable<\n RawEvent['origin']['clientInfo']\n>['clientLaunchMethod'];\n\nexport type ClientUserNameInput = NonNullable<RawClientEvent['userNameInput']>;\n\nexport type ClientEmailInput = NonNullable<RawClientEvent['emailInput']>;\n\nexport type BrowserLaunchMethodType = NonNullable<\n RawEvent['origin']['clientInfo']\n>['browserLaunchMethod'];\n\nexport type MetricEventProduct = 'webex' | 'wxcc_desktop' | 'wxcc_crm' | 'wxcc_sdk';\n\nexport type MetricEventAgent = 'user' | 'browser' | 'system' | 'sdk' | 'redux' | 'service' | 'api';\n\nexport type MetricEventVerb =\n | 'abort'\n | 'accept'\n | 'activate'\n | 'apply'\n | 'answer'\n | 'authorize'\n | 'build'\n | 'cancel'\n | 'change'\n | 'click'\n | 'close'\n | 'complete'\n | 'connect'\n | 'create'\n | 'deactivate'\n | 'decrypt'\n | 'delete'\n | 'deliver'\n | 'destroy'\n | 'disable'\n | 'disconnect'\n | 'dismiss'\n | 'display'\n | 'download'\n | 'edit'\n | 'enable'\n | 'encrypt'\n | 'end'\n | 'expire'\n | 'fail'\n | 'fetch'\n | 'fire'\n | 'generate'\n | 'get'\n | 'hide'\n | 'hover'\n | 'ignore'\n | 'initialize'\n | 'initiate'\n | 'invalidate'\n | 'join'\n | 'list'\n | 'load'\n | 'login'\n | 'logout'\n | 'notify'\n | 'offer'\n | 'open'\n | 'press'\n | 'receive'\n | 'refer'\n | 'refresh'\n | 'register'\n | 'release'\n | 'reload'\n | 'reject'\n | 'request'\n | 'reset'\n | 'resize'\n | 'respond'\n | 'retry'\n | 'revoke'\n | 'save'\n | 'search'\n | 'select'\n | 'send'\n | 'set'\n | 'sign'\n | 'start'\n | 'submit'\n | 'switch'\n | 'sync'\n | 'toggle'\n | 'transfer'\n | 'unregister'\n | 'update'\n | 'upload'\n | 'use'\n | 'validate'\n | 'view'\n | 'visit'\n | 'wait'\n | 'warn'\n | 'exit';\n\nexport type MetricEventJoinFlowVersion = 'Other' | 'NewFTE';\nexport type MetricEventMeetingJoinPhase = 'pre-join' | 'join' | 'in-meeting';\n\nexport type SubmitClientEventOptions = {\n meetingId?: string;\n mediaConnections?: any[];\n rawError?: any;\n correlationId?: string;\n sessionCorrelationId?: string;\n preLoginId?: string;\n environment?: EnvironmentType;\n newEnvironmentType?: NewEnvironmentType;\n clientLaunchMethod?: ClientLaunchMethodType;\n browserLaunchMethod?: BrowserLaunchMethodType;\n webexConferenceIdStr?: string;\n globalMeetingId?: string;\n joinFlowVersion?: MetricEventJoinFlowVersion;\n meetingJoinPhase?: MetricEventMeetingJoinPhase;\n triggeredTime?: string;\n emailInput?: ClientEmailInput;\n userNameInput?: ClientUserNameInput;\n};\n\nexport type SubmitMQEOptions = {\n meetingId: string;\n mediaConnections?: any[];\n networkType?: Event['origin']['networkType'];\n webexConferenceIdStr?: string;\n globalMeetingId?: string;\n};\n\nexport type InternalEvent = {\n name:\n | 'internal.client.meetinginfo.request'\n | 'internal.client.meetinginfo.response'\n | 'internal.register.device.request'\n | 'internal.register.device.response'\n | 'internal.reset.join.latencies'\n | 'internal.client.meeting.click.joinbutton'\n | 'internal.host.meeting.participant.admitted'\n | 'internal.client.meeting.interstitial-window.showed'\n | 'internal.client.interstitial-window.click.joinbutton'\n | 'internal.client.add-media.turn-discovery.start'\n | 'internal.client.add-media.turn-discovery.end';\n\n payload?: never;\n options?: never;\n};\n\nexport interface ClientEvent {\n name: RawClientEvent['name'];\n payload?: RawClientEvent;\n options?: SubmitClientEventOptions;\n}\n\nexport interface DeviceContext {\n app: {version: string};\n device: {id: string};\n locale: string;\n os: {\n name: string;\n version: string;\n };\n}\n\nexport type MetricType = 'behavioral' | 'operational' | 'business';\n\nexport type Table = 'wbxapp_callend_metrics' | 'business_metrics' | 'business_ucf' | 'default';\n\ntype InternalEventPayload = string | number | boolean;\nexport type EventPayload = Record<string, InternalEventPayload>;\nexport type BehavioralEventPayload = EventPayload; // for compatibilty, can be remove after wxcc-desktop did change their imports.\n\nexport interface BusinessEventPayload {\n metricName: string;\n timestamp: number;\n context: DeviceContext;\n browserDetails: EventPayload;\n value: EventPayload;\n}\n\nexport interface BusinessEvent {\n type: string[];\n eventPayload: BusinessEventPayload;\n}\n\nexport interface TaggedEvent {\n context: DeviceContext;\n metricName: string;\n tags: EventPayload;\n timestamp: number;\n type: [MetricType];\n}\n\nexport type BehavioralEvent = TaggedEvent;\nexport type OperationalEvent = TaggedEvent;\n\nexport interface FeatureEvent {\n name: RawFeatureEvent['name'];\n payload?: RawFeatureEvent;\n options?: SubmitClientEventOptions;\n}\n\nexport interface MediaQualityEvent {\n name: RawMediaQualityEvent['name'];\n payload?: RawMediaQualityEvent;\n options: SubmitMQEOptions;\n}\n\nexport type RecursivePartial<T> = {\n [P in keyof T]?: T[P] extends (infer U)[]\n ? RecursivePartial<U>[]\n : T[P] extends object\n ? RecursivePartial<T[P]>\n : T[P];\n};\n\nexport type MetricEventNames =\n | InternalEvent['name']\n | ClientEvent['name']\n | BehavioralEvent['metricName']\n | OperationalEvent['metricName']\n | BusinessEvent['eventPayload']['metricName']\n | FeatureEvent['name']\n | MediaQualityEvent['name'];\n\nexport type ClientInfo = NonNullable<RawEvent['origin']['clientInfo']>;\nexport type ClientType = NonNullable<RawEvent['origin']['clientInfo']>['clientType'];\nexport type SubClientType = NonNullable<RawEvent['origin']['clientInfo']>['subClientType'];\nexport type NetworkType = NonNullable<RawEvent['origin']>['networkType'];\n\nexport type ClientSubServiceType = ClientEvent['payload']['webexSubServiceType'];\nexport type ClientEventPayload = RecursivePartial<ClientEvent['payload']>;\nexport type ClientEventLeaveReason = ClientEvent['payload']['leaveReason'];\nexport type ClientEventPayloadError = ClientEvent['payload']['errors'];\n\nexport type ClientFeatureEventPayload = RecursivePartial<FeatureEvent['payload']>;\n\nexport type MediaQualityEventAudioSetupDelayPayload = NonNullable<\n MediaQualityEvent['payload']\n>['audioSetupDelay'];\nexport type MediaQualityEventVideoSetupDelayPayload = NonNullable<\n MediaQualityEvent['payload']\n>['videoSetupDelay'];\n\nexport type SubmitMQEPayload = RecursivePartial<MediaQualityEvent['payload']> & {\n intervals: NonNullable<MediaQualityEvent['payload']>['intervals'];\n};\n\nexport type SubmitInternalEvent = (args: {\n name: InternalEvent['name'];\n payload?: RecursivePartial<InternalEvent['payload']>;\n options?: any;\n}) => void;\n\nexport type SubmitBehavioralEvent = (args: {\n product: MetricEventProduct;\n agent: MetricEventAgent;\n target: string;\n verb: MetricEventVerb;\n payload?: EventPayload;\n}) => void;\n\nexport type SubmitClientEvent = (args: {\n name: ClientEvent['name'];\n payload?: RecursivePartial<ClientEvent['payload']>;\n options?: SubmitClientEventOptions;\n}) => Promise<any>;\n\nexport type SubmitOperationalEvent = (args: {\n name: OperationalEvent['metricName'];\n payload: EventPayload;\n}) => void;\n\nexport type SubmitBusinessEvent = (args: {\n name: OperationalEvent['metricName'];\n payload: EventPayload;\n metadata?: EventPayload;\n table?: Table;\n}) => void;\n\nexport type SubmitMQE = (args: {\n name: MediaQualityEvent['name'];\n payload: SubmitMQEPayload;\n options: any;\n}) => void;\n\nexport type BuildClientEventFetchRequestOptions = (args: {\n name: ClientEvent['name'];\n payload?: RecursivePartial<ClientEvent['payload']>;\n options?: SubmitClientEventOptions;\n}) => Promise<any>;\n\nexport type PreComputedLatencies =\n | 'internal.client.pageJMT'\n | 'internal.download.time'\n | 'internal.get.cluster.time'\n | 'internal.click.to.interstitial'\n | 'internal.click.to.interstitial.with.user.delay'\n | 'internal.refresh.captcha.time'\n | 'internal.exchange.ci.token.time'\n | 'internal.get.u2c.time'\n | 'internal.call.init.join.req'\n | 'internal.other.app.api.time'\n | 'internal.api.fetch.intelligence.models';\n\nexport interface IdType {\n meetingId?: string;\n callId?: string;\n}\n\nexport interface IMetricsAttributes {\n type: string;\n version: string;\n userId: string;\n correlationId: string;\n connectionId: string;\n data: any[];\n meetingId?: string;\n callId?: string;\n}\n\nexport interface DelayedClientEvent {\n name: ClientEvent['name'];\n payload?: RecursivePartial<ClientEvent['payload']>;\n options?: SubmitClientEventOptions;\n}\n\nexport type SubmitFeatureEvent = (args: {\n name: FeatureEvent['name'];\n payload?: RecursivePartial<FeatureEvent['payload']>;\n options?: SubmitClientEventOptions;\n}) => Promise<any>;\n\nexport interface DelayedClientFeatureEvent {\n name: FeatureEvent['name'];\n payload?: RecursivePartial<FeatureEvent['payload']>;\n options?: SubmitClientEventOptions;\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"names":[],"sources":["metrics.types.ts"],"sourcesContent":["import {\n ClientEvent as RawClientEvent,\n Event as RawEvent,\n MediaQualityEvent as RawMediaQualityEvent,\n FeatureEvent as RawFeatureEvent,\n} from '@webex/event-dictionary-ts';\n\nexport type Event = Omit<RawEvent, 'event'> & {\n event: RawClientEvent | RawMediaQualityEvent | RawFeatureEvent;\n};\n\nexport type ClientEventError = NonNullable<RawClientEvent['errors']>[0];\n\nexport type EnvironmentType = NonNullable<RawEvent['origin']['environment']>;\n\nexport type NewEnvironmentType = NonNullable<RawEvent['origin']['newEnvironment']>;\n\nexport type ClientLaunchMethodType = NonNullable<\n RawEvent['origin']['clientInfo']\n>['clientLaunchMethod'];\n\nexport type ClientUserNameInput = NonNullable<RawClientEvent['userNameInput']>;\n\nexport type ClientEmailInput = NonNullable<RawClientEvent['emailInput']>;\n\nexport type BrowserLaunchMethodType = NonNullable<\n RawEvent['origin']['clientInfo']\n>['browserLaunchMethod'];\n\nexport type MetricEventProduct = 'webex' | 'wxcc_desktop' | 'wxcc_crm' | 'wxcc_sdk';\n\nexport type MetricEventAgent = 'user' | 'browser' | 'system' | 'sdk' | 'redux' | 'service' | 'api';\n\nexport type MetricEventVerb =\n | 'abort'\n | 'accept'\n | 'activate'\n | 'apply'\n | 'answer'\n | 'authorize'\n | 'build'\n | 'cancel'\n | 'change'\n | 'click'\n | 'close'\n | 'complete'\n | 'connect'\n | 'create'\n | 'deactivate'\n | 'decrypt'\n | 'delete'\n | 'deliver'\n | 'destroy'\n | 'disable'\n | 'disconnect'\n | 'dismiss'\n | 'display'\n | 'download'\n | 'edit'\n | 'enable'\n | 'encrypt'\n | 'end'\n | 'expire'\n | 'fail'\n | 'fetch'\n | 'fire'\n | 'generate'\n | 'get'\n | 'hide'\n | 'hover'\n | 'ignore'\n | 'initialize'\n | 'initiate'\n | 'invalidate'\n | 'join'\n | 'list'\n | 'load'\n | 'login'\n | 'logout'\n | 'notify'\n | 'offer'\n | 'open'\n | 'press'\n | 'receive'\n | 'refer'\n | 'refresh'\n | 'register'\n | 'release'\n | 'reload'\n | 'reject'\n | 'request'\n | 'reset'\n | 'resize'\n | 'respond'\n | 'retry'\n | 'revoke'\n | 'save'\n | 'search'\n | 'select'\n | 'send'\n | 'set'\n | 'sign'\n | 'start'\n | 'submit'\n | 'switch'\n | 'sync'\n | 'toggle'\n | 'transfer'\n | 'unregister'\n | 'update'\n | 'upload'\n | 'use'\n | 'validate'\n | 'view'\n | 'visit'\n | 'wait'\n | 'warn'\n | 'exit';\n\nexport type MetricEventJoinFlowVersion = 'Other' | 'NewFTE';\nexport type MetricEventMeetingJoinPhase = 'pre-join' | 'join' | 'in-meeting';\n\nexport type SubmitClientEventOptions = {\n meetingId?: string;\n mediaConnections?: any[];\n rawError?: any;\n correlationId?: string;\n sessionCorrelationId?: string;\n preLoginId?: string;\n environment?: EnvironmentType;\n newEnvironmentType?: NewEnvironmentType;\n clientLaunchMethod?: ClientLaunchMethodType;\n browserLaunchMethod?: BrowserLaunchMethodType;\n webexConferenceIdStr?: string;\n globalMeetingId?: string;\n joinFlowVersion?: MetricEventJoinFlowVersion;\n meetingJoinPhase?: MetricEventMeetingJoinPhase;\n triggeredTime?: string;\n emailInput?: ClientEmailInput;\n userNameInput?: ClientUserNameInput;\n};\n\nexport type SubmitMQEOptions = {\n meetingId: string;\n mediaConnections?: any[];\n networkType?: Event['origin']['networkType'];\n webexConferenceIdStr?: string;\n globalMeetingId?: string;\n};\n\nexport type InternalEvent = {\n name:\n | 'internal.client.meetinginfo.request'\n | 'internal.client.meetinginfo.response'\n | 'internal.register.device.request'\n | 'internal.register.device.response'\n | 'internal.reset.join.latencies'\n | 'internal.client.meeting.click.joinbutton'\n | 'internal.host.meeting.participant.admitted'\n | 'internal.client.meeting.interstitial-window.showed'\n | 'internal.client.interstitial-window.click.joinbutton'\n | 'internal.client.add-media.turn-discovery.start'\n | 'internal.client.add-media.turn-discovery.end'\n | 'internal.client.share.initiated'\n | 'internal.client.share.stopped';\n\n payload?: never;\n options?: never;\n};\n\nexport interface ClientEvent {\n name: RawClientEvent['name'];\n payload?: RawClientEvent;\n options?: SubmitClientEventOptions;\n}\n\nexport interface DeviceContext {\n app: {version: string};\n device: {id: string};\n locale: string;\n os: {\n name: string;\n version: string;\n };\n}\n\nexport type MetricType = 'behavioral' | 'operational' | 'business';\n\nexport type Table = 'wbxapp_callend_metrics' | 'business_metrics' | 'business_ucf' | 'default';\n\ntype InternalEventPayload = string | number | boolean;\nexport type EventPayload = Record<string, InternalEventPayload>;\nexport type BehavioralEventPayload = EventPayload; // for compatibilty, can be remove after wxcc-desktop did change their imports.\n\nexport interface BusinessEventPayload {\n metricName: string;\n timestamp: number;\n context: DeviceContext;\n browserDetails: EventPayload;\n value: EventPayload;\n}\n\nexport interface BusinessEvent {\n type: string[];\n eventPayload: BusinessEventPayload;\n}\n\nexport interface TaggedEvent {\n context: DeviceContext;\n metricName: string;\n tags: EventPayload;\n timestamp: number;\n type: [MetricType];\n}\n\nexport type BehavioralEvent = TaggedEvent;\nexport type OperationalEvent = TaggedEvent;\n\nexport interface FeatureEvent {\n name: RawFeatureEvent['name'];\n payload?: RawFeatureEvent;\n options?: SubmitClientEventOptions;\n}\n\nexport interface MediaQualityEvent {\n name: RawMediaQualityEvent['name'];\n payload?: RawMediaQualityEvent;\n options: SubmitMQEOptions;\n}\n\nexport type RecursivePartial<T> = {\n [P in keyof T]?: T[P] extends (infer U)[]\n ? RecursivePartial<U>[]\n : T[P] extends object\n ? RecursivePartial<T[P]>\n : T[P];\n};\n\nexport type MetricEventNames =\n | InternalEvent['name']\n | ClientEvent['name']\n | BehavioralEvent['metricName']\n | OperationalEvent['metricName']\n | BusinessEvent['eventPayload']['metricName']\n | FeatureEvent['name']\n | MediaQualityEvent['name'];\n\nexport type ClientInfo = NonNullable<RawEvent['origin']['clientInfo']>;\nexport type ClientType = NonNullable<RawEvent['origin']['clientInfo']>['clientType'];\nexport type SubClientType = NonNullable<RawEvent['origin']['clientInfo']>['subClientType'];\nexport type NetworkType = NonNullable<RawEvent['origin']>['networkType'];\n\nexport type ClientSubServiceType = ClientEvent['payload']['webexSubServiceType'];\nexport type ClientEventPayload = RecursivePartial<ClientEvent['payload']>;\nexport type ClientEventLeaveReason = ClientEvent['payload']['leaveReason'];\nexport type ClientEventPayloadError = ClientEvent['payload']['errors'];\n\nexport type ClientFeatureEventPayload = RecursivePartial<FeatureEvent['payload']>;\n\nexport type MediaQualityEventAudioSetupDelayPayload = NonNullable<\n MediaQualityEvent['payload']\n>['audioSetupDelay'];\nexport type MediaQualityEventVideoSetupDelayPayload = NonNullable<\n MediaQualityEvent['payload']\n>['videoSetupDelay'];\n\nexport type SubmitMQEPayload = RecursivePartial<MediaQualityEvent['payload']> & {\n intervals: NonNullable<MediaQualityEvent['payload']>['intervals'];\n};\n\nexport type SubmitInternalEvent = (args: {\n name: InternalEvent['name'];\n payload?: RecursivePartial<InternalEvent['payload']>;\n options?: any;\n}) => void;\n\nexport type SubmitBehavioralEvent = (args: {\n product: MetricEventProduct;\n agent: MetricEventAgent;\n target: string;\n verb: MetricEventVerb;\n payload?: EventPayload;\n}) => void;\n\nexport type SubmitClientEvent = (args: {\n name: ClientEvent['name'];\n payload?: RecursivePartial<ClientEvent['payload']>;\n options?: SubmitClientEventOptions;\n}) => Promise<any>;\n\nexport type SubmitOperationalEvent = (args: {\n name: OperationalEvent['metricName'];\n payload: EventPayload;\n}) => void;\n\nexport type SubmitBusinessEvent = (args: {\n name: OperationalEvent['metricName'];\n payload: EventPayload;\n metadata?: EventPayload;\n table?: Table;\n}) => void;\n\nexport type SubmitMQE = (args: {\n name: MediaQualityEvent['name'];\n payload: SubmitMQEPayload;\n options: any;\n}) => void;\n\nexport type BuildClientEventFetchRequestOptions = (args: {\n name: ClientEvent['name'];\n payload?: RecursivePartial<ClientEvent['payload']>;\n options?: SubmitClientEventOptions;\n}) => Promise<any>;\n\nexport type PreComputedLatencies =\n | 'internal.client.pageJMT'\n | 'internal.download.time'\n | 'internal.get.cluster.time'\n | 'internal.click.to.interstitial'\n | 'internal.click.to.interstitial.with.user.delay'\n | 'internal.refresh.captcha.time'\n | 'internal.exchange.ci.token.time'\n | 'internal.get.u2c.time'\n | 'internal.call.init.join.req'\n | 'internal.other.app.api.time'\n | 'internal.api.fetch.intelligence.models';\n\nexport interface IdType {\n meetingId?: string;\n callId?: string;\n}\n\nexport interface IMetricsAttributes {\n type: string;\n version: string;\n userId: string;\n correlationId: string;\n connectionId: string;\n data: any[];\n meetingId?: string;\n callId?: string;\n}\n\nexport interface DelayedClientEvent {\n name: ClientEvent['name'];\n payload?: RecursivePartial<ClientEvent['payload']>;\n options?: SubmitClientEventOptions;\n}\n\nexport type SubmitFeatureEvent = (args: {\n name: FeatureEvent['name'];\n payload?: RecursivePartial<FeatureEvent['payload']>;\n options?: SubmitClientEventOptions;\n}) => Promise<any>;\n\nexport interface DelayedClientFeatureEvent {\n name: FeatureEvent['name'];\n payload?: RecursivePartial<FeatureEvent['payload']>;\n options?: SubmitClientEventOptions;\n}\n"],"mappings":""}
|
|
@@ -73,7 +73,10 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
73
73
|
* @param b end
|
|
74
74
|
* @returns latency
|
|
75
75
|
*/
|
|
76
|
-
getDiffBetweenTimestamps(a: MetricEventNames, b: MetricEventNames
|
|
76
|
+
getDiffBetweenTimestamps(a: MetricEventNames, b: MetricEventNames, clampValues?: {
|
|
77
|
+
minimum?: number;
|
|
78
|
+
maximum?: number;
|
|
79
|
+
}): number;
|
|
77
80
|
/**
|
|
78
81
|
* Meeting Info Request
|
|
79
82
|
* @note Meeting Info request happen not just in the join phase. CA requires
|
|
@@ -230,6 +233,10 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
230
233
|
* Video setup delay transmit
|
|
231
234
|
*/
|
|
232
235
|
getVideoJoinRespTxStart(): number;
|
|
236
|
+
/**
|
|
237
|
+
* Time from share initiation to share stop (ms).
|
|
238
|
+
*/
|
|
239
|
+
getShareDuration(): number;
|
|
233
240
|
/**
|
|
234
241
|
* Total latency for all exchange ci token.
|
|
235
242
|
*/
|
|
@@ -33,6 +33,8 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
33
33
|
private delayedClientFeatureEvents;
|
|
34
34
|
private eventErrorCache;
|
|
35
35
|
private isMercuryConnected;
|
|
36
|
+
private eventLimitTracker;
|
|
37
|
+
private eventLimitWarningsLogged;
|
|
36
38
|
validator: (options: {
|
|
37
39
|
type: 'mqe' | 'ce';
|
|
38
40
|
event: Event;
|
|
@@ -111,7 +113,12 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
111
113
|
joinFirstUpdateLater?: "ep-enabled" | "sp-enabled" | "not-enabled";
|
|
112
114
|
standbyUsed?: boolean;
|
|
113
115
|
prefetchDocShowUsed?: boolean;
|
|
114
|
-
fastJoinUsed?: boolean;
|
|
116
|
+
fastJoinUsed?: boolean; /**
|
|
117
|
+
* Sets mercury connected status for event data object in CA events
|
|
118
|
+
* @public
|
|
119
|
+
* @param status - boolean value indicating mercury connection status
|
|
120
|
+
* @return {void}
|
|
121
|
+
*/
|
|
115
122
|
clientDownloadSize?: number;
|
|
116
123
|
clientDownloadFileCount?: number;
|
|
117
124
|
nodeId?: number;
|
|
@@ -482,6 +489,30 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
482
489
|
* Clear the error cache
|
|
483
490
|
*/
|
|
484
491
|
clearErrorCache(): void;
|
|
492
|
+
/**
|
|
493
|
+
* Checks if an event should be limited based on criteria defined in the event dictionary.
|
|
494
|
+
* Returns true if the event should be sent, false if it has reached its limit.
|
|
495
|
+
* @param event - The diagnostic event object
|
|
496
|
+
* @returns boolean indicating whether the event should be sent
|
|
497
|
+
*/
|
|
498
|
+
private shouldSendEvent;
|
|
499
|
+
/**
|
|
500
|
+
* Checks the current count for a limit key and increments if under limit.
|
|
501
|
+
* @param limitKey - The unique key for this limit combination
|
|
502
|
+
* @param maxCount - Maximum allowed count
|
|
503
|
+
* @param eventDescription - Description for logging
|
|
504
|
+
* @returns true if under limit and incremented, false if at/over limit
|
|
505
|
+
*/
|
|
506
|
+
private checkAndIncrementEventCount;
|
|
507
|
+
/**
|
|
508
|
+
* Clears event limit tracking
|
|
509
|
+
*/
|
|
510
|
+
clearEventLimits(): void;
|
|
511
|
+
/**
|
|
512
|
+
* Clears event limit tracking for a specific correlationId only.
|
|
513
|
+
* Keeps limits for other meetings intact.
|
|
514
|
+
*/
|
|
515
|
+
clearEventLimitsForCorrelationId(correlationId: string): void;
|
|
485
516
|
/**
|
|
486
517
|
* Generate error payload for Client Event
|
|
487
518
|
* @param rawError
|
package/dist/types/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import config from './config';
|
|
5
5
|
import NewMetrics from './new-metrics';
|
|
6
6
|
import * as Utils from './utils';
|
|
7
|
-
import { ClientEvent, ClientEventLeaveReason, SubmitBehavioralEvent, SubmitClientEvent, SubmitInternalEvent, SubmitOperationalEvent, SubmitBusinessEvent, SubmitMQE, PreComputedLatencies } from './metrics.types';
|
|
7
|
+
import { ClientEvent, ClientEventLeaveReason, SubmitBehavioralEvent, SubmitClientEvent, SubmitInternalEvent, SubmitOperationalEvent, SubmitBusinessEvent, SubmitMQE, PreComputedLatencies, SubmitFeatureEvent } from './metrics.types';
|
|
8
8
|
import * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';
|
|
9
9
|
import * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';
|
|
10
10
|
import CallDiagnosticMetrics from './call-diagnostic/call-diagnostic-metrics';
|
|
@@ -15,4 +15,4 @@ import BusinessMetrics from './business-metrics';
|
|
|
15
15
|
import RtcMetrics from './rtcMetrics';
|
|
16
16
|
export { default, getOSNameInternal } from './metrics';
|
|
17
17
|
export { config, CALL_DIAGNOSTIC_CONFIG, NewMetrics, Utils, CallDiagnosticUtils, CallDiagnosticLatencies, CallDiagnosticMetrics, BehavioralMetrics, OperationalMetrics, BusinessMetrics, RtcMetrics, };
|
|
18
|
-
export type { ClientEvent, ClientEventLeaveReason, SubmitBehavioralEvent, SubmitClientEvent, SubmitInternalEvent, SubmitMQE, SubmitOperationalEvent, SubmitBusinessEvent, PreComputedLatencies, };
|
|
18
|
+
export type { ClientEvent, ClientEventLeaveReason, SubmitBehavioralEvent, SubmitClientEvent, SubmitInternalEvent, SubmitMQE, SubmitOperationalEvent, SubmitBusinessEvent, PreComputedLatencies, SubmitFeatureEvent, };
|
|
@@ -41,7 +41,7 @@ export type SubmitMQEOptions = {
|
|
|
41
41
|
globalMeetingId?: string;
|
|
42
42
|
};
|
|
43
43
|
export type InternalEvent = {
|
|
44
|
-
name: 'internal.client.meetinginfo.request' | 'internal.client.meetinginfo.response' | 'internal.register.device.request' | 'internal.register.device.response' | 'internal.reset.join.latencies' | 'internal.client.meeting.click.joinbutton' | 'internal.host.meeting.participant.admitted' | 'internal.client.meeting.interstitial-window.showed' | 'internal.client.interstitial-window.click.joinbutton' | 'internal.client.add-media.turn-discovery.start' | 'internal.client.add-media.turn-discovery.end';
|
|
44
|
+
name: 'internal.client.meetinginfo.request' | 'internal.client.meetinginfo.response' | 'internal.register.device.request' | 'internal.register.device.response' | 'internal.reset.join.latencies' | 'internal.client.meeting.click.joinbutton' | 'internal.host.meeting.participant.admitted' | 'internal.client.meeting.interstitial-window.showed' | 'internal.client.interstitial-window.click.joinbutton' | 'internal.client.add-media.turn-discovery.start' | 'internal.client.add-media.turn-discovery.end' | 'internal.client.share.initiated' | 'internal.client.share.stopped';
|
|
45
45
|
payload?: never;
|
|
46
46
|
options?: never;
|
|
47
47
|
};
|
package/package.json
CHANGED
|
@@ -26,22 +26,21 @@
|
|
|
26
26
|
"@webex/eslint-config-legacy": "0.0.0",
|
|
27
27
|
"@webex/jest-config-legacy": "0.0.0",
|
|
28
28
|
"@webex/legacy-tools": "0.0.0",
|
|
29
|
-
"@webex/test-helper-chai": "3.
|
|
30
|
-
"@webex/test-helper-mocha": "3.
|
|
31
|
-
"@webex/test-helper-mock-webex": "3.
|
|
32
|
-
"@webex/test-helper-test-users": "3.
|
|
29
|
+
"@webex/test-helper-chai": "3.9.0",
|
|
30
|
+
"@webex/test-helper-mocha": "3.9.0",
|
|
31
|
+
"@webex/test-helper-mock-webex": "3.9.0",
|
|
32
|
+
"@webex/test-helper-test-users": "3.9.0",
|
|
33
33
|
"eslint": "^8.24.0",
|
|
34
34
|
"prettier": "^2.7.1",
|
|
35
35
|
"sinon": "^9.2.4"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@webex/common": "3.
|
|
39
|
-
"@webex/common-timers": "3.
|
|
38
|
+
"@webex/common": "3.9.0",
|
|
39
|
+
"@webex/common-timers": "3.9.0",
|
|
40
40
|
"@webex/event-dictionary-ts": "^1.0.1819",
|
|
41
|
-
"@webex/
|
|
42
|
-
"@webex/test-helper-
|
|
43
|
-
"@webex/
|
|
44
|
-
"@webex/webex-core": "3.8.1-web-workers-keepalive.1",
|
|
41
|
+
"@webex/test-helper-chai": "3.9.0",
|
|
42
|
+
"@webex/test-helper-mock-webex": "3.9.0",
|
|
43
|
+
"@webex/webex-core": "3.9.0",
|
|
45
44
|
"ip-anonymize": "^0.1.0",
|
|
46
45
|
"lodash": "^4.17.21",
|
|
47
46
|
"uuid": "^3.3.2"
|
|
@@ -54,5 +53,5 @@
|
|
|
54
53
|
"test:style": "eslint ./src/**/*.*",
|
|
55
54
|
"test:unit": "webex-legacy-tools test --unit --runner mocha"
|
|
56
55
|
},
|
|
57
|
-
"version": "3.
|
|
56
|
+
"version": "3.9.0"
|
|
58
57
|
}
|
|
@@ -148,15 +148,27 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
148
148
|
* @param b end
|
|
149
149
|
* @returns latency
|
|
150
150
|
*/
|
|
151
|
-
public getDiffBetweenTimestamps(
|
|
151
|
+
public getDiffBetweenTimestamps(
|
|
152
|
+
a: MetricEventNames,
|
|
153
|
+
b: MetricEventNames,
|
|
154
|
+
clampValues?: {minimum?: number; maximum?: number}
|
|
155
|
+
) {
|
|
152
156
|
const start = this.latencyTimestamps.get(a);
|
|
153
157
|
const end = this.latencyTimestamps.get(b);
|
|
154
158
|
|
|
155
|
-
if (typeof start
|
|
156
|
-
return
|
|
159
|
+
if (typeof start !== 'number' || typeof end !== 'number') {
|
|
160
|
+
return undefined;
|
|
157
161
|
}
|
|
158
162
|
|
|
159
|
-
|
|
163
|
+
const diff = end - start;
|
|
164
|
+
|
|
165
|
+
if (!clampValues) {
|
|
166
|
+
return diff;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const {minimum = 0, maximum} = clampValues;
|
|
170
|
+
|
|
171
|
+
return Math.min(maximum ?? Infinity, Math.max(diff, minimum));
|
|
160
172
|
}
|
|
161
173
|
|
|
162
174
|
/**
|
|
@@ -172,7 +184,8 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
172
184
|
public getMeetingInfoReqResp() {
|
|
173
185
|
return this.getDiffBetweenTimestamps(
|
|
174
186
|
'internal.client.meetinginfo.request',
|
|
175
|
-
'internal.client.meetinginfo.response'
|
|
187
|
+
'internal.client.meetinginfo.response',
|
|
188
|
+
{maximum: 1200000}
|
|
176
189
|
);
|
|
177
190
|
}
|
|
178
191
|
|
|
@@ -215,7 +228,8 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
215
228
|
public getCallInitJoinReq() {
|
|
216
229
|
return this.getDiffBetweenTimestamps(
|
|
217
230
|
'internal.client.interstitial-window.click.joinbutton',
|
|
218
|
-
'client.locus.join.request'
|
|
231
|
+
'client.locus.join.request',
|
|
232
|
+
{maximum: 1200000}
|
|
219
233
|
);
|
|
220
234
|
}
|
|
221
235
|
|
|
@@ -224,7 +238,11 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
224
238
|
* @returns - latency
|
|
225
239
|
*/
|
|
226
240
|
public getJoinReqResp() {
|
|
227
|
-
return this.getDiffBetweenTimestamps(
|
|
241
|
+
return this.getDiffBetweenTimestamps(
|
|
242
|
+
'client.locus.join.request',
|
|
243
|
+
'client.locus.join.response',
|
|
244
|
+
{maximum: 1200000}
|
|
245
|
+
);
|
|
228
246
|
}
|
|
229
247
|
|
|
230
248
|
/**
|
|
@@ -245,7 +263,8 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
245
263
|
public getLocalSDPGenRemoteSDPRecv() {
|
|
246
264
|
return this.getDiffBetweenTimestamps(
|
|
247
265
|
'client.media-engine.local-sdp-generated',
|
|
248
|
-
'client.media-engine.remote-sdp-received'
|
|
266
|
+
'client.media-engine.remote-sdp-received',
|
|
267
|
+
{maximum: 1200000}
|
|
249
268
|
);
|
|
250
269
|
}
|
|
251
270
|
|
|
@@ -254,7 +273,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
254
273
|
* @returns - latency
|
|
255
274
|
*/
|
|
256
275
|
public getICESetupTime() {
|
|
257
|
-
return this.getDiffBetweenTimestamps('client.ice.start', 'client.ice.end');
|
|
276
|
+
return this.getDiffBetweenTimestamps('client.ice.start', 'client.ice.end', {maximum: 1200000});
|
|
258
277
|
}
|
|
259
278
|
|
|
260
279
|
/**
|
|
@@ -378,7 +397,8 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
378
397
|
public getCallInitMediaEngineReady() {
|
|
379
398
|
return this.getDiffBetweenTimestamps(
|
|
380
399
|
'internal.client.interstitial-window.click.joinbutton',
|
|
381
|
-
'client.media-engine.ready'
|
|
400
|
+
'client.media-engine.ready',
|
|
401
|
+
{maximum: 1200000}
|
|
382
402
|
);
|
|
383
403
|
}
|
|
384
404
|
|
|
@@ -398,7 +418,9 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
398
418
|
const lobbyTime = typeof lobbyTimeLatency === 'number' ? lobbyTimeLatency : 0;
|
|
399
419
|
|
|
400
420
|
if (interstitialJoinClickTimestamp && connectedMedia) {
|
|
401
|
-
|
|
421
|
+
const interstitialToMediaOKJmt = connectedMedia - interstitialJoinClickTimestamp - lobbyTime;
|
|
422
|
+
|
|
423
|
+
return Math.max(0, interstitialToMediaOKJmt);
|
|
402
424
|
}
|
|
403
425
|
|
|
404
426
|
return undefined;
|
|
@@ -463,12 +485,12 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
463
485
|
const lobbyTime = this.getStayLobbyTime();
|
|
464
486
|
|
|
465
487
|
if (clickToInterstitial && interstitialToJoinOk && joinConfJMT) {
|
|
466
|
-
const totalMediaJMT = clickToInterstitial + interstitialToJoinOk + joinConfJMT;
|
|
488
|
+
const totalMediaJMT = Math.max(0, clickToInterstitial + interstitialToJoinOk + joinConfJMT);
|
|
467
489
|
if (this.getMeeting()?.allowMediaInLobby) {
|
|
468
490
|
return totalMediaJMT;
|
|
469
491
|
}
|
|
470
492
|
|
|
471
|
-
return totalMediaJMT - lobbyTime;
|
|
493
|
+
return Math.max(0, totalMediaJMT - lobbyTime);
|
|
472
494
|
}
|
|
473
495
|
|
|
474
496
|
return undefined;
|
|
@@ -484,7 +506,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
484
506
|
const joinConfJMT = this.getJoinConfJMT();
|
|
485
507
|
|
|
486
508
|
if (clickToInterstitialWithUserDelay && interstitialToJoinOk && joinConfJMT) {
|
|
487
|
-
return clickToInterstitialWithUserDelay + interstitialToJoinOk + joinConfJMT;
|
|
509
|
+
return Math.max(0, clickToInterstitialWithUserDelay + interstitialToJoinOk + joinConfJMT);
|
|
488
510
|
}
|
|
489
511
|
|
|
490
512
|
return undefined;
|
|
@@ -499,7 +521,7 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
499
521
|
const joinConfJMT = this.getJoinConfJMT();
|
|
500
522
|
|
|
501
523
|
if (typeof interstitialToJoinOk === 'number' && typeof joinConfJMT === 'number') {
|
|
502
|
-
return interstitialToJoinOk - joinConfJMT;
|
|
524
|
+
return Math.max(0, interstitialToJoinOk - joinConfJMT);
|
|
503
525
|
}
|
|
504
526
|
|
|
505
527
|
return undefined;
|
|
@@ -544,6 +566,16 @@ export default class CallDiagnosticLatencies extends WebexPlugin {
|
|
|
544
566
|
return this.getDiffBetweenTimestamps('client.locus.join.response', 'client.media.tx.start');
|
|
545
567
|
}
|
|
546
568
|
|
|
569
|
+
/**
|
|
570
|
+
* Time from share initiation to share stop (ms).
|
|
571
|
+
*/
|
|
572
|
+
public getShareDuration() {
|
|
573
|
+
return this.getDiffBetweenTimestamps(
|
|
574
|
+
'internal.client.share.initiated',
|
|
575
|
+
'internal.client.share.stopped'
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
547
579
|
/**
|
|
548
580
|
* Total latency for all exchange ci token.
|
|
549
581
|
*/
|
|
@@ -105,6 +105,8 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
105
105
|
private delayedClientFeatureEvents: DelayedClientFeatureEvent[] = [];
|
|
106
106
|
private eventErrorCache: WeakMap<any, any> = new WeakMap();
|
|
107
107
|
private isMercuryConnected = false;
|
|
108
|
+
private eventLimitTracker: Map<string, number> = new Map();
|
|
109
|
+
private eventLimitWarningsLogged: Set<string> = new Set();
|
|
108
110
|
|
|
109
111
|
// the default validator before piping an event to the batcher
|
|
110
112
|
// this function can be overridden by the user
|
|
@@ -665,6 +667,144 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
665
667
|
this.eventErrorCache = new WeakMap();
|
|
666
668
|
}
|
|
667
669
|
|
|
670
|
+
/**
|
|
671
|
+
* Checks if an event should be limited based on criteria defined in the event dictionary.
|
|
672
|
+
* Returns true if the event should be sent, false if it has reached its limit.
|
|
673
|
+
* @param event - The diagnostic event object
|
|
674
|
+
* @returns boolean indicating whether the event should be sent
|
|
675
|
+
*/
|
|
676
|
+
private shouldSendEvent({event}: Event): boolean {
|
|
677
|
+
const eventName = event?.name as string;
|
|
678
|
+
const correlationId = event?.identifiers?.correlationId;
|
|
679
|
+
|
|
680
|
+
if (!correlationId || correlationId === 'unknown') {
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
const limitKeyPrefix = `${eventName}:${correlationId}`;
|
|
685
|
+
|
|
686
|
+
switch (eventName) {
|
|
687
|
+
case 'client.media.render.start':
|
|
688
|
+
case 'client.media.render.stop':
|
|
689
|
+
case 'client.media.rx.start':
|
|
690
|
+
case 'client.media.rx.stop':
|
|
691
|
+
case 'client.media.tx.start':
|
|
692
|
+
case 'client.media.tx.stop': {
|
|
693
|
+
// Send only once per mediaType-correlationId pair (or mediaType-correlationId-shareInstanceId for share/share_audio)
|
|
694
|
+
const mediaType = event?.mediaType;
|
|
695
|
+
if (mediaType) {
|
|
696
|
+
if (mediaType === 'share' || mediaType === 'share_audio') {
|
|
697
|
+
const shareInstanceId = event?.shareInstanceId;
|
|
698
|
+
if (shareInstanceId) {
|
|
699
|
+
const limitKey = `${limitKeyPrefix}:${mediaType}:${shareInstanceId}`;
|
|
700
|
+
|
|
701
|
+
return this.checkAndIncrementEventCount(
|
|
702
|
+
limitKey,
|
|
703
|
+
1,
|
|
704
|
+
`${eventName} for ${mediaType} instance ${shareInstanceId}`
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
} else {
|
|
708
|
+
const limitKey = `${limitKeyPrefix}:${mediaType}`;
|
|
709
|
+
|
|
710
|
+
return this.checkAndIncrementEventCount(
|
|
711
|
+
limitKey,
|
|
712
|
+
1,
|
|
713
|
+
`${eventName} for mediaType ${mediaType}`
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
break;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
case 'client.roap-message.received':
|
|
721
|
+
case 'client.roap-message.sent': {
|
|
722
|
+
// Send only once per correlationId and roap.messageType/roap.type
|
|
723
|
+
const roapMessageType = event?.roap?.messageType || event?.roap?.type;
|
|
724
|
+
if (roapMessageType) {
|
|
725
|
+
const limitKey = `${limitKeyPrefix}:${roapMessageType}`;
|
|
726
|
+
|
|
727
|
+
return this.checkAndIncrementEventCount(
|
|
728
|
+
limitKey,
|
|
729
|
+
1,
|
|
730
|
+
`${eventName} for ROAP type ${roapMessageType}`
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
break;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
default:
|
|
737
|
+
return true;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
return true;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Checks the current count for a limit key and increments if under limit.
|
|
745
|
+
* @param limitKey - The unique key for this limit combination
|
|
746
|
+
* @param maxCount - Maximum allowed count
|
|
747
|
+
* @param eventDescription - Description for logging
|
|
748
|
+
* @returns true if under limit and incremented, false if at/over limit
|
|
749
|
+
*/
|
|
750
|
+
private checkAndIncrementEventCount(
|
|
751
|
+
limitKey: string,
|
|
752
|
+
maxCount: number,
|
|
753
|
+
eventDescription: string
|
|
754
|
+
): boolean {
|
|
755
|
+
const currentCount = this.eventLimitTracker.get(limitKey) || 0;
|
|
756
|
+
|
|
757
|
+
if (currentCount >= maxCount) {
|
|
758
|
+
// Log warning only once per limit key
|
|
759
|
+
if (!this.eventLimitWarningsLogged.has(limitKey)) {
|
|
760
|
+
this.logger.log(
|
|
761
|
+
CALL_DIAGNOSTIC_LOG_IDENTIFIER,
|
|
762
|
+
`CallDiagnosticMetrics: Event limit reached for ${eventDescription}. ` +
|
|
763
|
+
`Max count ${maxCount} exceeded. Event will not be sent.`,
|
|
764
|
+
`limitKey: ${limitKey}`
|
|
765
|
+
);
|
|
766
|
+
this.eventLimitWarningsLogged.add(limitKey);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
return false;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// Increment count and allow event
|
|
773
|
+
this.eventLimitTracker.set(limitKey, currentCount + 1);
|
|
774
|
+
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Clears event limit tracking
|
|
780
|
+
*/
|
|
781
|
+
public clearEventLimits(): void {
|
|
782
|
+
this.eventLimitTracker.clear();
|
|
783
|
+
this.eventLimitWarningsLogged.clear();
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* Clears event limit tracking for a specific correlationId only.
|
|
788
|
+
* Keeps limits for other meetings intact.
|
|
789
|
+
*/
|
|
790
|
+
public clearEventLimitsForCorrelationId(correlationId: string): void {
|
|
791
|
+
if (!correlationId) {
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
// Keys are formatted as "eventName:correlationId:..." across all limiters.
|
|
795
|
+
const hasCorrIdAtSecondToken = (key: string) => key.split(':')[1] === correlationId;
|
|
796
|
+
for (const key of Array.from(this.eventLimitTracker.keys())) {
|
|
797
|
+
if (hasCorrIdAtSecondToken(key)) {
|
|
798
|
+
this.eventLimitTracker.delete(key);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
for (const key of Array.from(this.eventLimitWarningsLogged.values())) {
|
|
802
|
+
if (hasCorrIdAtSecondToken(key)) {
|
|
803
|
+
this.eventLimitWarningsLogged.delete(key);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
668
808
|
/**
|
|
669
809
|
* Generate error payload for Client Event
|
|
670
810
|
* @param rawError
|
|
@@ -1099,6 +1239,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1099
1239
|
);
|
|
1100
1240
|
const diagnosticEvent = this.prepareClientEvent({name, payload, options});
|
|
1101
1241
|
|
|
1242
|
+
if (!this.shouldSendEvent(diagnosticEvent)) {
|
|
1243
|
+
return Promise.resolve();
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1102
1246
|
if (options?.preLoginId) {
|
|
1103
1247
|
return this.submitToCallDiagnosticsPreLogin(diagnosticEvent, options?.preLoginId);
|
|
1104
1248
|
}
|
|
@@ -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,
|
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
|
@@ -160,7 +160,9 @@ export type InternalEvent = {
|
|
|
160
160
|
| 'internal.client.meeting.interstitial-window.showed'
|
|
161
161
|
| 'internal.client.interstitial-window.click.joinbutton'
|
|
162
162
|
| 'internal.client.add-media.turn-discovery.start'
|
|
163
|
-
| 'internal.client.add-media.turn-discovery.end'
|
|
163
|
+
| 'internal.client.add-media.turn-discovery.end'
|
|
164
|
+
| 'internal.client.share.initiated'
|
|
165
|
+
| 'internal.client.share.stopped';
|
|
164
166
|
|
|
165
167
|
payload?: never;
|
|
166
168
|
options?: never;
|