#121908 - [Maps] Use UI counters to instrument when a geo field is visualized with visualizeGeoFieldAction (#123540)

* #121908 - Maps usage tracking feature

* #e-121908 - refactoring; Added naming constants for used apps;

* #121908 - updates for UiCounterMetricType, schema, metric analytics

* #121908 - refactoring; Fixed import syntax, type in report

* #121908 - refactoring

* #121908 - refactoring

* 121908 - refactoring

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Max Kovalev 2022-02-16 01:52:38 +02:00 committed by GitHub
parent 81c5fbf538
commit cf102be8be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 56 additions and 46 deletions

View file

@ -8,16 +8,20 @@
import { METRIC_TYPE } from './';
export type UiCounterMetricType = METRIC_TYPE.CLICK | METRIC_TYPE.LOADED | METRIC_TYPE.COUNT;
export type UiCounterMetricType =
| METRIC_TYPE.CLICK
| METRIC_TYPE.LOADED
| METRIC_TYPE.COUNT
| string;
export interface UiCounterMetricConfig {
type: UiCounterMetricType;
type: string;
appName: string;
eventName: string;
count?: number;
}
export interface UiCounterMetric {
type: UiCounterMetricType;
type: string;
appName: string;
eventName: string;
count: number;

View file

@ -7,11 +7,15 @@
*/
import moment from 'moment-timezone';
import { UnreachableCaseError, wrapArray } from './util';
import { wrapArray } from './util';
import { ApplicationUsageTracker } from './application_usage_tracker';
import { Metric, UiCounterMetricType, METRIC_TYPE } from './metrics';
import { Metric, METRIC_TYPE } from './metrics';
const REPORT_VERSION = 3;
import type { UiCounterMetric, UiCounterMetricType } from './metrics/ui_counter';
import type { UserAgentMetric } from './metrics/user_agent';
import type { ApplicationUsageMetric } from './metrics/application_usage';
export interface Report {
reportVersion: typeof REPORT_VERSION;
uiCounter?: Record<
@ -77,55 +81,35 @@ export class ReportManager {
const { appName, type } = metric;
return `${appName}-${type}`;
}
case METRIC_TYPE.CLICK:
case METRIC_TYPE.LOADED:
case METRIC_TYPE.COUNT: {
const { appName, eventName, type } = metric;
return `${appName}-${type}-${eventName}`;
}
case METRIC_TYPE.APPLICATION_USAGE: {
const { appId, viewId } = metric;
const { appId, viewId } = metric as ApplicationUsageMetric;
return ApplicationUsageTracker.serializeKey({ appId, viewId });
}
default:
throw new UnreachableCaseError(metric);
const { appName, eventName, type } = metric as UiCounterMetric;
return `${appName}-${type}-${eventName}`;
}
}
private assignReport(report: Report, metric: Metric) {
const key = ReportManager.createMetricKey(metric);
switch (metric.type) {
case METRIC_TYPE.USER_AGENT: {
const { appName, type, userAgent } = metric;
const { appName, type, userAgent } = metric as UserAgentMetric;
if (userAgent) {
report.userAgent = {
[key]: {
key,
appName,
type,
userAgent: metric.userAgent,
userAgent,
},
};
}
return;
}
case METRIC_TYPE.CLICK:
case METRIC_TYPE.LOADED:
case METRIC_TYPE.COUNT: {
const { appName, type, eventName, count } = metric;
report.uiCounter = report.uiCounter || {};
const currentTotal = report.uiCounter[key]?.total;
report.uiCounter[key] = {
key,
appName,
eventName,
type,
total: this.incrementTotal(count, currentTotal),
};
return;
}
case METRIC_TYPE.APPLICATION_USAGE: {
const { numberOfClicks, startTime, appId, viewId } = metric;
const { numberOfClicks, startTime, appId, viewId } = metric as ApplicationUsageMetric;
const minutesOnScreen = moment().diff(startTime, 'minutes', true);
report.application_usage = report.application_usage || {};
@ -144,7 +128,17 @@ export class ReportManager {
return;
}
default:
throw new UnreachableCaseError(metric);
const { appName, type, eventName, count } = metric as UiCounterMetric;
report.uiCounter = report.uiCounter || {};
const currentTotal = report.uiCounter[key]?.total;
report.uiCounter[key] = {
key,
appName,
eventName,
type,
total: this.incrementTotal(count, currentTotal),
};
return;
}
}
}

View file

