Skip to content

Commit

Permalink
Report the value suggestions funnel
Browse files Browse the repository at this point in the history
  • Loading branch information
Liza K committed Feb 16, 2021
1 parent 320e723 commit 69cdc32
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 17 deletions.
14 changes: 12 additions & 2 deletions src/plugins/data/public/autocomplete/autocomplete_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
} from './providers/value_suggestion_provider';

import { ConfigSchema } from '../../config';
import { UsageCollectionSetup } from '../../../usage_collection/public';
import { createUsageCollector } from './collectors';

export class AutocompleteService {
autocompleteConfig: ConfigSchema['autocomplete'];
Expand Down Expand Up @@ -47,9 +49,17 @@ export class AutocompleteService {
private hasQuerySuggestions = (language: string) => this.querySuggestionProviders.has(language);

/** @public **/
public setup(core: CoreSetup, { timefilter }: { timefilter: TimefilterSetup }) {
public setup(
core: CoreSetup,
{
timefilter,
usageCollection,
}: { timefilter: TimefilterSetup; usageCollection?: UsageCollectionSetup }
) {
const usageCollector = createUsageCollector(core.getStartServices, usageCollection);

this.getValueSuggestions = this.autocompleteConfig.valueSuggestions.enabled
? setupValueSuggestionProvider(core, { timefilter })
? setupValueSuggestionProvider(core, { timefilter, usageCollector })
: getEmptyValueSuggestions;

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { first } from 'rxjs/operators';
import { StartServicesAccessor } from '../../../../../core/public';
import { METRIC_TYPE, UsageCollectionSetup } from '../../../../usage_collection/public';
import { AUTOCOMPLETE_EVENT_TYPE, AutocompleteUsageCollector } from './types';

export const createUsageCollector = (
getStartServices: StartServicesAccessor,
usageCollection?: UsageCollectionSetup
): AutocompleteUsageCollector => {
const getCurrentApp = async () => {
const [{ application }] = await getStartServices();
return application.currentAppId$.pipe(first()).toPromise();
};

return {
trackCall: async () => {
const currentApp = await getCurrentApp();
return usageCollection?.reportUiCounter(
currentApp!,
METRIC_TYPE.LOADED,
AUTOCOMPLETE_EVENT_TYPE.CALL
);
},
trackRequest: async () => {
const currentApp = await getCurrentApp();
return usageCollection?.reportUiCounter(
currentApp!,
METRIC_TYPE.LOADED,
AUTOCOMPLETE_EVENT_TYPE.REQUEST
);
},
trackResult: async () => {
const currentApp = await getCurrentApp();
return usageCollection?.reportUiCounter(
currentApp!,
METRIC_TYPE.LOADED,
AUTOCOMPLETE_EVENT_TYPE.RESULT
);
},
trackError: async () => {
const currentApp = await getCurrentApp();
return usageCollection?.reportUiCounter(
currentApp!,
METRIC_TYPE.LOADED,
AUTOCOMPLETE_EVENT_TYPE.ERROR
);
},
};
};
10 changes: 10 additions & 0 deletions src/plugins/data/public/autocomplete/collectors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export { createUsageCollector } from './create_usage_collector';
export { AUTOCOMPLETE_EVENT_TYPE, AutocompleteUsageCollector } from './types';
21 changes: 21 additions & 0 deletions src/plugins/data/public/autocomplete/collectors/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export enum AUTOCOMPLETE_EVENT_TYPE {
CALL = 'call',
REQUEST = 'req',
RESULT = 'res',
ERROR = 'err',
}

export interface AutocompleteUsageCollector {
trackCall: () => Promise<void>;
trackRequest: () => Promise<void>;
trackResult: () => Promise<void>;
trackError: () => Promise<void>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@ import { memoize } from 'lodash';
import { CoreSetup } from 'src/core/public';
import { IIndexPattern, IFieldType, UI_SETTINGS, buildQueryFromFilters } from '../../../common';
import { TimefilterSetup } from '../../query';

function resolver(title: string, field: IFieldType, query: string, filters: any[]) {
// Only cache results for a minute
const ttl = Math.floor(Date.now() / 1000 / 60);
return [ttl, query, title, field.name, JSON.stringify(filters)].join('|');
}
import { AutocompleteUsageCollector } from '../collectors';

export type ValueSuggestionsGetFn = (args: ValueSuggestionsGetFnArgs) => Promise<any[]>;

Expand Down Expand Up @@ -47,15 +42,32 @@ export const getEmptyValueSuggestions = (() => Promise.resolve([])) as ValueSugg

export const setupValueSuggestionProvider = (
core: CoreSetup,
{ timefilter }: { timefilter: TimefilterSetup }
{
timefilter,
usageCollector,
}: { timefilter: TimefilterSetup; usageCollector?: AutocompleteUsageCollector }
): ValueSuggestionsGetFn => {
function resolver(title: string, field: IFieldType, query: string, filters: any[]) {
usageCollector?.trackCall();
// Only cache results for a minute
const ttl = Math.floor(Date.now() / 1000 / 60);
return [ttl, query, title, field.name, JSON.stringify(filters)].join('|');
}

const requestSuggestions = memoize(
(index: string, field: IFieldType, query: string, filters: any = [], signal?: AbortSignal) =>
core.http.fetch(`/api/kibana/suggestions/values/${index}`, {
method: 'POST',
body: JSON.stringify({ query, field: field.name, filters }),
signal,
}),
(index: string, field: IFieldType, query: string, filters: any = [], signal?: AbortSignal) => {
usageCollector?.trackRequest();
return core.http
.fetch(`/api/kibana/suggestions/values/${index}`, {
method: 'POST',
body: JSON.stringify({ query, field: field.name, filters }),
signal,
})
.then((r) => {
usageCollector?.trackResult();
return r;
});
},
resolver
);

Expand Down Expand Up @@ -85,6 +97,15 @@ export const setupValueSuggestionProvider = (
: undefined;
const filterQuery = timeFilter ? buildQueryFromFilters([timeFilter], indexPattern).filter : [];
const filters = [...(boolFilter ? boolFilter : []), ...filterQuery];
return await requestSuggestions(title, field, query, filters, signal);
try {
return await requestSuggestions(title, field, query, filters, signal);
} catch (e) {
if (!signal?.aborted) {
usageCollector?.trackError();
}
// Remove rejected results from memoize cache
requestSuggestions.cache.delete(resolver(title, field, query, filters));
return [];
}
};
};
5 changes: 4 additions & 1 deletion src/plugins/data/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,10 @@ export class DataPublicPlugin
);

return {
autocomplete: this.autocomplete.setup(core, { timefilter: queryService.timefilter }),
autocomplete: this.autocomplete.setup(core, {
timefilter: queryService.timefilter,
usageCollection,
}),
search: searchService,
fieldFormats: this.fieldFormatsService.setup(core),
query: queryService,
Expand Down

0 comments on commit 69cdc32

Please sign in to comment.