Skip to content

Commit

Permalink
Extract esaggs start dependencies.
Browse files Browse the repository at this point in the history
  • Loading branch information
lukeelmers committed Nov 12, 2020
1 parent 17715c7 commit be56df1
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 120 deletions.
18 changes: 16 additions & 2 deletions src/plugins/data/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import {
setUiSettings,
} from './services';
import { createSearchBar } from './ui/search_bar/create_search_bar';
import { esaggs } from './search/expressions';
import { getEsaggs } from './search/expressions';
import {
SELECT_RANGE_TRIGGER,
VALUE_CLICK_TRIGGER,
Expand Down Expand Up @@ -109,8 +109,22 @@ export class DataPublicPlugin
): DataPublicPluginSetup {
const startServices = createStartServicesGetter(core.getStartServices);

expressions.registerFunction(esaggs);
expressions.registerFunction(indexPatternLoad);
expressions.registerFunction(
getEsaggs({
getStartDependencies: async () => {
const [, , self] = await core.getStartServices();
const { fieldFormats, indexPatterns, query, search } = self;
return {
addFilters: query.filterManager.addFilters.bind(query.filterManager),
aggs: search.aggs,
deserializeFieldFormat: fieldFormats.deserialize.bind(fieldFormats),
indexPatterns,
searchSource: search.searchSource,
};
},
})
);

const queryService = this.queryService.setup({
uiSettings: core.uiSettings,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,20 @@ import {
TabularData,
TabularDataValue,
} from '../../../../../../plugins/inspector/common';
import { TabbedTable } from '../../../../common';
import { Filter, TabbedTable } from '../../../../common';
import { FormatFactory } from '../../../../common/field_formats/utils';
import { FilterManager } from '../../../query';
import { createFilter } from '../create_filter';

/**
* Type borrowed from the client-side FilterManager['addFilters'].
*
* We need to use a custom type to make this isomorphic since FilterManager
* doesn't exist on the server.
*
* @internal
*/
export type AddFilters = (filters: Filter[] | Filter, pinFilterStatus?: boolean) => void;

/**
* This function builds tabular data from the response and attaches it to the
* inspector. It will only be called when the data view in the inspector is opened.
Expand All @@ -40,7 +49,7 @@ export async function buildTabularInspectorData(
addFilters,
deserializeFieldFormat,
}: {
addFilters?: FilterManager['addFilters'];
addFilters?: AddFilters;
deserializeFieldFormat: FormatFactory;
}
): Promise<TabularData> {
Expand Down
225 changes: 118 additions & 107 deletions src/plugins/data/public/search/expressions/esaggs/esaggs_fn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,121 +24,132 @@ import { Datatable, DatatableColumn } from 'src/plugins/expressions/common';
import { Adapters } from 'src/plugins/inspector/common';

import { calculateBounds, EsaggsExpressionFunctionDefinition } from '../../../../common';
import {
getFieldFormats,
getIndexPatterns,
getQueryService,
getSearchService,
} from '../../../services';
import { FormatFactory } from '../../../../common/field_formats/utils';
import { IndexPatternsContract } from '../../../../common/index_patterns/index_patterns';
import { ISearchStartSearchSource, AggsStart } from '../../../../common/search';

import { AddFilters } from './build_tabular_inspector_data';
import { handleRequest } from './request_handler';

const name = 'esaggs';

export const esaggs = (): EsaggsExpressionFunctionDefinition => ({
name,
type: 'datatable',
inputTypes: ['kibana_context', 'null'],
help: i18n.translate('data.functions.esaggs.help', {
defaultMessage: 'Run AggConfig aggregation',
}),
args: {
index: {
types: ['string'],
help: '',
},
metricsAtAllLevels: {
types: ['boolean'],
default: false,
help: '',
},
partialRows: {
types: ['boolean'],
default: false,
help: '',
},
includeFormatHints: {
types: ['boolean'],
default: false,
help: '',
},
aggConfigs: {
types: ['string'],
default: '""',
help: '',
},
timeFields: {
types: ['string'],
help: '',
multi: true,
},
},
async fn(input, args, { inspectorAdapters, abortSignal, getSearchSessionId }) {
const indexPatterns = getIndexPatterns();
const { filterManager } = getQueryService();
const searchService = getSearchService();

const aggConfigsState = JSON.parse(args.aggConfigs);
const indexPattern = await indexPatterns.get(args.index);
const aggs = searchService.aggs.createAggConfigs(indexPattern, aggConfigsState);
interface StartDependencies {
addFilters: AddFilters;
aggs: AggsStart;
deserializeFieldFormat: FormatFactory;
indexPatterns: IndexPatternsContract;
searchSource: ISearchStartSearchSource;
}

// we should move searchSource creation inside request handler
const searchSource = await searchService.searchSource.create();
export function getEsaggs({
getStartDependencies,
}: {
getStartDependencies: () => Promise<StartDependencies>;
}) {
return (): EsaggsExpressionFunctionDefinition => ({
name,
type: 'datatable',
inputTypes: ['kibana_context', 'null'],
help: i18n.translate('data.functions.esaggs.help', {
defaultMessage: 'Run AggConfig aggregation',
}),
args: {
index: {
types: ['string'],
help: '',
},
metricsAtAllLevels: {
types: ['boolean'],
default: false,
help: '',
},
partialRows: {
types: ['boolean'],
default: false,
help: '',
},
includeFormatHints: {
types: ['boolean'],
default: false,
help: '',
},
aggConfigs: {
types: ['string'],
default: '""',
help: '',
},
timeFields: {
types: ['string'],
help: '',
multi: true,
},
},
async fn(input, args, { inspectorAdapters, abortSignal, getSearchSessionId }) {
const {
addFilters,
aggs,
deserializeFieldFormat,
indexPatterns,
searchSource,
} = await getStartDependencies();

searchSource.setField('index', indexPattern);
searchSource.setField('size', 0);
const aggConfigsState = JSON.parse(args.aggConfigs);
const indexPattern = await indexPatterns.get(args.index);
const aggConfigs = aggs.createAggConfigs(indexPattern, aggConfigsState);

const resolvedTimeRange = input?.timeRange && calculateBounds(input.timeRange);
const resolvedTimeRange = input?.timeRange && calculateBounds(input.timeRange);

const response = await handleRequest({
abortSignal: (abortSignal as unknown) as AbortSignal,
addFilters: filterManager.addFilters,
aggs,
deserializeFieldFormat: getFieldFormats().deserialize,
filters: get(input, 'filters', undefined),
indexPattern,
inspectorAdapters: inspectorAdapters as Adapters,
metricsAtAllLevels: args.metricsAtAllLevels,
partialRows: args.partialRows,
query: get(input, 'query', undefined) as any,
searchSessionId: getSearchSessionId(),
searchSource,
timeFields: args.timeFields,
timeRange: get(input, 'timeRange', undefined),
});
const response = await handleRequest({
abortSignal: (abortSignal as unknown) as AbortSignal,
addFilters,
aggs: aggConfigs,
deserializeFieldFormat,
filters: get(input, 'filters', undefined),
indexPattern,
inspectorAdapters: inspectorAdapters as Adapters,
metricsAtAllLevels: args.metricsAtAllLevels,
partialRows: args.partialRows,
query: get(input, 'query', undefined) as any,
searchSessionId: getSearchSessionId(),
searchSourceService: searchSource,
timeFields: args.timeFields,
timeRange: get(input, 'timeRange', undefined),
});

const table: Datatable = {
type: 'datatable',
rows: response.rows,
columns: response.columns.map((column) => {
const cleanedColumn: DatatableColumn = {
id: column.id,
name: column.name,
meta: {
type: column.aggConfig.params.field?.type || 'number',
field: column.aggConfig.params.field?.name,
index: indexPattern.title,
params: column.aggConfig.toSerializedFieldFormat(),
source: 'esaggs',
sourceParams: {
indexPatternId: indexPattern.id,
appliedTimeRange:
column.aggConfig.params.field?.name &&
input?.timeRange &&
args.timeFields &&
args.timeFields.includes(column.aggConfig.params.field?.name)
? {
from: resolvedTimeRange?.min?.toISOString(),
to: resolvedTimeRange?.max?.toISOString(),
}
: undefined,
...column.aggConfig.serialize(),
const table: Datatable = {
type: 'datatable',
rows: response.rows,
columns: response.columns.map((column) => {
const cleanedColumn: DatatableColumn = {
id: column.id,
name: column.name,
meta: {
type: column.aggConfig.params.field?.type || 'number',
field: column.aggConfig.params.field?.name,
index: indexPattern.title,
params: column.aggConfig.toSerializedFieldFormat(),
source: name,
sourceParams: {
indexPatternId: indexPattern.id,
appliedTimeRange:
column.aggConfig.params.field?.name &&
input?.timeRange &&
args.timeFields &&
args.timeFields.includes(column.aggConfig.params.field?.name)
? {
from: resolvedTimeRange?.min?.toISOString(),
to: resolvedTimeRange?.max?.toISOString(),
}
: undefined,
...column.aggConfig.serialize(),
},
},
},
};
return cleanedColumn;
}),
};
};
return cleanedColumn;
}),
};

return table;
},
});
return table;
},
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
calculateBounds,
Filter,
getTime,
IIndexPattern,
IndexPattern,
isRangeFilter,
Query,
TimeRange,
Expand All @@ -33,27 +33,26 @@ import {
getRequestInspectorStats,
getResponseInspectorStats,
IAggConfigs,
ISearchSource,
ISearchStartSearchSource,
tabifyAggResponse,
} from '../../../../common/search';
import { FormatFactory } from '../../../../common/field_formats/utils';

import { FilterManager } from '../../../query';
import { buildTabularInspectorData } from './build_tabular_inspector_data';
import { AddFilters, buildTabularInspectorData } from './build_tabular_inspector_data';

interface RequestHandlerParams {
abortSignal?: AbortSignal;
addFilters?: FilterManager['addFilters'];
addFilters?: AddFilters;
aggs: IAggConfigs;
deserializeFieldFormat: FormatFactory;
filters?: Filter[];
indexPattern?: IIndexPattern;
indexPattern?: IndexPattern;
inspectorAdapters: Adapters;
metricsAtAllLevels?: boolean;
partialRows?: boolean;
query?: Query;
searchSessionId?: string;
searchSource: ISearchSource;
searchSourceService: ISearchStartSearchSource;
timeFields?: string[];
timeRange?: TimeRange;
}
Expand All @@ -70,10 +69,15 @@ export const handleRequest = async ({
partialRows,
query,
searchSessionId,
searchSource,
searchSourceService,
timeFields,
timeRange,
}: RequestHandlerParams) => {
const searchSource = await searchSourceService.create();

searchSource.setField('index', indexPattern);
searchSource.setField('size', 0);

// Create a new search source that inherits the original search source
// but has the appropriate timeRange applied via a filter.
// This is a temporary solution until we properly pass down all required
Expand Down

0 comments on commit be56df1

Please sign in to comment.