@ -7,13 +7,7 @@
*/
import { wrapArray } from './util';
import {
Metric,
createUiCounterMetric,
trackUsageAgent,
UiCounterMetricType,
ApplicationUsageMetric,
} from './metrics';
import { Metric, createUiCounterMetric, trackUsageAgent, ApplicationUsageMetric } from './metrics';
import { Storage, ReportStorageManager } from './storage';
import { Report, ReportManager } from './report';
@ -77,7 +71,7 @@ export class Reporter {
public reportUiCounter = (
appName: string,
type: UiCounterMetricType,
type: string,
eventNames: string | string[],
count?: number
) => {

View file

@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
export const APP_ID = 'discover';
export const DEFAULT_COLUMNS_SETTING = 'defaultColumns';
export const SAMPLE_SIZE_SETTING = 'discover:sampleSize';
export const SORT_DEFAULT_ORDER_SETTING = 'discover:sort:defaultOrder';

View file

@ -14,6 +14,7 @@ import {
} from '../../../../../../../ui_actions/public';
import { getUiActions } from '../../../../../kibana_services';
import { DataViewField, KBN_FIELD_TYPES } from '../../../../../../../data/common';
import { APP_ID } from '../../../../../../common';
function getTriggerConstant(type: string) {
return type === KBN_FIELD_TYPES.GEO_POINT || type === KBN_FIELD_TYPES.GEO_SHAPE
@ -52,6 +53,7 @@ export function triggerVisualizeActions(
indexPatternId,
fieldName: field.name,
contextualFields,
originatingApp: APP_ID,
};
getUiActions().getTrigger(trigger).exec(triggerOptions);
}

View file

@ -17,6 +17,7 @@ export interface VisualizeFieldContext {
fieldName: string;
indexPatternId: string;
contextualFields?: string[];
originatingApp?: string;
}
export const ACTION_VISUALIZE_FIELD = 'ACTION_VISUALIZE_FIELD';

View file

@ -7,7 +7,6 @@
*/
import { schema, TypeOf } from '@kbn/config-schema';
import { METRIC_TYPE } from '@kbn/analytics';
const applicationUsageReportSchema = schema.object({
minutesOnScreen: schema.number(),
@ -34,11 +33,7 @@ export const reportSchema = schema.object({
schema.string(),
schema.object({
key: schema.string(),
type: schema.oneOf([
schema.literal<METRIC_TYPE>(METRIC_TYPE.CLICK),
schema.literal<METRIC_TYPE>(METRIC_TYPE.LOADED),
schema.literal<METRIC_TYPE>(METRIC_TYPE.COUNT),
]),
type: schema.string(),
appName: schema.string(),
eventName: schema.string(),
total: schema.number(),

View file

@ -8,6 +8,7 @@
import { i18n } from '@kbn/i18n';
import { KBN_FIELD_TYPES } from '../../../../src/plugins/data/common';
export const APP_ID = 'data_visualizer';
export const UI_SETTING_MAX_FILE_SIZE = 'fileUpload:maxFileSize';
export const MB = Math.pow(2, 20);

View file

@ -19,6 +19,7 @@ import {
} from '../../../../index_data_visualizer/services/timefilter_refresh_service';
import { JOB_FIELD_TYPES } from '../../../../../../common/constants';
import { VISUALIZE_GEO_FIELD_TRIGGER } from '../../../../../../../../../src/plugins/ui_actions/public';
import { APP_ID } from '../../../../../../common/constants';
export function getActions(
indexPattern: IndexPattern,
@ -87,6 +88,7 @@ export function getActions(
indexPatternId: indexPattern.id,
fieldName: item.fieldName,
contextualFields: [],
originatingApp: APP_ID,
};
const testActions = await services?.uiActions.getTriggerCompatibleActions(
VISUALIZE_GEO_FIELD_TRIGGER,

View file

@ -16,6 +16,7 @@ import {
import { getVisualizeGeoFieldMessage } from '../../../utils';
import { DragDrop } from '../../../drag_drop';
import { GlobeIllustration } from '../../../assets/globe_illustration';
import { APP_ID } from '../../../../common/constants';
import './geo_field_workspace_panel.scss';
interface Props {
@ -41,6 +42,7 @@ export function GeoFieldWorkspacePanel(props: Props) {
props.uiActions.getTrigger(VISUALIZE_GEO_FIELD_TRIGGER).exec({
indexPatternId: props.indexPatternId,
fieldName: props.fieldName,
originatingApp: APP_ID,
});
}

View file

@ -13,6 +13,7 @@ import {
VISUALIZE_GEO_FIELD_TRIGGER,
UiActionsStart,
} from '../../../../../src/plugins/ui_actions/public';
import { APP_ID } from '../../common/constants';
interface Props {
indexPatternId: string;
@ -50,6 +51,7 @@ export function VisualizeGeoFieldButton(props: Props) {
props.uiActions.getTrigger(VISUALIZE_GEO_FIELD_TRIGGER).exec({
indexPatternId: props.indexPatternId,
fieldName: props.fieldName,
originatingApp: APP_ID,
});
}

View file

@ -55,6 +55,7 @@ export const getPresentationUtilContext = () => pluginsStart.presentationUtil.Co
export const getSecurityService = () => pluginsStart.security;
export const getSpacesApi = () => pluginsStart.spaces;
export const getTheme = () => coreStart.theme;
export const getUsageCollection = () => pluginsStart.usageCollection;
// xpack.maps.* kibana.yml settings from this plugin
let mapAppConfig: MapsConfigType;

View file

@ -103,6 +103,7 @@ export interface MapsPluginStartDependencies {
security?: SecurityPluginStart;
spaces?: SpacesPluginStart;
mapsEms: MapsEmsPluginPublicStart;
usageCollection?: UsageCollectionSetup;
}
/**

View file

@ -8,6 +8,9 @@
import uuid from 'uuid/v4';
import { i18n } from '@kbn/i18n';
import type { SerializableRecord } from '@kbn/utility-types';
import { getUsageCollection } from '../kibana_services';
import { APP_ID } from '../../common/constants';
import {
createAction,
ACTION_VISUALIZE_GEO_FIELD,
@ -43,6 +46,13 @@ export const visualizeGeoFieldAction = createAction<VisualizeFieldContext>({
execute: async (context) => {
const { app, path, state } = await getMapsLink(context);
const usageCollection = getUsageCollection();
usageCollection?.reportUiCounter(
APP_ID,
'visualize_geo_field',
context.originatingApp ? context.originatingApp : 'unknownOriginatingApp'
);
getCore().application.navigateToApp(app, {
path,
state,