diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 9121b0aade470..08ed14b92d24c 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -89,7 +89,7 @@ | [SavedQueryService](./kibana-plugin-plugins-data-public.savedqueryservice.md) | | | [SearchError](./kibana-plugin-plugins-data-public.searcherror.md) | | | [SearchInterceptorDeps](./kibana-plugin-plugins-data-public.searchinterceptordeps.md) | | -| [SearchSessionInfoProvider](./kibana-plugin-plugins-data-public.searchSessionInfoprovider.md) | Provide info about current search session to be stored in backgroundSearch saved object | +| [SearchSessionInfoProvider](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.md) | Provide info about current search session to be stored in backgroundSearch saved object | | [SearchSourceFields](./kibana-plugin-plugins-data-public.searchsourcefields.md) | search source fields | | [TabbedAggColumn](./kibana-plugin-plugins-data-public.tabbedaggcolumn.md) | \* | | [TabbedTable](./kibana-plugin-plugins-data-public.tabbedtable.md) | \* | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessionrestorationinfoprovider.getname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessioninfoprovider.getname.md similarity index 83% rename from docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessionrestorationinfoprovider.getname.md rename to docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessioninfoprovider.getname.md index 0f0b616066dd6..2a5e1d2a3135f 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessionrestorationinfoprovider.getname.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessioninfoprovider.getname.md @@ -1,6 +1,6 @@ -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSessionInfoProvider](./kibana-plugin-plugins-data-public.searchSessionInfoprovider.md) > [getName](./kibana-plugin-plugins-data-public.searchSessionInfoprovider.getname.md) +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSessionInfoProvider](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.md) > [getName](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.getname.md) ## SearchSessionInfoProvider.getName property diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessionrestorationinfoprovider.geturlgeneratordata.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessioninfoprovider.geturlgeneratordata.md similarity index 82% rename from docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessionrestorationinfoprovider.geturlgeneratordata.md rename to docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessioninfoprovider.geturlgeneratordata.md index 207adaf2bd50b..01558ed3dddad 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessionrestorationinfoprovider.geturlgeneratordata.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessioninfoprovider.geturlgeneratordata.md @@ -1,6 +1,6 @@ -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSessionInfoProvider](./kibana-plugin-plugins-data-public.searchSessionInfoprovider.md) > [getUrlGeneratorData](./kibana-plugin-plugins-data-public.searchSessionInfoprovider.geturlgeneratordata.md) +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSessionInfoProvider](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.md) > [getUrlGeneratorData](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.geturlgeneratordata.md) ## SearchSessionInfoProvider.getUrlGeneratorData property diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessionrestorationinfoprovider.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessioninfoprovider.md similarity index 84% rename from docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessionrestorationinfoprovider.md rename to docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessioninfoprovider.md index a3d294f5e3303..bcc4a5508eb59 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessionrestorationinfoprovider.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsessioninfoprovider.md @@ -1,6 +1,6 @@ -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSessionInfoProvider](./kibana-plugin-plugins-data-public.searchSessionInfoprovider.md) +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSessionInfoProvider](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.md) ## SearchSessionInfoProvider interface @@ -16,6 +16,6 @@ export interface SearchSessionInfoProvider() => Promise<string> | User-facing name of the session. e.g. will be displayed in background sessions management list | -| [getUrlGeneratorData](./kibana-plugin-plugins-data-public.searchSessionInfoprovider.geturlgeneratordata.md) | () => Promise<{
urlGeneratorId: ID;
initialState: UrlGeneratorStateMapping[ID]['State'];
restoreState: UrlGeneratorStateMapping[ID]['State'];
}> | | +| [getName](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.getname.md) | () => Promise<string> | User-facing name of the session. e.g. will be displayed in background sessions management list | +| [getUrlGeneratorData](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.geturlgeneratordata.md) | () => Promise<{
urlGeneratorId: ID;
initialState: UrlGeneratorStateMapping[ID]['State'];
restoreState: UrlGeneratorStateMapping[ID]['State'];
}> | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md index 1980227bee623..5a6e873a4f861 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md @@ -13,7 +13,7 @@ getFields(): { type?: string | undefined; query?: import("../..").Query | undefined; filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined; - sort?: Record | Record[] | undefined; + sort?: Record | Record[] | undefined; highlight?: any; highlightAll?: boolean | undefined; aggs?: any; @@ -34,7 +34,7 @@ getFields(): { type?: string | undefined; query?: import("../..").Query | undefined; filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined; - sort?: Record | Record[] | undefined; + sort?: Record | Record[] | undefined; highlight?: any; highlightAll?: boolean | undefined; aggs?: any; diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.md index aa78c055f4f5c..439f4ff9fa78d 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.md @@ -14,6 +14,6 @@ export declare class IndexPatternsService implements PluginSignature: ```typescript -setup(core: CoreSetup): void; +setup(core: CoreSetup, { expressions }: IndexPatternsServiceSetupDeps): void; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| core | CoreSetup | | +| core | CoreSetup<DataPluginStartDependencies, DataPluginStart> | | +| { expressions } | IndexPatternsServiceSetupDeps | | Returns: diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.getkibanarequest.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.getkibanarequest.md new file mode 100644 index 0000000000000..a731d08a0d694 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.getkibanarequest.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-public.executioncontext.md) > [getKibanaRequest](./kibana-plugin-plugins-expressions-public.executioncontext.getkibanarequest.md) + +## ExecutionContext.getKibanaRequest property + +Getter to retrieve the `KibanaRequest` object inside an expression function. Useful for functions which are running on the server and need to perform operations that are scoped to a specific user. + +Signature: + +```typescript +getKibanaRequest?: () => KibanaRequest; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.md index 86d24534f7a44..1c0d10a382abf 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.md @@ -17,6 +17,7 @@ export interface ExecutionContextAbortSignal | Adds ability to abort current execution. | +| [getKibanaRequest](./kibana-plugin-plugins-expressions-public.executioncontext.getkibanarequest.md) | () => KibanaRequest | Getter to retrieve the KibanaRequest object inside an expression function. Useful for functions which are running on the server and need to perform operations that are scoped to a specific user. | | [getSavedObject](./kibana-plugin-plugins-expressions-public.executioncontext.getsavedobject.md) | <T extends SavedObjectAttributes = SavedObjectAttributes>(type: string, id: string) => Promise<SavedObject<T>> | Allows to fetch saved objects from ElasticSearch. In browser getSavedObject function is provided automatically by the Expressions plugin. On the server the caller of the expression has to provide this context function. The reason is because on the browser we always know the user who tries to fetch a saved object, thus saved object client is scoped automatically to that user. However, on the server we can scope that saved object client to any user, or even not scope it at all and execute it as an "internal" user. | | [getSearchContext](./kibana-plugin-plugins-expressions-public.executioncontext.getsearchcontext.md) | () => ExecutionContextSearch | Get search context of the expression. | | [getSearchSessionId](./kibana-plugin-plugins-expressions-public.executioncontext.getsearchsessionid.md) | () => string | undefined | Search context in which expression should operate. | diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.getkibanarequest.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.getkibanarequest.md new file mode 100644 index 0000000000000..203794a9d0302 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.getkibanarequest.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-server.executioncontext.md) > [getKibanaRequest](./kibana-plugin-plugins-expressions-server.executioncontext.getkibanarequest.md) + +## ExecutionContext.getKibanaRequest property + +Getter to retrieve the `KibanaRequest` object inside an expression function. Useful for functions which are running on the server and need to perform operations that are scoped to a specific user. + +Signature: + +```typescript +getKibanaRequest?: () => KibanaRequest; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.md index e2547cc9470d1..fbf9dc634d563 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.md @@ -17,6 +17,7 @@ export interface ExecutionContextAbortSignal | Adds ability to abort current execution. | +| [getKibanaRequest](./kibana-plugin-plugins-expressions-server.executioncontext.getkibanarequest.md) | () => KibanaRequest | Getter to retrieve the KibanaRequest object inside an expression function. Useful for functions which are running on the server and need to perform operations that are scoped to a specific user. | | [getSavedObject](./kibana-plugin-plugins-expressions-server.executioncontext.getsavedobject.md) | <T extends SavedObjectAttributes = SavedObjectAttributes>(type: string, id: string) => Promise<SavedObject<T>> | Allows to fetch saved objects from ElasticSearch. In browser getSavedObject function is provided automatically by the Expressions plugin. On the server the caller of the expression has to provide this context function. The reason is because on the browser we always know the user who tries to fetch a saved object, thus saved object client is scoped automatically to that user. However, on the server we can scope that saved object client to any user, or even not scope it at all and execute it as an "internal" user. | | [getSearchContext](./kibana-plugin-plugins-expressions-server.executioncontext.getsearchcontext.md) | () => ExecutionContextSearch | Get search context of the expression. | | [getSearchSessionId](./kibana-plugin-plugins-expressions-server.executioncontext.getsearchsessionid.md) | () => string | undefined | Search context in which expression should operate. | diff --git a/src/plugins/data/common/field_formats/field_formats_registry.ts b/src/plugins/data/common/field_formats/field_formats_registry.ts index dbc3693c99779..c7e99821d24c7 100644 --- a/src/plugins/data/common/field_formats/field_formats_registry.ts +++ b/src/plugins/data/common/field_formats/field_formats_registry.ts @@ -27,11 +27,10 @@ import { FieldFormatInstanceType, FieldFormatId, IFieldFormatMetaParams, - IFieldFormat, } from './types'; import { baseFormatters } from './constants/base_formatters'; import { FieldFormat } from './field_format'; -import { SerializedFieldFormat } from '../../../expressions/common/types'; +import { FormatFactory } from './utils'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../kbn_field_types/types'; import { UI_SETTINGS } from '../constants'; import { FieldFormatNotFoundError } from '../field_formats'; @@ -42,7 +41,7 @@ export class FieldFormatsRegistry { protected metaParamsOptions: Record = {}; protected getConfig?: FieldFormatsGetConfigFn; // overriden on the public contract - public deserialize: (mapping: SerializedFieldFormat) => IFieldFormat = () => { + public deserialize: FormatFactory = () => { return new (FieldFormat.from(identity))(); }; diff --git a/src/plugins/data/common/search/expressions/esaggs.ts b/src/plugins/data/common/index_patterns/expressions/index.ts similarity index 60% rename from src/plugins/data/common/search/expressions/esaggs.ts rename to src/plugins/data/common/index_patterns/expressions/index.ts index 47d97a81a67b1..fa37e3b216ac9 100644 --- a/src/plugins/data/common/search/expressions/esaggs.ts +++ b/src/plugins/data/common/index_patterns/expressions/index.ts @@ -17,24 +17,4 @@ * under the License. */ -import { Datatable, ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; -import { KibanaContext } from './kibana_context_type'; - -type Input = KibanaContext | null; -type Output = Promise; - -interface Arguments { - index: string; - metricsAtAllLevels: boolean; - partialRows: boolean; - includeFormatHints: boolean; - aggConfigs: string; - timeFields?: string[]; -} - -export type EsaggsExpressionFunctionDefinition = ExpressionFunctionDefinition< - 'esaggs', - Input, - Arguments, - Output ->; +export * from './load_index_pattern'; diff --git a/src/plugins/data/common/index_patterns/expressions/load_index_pattern.ts b/src/plugins/data/common/index_patterns/expressions/load_index_pattern.ts new file mode 100644 index 0000000000000..4c1b56df6e864 --- /dev/null +++ b/src/plugins/data/common/index_patterns/expressions/load_index_pattern.ts @@ -0,0 +1,65 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; +import { IndexPatternsContract } from '../index_patterns'; +import { IndexPatternSpec } from '..'; + +const name = 'indexPatternLoad'; + +type Input = null; +type Output = Promise<{ type: 'index_pattern'; value: IndexPatternSpec }>; + +interface Arguments { + id: string; +} + +/** @internal */ +export interface IndexPatternLoadStartDependencies { + indexPatterns: IndexPatternsContract; +} + +export type IndexPatternLoadExpressionFunctionDefinition = ExpressionFunctionDefinition< + typeof name, + Input, + Arguments, + Output +>; + +export const getIndexPatternLoadMeta = (): Omit< + IndexPatternLoadExpressionFunctionDefinition, + 'fn' +> => ({ + name, + type: 'index_pattern', + inputTypes: ['null'], + help: i18n.translate('data.functions.indexPatternLoad.help', { + defaultMessage: 'Loads an index pattern', + }), + args: { + id: { + types: ['string'], + required: true, + help: i18n.translate('data.functions.indexPatternLoad.id.help', { + defaultMessage: 'index pattern id to load', + }), + }, + }, +}); diff --git a/src/plugins/data/common/search/aggs/agg_type.test.ts b/src/plugins/data/common/search/aggs/agg_type.test.ts index 16a5586858ab9..102ec70188562 100644 --- a/src/plugins/data/common/search/aggs/agg_type.test.ts +++ b/src/plugins/data/common/search/aggs/agg_type.test.ts @@ -33,6 +33,7 @@ describe('AggType Class', () => { test('assigns the config value to itself', () => { const config: AggTypeConfig = { name: 'name', + expressionName: 'aggName', title: 'title', }; @@ -48,6 +49,7 @@ describe('AggType Class', () => { const aggConfig = {} as IAggConfig; const config: AggTypeConfig = { name: 'name', + expressionName: 'aggName', title: 'title', makeLabel, }; @@ -65,6 +67,7 @@ describe('AggType Class', () => { const aggType = new AggType({ name: 'name', + expressionName: 'aggName', title: 'title', getResponseAggs: testConfig, getRequestAggs: testConfig, @@ -78,6 +81,7 @@ describe('AggType Class', () => { const aggConfig = {} as IAggConfig; const aggType = new AggType({ name: 'name', + expressionName: 'aggName', title: 'title', }); const responseAggs = aggType.getRequestAggs(aggConfig); @@ -90,6 +94,7 @@ describe('AggType Class', () => { test('defaults to AggParams object with JSON param', () => { const aggType = new AggType({ name: 'smart agg', + expressionName: 'aggSmart', title: 'title', }); @@ -102,6 +107,7 @@ describe('AggType Class', () => { test('disables json param', () => { const aggType = new AggType({ name: 'name', + expressionName: 'aggName', title: 'title', json: false, }); @@ -113,6 +119,7 @@ describe('AggType Class', () => { test('can disable customLabel', () => { const aggType = new AggType({ name: 'smart agg', + expressionName: 'aggSmart', title: 'title', customLabels: false, }); @@ -127,6 +134,7 @@ describe('AggType Class', () => { const aggType = new AggType({ name: 'bucketeer', + expressionName: 'aggBucketeer', title: 'title', params, }); @@ -153,6 +161,7 @@ describe('AggType Class', () => { } as unknown) as IAggConfig; const aggType = new AggType({ name: 'name', + expressionName: 'aggName', title: 'title', }); expect(aggType.getSerializedFormat(aggConfig)).toMatchInlineSnapshot(` @@ -168,6 +177,7 @@ describe('AggType Class', () => { } as unknown) as IAggConfig; const aggType = new AggType({ name: 'name', + expressionName: 'aggName', title: 'title', }); expect(aggType.getSerializedFormat(aggConfig)).toMatchInlineSnapshot(`Object {}`); @@ -186,6 +196,7 @@ describe('AggType Class', () => { const getSerializedFormat = jest.fn().mockReturnValue({ id: 'hello' }); const aggType = new AggType({ name: 'name', + expressionName: 'aggName', title: 'title', getSerializedFormat, }); diff --git a/src/plugins/data/common/search/aggs/agg_type.ts b/src/plugins/data/common/search/aggs/agg_type.ts index bf6fe11f746f9..78e8c2405c510 100644 --- a/src/plugins/data/common/search/aggs/agg_type.ts +++ b/src/plugins/data/common/search/aggs/agg_type.ts @@ -39,7 +39,7 @@ export interface AggTypeConfig< createFilter?: (aggConfig: TAggConfig, key: any, params?: any) => any; type?: string; dslName?: string; - expressionName?: string; + expressionName: string; makeLabel?: ((aggConfig: TAggConfig) => string) | (() => string); ordered?: any; hasNoDsl?: boolean; @@ -90,12 +90,11 @@ export class AggType< dslName: string; /** * the name of the expression function that this aggType represents. - * TODO: this should probably be a required field. * * @property name * @type {string} */ - expressionName?: string; + expressionName: string; /** * the user friendly name that will be shown in the ui for this aggType * diff --git a/src/plugins/data/common/search/aggs/buckets/date_histogram.ts b/src/plugins/data/common/search/aggs/buckets/date_histogram.ts index 694b03f660452..ba79a4264d603 100644 --- a/src/plugins/data/common/search/aggs/buckets/date_histogram.ts +++ b/src/plugins/data/common/search/aggs/buckets/date_histogram.ts @@ -27,6 +27,7 @@ import { intervalOptions, autoInterval, isAutoInterval } from './_interval_optio import { createFilterDateHistogram } from './create_filter/date_histogram'; import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; +import { aggDateHistogramFnName } from './date_histogram_fn'; import { ExtendedBounds } from './lib/extended_bounds'; import { TimeBuckets } from './lib/time_buckets'; @@ -87,6 +88,7 @@ export const getDateHistogramBucketAgg = ({ }: DateHistogramBucketAggDependencies) => new BucketAggType({ name: BUCKET_TYPES.DATE_HISTOGRAM, + expressionName: aggDateHistogramFnName, title: i18n.translate('data.search.aggs.buckets.dateHistogramTitle', { defaultMessage: 'Date Histogram', }), diff --git a/src/plugins/data/common/search/aggs/buckets/date_histogram_fn.ts b/src/plugins/data/common/search/aggs/buckets/date_histogram_fn.ts index 1cc5b41fa6bb3..3e3895b7b50db 100644 --- a/src/plugins/data/common/search/aggs/buckets/date_histogram_fn.ts +++ b/src/plugins/data/common/search/aggs/buckets/date_histogram_fn.ts @@ -23,7 +23,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggDateHistogram'; +export const aggDateHistogramFnName = 'aggDateHistogram'; type Input = any; type AggArgs = AggExpressionFunctionArgs; @@ -31,10 +31,15 @@ type AggArgs = AggExpressionFunctionArgs; type Arguments = Assign; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggDateHistogramFnName, + Input, + Arguments, + Output +>; export const aggDateHistogram = (): FunctionDefinition => ({ - name: fnName, + name: aggDateHistogramFnName, help: i18n.translate('data.search.aggs.function.buckets.dateHistogram.help', { defaultMessage: 'Generates a serialized agg config for a Histogram agg', }), diff --git a/src/plugins/data/common/search/aggs/buckets/date_range.test.ts b/src/plugins/data/common/search/aggs/buckets/date_range.test.ts index 66f8e269cd38d..3cd06cc06545d 100644 --- a/src/plugins/data/common/search/aggs/buckets/date_range.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/date_range.test.ts @@ -74,6 +74,31 @@ describe('date_range params', () => { ); }; + test('produces the expected expression ast', () => { + const aggConfigs = getAggConfigs(); + const dateRange = aggConfigs.aggs[0]; + expect(dateRange.toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "enabled": Array [ + true, + ], + "id": Array [ + "date_range", + ], + "ranges": Array [ + "[{\\"from\\":\\"now-1w/w\\",\\"to\\":\\"now\\"}]", + ], + "schema": Array [ + "buckets", + ], + }, + "function": "aggDateRange", + "type": "function", + } + `); + }); + describe('getKey', () => { test('should return object', () => { const aggConfigs = getAggConfigs(); diff --git a/src/plugins/data/common/search/aggs/buckets/date_range.ts b/src/plugins/data/common/search/aggs/buckets/date_range.ts index f9a3acb990fbf..cb01922170664 100644 --- a/src/plugins/data/common/search/aggs/buckets/date_range.ts +++ b/src/plugins/data/common/search/aggs/buckets/date_range.ts @@ -24,6 +24,7 @@ import { i18n } from '@kbn/i18n'; import { BUCKET_TYPES } from './bucket_agg_types'; import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { createFilterDateRange } from './create_filter/date_range'; +import { aggDateRangeFnName } from './date_range_fn'; import { DateRangeKey } from './lib/date_range'; import { KBN_FIELD_TYPES } from '../../../../common/kbn_field_types/types'; @@ -50,6 +51,7 @@ export const getDateRangeBucketAgg = ({ }: DateRangeBucketAggDependencies) => new BucketAggType({ name: BUCKET_TYPES.DATE_RANGE, + expressionName: aggDateRangeFnName, title: dateRangeTitle, createFilter: createFilterDateRange, getKey({ from, to }): DateRangeKey { diff --git a/src/plugins/data/common/search/aggs/buckets/date_range_fn.ts b/src/plugins/data/common/search/aggs/buckets/date_range_fn.ts index 5027aadbb7331..0dc66be5b84f2 100644 --- a/src/plugins/data/common/search/aggs/buckets/date_range_fn.ts +++ b/src/plugins/data/common/search/aggs/buckets/date_range_fn.ts @@ -23,7 +23,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggDateRange'; +export const aggDateRangeFnName = 'aggDateRange'; type Input = any; type AggArgs = AggExpressionFunctionArgs; @@ -31,10 +31,15 @@ type AggArgs = AggExpressionFunctionArgs; type Arguments = Assign; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggDateRangeFnName, + Input, + Arguments, + Output +>; export const aggDateRange = (): FunctionDefinition => ({ - name: fnName, + name: aggDateRangeFnName, help: i18n.translate('data.search.aggs.function.buckets.dateRange.help', { defaultMessage: 'Generates a serialized agg config for a Date Range agg', }), diff --git a/src/plugins/data/common/search/aggs/buckets/filter.ts b/src/plugins/data/common/search/aggs/buckets/filter.ts index 5d146e125b996..84faaa2b360bd 100644 --- a/src/plugins/data/common/search/aggs/buckets/filter.ts +++ b/src/plugins/data/common/search/aggs/buckets/filter.ts @@ -21,6 +21,7 @@ import { i18n } from '@kbn/i18n'; import { BucketAggType } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { GeoBoundingBox } from './lib/geo_point'; +import { aggFilterFnName } from './filter_fn'; import { BaseAggParams } from '../types'; const filterTitle = i18n.translate('data.search.aggs.buckets.filterTitle', { @@ -34,6 +35,7 @@ export interface AggParamsFilter extends BaseAggParams { export const getFilterBucketAgg = () => new BucketAggType({ name: BUCKET_TYPES.FILTER, + expressionName: aggFilterFnName, title: filterTitle, makeLabel: () => filterTitle, params: [ diff --git a/src/plugins/data/common/search/aggs/buckets/filter_fn.ts b/src/plugins/data/common/search/aggs/buckets/filter_fn.ts index ae60da3e8a47c..8c8c0f430184a 100644 --- a/src/plugins/data/common/search/aggs/buckets/filter_fn.ts +++ b/src/plugins/data/common/search/aggs/buckets/filter_fn.ts @@ -23,7 +23,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggFilter'; +export const aggFilterFnName = 'aggFilter'; type Input = any; type AggArgs = AggExpressionFunctionArgs; @@ -31,10 +31,15 @@ type AggArgs = AggExpressionFunctionArgs; type Arguments = Assign; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggFilterFnName, + Input, + Arguments, + Output +>; export const aggFilter = (): FunctionDefinition => ({ - name: fnName, + name: aggFilterFnName, help: i18n.translate('data.search.aggs.function.buckets.filter.help', { defaultMessage: 'Generates a serialized agg config for a Filter agg', }), diff --git a/src/plugins/data/common/search/aggs/buckets/filters.test.ts b/src/plugins/data/common/search/aggs/buckets/filters.test.ts index f745b4537131a..326a3af712e70 100644 --- a/src/plugins/data/common/search/aggs/buckets/filters.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/filters.test.ts @@ -74,6 +74,33 @@ describe('Filters Agg', () => { }, }); + test('produces the expected expression ast', () => { + const aggConfigs = getAggConfigs({ + filters: [ + generateFilter('a', 'lucene', 'foo'), + generateFilter('b', 'lucene', 'status:200'), + generateFilter('c', 'lucene', 'status:[400 TO 499] AND (foo OR bar)'), + ], + }); + expect(aggConfigs.aggs[0].toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "enabled": Array [ + true, + ], + "filters": Array [ + "[{\\"label\\":\\"a\\",\\"input\\":{\\"language\\":\\"lucene\\",\\"query\\":\\"foo\\"}},{\\"label\\":\\"b\\",\\"input\\":{\\"language\\":\\"lucene\\",\\"query\\":\\"status:200\\"}},{\\"label\\":\\"c\\",\\"input\\":{\\"language\\":\\"lucene\\",\\"query\\":\\"status:[400 TO 499] AND (foo OR bar)\\"}}]", + ], + "id": Array [ + "test", + ], + }, + "function": "aggFilters", + "type": "function", + } + `); + }); + describe('using Lucene', () => { test('works with lucene filters', () => { const aggConfigs = getAggConfigs({ diff --git a/src/plugins/data/common/search/aggs/buckets/filters.ts b/src/plugins/data/common/search/aggs/buckets/filters.ts index 7310fa08b68e0..7f43d01808882 100644 --- a/src/plugins/data/common/search/aggs/buckets/filters.ts +++ b/src/plugins/data/common/search/aggs/buckets/filters.ts @@ -24,6 +24,7 @@ import { createFilterFilters } from './create_filter/filters'; import { toAngularJSON } from '../utils'; import { BucketAggType } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; +import { aggFiltersFnName } from './filters_fn'; import { getEsQueryConfig, buildEsQuery, Query, UI_SETTINGS } from '../../../../common'; import { BaseAggParams } from '../types'; @@ -53,6 +54,7 @@ export interface AggParamsFilters extends Omit { export const getFiltersBucketAgg = ({ getConfig }: FiltersBucketAggDependencies) => new BucketAggType({ name: BUCKET_TYPES.FILTERS, + expressionName: aggFiltersFnName, title: filtersTitle, createFilter: createFilterFilters, customLabels: false, diff --git a/src/plugins/data/common/search/aggs/buckets/filters_fn.ts b/src/plugins/data/common/search/aggs/buckets/filters_fn.ts index 55380ea815315..194feb67d3366 100644 --- a/src/plugins/data/common/search/aggs/buckets/filters_fn.ts +++ b/src/plugins/data/common/search/aggs/buckets/filters_fn.ts @@ -23,7 +23,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggFilters'; +export const aggFiltersFnName = 'aggFilters'; type Input = any; type AggArgs = AggExpressionFunctionArgs; @@ -31,10 +31,15 @@ type AggArgs = AggExpressionFunctionArgs; type Arguments = Assign; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggFiltersFnName, + Input, + Arguments, + Output +>; export const aggFilters = (): FunctionDefinition => ({ - name: fnName, + name: aggFiltersFnName, help: i18n.translate('data.search.aggs.function.buckets.filters.help', { defaultMessage: 'Generates a serialized agg config for a Filter agg', }), diff --git a/src/plugins/data/common/search/aggs/buckets/geo_hash.test.ts b/src/plugins/data/common/search/aggs/buckets/geo_hash.test.ts index e77d2bf1eaf5f..8de6834022639 100644 --- a/src/plugins/data/common/search/aggs/buckets/geo_hash.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/geo_hash.test.ts @@ -87,6 +87,42 @@ describe('Geohash Agg', () => { }); }); + test('produces the expected expression ast', () => { + const aggConfigs = getAggConfigs(); + expect(aggConfigs.aggs[0].toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "autoPrecision": Array [ + true, + ], + "enabled": Array [ + true, + ], + "field": Array [ + "location", + ], + "id": Array [ + "geohash_grid", + ], + "isFilteredByCollar": Array [ + true, + ], + "precision": Array [ + 2, + ], + "schema": Array [ + "segment", + ], + "useGeocentroid": Array [ + true, + ], + }, + "function": "aggGeoHash", + "type": "function", + } + `); + }); + describe('getRequestAggs', () => { describe('initial aggregation creation', () => { let aggConfigs: IAggConfigs; diff --git a/src/plugins/data/common/search/aggs/buckets/geo_hash.ts b/src/plugins/data/common/search/aggs/buckets/geo_hash.ts index a0ef8a27b0d1e..b7ddf24dbfc84 100644 --- a/src/plugins/data/common/search/aggs/buckets/geo_hash.ts +++ b/src/plugins/data/common/search/aggs/buckets/geo_hash.ts @@ -21,6 +21,7 @@ import { i18n } from '@kbn/i18n'; import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { KBN_FIELD_TYPES } from '../../../../common'; import { BUCKET_TYPES } from './bucket_agg_types'; +import { aggGeoHashFnName } from './geo_hash_fn'; import { GeoBoundingBox } from './lib/geo_point'; import { BaseAggParams } from '../types'; @@ -47,6 +48,7 @@ export interface AggParamsGeoHash extends BaseAggParams { export const getGeoHashBucketAgg = () => new BucketAggType({ name: BUCKET_TYPES.GEOHASH_GRID, + expressionName: aggGeoHashFnName, title: geohashGridTitle, makeLabel: () => geohashGridTitle, params: [ diff --git a/src/plugins/data/common/search/aggs/buckets/geo_hash_fn.ts b/src/plugins/data/common/search/aggs/buckets/geo_hash_fn.ts index 5152804bf8122..aa5f473f73f9d 100644 --- a/src/plugins/data/common/search/aggs/buckets/geo_hash_fn.ts +++ b/src/plugins/data/common/search/aggs/buckets/geo_hash_fn.ts @@ -23,17 +23,22 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggGeoHash'; +export const aggGeoHashFnName = 'aggGeoHash'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Arguments = Assign; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggGeoHashFnName, + Input, + Arguments, + Output +>; export const aggGeoHash = (): FunctionDefinition => ({ - name: fnName, + name: aggGeoHashFnName, help: i18n.translate('data.search.aggs.function.buckets.geoHash.help', { defaultMessage: 'Generates a serialized agg config for a Geo Hash agg', }), diff --git a/src/plugins/data/common/search/aggs/buckets/geo_tile.ts b/src/plugins/data/common/search/aggs/buckets/geo_tile.ts index e6eff1e1a5d8e..fc87d632c7e9c 100644 --- a/src/plugins/data/common/search/aggs/buckets/geo_tile.ts +++ b/src/plugins/data/common/search/aggs/buckets/geo_tile.ts @@ -22,6 +22,7 @@ import { noop } from 'lodash'; import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; +import { aggGeoTileFnName } from './geo_tile_fn'; import { KBN_FIELD_TYPES } from '../../../../common'; import { METRIC_TYPES } from '../metrics/metric_agg_types'; import { BaseAggParams } from '../types'; @@ -39,6 +40,7 @@ export interface AggParamsGeoTile extends BaseAggParams { export const getGeoTitleBucketAgg = () => new BucketAggType({ name: BUCKET_TYPES.GEOTILE_GRID, + expressionName: aggGeoTileFnName, title: geotileGridTitle, params: [ { diff --git a/src/plugins/data/common/search/aggs/buckets/geo_tile_fn.ts b/src/plugins/data/common/search/aggs/buckets/geo_tile_fn.ts index ed3142408892a..346c70bba31fd 100644 --- a/src/plugins/data/common/search/aggs/buckets/geo_tile_fn.ts +++ b/src/plugins/data/common/search/aggs/buckets/geo_tile_fn.ts @@ -22,16 +22,21 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggGeoTile'; +export const aggGeoTileFnName = 'aggGeoTile'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggGeoTileFnName, + Input, + AggArgs, + Output +>; export const aggGeoTile = (): FunctionDefinition => ({ - name: fnName, + name: aggGeoTileFnName, help: i18n.translate('data.search.aggs.function.buckets.geoTile.help', { defaultMessage: 'Generates a serialized agg config for a Geo Tile agg', }), diff --git a/src/plugins/data/common/search/aggs/buckets/histogram.test.ts b/src/plugins/data/common/search/aggs/buckets/histogram.test.ts index a8ac72c174c72..1b01b1f235cb5 100644 --- a/src/plugins/data/common/search/aggs/buckets/histogram.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/histogram.test.ts @@ -72,6 +72,50 @@ describe('Histogram Agg', () => { return aggConfigs.aggs[0].toDsl()[BUCKET_TYPES.HISTOGRAM]; }; + test('produces the expected expression ast', () => { + const aggConfigs = getAggConfigs({ + intervalBase: 100, + field: { + name: 'field', + }, + }); + expect(aggConfigs.aggs[0].toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "enabled": Array [ + true, + ], + "extended_bounds": Array [ + "{\\"min\\":\\"\\",\\"max\\":\\"\\"}", + ], + "field": Array [ + "field", + ], + "has_extended_bounds": Array [ + false, + ], + "id": Array [ + "test", + ], + "interval": Array [ + "auto", + ], + "intervalBase": Array [ + 100, + ], + "min_doc_count": Array [ + false, + ], + "schema": Array [ + "segment", + ], + }, + "function": "aggHistogram", + "type": "function", + } + `); + }); + describe('ordered', () => { let histogramType: BucketAggType; diff --git a/src/plugins/data/common/search/aggs/buckets/histogram.ts b/src/plugins/data/common/search/aggs/buckets/histogram.ts index c3d3f041dd0c7..ab0d566b273c7 100644 --- a/src/plugins/data/common/search/aggs/buckets/histogram.ts +++ b/src/plugins/data/common/search/aggs/buckets/histogram.ts @@ -27,6 +27,7 @@ import { BaseAggParams } from '../types'; import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { createFilterHistogram } from './create_filter/histogram'; import { BUCKET_TYPES } from './bucket_agg_types'; +import { aggHistogramFnName } from './histogram_fn'; import { ExtendedBounds } from './lib/extended_bounds'; import { isAutoInterval, autoInterval } from './_interval_options'; import { calculateHistogramInterval } from './lib/histogram_calculate_interval'; @@ -62,6 +63,7 @@ export const getHistogramBucketAgg = ({ }: HistogramBucketAggDependencies) => new BucketAggType({ name: BUCKET_TYPES.HISTOGRAM, + expressionName: aggHistogramFnName, title: i18n.translate('data.search.aggs.buckets.histogramTitle', { defaultMessage: 'Histogram', }), diff --git a/src/plugins/data/common/search/aggs/buckets/histogram_fn.ts b/src/plugins/data/common/search/aggs/buckets/histogram_fn.ts index 2e833bbe0a3eb..153a7bfc1c592 100644 --- a/src/plugins/data/common/search/aggs/buckets/histogram_fn.ts +++ b/src/plugins/data/common/search/aggs/buckets/histogram_fn.ts @@ -23,7 +23,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggHistogram'; +export const aggHistogramFnName = 'aggHistogram'; type Input = any; type AggArgs = AggExpressionFunctionArgs; @@ -31,10 +31,15 @@ type AggArgs = AggExpressionFunctionArgs; type Arguments = Assign; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggHistogramFnName, + Input, + Arguments, + Output +>; export const aggHistogram = (): FunctionDefinition => ({ - name: fnName, + name: aggHistogramFnName, help: i18n.translate('data.search.aggs.function.buckets.histogram.help', { defaultMessage: 'Generates a serialized agg config for a Histogram agg', }), diff --git a/src/plugins/data/common/search/aggs/buckets/ip_range.ts b/src/plugins/data/common/search/aggs/buckets/ip_range.ts index d0a6174b011fc..233acdd71e59a 100644 --- a/src/plugins/data/common/search/aggs/buckets/ip_range.ts +++ b/src/plugins/data/common/search/aggs/buckets/ip_range.ts @@ -24,6 +24,7 @@ import { BUCKET_TYPES } from './bucket_agg_types'; import { createFilterIpRange } from './create_filter/ip_range'; import { IpRangeKey, RangeIpRangeAggKey, CidrMaskIpRangeAggKey } from './lib/ip_range'; +import { aggIpRangeFnName } from './ip_range_fn'; import { KBN_FIELD_TYPES } from '../../../../common'; import { BaseAggParams } from '../types'; @@ -48,6 +49,7 @@ export interface AggParamsIpRange extends BaseAggParams { export const getIpRangeBucketAgg = () => new BucketAggType({ name: BUCKET_TYPES.IP_RANGE, + expressionName: aggIpRangeFnName, title: ipRangeTitle, createFilter: createFilterIpRange, getKey(bucket, key, agg): IpRangeKey { diff --git a/src/plugins/data/common/search/aggs/buckets/ip_range_fn.ts b/src/plugins/data/common/search/aggs/buckets/ip_range_fn.ts index 15b763fd42d6b..7ad61a9c27d86 100644 --- a/src/plugins/data/common/search/aggs/buckets/ip_range_fn.ts +++ b/src/plugins/data/common/search/aggs/buckets/ip_range_fn.ts @@ -23,7 +23,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggIpRange'; +export const aggIpRangeFnName = 'aggIpRange'; type Input = any; type AggArgs = AggExpressionFunctionArgs; @@ -31,10 +31,15 @@ type AggArgs = AggExpressionFunctionArgs; type Arguments = Assign; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggIpRangeFnName, + Input, + Arguments, + Output +>; export const aggIpRange = (): FunctionDefinition => ({ - name: fnName, + name: aggIpRangeFnName, help: i18n.translate('data.search.aggs.function.buckets.ipRange.help', { defaultMessage: 'Generates a serialized agg config for a Ip Range agg', }), diff --git a/src/plugins/data/common/search/aggs/buckets/range.test.ts b/src/plugins/data/common/search/aggs/buckets/range.test.ts index b8241e04ea1ee..c878e6b81a0ae 100644 --- a/src/plugins/data/common/search/aggs/buckets/range.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/range.test.ts @@ -66,6 +66,33 @@ describe('Range Agg', () => { ); }; + test('produces the expected expression ast', () => { + const aggConfigs = getAggConfigs(); + expect(aggConfigs.aggs[0].toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "enabled": Array [ + true, + ], + "field": Array [ + "bytes", + ], + "id": Array [ + "1", + ], + "ranges": Array [ + "[{\\"from\\":0,\\"to\\":1000},{\\"from\\":1000,\\"to\\":2000}]", + ], + "schema": Array [ + "segment", + ], + }, + "function": "aggRange", + "type": "function", + } + `); + }); + describe('getSerializedFormat', () => { test('generates a serialized field format in the expected shape', () => { const aggConfigs = getAggConfigs(); diff --git a/src/plugins/data/common/search/aggs/buckets/range.ts b/src/plugins/data/common/search/aggs/buckets/range.ts index bdb6ea7cd4b98..4486ad3c06dd1 100644 --- a/src/plugins/data/common/search/aggs/buckets/range.ts +++ b/src/plugins/data/common/search/aggs/buckets/range.ts @@ -24,6 +24,7 @@ import { AggTypesDependencies } from '../agg_types'; import { BaseAggParams } from '../types'; import { BucketAggType } from './bucket_agg_type'; +import { aggRangeFnName } from './range_fn'; import { RangeKey } from './range_key'; import { createFilterRange } from './create_filter/range'; import { BUCKET_TYPES } from './bucket_agg_types'; @@ -50,6 +51,7 @@ export const getRangeBucketAgg = ({ getFieldFormatsStart }: RangeBucketAggDepend return new BucketAggType({ name: BUCKET_TYPES.RANGE, + expressionName: aggRangeFnName, title: rangeTitle, createFilter: createFilterRange(getFieldFormatsStart), makeLabel(aggConfig) { diff --git a/src/plugins/data/common/search/aggs/buckets/range_fn.ts b/src/plugins/data/common/search/aggs/buckets/range_fn.ts index 6806125a10f6d..a52b2427b9845 100644 --- a/src/plugins/data/common/search/aggs/buckets/range_fn.ts +++ b/src/plugins/data/common/search/aggs/buckets/range_fn.ts @@ -23,7 +23,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggRange'; +export const aggRangeFnName = 'aggRange'; type Input = any; type AggArgs = AggExpressionFunctionArgs; @@ -31,10 +31,15 @@ type AggArgs = AggExpressionFunctionArgs; type Arguments = Assign; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggRangeFnName, + Input, + Arguments, + Output +>; export const aggRange = (): FunctionDefinition => ({ - name: fnName, + name: aggRangeFnName, help: i18n.translate('data.search.aggs.function.buckets.range.help', { defaultMessage: 'Generates a serialized agg config for a Range agg', }), diff --git a/src/plugins/data/common/search/aggs/buckets/shard_delay.test.ts b/src/plugins/data/common/search/aggs/buckets/shard_delay.test.ts index 15399ffc43791..063dec97dadd4 100644 --- a/src/plugins/data/common/search/aggs/buckets/shard_delay.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/shard_delay.test.ts @@ -60,6 +60,27 @@ describe('Shard Delay Agg', () => { ); }; + test('produces the expected expression ast', () => { + const aggConfigs = getAggConfigs(); + expect(aggConfigs.aggs[0].toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "delay": Array [ + "5s", + ], + "enabled": Array [ + true, + ], + "id": Array [ + "1", + ], + }, + "function": "aggShardDelay", + "type": "function", + } + `); + }); + describe('write', () => { test('writes the delay as the value parameter', () => { const aggConfigs = getAggConfigs(); diff --git a/src/plugins/data/common/search/aggs/buckets/significant_terms.test.ts b/src/plugins/data/common/search/aggs/buckets/significant_terms.test.ts index e6c7bbee72a72..be40ff2267f11 100644 --- a/src/plugins/data/common/search/aggs/buckets/significant_terms.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/significant_terms.test.ts @@ -64,6 +64,38 @@ describe('Significant Terms Agg', () => { expect(params.exclude).toBe('400'); }; + test('produces the expected expression ast', () => { + const aggConfigs = getAggConfigs({ + size: 'SIZE', + field: { + name: 'FIELD', + }, + }); + expect(aggConfigs.aggs[0].toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "enabled": Array [ + true, + ], + "field": Array [ + "FIELD", + ], + "id": Array [ + "test", + ], + "schema": Array [ + "segment", + ], + "size": Array [ + "SIZE", + ], + }, + "function": "aggSignificantTerms", + "type": "function", + } + `); + }); + test('should generate correct label', () => { const aggConfigs = getAggConfigs({ size: 'SIZE', diff --git a/src/plugins/data/common/search/aggs/buckets/significant_terms.ts b/src/plugins/data/common/search/aggs/buckets/significant_terms.ts index 4dc8aafd8a7a7..5632c08378f4c 100644 --- a/src/plugins/data/common/search/aggs/buckets/significant_terms.ts +++ b/src/plugins/data/common/search/aggs/buckets/significant_terms.ts @@ -22,6 +22,7 @@ import { BucketAggType } from './bucket_agg_type'; import { createFilterTerms } from './create_filter/terms'; import { isStringType, migrateIncludeExcludeFormat } from './migrate_include_exclude_format'; import { BUCKET_TYPES } from './bucket_agg_types'; +import { aggSignificantTermsFnName } from './significant_terms_fn'; import { KBN_FIELD_TYPES } from '../../../../common'; import { BaseAggParams } from '../types'; @@ -39,6 +40,7 @@ export interface AggParamsSignificantTerms extends BaseAggParams { export const getSignificantTermsBucketAgg = () => new BucketAggType({ name: BUCKET_TYPES.SIGNIFICANT_TERMS, + expressionName: aggSignificantTermsFnName, title: significantTermsTitle, makeLabel(aggConfig) { return i18n.translate('data.search.aggs.buckets.significantTermsLabel', { diff --git a/src/plugins/data/common/search/aggs/buckets/significant_terms_fn.ts b/src/plugins/data/common/search/aggs/buckets/significant_terms_fn.ts index 1fecfcc914313..a1a7500678fd6 100644 --- a/src/plugins/data/common/search/aggs/buckets/significant_terms_fn.ts +++ b/src/plugins/data/common/search/aggs/buckets/significant_terms_fn.ts @@ -22,7 +22,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggSignificantTerms'; +export const aggSignificantTermsFnName = 'aggSignificantTerms'; type Input = any; type AggArgs = AggExpressionFunctionArgs; @@ -30,10 +30,15 @@ type AggArgs = AggExpressionFunctionArgs; type Arguments = AggArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggSignificantTermsFnName, + Input, + Arguments, + Output +>; export const aggSignificantTerms = (): FunctionDefinition => ({ - name: fnName, + name: aggSignificantTermsFnName, help: i18n.translate('data.search.aggs.function.buckets.significantTerms.help', { defaultMessage: 'Generates a serialized agg config for a Significant Terms agg', }), diff --git a/src/plugins/data/common/search/aggs/buckets/terms.test.ts b/src/plugins/data/common/search/aggs/buckets/terms.test.ts index 8f645b4712c7f..a4116500bec12 100644 --- a/src/plugins/data/common/search/aggs/buckets/terms.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/terms.test.ts @@ -52,6 +52,80 @@ describe('Terms Agg', () => { ); }; + test('produces the expected expression ast', () => { + const aggConfigs = getAggConfigs({ + include: { + pattern: '404', + }, + exclude: { + pattern: '400', + }, + field: { + name: 'field', + }, + orderAgg: { + type: 'count', + }, + }); + expect(aggConfigs.aggs[0].toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "enabled": Array [ + true, + ], + "field": Array [ + "field", + ], + "id": Array [ + "test", + ], + "missingBucket": Array [ + false, + ], + "missingBucketLabel": Array [ + "Missing", + ], + "order": Array [ + "desc", + ], + "orderAgg": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "enabled": Array [ + true, + ], + "id": Array [ + "test-orderAgg", + ], + "schema": Array [ + "orderAgg", + ], + }, + "function": "aggCount", + "type": "function", + }, + ], + "type": "expression", + }, + ], + "otherBucket": Array [ + false, + ], + "otherBucketLabel": Array [ + "Other", + ], + "size": Array [ + 5, + ], + }, + "function": "aggTerms", + "type": "function", + } + `); + }); + test('converts object to string type', () => { const aggConfigs = getAggConfigs({ include: { diff --git a/src/plugins/data/common/search/aggs/buckets/terms.ts b/src/plugins/data/common/search/aggs/buckets/terms.ts index 7071d9c1dc9c4..8683b23b39c85 100644 --- a/src/plugins/data/common/search/aggs/buckets/terms.ts +++ b/src/plugins/data/common/search/aggs/buckets/terms.ts @@ -28,6 +28,7 @@ import { isStringOrNumberType, migrateIncludeExcludeFormat, } from './migrate_include_exclude_format'; +import { aggTermsFnName } from './terms_fn'; import { AggConfigSerialized, BaseAggParams } from '../types'; import { KBN_FIELD_TYPES } from '../../../../common'; @@ -75,7 +76,7 @@ export interface AggParamsTerms extends BaseAggParams { export const getTermsBucketAgg = () => new BucketAggType({ name: BUCKET_TYPES.TERMS, - expressionName: 'aggTerms', + expressionName: aggTermsFnName, title: termsTitle, makeLabel(agg) { const params = agg.params; diff --git a/src/plugins/data/common/search/aggs/buckets/terms_fn.ts b/src/plugins/data/common/search/aggs/buckets/terms_fn.ts index 975941506da4e..7737cb1e1c952 100644 --- a/src/plugins/data/common/search/aggs/buckets/terms_fn.ts +++ b/src/plugins/data/common/search/aggs/buckets/terms_fn.ts @@ -23,7 +23,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, BUCKET_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggTerms'; +export const aggTermsFnName = 'aggTerms'; type Input = any; type AggArgs = AggExpressionFunctionArgs; @@ -33,10 +33,15 @@ type AggArgs = AggExpressionFunctionArgs; type Arguments = Assign; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggTermsFnName, + Input, + Arguments, + Output +>; export const aggTerms = (): FunctionDefinition => ({ - name: fnName, + name: aggTermsFnName, help: i18n.translate('data.search.aggs.function.buckets.terms.help', { defaultMessage: 'Generates a serialized agg config for a Terms agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/avg.ts b/src/plugins/data/common/search/aggs/metrics/avg.ts index 651aaf857c757..49c81b2918346 100644 --- a/src/plugins/data/common/search/aggs/metrics/avg.ts +++ b/src/plugins/data/common/search/aggs/metrics/avg.ts @@ -18,6 +18,7 @@ */ import { i18n } from '@kbn/i18n'; +import { aggAvgFnName } from './avg_fn'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; @@ -34,6 +35,7 @@ export interface AggParamsAvg extends BaseAggParams { export const getAvgMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.AVG, + expressionName: aggAvgFnName, title: averageTitle, makeLabel: (aggConfig) => { return i18n.translate('data.search.aggs.metrics.averageLabel', { diff --git a/src/plugins/data/common/search/aggs/metrics/avg_fn.ts b/src/plugins/data/common/search/aggs/metrics/avg_fn.ts index 18629927d7814..57dd3dae70fba 100644 --- a/src/plugins/data/common/search/aggs/metrics/avg_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/avg_fn.ts @@ -22,15 +22,15 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggAvg'; +export const aggAvgFnName = 'aggAvg'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition; export const aggAvg = (): FunctionDefinition => ({ - name: fnName, + name: aggAvgFnName, help: i18n.translate('data.search.aggs.function.metrics.avg.help', { defaultMessage: 'Generates a serialized agg config for a Avg agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/bucket_avg.ts b/src/plugins/data/common/search/aggs/metrics/bucket_avg.ts index 92fa675ac2d38..003627ddec2a1 100644 --- a/src/plugins/data/common/search/aggs/metrics/bucket_avg.ts +++ b/src/plugins/data/common/search/aggs/metrics/bucket_avg.ts @@ -19,6 +19,7 @@ import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; +import { aggBucketAvgFnName } from './bucket_avg_fn'; import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; @@ -43,6 +44,7 @@ export const getBucketAvgMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.AVG_BUCKET, + expressionName: aggBucketAvgFnName, title: averageBucketTitle, makeLabel: (agg) => makeNestedLabel(agg, overallAverageLabel), subtype, diff --git a/src/plugins/data/common/search/aggs/metrics/bucket_avg_fn.ts b/src/plugins/data/common/search/aggs/metrics/bucket_avg_fn.ts index 4e0c1d7311cd6..595d49647d9c2 100644 --- a/src/plugins/data/common/search/aggs/metrics/bucket_avg_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/bucket_avg_fn.ts @@ -23,7 +23,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggBucketAvg'; +export const aggBucketAvgFnName = 'aggBucketAvg'; type Input = any; type AggArgs = AggExpressionFunctionArgs; @@ -32,10 +32,15 @@ type Arguments = Assign< { customBucket?: AggExpressionType; customMetric?: AggExpressionType } >; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggBucketAvgFnName, + Input, + Arguments, + Output +>; export const aggBucketAvg = (): FunctionDefinition => ({ - name: fnName, + name: aggBucketAvgFnName, help: i18n.translate('data.search.aggs.function.metrics.bucket_avg.help', { defaultMessage: 'Generates a serialized agg config for a Avg Bucket agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/bucket_max.ts b/src/plugins/data/common/search/aggs/metrics/bucket_max.ts index 8e2606676ec33..c37e0d6e09e23 100644 --- a/src/plugins/data/common/search/aggs/metrics/bucket_max.ts +++ b/src/plugins/data/common/search/aggs/metrics/bucket_max.ts @@ -18,6 +18,7 @@ */ import { i18n } from '@kbn/i18n'; +import { aggBucketMaxFnName } from './bucket_max_fn'; import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; @@ -42,6 +43,7 @@ export const getBucketMaxMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.MAX_BUCKET, + expressionName: aggBucketMaxFnName, title: maxBucketTitle, makeLabel: (agg) => makeNestedLabel(agg, overallMaxLabel), subtype, diff --git a/src/plugins/data/common/search/aggs/metrics/bucket_max_fn.ts b/src/plugins/data/common/search/aggs/metrics/bucket_max_fn.ts index 66ae7601470fb..482c73e7d3005 100644 --- a/src/plugins/data/common/search/aggs/metrics/bucket_max_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/bucket_max_fn.ts @@ -23,7 +23,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggBucketMax'; +export const aggBucketMaxFnName = 'aggBucketMax'; type Input = any; type AggArgs = AggExpressionFunctionArgs; @@ -32,10 +32,15 @@ type Arguments = Assign< { customBucket?: AggExpressionType; customMetric?: AggExpressionType } >; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggBucketMaxFnName, + Input, + Arguments, + Output +>; export const aggBucketMax = (): FunctionDefinition => ({ - name: fnName, + name: aggBucketMaxFnName, help: i18n.translate('data.search.aggs.function.metrics.bucket_max.help', { defaultMessage: 'Generates a serialized agg config for a Max Bucket agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/bucket_min.ts b/src/plugins/data/common/search/aggs/metrics/bucket_min.ts index dedc3a9de3dd1..2aee271a69cc3 100644 --- a/src/plugins/data/common/search/aggs/metrics/bucket_min.ts +++ b/src/plugins/data/common/search/aggs/metrics/bucket_min.ts @@ -18,6 +18,7 @@ */ import { i18n } from '@kbn/i18n'; +import { aggBucketMinFnName } from './bucket_min_fn'; import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; @@ -42,6 +43,7 @@ export const getBucketMinMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.MIN_BUCKET, + expressionName: aggBucketMinFnName, title: minBucketTitle, makeLabel: (agg) => makeNestedLabel(agg, overallMinLabel), subtype, diff --git a/src/plugins/data/common/search/aggs/metrics/bucket_min_fn.ts b/src/plugins/data/common/search/aggs/metrics/bucket_min_fn.ts index 009cc0102b05d..68beffbf05660 100644 --- a/src/plugins/data/common/search/aggs/metrics/bucket_min_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/bucket_min_fn.ts @@ -23,7 +23,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggBucketMin'; +export const aggBucketMinFnName = 'aggBucketMin'; type Input = any; type AggArgs = AggExpressionFunctionArgs; @@ -32,10 +32,15 @@ type Arguments = Assign< { customBucket?: AggExpressionType; customMetric?: AggExpressionType } >; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggBucketMinFnName, + Input, + Arguments, + Output +>; export const aggBucketMin = (): FunctionDefinition => ({ - name: fnName, + name: aggBucketMinFnName, help: i18n.translate('data.search.aggs.function.metrics.bucket_min.help', { defaultMessage: 'Generates a serialized agg config for a Min Bucket agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/bucket_sum.ts b/src/plugins/data/common/search/aggs/metrics/bucket_sum.ts index c6ccd498a0eb9..d7a7ed47ac2df 100644 --- a/src/plugins/data/common/search/aggs/metrics/bucket_sum.ts +++ b/src/plugins/data/common/search/aggs/metrics/bucket_sum.ts @@ -18,6 +18,7 @@ */ import { i18n } from '@kbn/i18n'; +import { aggBucketSumFnName } from './bucket_sum_fn'; import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; @@ -42,6 +43,7 @@ export const getBucketSumMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.SUM_BUCKET, + expressionName: aggBucketSumFnName, title: sumBucketTitle, makeLabel: (agg) => makeNestedLabel(agg, overallSumLabel), subtype, diff --git a/src/plugins/data/common/search/aggs/metrics/bucket_sum_fn.ts b/src/plugins/data/common/search/aggs/metrics/bucket_sum_fn.ts index 920285e89e8f4..7994bb85be2a7 100644 --- a/src/plugins/data/common/search/aggs/metrics/bucket_sum_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/bucket_sum_fn.ts @@ -23,7 +23,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggBucketSum'; +export const aggBucketSumFnName = 'aggBucketSum'; type Input = any; type AggArgs = AggExpressionFunctionArgs; @@ -32,10 +32,15 @@ type Arguments = Assign< { customBucket?: AggExpressionType; customMetric?: AggExpressionType } >; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggBucketSumFnName, + Input, + Arguments, + Output +>; export const aggBucketSum = (): FunctionDefinition => ({ - name: fnName, + name: aggBucketSumFnName, help: i18n.translate('data.search.aggs.function.metrics.bucket_sum.help', { defaultMessage: 'Generates a serialized agg config for a Sum Bucket agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/cardinality.ts b/src/plugins/data/common/search/aggs/metrics/cardinality.ts index 777cb833849f4..91f2b729e9dda 100644 --- a/src/plugins/data/common/search/aggs/metrics/cardinality.ts +++ b/src/plugins/data/common/search/aggs/metrics/cardinality.ts @@ -18,6 +18,7 @@ */ import { i18n } from '@kbn/i18n'; +import { aggCardinalityFnName } from './cardinality_fn'; import { MetricAggType, IMetricAggConfig } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; @@ -34,6 +35,7 @@ export interface AggParamsCardinality extends BaseAggParams { export const getCardinalityMetricAgg = () => new MetricAggType({ name: METRIC_TYPES.CARDINALITY, + expressionName: aggCardinalityFnName, title: uniqueCountTitle, makeLabel(aggConfig: IMetricAggConfig) { return i18n.translate('data.search.aggs.metrics.uniqueCountLabel', { diff --git a/src/plugins/data/common/search/aggs/metrics/cardinality_fn.ts b/src/plugins/data/common/search/aggs/metrics/cardinality_fn.ts index 2542c76e7be57..6e78a42fea90f 100644 --- a/src/plugins/data/common/search/aggs/metrics/cardinality_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/cardinality_fn.ts @@ -22,15 +22,20 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggCardinality'; +export const aggCardinalityFnName = 'aggCardinality'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggCardinalityFnName, + Input, + AggArgs, + Output +>; export const aggCardinality = (): FunctionDefinition => ({ - name: fnName, + name: aggCardinalityFnName, help: i18n.translate('data.search.aggs.function.metrics.cardinality.help', { defaultMessage: 'Generates a serialized agg config for a Cardinality agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/count.ts b/src/plugins/data/common/search/aggs/metrics/count.ts index 9c9f36651f4d2..a50b627ae2398 100644 --- a/src/plugins/data/common/search/aggs/metrics/count.ts +++ b/src/plugins/data/common/search/aggs/metrics/count.ts @@ -18,12 +18,14 @@ */ import { i18n } from '@kbn/i18n'; +import { aggCountFnName } from './count_fn'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; export const getCountMetricAgg = () => new MetricAggType({ name: METRIC_TYPES.COUNT, + expressionName: aggCountFnName, title: i18n.translate('data.search.aggs.metrics.countTitle', { defaultMessage: 'Count', }), diff --git a/src/plugins/data/common/search/aggs/metrics/count_fn.ts b/src/plugins/data/common/search/aggs/metrics/count_fn.ts index 7d4616ffdc619..a4df6f9ebd061 100644 --- a/src/plugins/data/common/search/aggs/metrics/count_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/count_fn.ts @@ -21,15 +21,20 @@ import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; -const fnName = 'aggCount'; +export const aggCountFnName = 'aggCount'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggCountFnName, + Input, + AggArgs, + Output +>; export const aggCount = (): FunctionDefinition => ({ - name: fnName, + name: aggCountFnName, help: i18n.translate('data.search.aggs.function.metrics.count.help', { defaultMessage: 'Generates a serialized agg config for a Count agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/cumulative_sum.ts b/src/plugins/data/common/search/aggs/metrics/cumulative_sum.ts index b10bdd31a5817..bb0d15782c342 100644 --- a/src/plugins/data/common/search/aggs/metrics/cumulative_sum.ts +++ b/src/plugins/data/common/search/aggs/metrics/cumulative_sum.ts @@ -18,6 +18,7 @@ */ import { i18n } from '@kbn/i18n'; +import { aggCumulativeSumFnName } from './cumulative_sum_fn'; import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; @@ -43,6 +44,7 @@ export const getCumulativeSumMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.CUMULATIVE_SUM, + expressionName: aggCumulativeSumFnName, title: cumulativeSumTitle, makeLabel: (agg) => makeNestedLabel(agg, cumulativeSumLabel), subtype, diff --git a/src/plugins/data/common/search/aggs/metrics/cumulative_sum_fn.ts b/src/plugins/data/common/search/aggs/metrics/cumulative_sum_fn.ts index 411cbd256c37e..43df5301e1a04 100644 --- a/src/plugins/data/common/search/aggs/metrics/cumulative_sum_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/cumulative_sum_fn.ts @@ -23,16 +23,21 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggCumulativeSum'; +export const aggCumulativeSumFnName = 'aggCumulativeSum'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Arguments = Assign; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggCumulativeSumFnName, + Input, + Arguments, + Output +>; export const aggCumulativeSum = (): FunctionDefinition => ({ - name: fnName, + name: aggCumulativeSumFnName, help: i18n.translate('data.search.aggs.function.metrics.cumulative_sum.help', { defaultMessage: 'Generates a serialized agg config for a Cumulative Sum agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/derivative.ts b/src/plugins/data/common/search/aggs/metrics/derivative.ts index c03c33ba80710..ee32d12e5c85d 100644 --- a/src/plugins/data/common/search/aggs/metrics/derivative.ts +++ b/src/plugins/data/common/search/aggs/metrics/derivative.ts @@ -18,6 +18,7 @@ */ import { i18n } from '@kbn/i18n'; +import { aggDerivativeFnName } from './derivative_fn'; import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; @@ -43,6 +44,7 @@ export const getDerivativeMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.DERIVATIVE, + expressionName: aggDerivativeFnName, title: derivativeTitle, makeLabel(agg) { return makeNestedLabel(agg, derivativeLabel); diff --git a/src/plugins/data/common/search/aggs/metrics/derivative_fn.ts b/src/plugins/data/common/search/aggs/metrics/derivative_fn.ts index 1d87dfdac6da3..354166ad728ad 100644 --- a/src/plugins/data/common/search/aggs/metrics/derivative_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/derivative_fn.ts @@ -23,16 +23,21 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggDerivative'; +export const aggDerivativeFnName = 'aggDerivative'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Arguments = Assign; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggDerivativeFnName, + Input, + Arguments, + Output +>; export const aggDerivative = (): FunctionDefinition => ({ - name: fnName, + name: aggDerivativeFnName, help: i18n.translate('data.search.aggs.function.metrics.derivative.help', { defaultMessage: 'Generates a serialized agg config for a Derivative agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/geo_bounds.ts b/src/plugins/data/common/search/aggs/metrics/geo_bounds.ts index c86f42f066bdf..5157ef1a134a7 100644 --- a/src/plugins/data/common/search/aggs/metrics/geo_bounds.ts +++ b/src/plugins/data/common/search/aggs/metrics/geo_bounds.ts @@ -18,6 +18,7 @@ */ import { i18n } from '@kbn/i18n'; +import { aggGeoBoundsFnName } from './geo_bounds_fn'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; @@ -38,6 +39,7 @@ const geoBoundsLabel = i18n.translate('data.search.aggs.metrics.geoBoundsLabel', export const getGeoBoundsMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.GEO_BOUNDS, + expressionName: aggGeoBoundsFnName, title: geoBoundsTitle, makeLabel: () => geoBoundsLabel, params: [ diff --git a/src/plugins/data/common/search/aggs/metrics/geo_bounds_fn.ts b/src/plugins/data/common/search/aggs/metrics/geo_bounds_fn.ts index 927f7f42d0f50..af5ea3c80506c 100644 --- a/src/plugins/data/common/search/aggs/metrics/geo_bounds_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/geo_bounds_fn.ts @@ -22,15 +22,20 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggGeoBounds'; +export const aggGeoBoundsFnName = 'aggGeoBounds'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggGeoBoundsFnName, + Input, + AggArgs, + Output +>; export const aggGeoBounds = (): FunctionDefinition => ({ - name: fnName, + name: aggGeoBoundsFnName, help: i18n.translate('data.search.aggs.function.metrics.geo_bounds.help', { defaultMessage: 'Generates a serialized agg config for a Geo Bounds agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/geo_centroid.ts b/src/plugins/data/common/search/aggs/metrics/geo_centroid.ts index b98ce45d35229..c293d4a4b1620 100644 --- a/src/plugins/data/common/search/aggs/metrics/geo_centroid.ts +++ b/src/plugins/data/common/search/aggs/metrics/geo_centroid.ts @@ -18,6 +18,7 @@ */ import { i18n } from '@kbn/i18n'; +import { aggGeoCentroidFnName } from './geo_centroid_fn'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; @@ -38,6 +39,7 @@ const geoCentroidLabel = i18n.translate('data.search.aggs.metrics.geoCentroidLab export const getGeoCentroidMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.GEO_CENTROID, + expressionName: aggGeoCentroidFnName, title: geoCentroidTitle, makeLabel: () => geoCentroidLabel, params: [ diff --git a/src/plugins/data/common/search/aggs/metrics/geo_centroid_fn.ts b/src/plugins/data/common/search/aggs/metrics/geo_centroid_fn.ts index 98bd7365f8b3f..2c2d60711def3 100644 --- a/src/plugins/data/common/search/aggs/metrics/geo_centroid_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/geo_centroid_fn.ts @@ -22,15 +22,20 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggGeoCentroid'; +export const aggGeoCentroidFnName = 'aggGeoCentroid'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggGeoCentroidFnName, + Input, + AggArgs, + Output +>; export const aggGeoCentroid = (): FunctionDefinition => ({ - name: fnName, + name: aggGeoCentroidFnName, help: i18n.translate('data.search.aggs.function.metrics.geo_centroid.help', { defaultMessage: 'Generates a serialized agg config for a Geo Centroid agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/max.ts b/src/plugins/data/common/search/aggs/metrics/max.ts index 5b2f08c5b0260..f69b64c47f652 100644 --- a/src/plugins/data/common/search/aggs/metrics/max.ts +++ b/src/plugins/data/common/search/aggs/metrics/max.ts @@ -18,6 +18,7 @@ */ import { i18n } from '@kbn/i18n'; +import { aggMaxFnName } from './max_fn'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; @@ -34,6 +35,7 @@ export interface AggParamsMax extends BaseAggParams { export const getMaxMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.MAX, + expressionName: aggMaxFnName, title: maxTitle, makeLabel(aggConfig) { return i18n.translate('data.search.aggs.metrics.maxLabel', { diff --git a/src/plugins/data/common/search/aggs/metrics/max_fn.ts b/src/plugins/data/common/search/aggs/metrics/max_fn.ts index d1bccd08982f8..9624cd3012398 100644 --- a/src/plugins/data/common/search/aggs/metrics/max_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/max_fn.ts @@ -22,15 +22,15 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggMax'; +export const aggMaxFnName = 'aggMax'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition; export const aggMax = (): FunctionDefinition => ({ - name: fnName, + name: aggMaxFnName, help: i18n.translate('data.search.aggs.function.metrics.max.help', { defaultMessage: 'Generates a serialized agg config for a Max agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/median.test.ts b/src/plugins/data/common/search/aggs/metrics/median.test.ts index 42298586cb68f..42ea942098c4a 100644 --- a/src/plugins/data/common/search/aggs/metrics/median.test.ts +++ b/src/plugins/data/common/search/aggs/metrics/median.test.ts @@ -82,4 +82,28 @@ describe('AggTypeMetricMedianProvider class', () => { }) ).toEqual(10); }); + + it('produces the expected expression ast', () => { + const agg = aggConfigs.getResponseAggs()[0]; + expect(agg.toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "enabled": Array [ + true, + ], + "field": Array [ + "bytes", + ], + "id": Array [ + "median", + ], + "schema": Array [ + "metric", + ], + }, + "function": "aggMedian", + "type": "function", + } + `); + }); }); diff --git a/src/plugins/data/common/search/aggs/metrics/median.ts b/src/plugins/data/common/search/aggs/metrics/median.ts index a189461020915..c511a7018575d 100644 --- a/src/plugins/data/common/search/aggs/metrics/median.ts +++ b/src/plugins/data/common/search/aggs/metrics/median.ts @@ -18,6 +18,7 @@ */ import { i18n } from '@kbn/i18n'; +import { aggMedianFnName } from './median_fn'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; @@ -34,6 +35,7 @@ export interface AggParamsMedian extends BaseAggParams { export const getMedianMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.MEDIAN, + expressionName: aggMedianFnName, dslName: 'percentiles', title: medianTitle, makeLabel(aggConfig) { diff --git a/src/plugins/data/common/search/aggs/metrics/median_fn.ts b/src/plugins/data/common/search/aggs/metrics/median_fn.ts index c5e9edb86e81c..e2ea8ae0fe2e7 100644 --- a/src/plugins/data/common/search/aggs/metrics/median_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/median_fn.ts @@ -22,15 +22,20 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggMedian'; +export const aggMedianFnName = 'aggMedian'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggMedianFnName, + Input, + AggArgs, + Output +>; export const aggMedian = (): FunctionDefinition => ({ - name: fnName, + name: aggMedianFnName, help: i18n.translate('data.search.aggs.function.metrics.median.help', { defaultMessage: 'Generates a serialized agg config for a Median agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/min.ts b/src/plugins/data/common/search/aggs/metrics/min.ts index 6472c3ae12990..a0ed0cd19c127 100644 --- a/src/plugins/data/common/search/aggs/metrics/min.ts +++ b/src/plugins/data/common/search/aggs/metrics/min.ts @@ -20,6 +20,7 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; +import { aggMinFnName } from './min_fn'; import { KBN_FIELD_TYPES } from '../../../../common'; import { BaseAggParams } from '../types'; @@ -34,6 +35,7 @@ export interface AggParamsMin extends BaseAggParams { export const getMinMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.MIN, + expressionName: aggMinFnName, title: minTitle, makeLabel(aggConfig) { return i18n.translate('data.search.aggs.metrics.minLabel', { diff --git a/src/plugins/data/common/search/aggs/metrics/min_fn.ts b/src/plugins/data/common/search/aggs/metrics/min_fn.ts index 7a57c79a350fa..b880937eea2d7 100644 --- a/src/plugins/data/common/search/aggs/metrics/min_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/min_fn.ts @@ -22,15 +22,15 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggMin'; +export const aggMinFnName = 'aggMin'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition; export const aggMin = (): FunctionDefinition => ({ - name: fnName, + name: aggMinFnName, help: i18n.translate('data.search.aggs.function.metrics.min.help', { defaultMessage: 'Generates a serialized agg config for a Min agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/moving_avg.ts b/src/plugins/data/common/search/aggs/metrics/moving_avg.ts index 1791d49b98437..60e0f4293cb9e 100644 --- a/src/plugins/data/common/search/aggs/metrics/moving_avg.ts +++ b/src/plugins/data/common/search/aggs/metrics/moving_avg.ts @@ -19,6 +19,7 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; +import { aggMovingAvgFnName } from './moving_avg_fn'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; @@ -45,6 +46,7 @@ export const getMovingAvgMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.MOVING_FN, + expressionName: aggMovingAvgFnName, dslName: 'moving_fn', title: movingAvgTitle, makeLabel: (agg) => makeNestedLabel(agg, movingAvgLabel), diff --git a/src/plugins/data/common/search/aggs/metrics/moving_avg_fn.ts b/src/plugins/data/common/search/aggs/metrics/moving_avg_fn.ts index e1c1637d3ad1d..f517becf2bd65 100644 --- a/src/plugins/data/common/search/aggs/metrics/moving_avg_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/moving_avg_fn.ts @@ -23,16 +23,21 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggMovingAvg'; +export const aggMovingAvgFnName = 'aggMovingAvg'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Arguments = Assign; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggMovingAvgFnName, + Input, + Arguments, + Output +>; export const aggMovingAvg = (): FunctionDefinition => ({ - name: fnName, + name: aggMovingAvgFnName, help: i18n.translate('data.search.aggs.function.metrics.moving_avg.help', { defaultMessage: 'Generates a serialized agg config for a Moving Average agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/percentile_ranks.test.ts b/src/plugins/data/common/search/aggs/metrics/percentile_ranks.test.ts index 970daf5b62458..9955aeef4e0d2 100644 --- a/src/plugins/data/common/search/aggs/metrics/percentile_ranks.test.ts +++ b/src/plugins/data/common/search/aggs/metrics/percentile_ranks.test.ts @@ -63,7 +63,7 @@ describe('AggTypesMetricsPercentileRanksProvider class', function () { ); }); - it('uses the custom label if it is set', function () { + it('uses the custom label if it is set', () => { const responseAggs: any = getPercentileRanksMetricAgg(aggTypesDependencies).getResponseAggs( aggConfigs.aggs[0] as IPercentileRanksAggConfig ); @@ -74,4 +74,62 @@ describe('AggTypesMetricsPercentileRanksProvider class', function () { expect(percentileRankLabelFor5kBytes).toBe('Percentile rank 5000 of "my custom field label"'); expect(percentileRankLabelFor10kBytes).toBe('Percentile rank 10000 of "my custom field label"'); }); + + it('produces the expected expression ast', () => { + const responseAggs: any = getPercentileRanksMetricAgg(aggTypesDependencies).getResponseAggs( + aggConfigs.aggs[0] as IPercentileRanksAggConfig + ); + expect(responseAggs[0].toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "customLabel": Array [ + "my custom field label", + ], + "enabled": Array [ + true, + ], + "field": Array [ + "bytes", + ], + "id": Array [ + "percentile_ranks.5000", + ], + "schema": Array [ + "metric", + ], + "values": Array [ + "[5000,10000]", + ], + }, + "function": "aggPercentileRanks", + "type": "function", + } + `); + expect(responseAggs[1].toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "customLabel": Array [ + "my custom field label", + ], + "enabled": Array [ + true, + ], + "field": Array [ + "bytes", + ], + "id": Array [ + "percentile_ranks.10000", + ], + "schema": Array [ + "metric", + ], + "values": Array [ + "[5000,10000]", + ], + }, + "function": "aggPercentileRanks", + "type": "function", + } + `); + }); }); diff --git a/src/plugins/data/common/search/aggs/metrics/percentile_ranks.ts b/src/plugins/data/common/search/aggs/metrics/percentile_ranks.ts index 664cc1ad02ada..5260f52731a88 100644 --- a/src/plugins/data/common/search/aggs/metrics/percentile_ranks.ts +++ b/src/plugins/data/common/search/aggs/metrics/percentile_ranks.ts @@ -25,6 +25,7 @@ import { BaseAggParams } from '../types'; import { MetricAggType } from './metric_agg_type'; import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_response_agg_config_class'; +import { aggPercentileRanksFnName } from './percentile_ranks_fn'; import { getPercentileValue } from './percentiles_get_value'; import { METRIC_TYPES } from './metric_agg_types'; @@ -64,6 +65,7 @@ export const getPercentileRanksMetricAgg = ({ }: PercentileRanksMetricAggDependencies) => { return new MetricAggType({ name: METRIC_TYPES.PERCENTILE_RANKS, + expressionName: aggPercentileRanksFnName, title: i18n.translate('data.search.aggs.metrics.percentileRanksTitle', { defaultMessage: 'Percentile Ranks', }), diff --git a/src/plugins/data/common/search/aggs/metrics/percentile_ranks_fn.ts b/src/plugins/data/common/search/aggs/metrics/percentile_ranks_fn.ts index 08e1489a856dd..9bf35c4dba9ff 100644 --- a/src/plugins/data/common/search/aggs/metrics/percentile_ranks_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/percentile_ranks_fn.ts @@ -22,15 +22,20 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggPercentileRanks'; +export const aggPercentileRanksFnName = 'aggPercentileRanks'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggPercentileRanksFnName, + Input, + AggArgs, + Output +>; export const aggPercentileRanks = (): FunctionDefinition => ({ - name: fnName, + name: aggPercentileRanksFnName, help: i18n.translate('data.search.aggs.function.metrics.percentile_ranks.help', { defaultMessage: 'Generates a serialized agg config for a Percentile Ranks agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/percentiles.test.ts b/src/plugins/data/common/search/aggs/metrics/percentiles.test.ts index 10e98df5a4eeb..78b00a48a9611 100644 --- a/src/plugins/data/common/search/aggs/metrics/percentiles.test.ts +++ b/src/plugins/data/common/search/aggs/metrics/percentiles.test.ts @@ -66,4 +66,36 @@ describe('AggTypesMetricsPercentilesProvider class', () => { expect(ninetyFifthPercentileLabel).toBe('95th percentile of prince'); }); + + it('produces the expected expression ast', () => { + const responseAggs: any = getPercentilesMetricAgg().getResponseAggs( + aggConfigs.aggs[0] as IPercentileAggConfig + ); + expect(responseAggs[0].toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "customLabel": Array [ + "prince", + ], + "enabled": Array [ + true, + ], + "field": Array [ + "bytes", + ], + "id": Array [ + "percentiles.95", + ], + "percents": Array [ + "[95]", + ], + "schema": Array [ + "metric", + ], + }, + "function": "aggPercentiles", + "type": "function", + } + `); + }); }); diff --git a/src/plugins/data/common/search/aggs/metrics/percentiles.ts b/src/plugins/data/common/search/aggs/metrics/percentiles.ts index 8ea493f324811..22aeb820dbe0b 100644 --- a/src/plugins/data/common/search/aggs/metrics/percentiles.ts +++ b/src/plugins/data/common/search/aggs/metrics/percentiles.ts @@ -22,6 +22,7 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_response_agg_config_class'; +import { aggPercentilesFnName } from './percentiles_fn'; import { getPercentileValue } from './percentiles_get_value'; import { ordinalSuffix } from './lib/ordinal_suffix'; import { BaseAggParams } from '../types'; @@ -48,6 +49,7 @@ const valueProps = { export const getPercentilesMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.PERCENTILES, + expressionName: aggPercentilesFnName, title: i18n.translate('data.search.aggs.metrics.percentilesTitle', { defaultMessage: 'Percentiles', }), diff --git a/src/plugins/data/common/search/aggs/metrics/percentiles_fn.ts b/src/plugins/data/common/search/aggs/metrics/percentiles_fn.ts index eb8952267f5ea..d7bcefc23f711 100644 --- a/src/plugins/data/common/search/aggs/metrics/percentiles_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/percentiles_fn.ts @@ -22,15 +22,20 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggPercentiles'; +export const aggPercentilesFnName = 'aggPercentiles'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggPercentilesFnName, + Input, + AggArgs, + Output +>; export const aggPercentiles = (): FunctionDefinition => ({ - name: fnName, + name: aggPercentilesFnName, help: i18n.translate('data.search.aggs.function.metrics.percentiles.help', { defaultMessage: 'Generates a serialized agg config for a Percentiles agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/serial_diff.ts b/src/plugins/data/common/search/aggs/metrics/serial_diff.ts index a4e4d7a8990fa..30158a312289f 100644 --- a/src/plugins/data/common/search/aggs/metrics/serial_diff.ts +++ b/src/plugins/data/common/search/aggs/metrics/serial_diff.ts @@ -19,6 +19,7 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; +import { aggSerialDiffFnName } from './serial_diff_fn'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; @@ -43,6 +44,7 @@ export const getSerialDiffMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.SERIAL_DIFF, + expressionName: aggSerialDiffFnName, title: serialDiffTitle, makeLabel: (agg) => makeNestedLabel(agg, serialDiffLabel), subtype, diff --git a/src/plugins/data/common/search/aggs/metrics/serial_diff_fn.ts b/src/plugins/data/common/search/aggs/metrics/serial_diff_fn.ts index 3cc1dacb87b3d..96f82e430a0b4 100644 --- a/src/plugins/data/common/search/aggs/metrics/serial_diff_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/serial_diff_fn.ts @@ -23,16 +23,21 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggSerialDiff'; +export const aggSerialDiffFnName = 'aggSerialDiff'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Arguments = Assign; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggSerialDiffFnName, + Input, + Arguments, + Output +>; export const aggSerialDiff = (): FunctionDefinition => ({ - name: fnName, + name: aggSerialDiffFnName, help: i18n.translate('data.search.aggs.function.metrics.serial_diff.help', { defaultMessage: 'Generates a serialized agg config for a Serial Differencing agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/std_deviation.test.ts b/src/plugins/data/common/search/aggs/metrics/std_deviation.test.ts index f2f30fcde42eb..6ca0c6698376f 100644 --- a/src/plugins/data/common/search/aggs/metrics/std_deviation.test.ts +++ b/src/plugins/data/common/search/aggs/metrics/std_deviation.test.ts @@ -82,4 +82,29 @@ describe('AggTypeMetricStandardDeviationProvider class', () => { expect(lowerStdDevLabel).toBe('Lower Standard Deviation of memory'); expect(upperStdDevLabel).toBe('Upper Standard Deviation of memory'); }); + + it('produces the expected expression ast', () => { + const aggConfigs = getAggConfigs(); + + const responseAggs: any = getStdDeviationMetricAgg().getResponseAggs( + aggConfigs.aggs[0] as IStdDevAggConfig + ); + expect(responseAggs[0].toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "enabled": Array [ + true, + ], + "id": Array [ + "std_dev.std_lower", + ], + "schema": Array [ + "metric", + ], + }, + "function": "aggStdDeviation", + "type": "function", + } + `); + }); }); diff --git a/src/plugins/data/common/search/aggs/metrics/std_deviation.ts b/src/plugins/data/common/search/aggs/metrics/std_deviation.ts index 9aba063776252..88b2fd69e2b85 100644 --- a/src/plugins/data/common/search/aggs/metrics/std_deviation.ts +++ b/src/plugins/data/common/search/aggs/metrics/std_deviation.ts @@ -20,6 +20,7 @@ import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; +import { aggStdDeviationFnName } from './std_deviation_fn'; import { METRIC_TYPES } from './metric_agg_types'; import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_response_agg_config_class'; import { KBN_FIELD_TYPES } from '../../../../common'; @@ -83,6 +84,7 @@ const responseAggConfigProps = { export const getStdDeviationMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.STD_DEV, + expressionName: aggStdDeviationFnName, dslName: 'extended_stats', title: i18n.translate('data.search.aggs.metrics.standardDeviationTitle', { defaultMessage: 'Standard Deviation', diff --git a/src/plugins/data/common/search/aggs/metrics/std_deviation_fn.ts b/src/plugins/data/common/search/aggs/metrics/std_deviation_fn.ts index 61b8a6f28f088..2a3c1bd33e17d 100644 --- a/src/plugins/data/common/search/aggs/metrics/std_deviation_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/std_deviation_fn.ts @@ -22,15 +22,20 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggStdDeviation'; +export const aggStdDeviationFnName = 'aggStdDeviation'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggStdDeviationFnName, + Input, + AggArgs, + Output +>; export const aggStdDeviation = (): FunctionDefinition => ({ - name: fnName, + name: aggStdDeviationFnName, help: i18n.translate('data.search.aggs.function.metrics.std_deviation.help', { defaultMessage: 'Generates a serialized agg config for a Standard Deviation agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/sum.ts b/src/plugins/data/common/search/aggs/metrics/sum.ts index fa44af98554da..c24887b5e0818 100644 --- a/src/plugins/data/common/search/aggs/metrics/sum.ts +++ b/src/plugins/data/common/search/aggs/metrics/sum.ts @@ -19,6 +19,7 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; +import { aggSumFnName } from './sum_fn'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; import { BaseAggParams } from '../types'; @@ -34,6 +35,7 @@ export interface AggParamsSum extends BaseAggParams { export const getSumMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.SUM, + expressionName: aggSumFnName, title: sumTitle, makeLabel(aggConfig) { return i18n.translate('data.search.aggs.metrics.sumLabel', { diff --git a/src/plugins/data/common/search/aggs/metrics/sum_fn.ts b/src/plugins/data/common/search/aggs/metrics/sum_fn.ts index e625befc8f1d9..a42510dc594ad 100644 --- a/src/plugins/data/common/search/aggs/metrics/sum_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/sum_fn.ts @@ -22,15 +22,15 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggSum'; +export const aggSumFnName = 'aggSum'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition; export const aggSum = (): FunctionDefinition => ({ - name: fnName, + name: aggSumFnName, help: i18n.translate('data.search.aggs.function.metrics.sum.help', { defaultMessage: 'Generates a serialized agg config for a Sum agg', }), diff --git a/src/plugins/data/common/search/aggs/metrics/top_hit.test.ts b/src/plugins/data/common/search/aggs/metrics/top_hit.test.ts index c0cbfb33c842b..2fdefa7679e9b 100644 --- a/src/plugins/data/common/search/aggs/metrics/top_hit.test.ts +++ b/src/plugins/data/common/search/aggs/metrics/top_hit.test.ts @@ -102,6 +102,42 @@ describe('Top hit metric', () => { expect(getTopHitMetricAgg().makeLabel(aggConfig)).toEqual('First bytes'); }); + it('produces the expected expression ast', () => { + init({ fieldName: 'machine.os' }); + expect(aggConfig.toExpressionAst()).toMatchInlineSnapshot(` + Object { + "arguments": Object { + "aggregate": Array [ + "concat", + ], + "enabled": Array [ + true, + ], + "field": Array [ + "machine.os", + ], + "id": Array [ + "1", + ], + "schema": Array [ + "metric", + ], + "size": Array [ + 1, + ], + "sortField": Array [ + "machine.os", + ], + "sortOrder": Array [ + "desc", + ], + }, + "function": "aggTopHit", + "type": "function", + } + `); + }); + it('should request the _source field', () => { init({ field: '_source' }); expect(aggDsl.top_hits._source).toBeTruthy(); diff --git a/src/plugins/data/common/search/aggs/metrics/top_hit.ts b/src/plugins/data/common/search/aggs/metrics/top_hit.ts index bee731dcc2e0d..3ef9f9ffa3ad0 100644 --- a/src/plugins/data/common/search/aggs/metrics/top_hit.ts +++ b/src/plugins/data/common/search/aggs/metrics/top_hit.ts @@ -19,6 +19,7 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; +import { aggTopHitFnName } from './top_hit_fn'; import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; @@ -41,6 +42,7 @@ const isNumericFieldSelected = (agg: IMetricAggConfig) => { export const getTopHitMetricAgg = () => { return new MetricAggType({ name: METRIC_TYPES.TOP_HITS, + expressionName: aggTopHitFnName, title: i18n.translate('data.search.aggs.metrics.topHitTitle', { defaultMessage: 'Top Hit', }), diff --git a/src/plugins/data/common/search/aggs/metrics/top_hit_fn.ts b/src/plugins/data/common/search/aggs/metrics/top_hit_fn.ts index e0c3fd0d070b2..38a3bc6a59bfc 100644 --- a/src/plugins/data/common/search/aggs/metrics/top_hit_fn.ts +++ b/src/plugins/data/common/search/aggs/metrics/top_hit_fn.ts @@ -22,15 +22,20 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { AggExpressionType, AggExpressionFunctionArgs, METRIC_TYPES } from '../'; import { getParsedValue } from '../utils/get_parsed_value'; -const fnName = 'aggTopHit'; +export const aggTopHitFnName = 'aggTopHit'; type Input = any; type AggArgs = AggExpressionFunctionArgs; type Output = AggExpressionType; -type FunctionDefinition = ExpressionFunctionDefinition; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggTopHitFnName, + Input, + AggArgs, + Output +>; export const aggTopHit = (): FunctionDefinition => ({ - name: fnName, + name: aggTopHitFnName, help: i18n.translate('data.search.aggs.function.metrics.top_hit.help', { defaultMessage: 'Generates a serialized agg config for a Top Hit agg', }), diff --git a/src/plugins/data/public/search/expressions/esaggs/build_tabular_inspector_data.ts b/src/plugins/data/common/search/expressions/esaggs/build_tabular_inspector_data.ts similarity index 95% rename from src/plugins/data/public/search/expressions/esaggs/build_tabular_inspector_data.ts rename to src/plugins/data/common/search/expressions/esaggs/build_tabular_inspector_data.ts index 79dedf4131764..2db3694884e2c 100644 --- a/src/plugins/data/public/search/expressions/esaggs/build_tabular_inspector_data.ts +++ b/src/plugins/data/common/search/expressions/esaggs/build_tabular_inspector_data.ts @@ -23,9 +23,10 @@ import { TabularData, TabularDataValue, } from '../../../../../../plugins/inspector/common'; -import { Filter, TabbedTable } from '../../../../common'; -import { FormatFactory } from '../../../../common/field_formats/utils'; -import { createFilter } from '../create_filter'; +import { Filter } from '../../../es_query'; +import { FormatFactory } from '../../../field_formats/utils'; +import { TabbedTable } from '../../tabify'; +import { createFilter } from './create_filter'; /** * Type borrowed from the client-side FilterManager['addFilters']. diff --git a/src/plugins/data/public/search/expressions/create_filter.test.ts b/src/plugins/data/common/search/expressions/esaggs/create_filter.test.ts similarity index 91% rename from src/plugins/data/public/search/expressions/create_filter.test.ts rename to src/plugins/data/common/search/expressions/esaggs/create_filter.test.ts index 7cc336a1c20e9..de0990ea9e287 100644 --- a/src/plugins/data/public/search/expressions/create_filter.test.ts +++ b/src/plugins/data/common/search/expressions/esaggs/create_filter.test.ts @@ -17,15 +17,11 @@ * under the License. */ -import { - AggConfigs, - IAggConfig, - TabbedTable, - isRangeFilter, - BytesFormat, - FieldFormatsGetConfigFn, -} from '../../../common'; -import { mockAggTypesRegistry } from '../../../common/search/aggs/test_helpers'; +import { isRangeFilter } from '../../../es_query/filters'; +import { BytesFormat, FieldFormatsGetConfigFn } from '../../../field_formats'; +import { AggConfigs, IAggConfig } from '../../aggs'; +import { mockAggTypesRegistry } from '../../aggs/test_helpers'; +import { TabbedTable } from '../../tabify'; import { createFilter } from './create_filter'; diff --git a/src/plugins/data/public/search/expressions/create_filter.ts b/src/plugins/data/common/search/expressions/esaggs/create_filter.ts similarity index 94% rename from src/plugins/data/public/search/expressions/create_filter.ts rename to src/plugins/data/common/search/expressions/esaggs/create_filter.ts index 09200c2e17b31..cfb406e18e6c3 100644 --- a/src/plugins/data/public/search/expressions/create_filter.ts +++ b/src/plugins/data/common/search/expressions/esaggs/create_filter.ts @@ -17,7 +17,9 @@ * under the License. */ -import { Filter, IAggConfig, TabbedTable } from '../../../common'; +import { Filter } from '../../../es_query'; +import { IAggConfig } from '../../aggs'; +import { TabbedTable } from '../../tabify'; const getOtherBucketFilterTerms = (table: TabbedTable, columnIndex: number, rowIndex: number) => { if (rowIndex === -1) { diff --git a/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts b/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts new file mode 100644 index 0000000000000..ca1234276f416 --- /dev/null +++ b/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts @@ -0,0 +1,154 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; + +import { + Datatable, + DatatableColumn, + ExpressionFunctionDefinition, +} from 'src/plugins/expressions/common'; + +import { FormatFactory } from '../../../field_formats/utils'; +import { IndexPatternsContract } from '../../../index_patterns/index_patterns'; +import { calculateBounds } from '../../../query'; + +import { AggsStart } from '../../aggs'; +import { ISearchStartSearchSource } from '../../search_source'; + +import { KibanaContext } from '../kibana_context_type'; +import { AddFilters } from './build_tabular_inspector_data'; +import { handleRequest, RequestHandlerParams } from './request_handler'; + +const name = 'esaggs'; + +type Input = KibanaContext | null; +type Output = Promise; + +interface Arguments { + index: string; + metricsAtAllLevels: boolean; + partialRows: boolean; + includeFormatHints: boolean; + aggConfigs: string; + timeFields?: string[]; +} + +export type EsaggsExpressionFunctionDefinition = ExpressionFunctionDefinition< + 'esaggs', + Input, + Arguments, + Output +>; + +/** @internal */ +export interface EsaggsStartDependencies { + addFilters?: AddFilters; + aggs: AggsStart; + deserializeFieldFormat: FormatFactory; + indexPatterns: IndexPatternsContract; + searchSource: ISearchStartSearchSource; +} + +/** @internal */ +export const getEsaggsMeta: () => Omit = () => ({ + 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, + }, + }, +}); + +/** @internal */ +export async function handleEsaggsRequest( + input: Input, + args: Arguments, + params: RequestHandlerParams +): Promise { + const resolvedTimeRange = input?.timeRange && calculateBounds(input.timeRange); + + const response = await handleRequest(params); + + 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: params.indexPattern?.title, + params: column.aggConfig.toSerializedFieldFormat(), + source: name, + sourceParams: { + indexPatternId: params.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 table; +} diff --git a/src/plugins/data/public/search/expressions/esaggs/index.ts b/src/plugins/data/common/search/expressions/esaggs/index.ts similarity index 100% rename from src/plugins/data/public/search/expressions/esaggs/index.ts rename to src/plugins/data/common/search/expressions/esaggs/index.ts diff --git a/src/plugins/data/public/search/expressions/esaggs/request_handler.ts b/src/plugins/data/common/search/expressions/esaggs/request_handler.ts similarity index 99% rename from src/plugins/data/public/search/expressions/esaggs/request_handler.ts rename to src/plugins/data/common/search/expressions/esaggs/request_handler.ts index 7a27d65267149..a424ed9e0513d 100644 --- a/src/plugins/data/public/search/expressions/esaggs/request_handler.ts +++ b/src/plugins/data/common/search/expressions/esaggs/request_handler.ts @@ -40,7 +40,8 @@ import { FormatFactory } from '../../../../common/field_formats/utils'; import { AddFilters, buildTabularInspectorData } from './build_tabular_inspector_data'; -interface RequestHandlerParams { +/** @internal */ +export interface RequestHandlerParams { abortSignal?: AbortSignal; addFilters?: AddFilters; aggs: IAggConfigs; diff --git a/src/plugins/data/public/index_patterns/expressions/load_index_pattern.test.ts b/src/plugins/data/public/index_patterns/expressions/load_index_pattern.test.ts index 378ceb376f5f1..eebe1ab80a536 100644 --- a/src/plugins/data/public/index_patterns/expressions/load_index_pattern.test.ts +++ b/src/plugins/data/public/index_patterns/expressions/load_index_pattern.test.ts @@ -17,22 +17,27 @@ * under the License. */ -import { indexPatternLoad } from './load_index_pattern'; - -jest.mock('../../services', () => ({ - getIndexPatterns: () => ({ - get: (id: string) => ({ - toSpec: () => ({ - title: 'value', - }), - }), - }), -})); +import { IndexPatternLoadStartDependencies } from '../../../common/index_patterns/expressions'; +import { getFunctionDefinition } from './load_index_pattern'; describe('indexPattern expression function', () => { + let getStartDependencies: () => Promise; + + beforeEach(() => { + getStartDependencies = jest.fn().mockResolvedValue({ + indexPatterns: { + get: (id: string) => ({ + toSpec: () => ({ + title: 'value', + }), + }), + }, + }); + }); + test('returns serialized index pattern', async () => { - const indexPatternDefinition = indexPatternLoad(); - const result = await indexPatternDefinition.fn(null, { id: '1' }, {} as any); + const indexPatternDefinition = getFunctionDefinition({ getStartDependencies }); + const result = await indexPatternDefinition().fn(null, { id: '1' }, {} as any); expect(result.type).toEqual('index_pattern'); expect(result.value.title).toEqual('value'); }); diff --git a/src/plugins/data/public/index_patterns/expressions/load_index_pattern.ts b/src/plugins/data/public/index_patterns/expressions/load_index_pattern.ts index 901d6aac7fbff..64e86f967c2b1 100644 --- a/src/plugins/data/public/index_patterns/expressions/load_index_pattern.ts +++ b/src/plugins/data/public/index_patterns/expressions/load_index_pattern.ts @@ -17,46 +17,66 @@ * under the License. */ -import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition } from '../../../../../plugins/expressions/public'; -import { getIndexPatterns } from '../../services'; -import { IndexPatternSpec } from '../../../common/index_patterns'; +import { StartServicesAccessor } from 'src/core/public'; +import { + getIndexPatternLoadMeta, + IndexPatternLoadExpressionFunctionDefinition, + IndexPatternLoadStartDependencies, +} from '../../../common/index_patterns/expressions'; +import { DataPublicPluginStart, DataStartDependencies } from '../../types'; -const name = 'indexPatternLoad'; +/** + * Returns the expression function definition. Any stateful dependencies are accessed + * at runtime via the `getStartDependencies` param, which provides the specific services + * needed for this function to run. + * + * This function is an implementation detail of this module, and is exported separately + * only for testing purposes. + * + * @param getStartDependencies - async function that resolves with IndexPatternLoadStartDependencies + * + * @internal + */ +export function getFunctionDefinition({ + getStartDependencies, +}: { + getStartDependencies: () => Promise; +}) { + return (): IndexPatternLoadExpressionFunctionDefinition => ({ + ...getIndexPatternLoadMeta(), + async fn(input, args) { + const { indexPatterns } = await getStartDependencies(); -type Input = null; -type Output = Promise<{ type: 'index_pattern'; value: IndexPatternSpec }>; + const indexPattern = await indexPatterns.get(args.id); -interface Arguments { - id: string; + return { type: 'index_pattern', value: indexPattern.toSpec() }; + }, + }); } -export const indexPatternLoad = (): ExpressionFunctionDefinition< - typeof name, - Input, - Arguments, - Output -> => ({ - name, - type: 'index_pattern', - inputTypes: ['null'], - help: i18n.translate('data.functions.indexPatternLoad.help', { - defaultMessage: 'Loads an index pattern', - }), - args: { - id: { - types: ['string'], - required: true, - help: i18n.translate('data.functions.indexPatternLoad.id.help', { - defaultMessage: 'index pattern id to load', - }), +/** + * This is some glue code that takes in `core.getStartServices`, extracts the dependencies + * needed for this function, and wraps them behind a `getStartDependencies` function that + * is then called at runtime. + * + * We do this so that we can be explicit about exactly which dependencies the function + * requires, without cluttering up the top-level `plugin.ts` with this logic. It also + * makes testing the expression function a bit easier since `getStartDependencies` is + * the only thing you should need to mock. + * + * @param getStartServices - core's StartServicesAccessor for this plugin + * + * @internal + */ +export function getIndexPatternLoad({ + getStartServices, +}: { + getStartServices: StartServicesAccessor; +}) { + return getFunctionDefinition({ + getStartDependencies: async () => { + const [, , { indexPatterns }] = await getStartServices(); + return { indexPatterns }; }, - }, - async fn(input, args) { - const indexPatterns = getIndexPatterns(); - - const indexPattern = await indexPatterns.get(args.id); - - return { type: 'index_pattern', value: indexPattern.toSpec() }; - }, -}); + }); +} diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index 8d40447a48ff0..3c8ea0351dee6 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -48,7 +48,6 @@ import { setUiSettings, } from './services'; import { createSearchBar } from './ui/search_bar/create_search_bar'; -import { getEsaggs } from './search/expressions'; import { SELECT_RANGE_TRIGGER, VALUE_CLICK_TRIGGER, @@ -69,7 +68,7 @@ import { } from './actions'; import { SavedObjectsClientPublicToCommon } from './index_patterns'; -import { indexPatternLoad } from './index_patterns/expressions/load_index_pattern'; +import { getIndexPatternLoad } from './index_patterns/expressions'; import { UsageCollectionSetup } from '../../usage_collection/public'; declare module '../../ui_actions/public' { @@ -109,22 +108,7 @@ export class DataPublicPlugin ): DataPublicPluginSetup { const startServices = createStartServicesGetter(core.getStartServices); - 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, - }; - }, - }) - ); + expressions.registerFunction(getIndexPatternLoad({ getStartServices: core.getStartServices })); this.usageCollection = usageCollection; diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index ad1861cecea0b..f8f006618f614 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -2163,7 +2163,7 @@ export class SearchSource { type?: string | undefined; query?: import("../..").Query | undefined; filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined; - sort?: Record | Record[] | undefined; + sort?: Record | Record[] | undefined; highlight?: any; highlightAll?: boolean | undefined; aggs?: any; diff --git a/src/plugins/data/public/search/expressions/esaggs.ts b/src/plugins/data/public/search/expressions/esaggs.ts new file mode 100644 index 0000000000000..efb31423afcdf --- /dev/null +++ b/src/plugins/data/public/search/expressions/esaggs.ts @@ -0,0 +1,115 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { get } from 'lodash'; +import { StartServicesAccessor } from 'src/core/public'; +import { Adapters } from 'src/plugins/inspector/common'; +import { + EsaggsExpressionFunctionDefinition, + EsaggsStartDependencies, + getEsaggsMeta, + handleEsaggsRequest, +} from '../../../common/search/expressions'; +import { DataPublicPluginStart, DataStartDependencies } from '../../types'; + +/** + * Returns the expression function definition. Any stateful dependencies are accessed + * at runtime via the `getStartDependencies` param, which provides the specific services + * needed for this function to run. + * + * This function is an implementation detail of this module, and is exported separately + * only for testing purposes. + * + * @param getStartDependencies - async function that resolves with EsaggsStartDependencies + * + * @internal + */ +export function getFunctionDefinition({ + getStartDependencies, +}: { + getStartDependencies: () => Promise; +}) { + return (): EsaggsExpressionFunctionDefinition => ({ + ...getEsaggsMeta(), + async fn(input, args, { inspectorAdapters, abortSignal, getSearchSessionId }) { + const { + addFilters, + aggs, + deserializeFieldFormat, + indexPatterns, + searchSource, + } = await getStartDependencies(); + + const aggConfigsState = JSON.parse(args.aggConfigs); + const indexPattern = await indexPatterns.get(args.index); + const aggConfigs = aggs.createAggConfigs(indexPattern, aggConfigsState); + + return await handleEsaggsRequest(input, args, { + 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), + }); + }, + }); +} + +/** + * This is some glue code that takes in `core.getStartServices`, extracts the dependencies + * needed for this function, and wraps them behind a `getStartDependencies` function that + * is then called at runtime. + * + * We do this so that we can be explicit about exactly which dependencies the function + * requires, without cluttering up the top-level `plugin.ts` with this logic. It also + * makes testing the expression function a bit easier since `getStartDependencies` is + * the only thing you should need to mock. + * + * @param getStartServices - core's StartServicesAccessor for this plugin + * + * @internal + */ +export function getEsaggs({ + getStartServices, +}: { + getStartServices: StartServicesAccessor; +}) { + return getFunctionDefinition({ + getStartDependencies: async () => { + const [, , self] = await 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, + }; + }, + }); +} diff --git a/src/plugins/data/public/search/expressions/esaggs/esaggs_fn.ts b/src/plugins/data/public/search/expressions/esaggs/esaggs_fn.ts deleted file mode 100644 index ce3bd9bdaee76..0000000000000 --- a/src/plugins/data/public/search/expressions/esaggs/esaggs_fn.ts +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { get } from 'lodash'; -import { i18n } from '@kbn/i18n'; - -import { Datatable, DatatableColumn } from 'src/plugins/expressions/common'; -import { Adapters } from 'src/plugins/inspector/common'; - -import { calculateBounds, EsaggsExpressionFunctionDefinition } from '../../../../common'; -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'; - -interface StartDependencies { - addFilters: AddFilters; - aggs: AggsStart; - deserializeFieldFormat: FormatFactory; - indexPatterns: IndexPatternsContract; - searchSource: ISearchStartSearchSource; -} - -export function getEsaggs({ - getStartDependencies, -}: { - getStartDependencies: () => Promise; -}) { - 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(); - - 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 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: 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 table; - }, - }); -} diff --git a/src/plugins/data/public/search/expressions/index.ts b/src/plugins/data/public/search/expressions/index.ts index 98ed1d08af8ad..9482a9748c466 100644 --- a/src/plugins/data/public/search/expressions/index.ts +++ b/src/plugins/data/public/search/expressions/index.ts @@ -17,6 +17,6 @@ * under the License. */ -export * from './esaggs'; export * from './es_raw_response'; +export * from './esaggs'; export * from './esdsl'; diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 60d2dfdf866cf..1c49de8f0ff4b 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -17,7 +17,13 @@ * under the License. */ -import { Plugin, CoreSetup, CoreStart, PluginInitializerContext } from 'src/core/public'; +import { + Plugin, + CoreSetup, + CoreStart, + PluginInitializerContext, + StartServicesAccessor, +} from 'src/core/public'; import { BehaviorSubject } from 'rxjs'; import { BfetchPublicSetup } from 'src/plugins/bfetch/public'; import { ISearchSetup, ISearchStart, SearchEnhancements } from './types'; @@ -37,7 +43,7 @@ import { IndexPatternsContract } from '../index_patterns/index_patterns'; import { ISearchInterceptor, SearchInterceptor } from './search_interceptor'; import { SearchUsageCollector, createUsageCollector } from './collectors'; import { UsageCollectionSetup } from '../../../usage_collection/public'; -import { esdsl, esRawResponse } from './expressions'; +import { esdsl, esRawResponse, getEsaggs } from './expressions'; import { ExpressionsSetup } from '../../../expressions/public'; import { ISessionsClient, ISessionService, SessionsClient, SessionService } from './session'; import { ConfigSchema } from '../../config'; @@ -46,6 +52,7 @@ import { getShardDelayBucketAgg, } from '../../common/search/aggs/buckets/shard_delay'; import { aggShardDelay } from '../../common/search/aggs/buckets/shard_delay_fn'; +import { DataPublicPluginStart, DataStartDependencies } from '../types'; /** @internal */ export interface SearchServiceSetupDependencies { @@ -96,6 +103,11 @@ export class SearchService implements Plugin { session: this.sessionService, }); + expressions.registerFunction( + getEsaggs({ getStartServices } as { + getStartServices: StartServicesAccessor; + }) + ); expressions.registerFunction(kibana); expressions.registerFunction(kibanaContextFunction); expressions.registerType(kibanaContext); diff --git a/src/plugins/data/server/index_patterns/expressions/index.ts b/src/plugins/data/server/index_patterns/expressions/index.ts new file mode 100644 index 0000000000000..fa37e3b216ac9 --- /dev/null +++ b/src/plugins/data/server/index_patterns/expressions/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './load_index_pattern'; diff --git a/src/plugins/data/server/index_patterns/expressions/load_index_pattern.test.ts b/src/plugins/data/server/index_patterns/expressions/load_index_pattern.test.ts new file mode 100644 index 0000000000000..944bd06d64891 --- /dev/null +++ b/src/plugins/data/server/index_patterns/expressions/load_index_pattern.test.ts @@ -0,0 +1,55 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IndexPatternLoadStartDependencies } from '../../../common/index_patterns/expressions'; +import { getFunctionDefinition } from './load_index_pattern'; + +describe('indexPattern expression function', () => { + let getStartDependencies: () => Promise; + + beforeEach(() => { + getStartDependencies = jest.fn().mockResolvedValue({ + indexPatterns: { + get: (id: string) => ({ + toSpec: () => ({ + title: 'value', + }), + }), + }, + }); + }); + + test('returns serialized index pattern', async () => { + const indexPatternDefinition = getFunctionDefinition({ getStartDependencies }); + const result = await indexPatternDefinition().fn(null, { id: '1' }, { + getKibanaRequest: () => ({}), + } as any); + expect(result.type).toEqual('index_pattern'); + expect(result.value.title).toEqual('value'); + }); + + test('throws if getKibanaRequest is not available', async () => { + const indexPatternDefinition = getFunctionDefinition({ getStartDependencies }); + expect(async () => { + await indexPatternDefinition().fn(null, { id: '1' }, {} as any); + }).rejects.toThrowErrorMatchingInlineSnapshot( + `"A KibanaRequest is required to execute this search on the server. Please provide a request object to the expression execution params."` + ); + }); +}); diff --git a/src/plugins/data/server/index_patterns/expressions/load_index_pattern.ts b/src/plugins/data/server/index_patterns/expressions/load_index_pattern.ts new file mode 100644 index 0000000000000..8cf8492f77a3f --- /dev/null +++ b/src/plugins/data/server/index_patterns/expressions/load_index_pattern.ts @@ -0,0 +1,100 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { KibanaRequest, StartServicesAccessor } from 'src/core/server'; + +import { + getIndexPatternLoadMeta, + IndexPatternLoadExpressionFunctionDefinition, + IndexPatternLoadStartDependencies, +} from '../../../common/index_patterns/expressions'; +import { DataPluginStartDependencies, DataPluginStart } from '../../plugin'; + +/** + * Returns the expression function definition. Any stateful dependencies are accessed + * at runtime via the `getStartDependencies` param, which provides the specific services + * needed for this function to run. + * + * This function is an implementation detail of this module, and is exported separately + * only for testing purposes. + * + * @param getStartDependencies - async function that resolves with IndexPatternLoadStartDependencies + * + * @internal + */ +export function getFunctionDefinition({ + getStartDependencies, +}: { + getStartDependencies: (req: KibanaRequest) => Promise; +}) { + return (): IndexPatternLoadExpressionFunctionDefinition => ({ + ...getIndexPatternLoadMeta(), + async fn(input, args, { getKibanaRequest }) { + const kibanaRequest = getKibanaRequest ? getKibanaRequest() : null; + if (!kibanaRequest) { + throw new Error( + i18n.translate('data.indexPatterns.indexPatternLoad.error.kibanaRequest', { + defaultMessage: + 'A KibanaRequest is required to execute this search on the server. ' + + 'Please provide a request object to the expression execution params.', + }) + ); + } + + const { indexPatterns } = await getStartDependencies(kibanaRequest); + + const indexPattern = await indexPatterns.get(args.id); + + return { type: 'index_pattern', value: indexPattern.toSpec() }; + }, + }); +} + +/** + * This is some glue code that takes in `core.getStartServices`, extracts the dependencies + * needed for this function, and wraps them behind a `getStartDependencies` function that + * is then called at runtime. + * + * We do this so that we can be explicit about exactly which dependencies the function + * requires, without cluttering up the top-level `plugin.ts` with this logic. It also + * makes testing the expression function a bit easier since `getStartDependencies` is + * the only thing you should need to mock. + * + * @param getStartServices - core's StartServicesAccessor for this plugin + * + * @internal + */ +export function getIndexPatternLoad({ + getStartServices, +}: { + getStartServices: StartServicesAccessor; +}) { + return getFunctionDefinition({ + getStartDependencies: async (request: KibanaRequest) => { + const [{ elasticsearch, savedObjects }, , { indexPatterns }] = await getStartServices(); + return { + indexPatterns: await indexPatterns.indexPatternsServiceFactory( + savedObjects.getScopedClient(request), + elasticsearch.client.asScoped(request).asCurrentUser + ), + }; + }, + }); +} diff --git a/src/plugins/data/server/index_patterns/index_patterns_service.ts b/src/plugins/data/server/index_patterns/index_patterns_service.ts index af2d4d6a73e0f..82c96ba4ff7dc 100644 --- a/src/plugins/data/server/index_patterns/index_patterns_service.ts +++ b/src/plugins/data/server/index_patterns/index_patterns_service.ts @@ -25,11 +25,14 @@ import { SavedObjectsClientContract, ElasticsearchClient, } from 'kibana/server'; +import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; +import { DataPluginStartDependencies, DataPluginStart } from '../plugin'; import { registerRoutes } from './routes'; import { indexPatternSavedObjectType } from '../saved_objects'; import { capabilitiesProvider } from './capabilities_provider'; import { IndexPatternsService as IndexPatternsCommonService } from '../../common/index_patterns'; import { FieldFormatsStart } from '../field_formats'; +import { getIndexPatternLoad } from './expressions'; import { UiSettingsServerToCommon } from './ui_settings_wrapper'; import { IndexPatternsApiServer } from './index_patterns_api_client'; import { SavedObjectsClientServerToCommon } from './saved_objects_client_wrapper'; @@ -41,17 +44,26 @@ export interface IndexPatternsServiceStart { ) => Promise; } +export interface IndexPatternsServiceSetupDeps { + expressions: ExpressionsServerSetup; +} + export interface IndexPatternsServiceStartDeps { fieldFormats: FieldFormatsStart; logger: Logger; } export class IndexPatternsService implements Plugin { - public setup(core: CoreSetup) { + public setup( + core: CoreSetup, + { expressions }: IndexPatternsServiceSetupDeps + ) { core.savedObjects.registerType(indexPatternSavedObjectType); core.capabilities.registerProvider(capabilitiesProvider); registerRoutes(core.http); + + expressions.registerFunction(getIndexPatternLoad({ getStartServices: core.getStartServices })); } public start(core: CoreStart, { fieldFormats, logger }: IndexPatternsServiceStartDeps) { diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts index bba2c368ff7d1..12ad0dec0ccd1 100644 --- a/src/plugins/data/server/plugin.ts +++ b/src/plugins/data/server/plugin.ts @@ -17,7 +17,7 @@ * under the License. */ -import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from 'src/core/server'; +import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from 'src/core/server'; import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { BfetchServerSetup } from 'src/plugins/bfetch/server'; import { ConfigSchema } from '../config'; @@ -89,7 +89,7 @@ export class DataServerPlugin core: CoreSetup, { bfetch, expressions, usageCollection }: DataPluginSetupDependencies ) { - this.indexPatterns.setup(core); + this.indexPatterns.setup(core, { expressions }); this.scriptsService.setup(core); this.queryService.setup(core); this.autocompleteService.setup(core); diff --git a/src/plugins/data/server/search/expressions/esaggs.ts b/src/plugins/data/server/search/expressions/esaggs.ts new file mode 100644 index 0000000000000..04cfcd1eef043 --- /dev/null +++ b/src/plugins/data/server/search/expressions/esaggs.ts @@ -0,0 +1,136 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { get } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { KibanaRequest, StartServicesAccessor } from 'src/core/server'; +import { Adapters } from 'src/plugins/inspector/common'; +import { + EsaggsExpressionFunctionDefinition, + EsaggsStartDependencies, + getEsaggsMeta, + handleEsaggsRequest, +} from '../../../common/search/expressions'; +import { DataPluginStartDependencies, DataPluginStart } from '../../plugin'; + +/** + * Returns the expression function definition. Any stateful dependencies are accessed + * at runtime via the `getStartDependencies` param, which provides the specific services + * needed for this function to run. + * + * This function is an implementation detail of this module, and is exported separately + * only for testing purposes. + * + * @param getStartDependencies - async function that resolves with EsaggsStartDependencies + * + * @internal + */ +export function getFunctionDefinition({ + getStartDependencies, +}: { + getStartDependencies: (req: KibanaRequest) => Promise; +}): () => EsaggsExpressionFunctionDefinition { + return () => ({ + ...getEsaggsMeta(), + async fn( + input, + args, + { inspectorAdapters, abortSignal, getSearchSessionId, getKibanaRequest } + ) { + const kibanaRequest = getKibanaRequest ? getKibanaRequest() : null; + if (!kibanaRequest) { + throw new Error( + i18n.translate('data.search.esaggs.error.kibanaRequest', { + defaultMessage: + 'A KibanaRequest is required to execute this search on the server. ' + + 'Please provide a request object to the expression execution params.', + }) + ); + } + + const { + aggs, + deserializeFieldFormat, + indexPatterns, + searchSource, + } = await getStartDependencies(kibanaRequest); + + const aggConfigsState = JSON.parse(args.aggConfigs); + const indexPattern = await indexPatterns.get(args.index); + const aggConfigs = aggs.createAggConfigs(indexPattern, aggConfigsState); + + return await handleEsaggsRequest(input, args, { + abortSignal: (abortSignal as unknown) as AbortSignal, + 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), + }); + }, + }); +} + +/** + * This is some glue code that takes in `core.getStartServices`, extracts the dependencies + * needed for this function, and wraps them behind a `getStartDependencies` function that + * is then called at runtime. + * + * We do this so that we can be explicit about exactly which dependencies the function + * requires, without cluttering up the top-level `plugin.ts` with this logic. It also + * makes testing the expression function a bit easier since `getStartDependencies` is + * the only thing you should need to mock. + * + * @param getStartServices - core's StartServicesAccessor for this plugin + * + * @internal + */ +export function getEsaggs({ + getStartServices, +}: { + getStartServices: StartServicesAccessor; +}): () => EsaggsExpressionFunctionDefinition { + return getFunctionDefinition({ + getStartDependencies: async (request: KibanaRequest) => { + const [{ elasticsearch, savedObjects, uiSettings }, , self] = await getStartServices(); + const { fieldFormats, indexPatterns, search } = self; + const esClient = elasticsearch.client.asScoped(request); + const savedObjectsClient = savedObjects.getScopedClient(request); + const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient); + const scopedFieldFormats = await fieldFormats.fieldFormatServiceFactory(uiSettingsClient); + + return { + aggs: await search.aggs.asScopedToClient(savedObjectsClient, esClient.asCurrentUser), + deserializeFieldFormat: scopedFieldFormats.deserialize.bind(scopedFieldFormats), + indexPatterns: await indexPatterns.indexPatternsServiceFactory( + savedObjectsClient, + esClient.asCurrentUser + ), + searchSource: await search.searchSource.asScoped(request), + }; + }, + }); +} diff --git a/src/plugins/data/server/search/expressions/index.ts b/src/plugins/data/server/search/expressions/index.ts new file mode 100644 index 0000000000000..f1a39a8383629 --- /dev/null +++ b/src/plugins/data/server/search/expressions/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './esaggs'; diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index a9539a8fd3c15..f0c6b383b27e9 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -65,6 +65,7 @@ import { searchSourceRequiredUiSettings, SearchSourceService, } from '../../common/search'; +import { getEsaggs } from './expressions'; import { getShardDelayBucketAgg, SHARD_DELAY_AGG_NAME, @@ -195,6 +196,7 @@ export class SearchService implements Plugin { registerUsageCollector(usageCollection, this.initializerContext); } + expressions.registerFunction(getEsaggs({ getStartServices: core.getStartServices })); expressions.registerFunction(kibana); expressions.registerFunction(kibanaContextFunction); expressions.registerType(kibanaContext); diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 86ec784834ace..fd1f17b20a514 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -29,7 +29,7 @@ import { ExpressionAstFunction } from 'src/plugins/expressions/common'; import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { ExpressionValueBoxed } from 'src/plugins/expressions/common'; -import { FormatFactory } from 'src/plugins/data/common/field_formats/utils'; +import { FormatFactory as FormatFactory_2 } from 'src/plugins/data/common/field_formats/utils'; import { ISavedObjectsRepository } from 'src/core/server'; import { IScopedClusterClient } from 'src/core/server'; import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; @@ -733,8 +733,11 @@ export class IndexPatternsFetcher { // // @public (undocumented) export class IndexPatternsService implements Plugin_3 { + // Warning: (ae-forgotten-export) The symbol "DataPluginStartDependencies" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "IndexPatternsServiceSetupDeps" needs to be exported by the entry point index.d.ts + // // (undocumented) - setup(core: CoreSetup_2): void; + setup(core: CoreSetup_2, { expressions }: IndexPatternsServiceSetupDeps): void; // Warning: (ae-forgotten-export) The symbol "IndexPatternsServiceStartDeps" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -942,7 +945,6 @@ export type ParsedInterval = ReturnType; export function parseInterval(interval: string): moment.Duration | null; // Warning: (ae-forgotten-export) The symbol "DataPluginSetupDependencies" needs to be exported by the entry point index.d.ts -// Warning: (ae-forgotten-export) The symbol "DataPluginStartDependencies" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "DataServerPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1250,7 +1252,7 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // src/plugins/data/server/index.ts:271:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:274:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:275:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index_patterns/index_patterns_service.ts:58:14 - (ae-forgotten-export) The symbol "IndexPatternsService" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index_patterns/index_patterns_service.ts:70:14 - (ae-forgotten-export) The symbol "IndexPatternsService" needs to be exported by the entry point index.d.ts // src/plugins/data/server/plugin.ts:90:74 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts // src/plugins/data/server/search/types.ts:104:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts index 3bd29632f0902..10a18d0cbf435 100644 --- a/src/plugins/expressions/common/execution/execution.test.ts +++ b/src/plugins/expressions/common/execution/execution.test.ts @@ -195,6 +195,18 @@ describe('Execution', () => { expect(typeof result).toBe('object'); }); + test('context.getKibanaRequest is a function if provided', async () => { + const { result } = (await run('introspectContext key="getKibanaRequest"', { + kibanaRequest: {}, + })) as any; + expect(typeof result).toBe('function'); + }); + + test('context.getKibanaRequest is undefined if not provided', async () => { + const { result } = (await run('introspectContext key="getKibanaRequest"')) as any; + expect(typeof result).toBe('undefined'); + }); + test('unknown context key is undefined', async () => { const { result } = (await run('introspectContext key="foo"')) as any; expect(typeof result).toBe('undefined'); diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index e53a6f7d58e1c..9eae7fd717eda 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -152,6 +152,9 @@ export class Execution< this.context = { getSearchContext: () => this.execution.params.searchContext || {}, getSearchSessionId: () => execution.params.searchSessionId, + getKibanaRequest: execution.params.kibanaRequest + ? () => execution.params.kibanaRequest + : undefined, variables: execution.params.variables || {}, types: executor.getTypes(), abortSignal: this.abortController.signal, diff --git a/src/plugins/expressions/common/execution/types.ts b/src/plugins/expressions/common/execution/types.ts index abe3e08fc20c2..a41f97118c4b2 100644 --- a/src/plugins/expressions/common/execution/types.ts +++ b/src/plugins/expressions/common/execution/types.ts @@ -17,6 +17,9 @@ * under the License. */ +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import type { KibanaRequest } from 'src/core/server'; + import { ExpressionType, SerializableState } from '../expression_types'; import { Adapters, DataAdapter, RequestAdapter } from '../../../inspector/common'; import { SavedObject, SavedObjectAttributes } from '../../../../core/public'; @@ -59,6 +62,13 @@ export interface ExecutionContext< */ getSearchSessionId: () => string | undefined; + /** + * Getter to retrieve the `KibanaRequest` object inside an expression function. + * Useful for functions which are running on the server and need to perform + * operations that are scoped to a specific user. + */ + getKibanaRequest?: () => KibanaRequest; + /** * Allows to fetch saved objects from ElasticSearch. In browser `getSavedObject` * function is provided automatically by the Expressions plugin. On the server diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts index c9cc0680360bb..ec1fffe64f102 100644 --- a/src/plugins/expressions/common/service/expressions_services.ts +++ b/src/plugins/expressions/common/service/expressions_services.ts @@ -17,6 +17,9 @@ * under the License. */ +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import type { KibanaRequest } from 'src/core/server'; + import { Executor } from '../executor'; import { AnyExpressionRenderDefinition, ExpressionRendererRegistry } from '../expression_renderers'; import { ExpressionAstExpression } from '../ast'; @@ -58,6 +61,13 @@ export interface ExpressionExecutionParams { */ debug?: boolean; + /** + * Makes a `KibanaRequest` object available to expression functions. Useful for + * functions which are running on the server and need to perform operations that + * are scoped to a specific user. + */ + kibanaRequest?: KibanaRequest; + searchSessionId?: string; inspectorAdapters?: Adapters; diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md index 2a73cd6e208d1..97ff00db0966c 100644 --- a/src/plugins/expressions/public/public.api.md +++ b/src/plugins/expressions/public/public.api.md @@ -9,6 +9,7 @@ import { CoreStart } from 'src/core/public'; import { Ensure } from '@kbn/utility-types'; import { EnvironmentMode } from '@kbn/config'; import { EventEmitter } from 'events'; +import { KibanaRequest } from 'src/core/server'; import { Observable } from 'rxjs'; import { PackageInfo } from '@kbn/config'; import { PersistedState } from 'src/plugins/visualizations/public'; @@ -136,6 +137,7 @@ export type ExecutionContainer = StateContainer { abortSignal: AbortSignal; + getKibanaRequest?: () => KibanaRequest; // Warning: (ae-forgotten-export) The symbol "SavedObjectAttributes" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "SavedObject" needs to be exported by the entry point index.d.ts getSavedObject?: (type: string, id: string) => Promise>; diff --git a/src/plugins/expressions/server/server.api.md b/src/plugins/expressions/server/server.api.md index 33ff759faa3b1..761ddba8f9270 100644 --- a/src/plugins/expressions/server/server.api.md +++ b/src/plugins/expressions/server/server.api.md @@ -8,6 +8,7 @@ import { CoreSetup } from 'src/core/server'; import { CoreStart } from 'src/core/server'; import { Ensure } from '@kbn/utility-types'; import { EventEmitter } from 'events'; +import { KibanaRequest } from 'src/core/server'; import { Observable } from 'rxjs'; import { PersistedState } from 'src/plugins/visualizations/public'; import { Plugin as Plugin_2 } from 'src/core/server'; @@ -134,6 +135,7 @@ export type ExecutionContainer = StateContainer { abortSignal: AbortSignal; + getKibanaRequest?: () => KibanaRequest; // Warning: (ae-forgotten-export) The symbol "SavedObjectAttributes" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "SavedObject" needs to be exported by the entry point index.d.ts getSavedObject?: (type: string, id: string) => Promise>;