mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
This commit is contained in:
parent
8bdee234c5
commit
0f96249483
31 changed files with 810 additions and 152 deletions
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) > [enabled](./kibana-plugin-plugins-data-public.aggconfigoptions.enabled.md)
|
||||
|
||||
## AggConfigOptions.enabled property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
enabled?: boolean;
|
||||
```
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) > [id](./kibana-plugin-plugins-data-public.aggconfigoptions.id.md)
|
||||
|
||||
## AggConfigOptions.id property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
id?: string;
|
||||
```
|
|
@ -2,21 +2,12 @@
|
|||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md)
|
||||
|
||||
## AggConfigOptions interface
|
||||
## AggConfigOptions type
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface AggConfigOptions
|
||||
export declare type AggConfigOptions = Assign<AggConfigSerialized, {
|
||||
type: IAggType;
|
||||
}>;
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [enabled](./kibana-plugin-plugins-data-public.aggconfigoptions.enabled.md) | <code>boolean</code> | |
|
||||
| [id](./kibana-plugin-plugins-data-public.aggconfigoptions.id.md) | <code>string</code> | |
|
||||
| [params](./kibana-plugin-plugins-data-public.aggconfigoptions.params.md) | <code>Record<string, any></code> | |
|
||||
| [schema](./kibana-plugin-plugins-data-public.aggconfigoptions.schema.md) | <code>string</code> | |
|
||||
| [type](./kibana-plugin-plugins-data-public.aggconfigoptions.type.md) | <code>IAggType</code> | |
|
||||
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) > [params](./kibana-plugin-plugins-data-public.aggconfigoptions.params.md)
|
||||
|
||||
## AggConfigOptions.params property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
params?: Record<string, any>;
|
||||
```
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) > [schema](./kibana-plugin-plugins-data-public.aggconfigoptions.schema.md)
|
||||
|
||||
## AggConfigOptions.schema property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
schema?: string;
|
||||
```
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) > [type](./kibana-plugin-plugins-data-public.aggconfigoptions.type.md)
|
||||
|
||||
## AggConfigOptions.type property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
type: IAggType;
|
||||
```
|
|
@ -7,5 +7,5 @@
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
makeAgg: (agg: TAggConfig, state?: any) => TAggConfig;
|
||||
makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig;
|
||||
```
|
||||
|
|
|
@ -21,5 +21,5 @@ export declare class AggParamType<TAggConfig extends IAggConfig = IAggConfig> ex
|
|||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [allowedAggs](./kibana-plugin-plugins-data-public.aggparamtype.allowedaggs.md) | | <code>string[]</code> | |
|
||||
| [makeAgg](./kibana-plugin-plugins-data-public.aggparamtype.makeagg.md) | | <code>(agg: TAggConfig, state?: any) => TAggConfig</code> | |
|
||||
| [makeAgg](./kibana-plugin-plugins-data-public.aggparamtype.makeagg.md) | | <code>(agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig</code> | |
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
|
||||
| Interface | Description |
|
||||
| --- | --- |
|
||||
| [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) | |
|
||||
| [AggParamOption](./kibana-plugin-plugins-data-public.aggparamoption.md) | |
|
||||
| [DataPublicPluginSetup](./kibana-plugin-plugins-data-public.datapublicpluginsetup.md) | |
|
||||
| [DataPublicPluginStart](./kibana-plugin-plugins-data-public.datapublicpluginstart.md) | |
|
||||
|
@ -118,6 +117,7 @@
|
|||
|
||||
| Type Alias | Description |
|
||||
| --- | --- |
|
||||
| [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) | |
|
||||
| [AggParam](./kibana-plugin-plugins-data-public.aggparam.md) | |
|
||||
| [CustomFilter](./kibana-plugin-plugins-data-public.customfilter.md) | |
|
||||
| [EsQuerySortValue](./kibana-plugin-plugins-data-public.esquerysortvalue.md) | |
|
||||
|
|
|
@ -139,6 +139,7 @@ export class DataPublicPlugin implements Plugin<DataPublicPluginSetup, DataPubli
|
|||
return {
|
||||
autocomplete: this.autocomplete.setup(core),
|
||||
search: this.searchService.setup(core, {
|
||||
expressions,
|
||||
getInternalStartServices,
|
||||
packageInfo: this.packageInfo,
|
||||
query: queryService,
|
||||
|
|
|
@ -20,6 +20,7 @@ import { EuiConfirmModalProps } from '@elastic/eui';
|
|||
import { EuiFieldText } from '@elastic/eui';
|
||||
import { EuiGlobalToastListToast } from '@elastic/eui';
|
||||
import { ExclusiveUnion } from '@elastic/eui';
|
||||
import { ExpressionAstFunction } from 'src/plugins/expressions/public';
|
||||
import { ExpressionsSetup } from 'src/plugins/expressions/public';
|
||||
import { History } from 'history';
|
||||
import { HttpSetup } from 'src/core/public';
|
||||
|
@ -59,21 +60,13 @@ import { Unit } from '@elastic/datemath';
|
|||
import { UnregisterCallback } from 'history';
|
||||
import { UserProvidedValues } from 'src/core/server/types';
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "AggConfigSerialized" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "AggConfigOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface AggConfigOptions {
|
||||
// (undocumented)
|
||||
enabled?: boolean;
|
||||
// (undocumented)
|
||||
id?: string;
|
||||
// (undocumented)
|
||||
params?: Record<string, any>;
|
||||
// (undocumented)
|
||||
schema?: string;
|
||||
// (undocumented)
|
||||
export type AggConfigOptions = Assign<AggConfigSerialized, {
|
||||
type: IAggType;
|
||||
}
|
||||
}>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "AggGroupNames" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
|
@ -112,7 +105,7 @@ export class AggParamType<TAggConfig extends IAggConfig = IAggConfig> extends Ba
|
|||
// (undocumented)
|
||||
allowedAggs: string[];
|
||||
// (undocumented)
|
||||
makeAgg: (agg: TAggConfig, state?: any) => TAggConfig;
|
||||
makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "AggTypeFieldFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
|
|
|
@ -24,18 +24,21 @@ import { AggConfigs, CreateAggConfigParams } from './agg_configs';
|
|||
import { AggType } from './agg_type';
|
||||
import { AggTypesRegistryStart } from './agg_types_registry';
|
||||
import { mockDataServices, mockAggTypesRegistry } from './test_helpers';
|
||||
import { MetricAggType } from './metrics/metric_agg_type';
|
||||
import { Field as IndexPatternField, IndexPattern } from '../../index_patterns';
|
||||
import { stubIndexPatternWithFields } from '../../../public/stubs';
|
||||
import { FieldFormatsStart } from '../../field_formats';
|
||||
import { fieldFormatsServiceMock } from '../../field_formats/mocks';
|
||||
|
||||
describe('AggConfig', () => {
|
||||
let indexPattern: IndexPattern;
|
||||
let typesRegistry: AggTypesRegistryStart;
|
||||
const fieldFormats = fieldFormatsServiceMock.createStartContract();
|
||||
let fieldFormats: FieldFormatsStart;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
mockDataServices();
|
||||
fieldFormats = fieldFormatsServiceMock.createStartContract();
|
||||
indexPattern = stubIndexPatternWithFields as IndexPattern;
|
||||
typesRegistry = mockAggTypesRegistry();
|
||||
});
|
||||
|
@ -325,7 +328,7 @@ describe('AggConfig', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('#toJSON', () => {
|
||||
describe('#serialize', () => {
|
||||
it('includes the aggs id, params, type and schema', () => {
|
||||
const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats });
|
||||
const configStates = {
|
||||
|
@ -342,7 +345,7 @@ describe('AggConfig', () => {
|
|||
expect(aggConfig.type).toHaveProperty('name', 'date_histogram');
|
||||
expect(typeof aggConfig.schema).toBe('string');
|
||||
|
||||
const state = aggConfig.toJSON();
|
||||
const state = aggConfig.serialize();
|
||||
expect(state).toHaveProperty('id', '1');
|
||||
expect(typeof state.params).toBe('object');
|
||||
expect(state).toHaveProperty('type', 'date_histogram');
|
||||
|
@ -367,6 +370,201 @@ describe('AggConfig', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('#toExpressionAst', () => {
|
||||
beforeEach(() => {
|
||||
fieldFormats.getDefaultInstance = (() => ({
|
||||
getConverterFor: (t?: string) => t || identity,
|
||||
})) as any;
|
||||
indexPattern.fields.getByName = name =>
|
||||
({
|
||||
format: {
|
||||
getConverterFor: (t?: string) => t || identity,
|
||||
},
|
||||
} as IndexPatternField);
|
||||
});
|
||||
|
||||
it('works with primitive param types', () => {
|
||||
const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats });
|
||||
const configStates = {
|
||||
enabled: true,
|
||||
type: 'terms',
|
||||
schema: 'segment',
|
||||
params: {
|
||||
field: 'machine.os.keyword',
|
||||
order: 'asc',
|
||||
},
|
||||
};
|
||||
const aggConfig = ac.createAggConfig(configStates);
|
||||
expect(aggConfig.toExpressionAst()).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"arguments": Object {
|
||||
"enabled": Array [
|
||||
true,
|
||||
],
|
||||
"id": Array [
|
||||
"1",
|
||||
],
|
||||
"missingBucket": Array [
|
||||
false,
|
||||
],
|
||||
"missingBucketLabel": Array [
|
||||
"Missing",
|
||||
],
|
||||
"order": Array [
|
||||
"asc",
|
||||
],
|
||||
"otherBucket": Array [
|
||||
false,
|
||||
],
|
||||
"otherBucketLabel": Array [
|
||||
"Other",
|
||||
],
|
||||
"schema": Array [
|
||||
"segment",
|
||||
],
|
||||
"size": Array [
|
||||
5,
|
||||
],
|
||||
},
|
||||
"function": "aggTerms",
|
||||
"type": "function",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('creates a subexpression for params of type "agg"', () => {
|
||||
const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats });
|
||||
const configStates = {
|
||||
type: 'terms',
|
||||
params: {
|
||||
field: 'machine.os.keyword',
|
||||
order: 'asc',
|
||||
orderAgg: {
|
||||
enabled: true,
|
||||
type: 'terms',
|
||||
params: {
|
||||
field: 'bytes',
|
||||
order: 'asc',
|
||||
size: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const aggConfig = ac.createAggConfig(configStates);
|
||||
const aggArg = aggConfig.toExpressionAst()?.arguments.orderAgg;
|
||||
expect(aggArg).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"chain": Array [
|
||||
Object {
|
||||
"arguments": Object {
|
||||
"enabled": Array [
|
||||
true,
|
||||
],
|
||||
"id": Array [
|
||||
"1-orderAgg",
|
||||
],
|
||||
"missingBucket": Array [
|
||||
false,
|
||||
],
|
||||
"missingBucketLabel": Array [
|
||||
"Missing",
|
||||
],
|
||||
"order": Array [
|
||||
"asc",
|
||||
],
|
||||
"otherBucket": Array [
|
||||
false,
|
||||
],
|
||||
"otherBucketLabel": Array [
|
||||
"Other",
|
||||
],
|
||||
"schema": Array [
|
||||
"orderAgg",
|
||||
],
|
||||
"size": Array [
|
||||
5,
|
||||
],
|
||||
},
|
||||
"function": "aggTerms",
|
||||
"type": "function",
|
||||
},
|
||||
],
|
||||
"type": "expression",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('creates a subexpression for param types other than "agg" which have specified toExpressionAst', () => {
|
||||
// Overwrite the `ranges` param in the `range` agg with a mock toExpressionAst function
|
||||
const range: MetricAggType = typesRegistry.get('range');
|
||||
range.expressionName = 'aggRange';
|
||||
const rangesParam = range.params.find(p => p.name === 'ranges');
|
||||
rangesParam!.toExpressionAst = (val: any) => ({
|
||||
type: 'function',
|
||||
function: 'aggRanges',
|
||||
arguments: {
|
||||
ranges: ['oh hi there!'],
|
||||
},
|
||||
});
|
||||
|
||||
const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats });
|
||||
const configStates = {
|
||||
type: 'range',
|
||||
params: {
|
||||
field: 'bytes',
|
||||
},
|
||||
};
|
||||
|
||||
const aggConfig = ac.createAggConfig(configStates);
|
||||
const ranges = aggConfig.toExpressionAst()!.arguments.ranges;
|
||||
expect(ranges).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"chain": Array [
|
||||
Object {
|
||||
"arguments": Object {
|
||||
"ranges": Array [
|
||||
"oh hi there!",
|
||||
],
|
||||
},
|
||||
"function": "aggRanges",
|
||||
"type": "function",
|
||||
},
|
||||
],
|
||||
"type": "expression",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('stringifies any other params which are an object', () => {
|
||||
const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats });
|
||||
const configStates = {
|
||||
type: 'terms',
|
||||
params: {
|
||||
field: 'machine.os.keyword',
|
||||
order: 'asc',
|
||||
json: { foo: 'bar' },
|
||||
},
|
||||
};
|
||||
const aggConfig = ac.createAggConfig(configStates);
|
||||
const json = aggConfig.toExpressionAst()?.arguments.json;
|
||||
expect(json).toEqual([JSON.stringify(configStates.params.json)]);
|
||||
});
|
||||
|
||||
it(`returns undefined if an expressionName doesn't exist on the agg type`, () => {
|
||||
const ac = new AggConfigs(indexPattern, [], { typesRegistry, fieldFormats });
|
||||
const configStates = {
|
||||
type: 'unknown type',
|
||||
params: {},
|
||||
};
|
||||
const aggConfig = ac.createAggConfig(configStates);
|
||||
expect(aggConfig.toExpressionAst()).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#makeLabel', () => {
|
||||
let aggConfig: AggConfig;
|
||||
|
||||
|
@ -422,6 +620,9 @@ describe('AggConfig', () => {
|
|||
let aggConfig: AggConfig;
|
||||
|
||||
beforeEach(() => {
|
||||
fieldFormats.getDefaultInstance = (() => ({
|
||||
getConverterFor: (t?: string) => t || identity,
|
||||
})) as any;
|
||||
indexPattern.fields.getByName = name =>
|
||||
({
|
||||
format: {
|
||||
|
@ -434,11 +635,7 @@ describe('AggConfig', () => {
|
|||
type: 'histogram',
|
||||
schema: 'bucket',
|
||||
params: {
|
||||
field: {
|
||||
format: {
|
||||
getConverterFor: (t?: string) => t || identity,
|
||||
},
|
||||
},
|
||||
field: 'bytes',
|
||||
},
|
||||
};
|
||||
const ac = new AggConfigs(indexPattern, [configStates], { typesRegistry, fieldFormats });
|
||||
|
@ -446,6 +643,11 @@ describe('AggConfig', () => {
|
|||
});
|
||||
|
||||
it("returns the field's formatter", () => {
|
||||
aggConfig.params.field = {
|
||||
format: {
|
||||
getConverterFor: (t?: string) => t || identity,
|
||||
},
|
||||
};
|
||||
expect(aggConfig.fieldFormatter().toString()).toBe(
|
||||
aggConfig
|
||||
.getField()
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Assign } from '@kbn/utility-types';
|
||||
import { ExpressionAstFunction, ExpressionAstArgument } from 'src/plugins/expressions/public';
|
||||
import { IAggType } from './agg_type';
|
||||
import { writeParams } from './agg_params';
|
||||
import { IAggConfigs } from './agg_configs';
|
||||
|
@ -27,11 +29,17 @@ import { ISearchSource } from '../search_source';
|
|||
import { FieldFormatsContentType, KBN_FIELD_TYPES } from '../../../common';
|
||||
import { FieldFormatsStart } from '../../field_formats';
|
||||
|
||||
export interface AggConfigOptions {
|
||||
type: IAggType;
|
||||
type State = string | number | boolean | null | undefined | SerializableState;
|
||||
|
||||
interface SerializableState {
|
||||
[key: string]: State | State[];
|
||||
}
|
||||
|
||||
export interface AggConfigSerialized {
|
||||
type: string;
|
||||
enabled?: boolean;
|
||||
id?: string;
|
||||
params?: Record<string, any>;
|
||||
params?: SerializableState;
|
||||
schema?: string;
|
||||
}
|
||||
|
||||
|
@ -39,6 +47,8 @@ export interface AggConfigDependencies {
|
|||
fieldFormats: FieldFormatsStart;
|
||||
}
|
||||
|
||||
export type AggConfigOptions = Assign<AggConfigSerialized, { type: IAggType }>;
|
||||
|
||||
/**
|
||||
* @name AggConfig
|
||||
*
|
||||
|
@ -257,7 +267,10 @@ export class AggConfig {
|
|||
return configDsl;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
/**
|
||||
* @returns Returns a serialized representation of an AggConfig.
|
||||
*/
|
||||
serialize(): AggConfigSerialized {
|
||||
const params = this.params;
|
||||
|
||||
const outParams = _.transform(
|
||||
|
@ -281,7 +294,64 @@ export class AggConfig {
|
|||
enabled: this.enabled,
|
||||
type: this.type && this.type.name,
|
||||
schema: this.schema,
|
||||
params: outParams,
|
||||
params: outParams as SerializableState,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated - Use serialize() instead.
|
||||
*/
|
||||
toJSON(): AggConfigSerialized {
|
||||
return this.serialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Returns an ExpressionAst representing the function for this agg type.
|
||||
*/
|
||||
toExpressionAst(): ExpressionAstFunction | undefined {
|
||||
const functionName = this.type && this.type.expressionName;
|
||||
const { type, ...rest } = this.serialize();
|
||||
if (!functionName || !rest.params) {
|
||||
// Return undefined - there is no matching expression function for this agg
|
||||
return;
|
||||
}
|
||||
|
||||
// Go through each of the params and convert to an array of expression args.
|
||||
const params = Object.entries(rest.params).reduce((acc, [key, value]) => {
|
||||
const deserializedParam = this.getAggParams().find(p => p.name === key);
|
||||
|
||||
if (deserializedParam && deserializedParam.toExpressionAst) {
|
||||
// If the param provides `toExpressionAst`, we call it with the value
|
||||
const paramExpressionAst = deserializedParam.toExpressionAst(this.getParam(key));
|
||||
if (paramExpressionAst) {
|
||||
acc[key] = [
|
||||
{
|
||||
type: 'expression',
|
||||
chain: [paramExpressionAst],
|
||||
},
|
||||
];
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
// For object params which don't provide `toExpressionAst`, we stringify
|
||||
acc[key] = [JSON.stringify(value)];
|
||||
} else if (typeof value !== 'undefined') {
|
||||
// Everything else just gets stored in an array if it is defined
|
||||
acc[key] = [value];
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {} as Record<string, ExpressionAstArgument[]>);
|
||||
|
||||
return {
|
||||
type: 'function',
|
||||
function: functionName,
|
||||
arguments: {
|
||||
...params,
|
||||
// Expression args which are provided to all functions
|
||||
id: [this.id],
|
||||
enabled: [this.enabled],
|
||||
...(this.schema ? { schema: [this.schema] } : {}), // schema may be undefined
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import _ from 'lodash';
|
||||
import { Assign } from '@kbn/utility-types';
|
||||
|
||||
import { AggConfig, AggConfigOptions, IAggConfig } from './agg_config';
|
||||
import { AggConfig, AggConfigSerialized, IAggConfig } from './agg_config';
|
||||
import { IAggType } from './agg_type';
|
||||
import { AggTypesRegistryStart } from './agg_types_registry';
|
||||
import { AggGroupNames } from './agg_groups';
|
||||
|
@ -51,7 +51,7 @@ export interface AggConfigsOptions {
|
|||
fieldFormats: FieldFormatsStart;
|
||||
}
|
||||
|
||||
export type CreateAggConfigParams = Assign<AggConfigOptions, { type: string | IAggType }>;
|
||||
export type CreateAggConfigParams = Assign<AggConfigSerialized, { type: string | IAggType }>;
|
||||
|
||||
/**
|
||||
* @name AggConfigs
|
||||
|
|
|
@ -39,6 +39,7 @@ export interface AggTypeConfig<
|
|||
createFilter?: (aggConfig: TAggConfig, key: any, params?: any) => any;
|
||||
type?: string;
|
||||
dslName?: string;
|
||||
expressionName?: string;
|
||||
makeLabel?: ((aggConfig: TAggConfig) => string) | (() => string);
|
||||
ordered?: any;
|
||||
hasNoDsl?: boolean;
|
||||
|
@ -88,6 +89,14 @@ export class AggType<
|
|||
* @type {string}
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
* the user friendly name that will be shown in the ui for this aggType
|
||||
*
|
||||
|
@ -219,6 +228,7 @@ export class AggType<
|
|||
this.name = config.name;
|
||||
this.type = config.type || 'metrics';
|
||||
this.dslName = config.dslName || config.name;
|
||||
this.expressionName = config.expressionName;
|
||||
this.title = config.title;
|
||||
this.makeLabel = config.makeLabel || constant(this.name);
|
||||
this.ordered = config.ordered;
|
||||
|
|
|
@ -37,6 +37,7 @@ import { getDerivativeMetricAgg } from './metrics/derivative';
|
|||
import { getCumulativeSumMetricAgg } from './metrics/cumulative_sum';
|
||||
import { getMovingAvgMetricAgg } from './metrics/moving_avg';
|
||||
import { getSerialDiffMetricAgg } from './metrics/serial_diff';
|
||||
|
||||
import { getDateHistogramBucketAgg } from './buckets/date_histogram';
|
||||
import { getHistogramBucketAgg } from './buckets/histogram';
|
||||
import { getRangeBucketAgg } from './buckets/range';
|
||||
|
@ -103,3 +104,7 @@ export const getAggTypes = ({
|
|||
getGeoTitleBucketAgg({ getInternalStartServices }),
|
||||
],
|
||||
});
|
||||
|
||||
import { aggTerms } from './buckets/terms_fn';
|
||||
|
||||
export const getAggTypesFunctions = () => [aggTerms];
|
||||
|
|
|
@ -26,7 +26,7 @@ import {
|
|||
isStringOrNumberType,
|
||||
migrateIncludeExcludeFormat,
|
||||
} from './migrate_include_exclude_format';
|
||||
import { IAggConfigs } from '../agg_configs';
|
||||
import { AggConfigSerialized, IAggConfigs } from '../types';
|
||||
|
||||
import { Adapters } from '../../../../../inspector/public';
|
||||
import { ISearchSource } from '../../search_source';
|
||||
|
@ -63,10 +63,27 @@ export interface TermsBucketAggDependencies {
|
|||
getInternalStartServices: GetInternalStartServicesFn;
|
||||
}
|
||||
|
||||
export interface AggParamsTerms {
|
||||
field: string;
|
||||
order: 'asc' | 'desc';
|
||||
orderBy: string;
|
||||
orderAgg?: AggConfigSerialized;
|
||||
size?: number;
|
||||
missingBucket?: boolean;
|
||||
missingBucketLabel?: string;
|
||||
otherBucket?: boolean;
|
||||
otherBucketLabel?: string;
|
||||
// advanced
|
||||
exclude?: string;
|
||||
include?: string;
|
||||
json?: string;
|
||||
}
|
||||
|
||||
export const getTermsBucketAgg = ({ getInternalStartServices }: TermsBucketAggDependencies) =>
|
||||
new BucketAggType(
|
||||
{
|
||||
name: BUCKET_TYPES.TERMS,
|
||||
expressionName: 'aggTerms',
|
||||
title: termsTitle,
|
||||
makeLabel(agg) {
|
||||
const params = agg.params;
|
||||
|
@ -154,8 +171,7 @@ export const getTermsBucketAgg = ({ getInternalStartServices }: TermsBucketAggDe
|
|||
type: 'agg',
|
||||
allowedAggs: termsAggFilter,
|
||||
default: null,
|
||||
makeAgg(termsAgg, state) {
|
||||
state = state || {};
|
||||
makeAgg(termsAgg, state = { type: 'count' }) {
|
||||
state.schema = 'orderAgg';
|
||||
const orderAgg = termsAgg.aggConfigs.createAggConfig<IBucketAggConfig>(state, {
|
||||
addToAggConfigs: false,
|
||||
|
|
164
src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts
Normal file
164
src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* 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 { functionWrapper } from '../test_helpers';
|
||||
import { aggTerms } from './terms_fn';
|
||||
|
||||
describe('agg_expression_functions', () => {
|
||||
describe('aggTerms', () => {
|
||||
const fn = functionWrapper(aggTerms());
|
||||
|
||||
test('fills in defaults when only required args are provided', () => {
|
||||
const actual = fn({
|
||||
field: 'machine.os.keyword',
|
||||
order: 'asc',
|
||||
orderBy: '1',
|
||||
});
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"type": "agg_type",
|
||||
"value": Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"exclude": undefined,
|
||||
"field": "machine.os.keyword",
|
||||
"include": undefined,
|
||||
"json": undefined,
|
||||
"missingBucket": false,
|
||||
"missingBucketLabel": "Missing",
|
||||
"order": "asc",
|
||||
"orderAgg": undefined,
|
||||
"orderBy": "1",
|
||||
"otherBucket": false,
|
||||
"otherBucketLabel": "Other",
|
||||
"size": 5,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "terms",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('includes optional params when they are provided', () => {
|
||||
const actual = fn({
|
||||
id: '1',
|
||||
enabled: false,
|
||||
schema: 'whatever',
|
||||
field: 'machine.os.keyword',
|
||||
order: 'desc',
|
||||
orderBy: '2',
|
||||
size: 6,
|
||||
missingBucket: true,
|
||||
missingBucketLabel: 'missing',
|
||||
otherBucket: true,
|
||||
otherBucketLabel: 'other',
|
||||
exclude: 'ios',
|
||||
});
|
||||
|
||||
expect(actual.value).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"enabled": false,
|
||||
"id": "1",
|
||||
"params": Object {
|
||||
"exclude": "ios",
|
||||
"field": "machine.os.keyword",
|
||||
"include": undefined,
|
||||
"json": undefined,
|
||||
"missingBucket": true,
|
||||
"missingBucketLabel": "missing",
|
||||
"order": "desc",
|
||||
"orderAgg": undefined,
|
||||
"orderBy": "2",
|
||||
"otherBucket": true,
|
||||
"otherBucketLabel": "other",
|
||||
"size": 6,
|
||||
},
|
||||
"schema": "whatever",
|
||||
"type": "terms",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('handles orderAgg as a subexpression', () => {
|
||||
const actual = fn({
|
||||
field: 'machine.os.keyword',
|
||||
order: 'asc',
|
||||
orderBy: '1',
|
||||
orderAgg: fn({ field: 'name', order: 'asc', orderBy: '1' }),
|
||||
});
|
||||
|
||||
expect(actual.value.params).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"exclude": undefined,
|
||||
"field": "machine.os.keyword",
|
||||
"include": undefined,
|
||||
"json": undefined,
|
||||
"missingBucket": false,
|
||||
"missingBucketLabel": "Missing",
|
||||
"order": "asc",
|
||||
"orderAgg": Object {
|
||||
"enabled": true,
|
||||
"id": undefined,
|
||||
"params": Object {
|
||||
"exclude": undefined,
|
||||
"field": "name",
|
||||
"include": undefined,
|
||||
"json": undefined,
|
||||
"missingBucket": false,
|
||||
"missingBucketLabel": "Missing",
|
||||
"order": "asc",
|
||||
"orderAgg": undefined,
|
||||
"orderBy": "1",
|
||||
"otherBucket": false,
|
||||
"otherBucketLabel": "Other",
|
||||
"size": 5,
|
||||
},
|
||||
"schema": undefined,
|
||||
"type": "terms",
|
||||
},
|
||||
"orderBy": "1",
|
||||
"otherBucket": false,
|
||||
"otherBucketLabel": "Other",
|
||||
"size": 5,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('correctly parses json string argument', () => {
|
||||
const actual = fn({
|
||||
field: 'machine.os.keyword',
|
||||
order: 'asc',
|
||||
orderBy: '1',
|
||||
json: '{ "foo": true }',
|
||||
});
|
||||
|
||||
expect(actual.value.params.json).toEqual({ foo: true });
|
||||
expect(() => {
|
||||
fn({
|
||||
field: 'machine.os.keyword',
|
||||
order: 'asc',
|
||||
orderBy: '1',
|
||||
json: '/// intentionally malformed json ///',
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`);
|
||||
});
|
||||
});
|
||||
});
|
181
src/plugins/data/public/search/aggs/buckets/terms_fn.ts
Normal file
181
src/plugins/data/public/search/aggs/buckets/terms_fn.ts
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* 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 { Assign } from '@kbn/utility-types';
|
||||
import { ExpressionFunctionDefinition } from '../../../../../expressions/public';
|
||||
import { AggExpressionType, AggExpressionFunctionArgs } from '../';
|
||||
|
||||
const aggName = 'terms';
|
||||
const fnName = 'aggTerms';
|
||||
|
||||
type Input = any;
|
||||
type AggArgs = AggExpressionFunctionArgs<typeof aggName>;
|
||||
// Since the orderAgg param is an agg nested in a subexpression, we need to
|
||||
// overwrite the param type to expect a value of type AggExpressionType.
|
||||
type Arguments = AggArgs &
|
||||
Assign<
|
||||
AggArgs,
|
||||
{ orderAgg?: AggArgs['orderAgg'] extends undefined ? undefined : AggExpressionType }
|
||||
>;
|
||||
type Output = AggExpressionType;
|
||||
type FunctionDefinition = ExpressionFunctionDefinition<typeof fnName, Input, Arguments, Output>;
|
||||
|
||||
export const aggTerms = (): FunctionDefinition => ({
|
||||
name: fnName,
|
||||
help: i18n.translate('data.search.aggs.function.buckets.terms.help', {
|
||||
defaultMessage: 'Generates a serialized agg config for a terms agg',
|
||||
}),
|
||||
type: 'agg_type',
|
||||
args: {
|
||||
id: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.id.help', {
|
||||
defaultMessage: 'ID for this aggregation',
|
||||
}),
|
||||
},
|
||||
enabled: {
|
||||
types: ['boolean'],
|
||||
default: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.enabled.help', {
|
||||
defaultMessage: 'Specifies whether this aggregation should be enabled',
|
||||
}),
|
||||
},
|
||||
schema: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.schema.help', {
|
||||
defaultMessage: 'Schema to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
field: {
|
||||
types: ['string'],
|
||||
required: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.field.help', {
|
||||
defaultMessage: 'Field to use for this aggregation',
|
||||
}),
|
||||
},
|
||||
order: {
|
||||
types: ['string'],
|
||||
required: true,
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.order.help', {
|
||||
defaultMessage: 'Order in which to return the results: asc or desc',
|
||||
}),
|
||||
},
|
||||
orderBy: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.orderBy.help', {
|
||||
defaultMessage: 'Field to order results by',
|
||||
}),
|
||||
},
|
||||
orderAgg: {
|
||||
types: ['agg_type'],
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.orderAgg.help', {
|
||||
defaultMessage: 'Agg config to use for ordering results',
|
||||
}),
|
||||
},
|
||||
size: {
|
||||
types: ['number'],
|
||||
default: 5,
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.size.help', {
|
||||
defaultMessage: 'Max number of buckets to retrieve',
|
||||
}),
|
||||
},
|
||||
missingBucket: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.missingBucket.help', {
|
||||
defaultMessage: 'When set to true, groups together any buckets with missing fields',
|
||||
}),
|
||||
},
|
||||
missingBucketLabel: {
|
||||
types: ['string'],
|
||||
default: i18n.translate('data.search.aggs.buckets.terms.missingBucketLabel', {
|
||||
defaultMessage: 'Missing',
|
||||
description: `Default label used in charts when documents are missing a field.
|
||||
Visible when you create a chart with a terms aggregation and enable "Show missing values"`,
|
||||
}),
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.missingBucketLabel.help', {
|
||||
defaultMessage: 'Default label used in charts when documents are missing a field.',
|
||||
}),
|
||||
},
|
||||
otherBucket: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.otherBucket.help', {
|
||||
defaultMessage: 'When set to true, groups together any buckets beyond the allowed size',
|
||||
}),
|
||||
},
|
||||
otherBucketLabel: {
|
||||
types: ['string'],
|
||||
default: i18n.translate('data.search.aggs.buckets.terms.otherBucketLabel', {
|
||||
defaultMessage: 'Other',
|
||||
}),
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.otherBucketLabel.help', {
|
||||
defaultMessage: 'Default label used in charts for documents in the Other bucket',
|
||||
}),
|
||||
},
|
||||
exclude: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.exclude.help', {
|
||||
defaultMessage: 'Specific bucket values to exclude from results',
|
||||
}),
|
||||
},
|
||||
include: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.include.help', {
|
||||
defaultMessage: 'Specific bucket values to include in results',
|
||||
}),
|
||||
},
|
||||
json: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.aggs.buckets.terms.json.help', {
|
||||
defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn: (input, args) => {
|
||||
const { id, enabled, schema, ...rest } = args;
|
||||
|
||||
let json;
|
||||
try {
|
||||
json = args.json ? JSON.parse(args.json) : undefined;
|
||||
} catch (e) {
|
||||
throw new Error('Unable to parse json argument string');
|
||||
}
|
||||
|
||||
// Need to spread this object to work around TS bug:
|
||||
// https://github.com/microsoft/TypeScript/issues/15300#issuecomment-436793742
|
||||
const orderAgg = args.orderAgg?.value ? { ...args.orderAgg.value } : undefined;
|
||||
|
||||
return {
|
||||
type: 'agg_type',
|
||||
value: {
|
||||
id,
|
||||
enabled,
|
||||
schema,
|
||||
type: aggName,
|
||||
params: {
|
||||
...rest,
|
||||
orderAgg,
|
||||
json,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
|
@ -36,14 +36,14 @@ const metricAggFilter = [
|
|||
'!geo_centroid',
|
||||
];
|
||||
|
||||
const parentPipelineType = i18n.translate(
|
||||
export const parentPipelineType = i18n.translate(
|
||||
'data.search.aggs.metrics.parentPipelineAggregationsSubtypeTitle',
|
||||
{
|
||||
defaultMessage: 'Parent Pipeline Aggregations',
|
||||
}
|
||||
);
|
||||
|
||||
const parentPipelineAggHelper = {
|
||||
export const parentPipelineAggHelper = {
|
||||
subtype: parentPipelineType,
|
||||
params() {
|
||||
return [
|
||||
|
@ -56,13 +56,9 @@ const parentPipelineAggHelper = {
|
|||
name: 'customMetric',
|
||||
type: 'agg',
|
||||
allowedAggs: metricAggFilter,
|
||||
makeAgg(termsAgg, state: any) {
|
||||
state = state || { type: 'count' };
|
||||
|
||||
makeAgg(termsAgg, state = { type: 'count' }) {
|
||||
const metricAgg = termsAgg.aggConfigs.createAggConfig(state, { addToAggConfigs: false });
|
||||
|
||||
metricAgg.id = termsAgg.id + '-metric';
|
||||
|
||||
return metricAgg;
|
||||
},
|
||||
modifyAggConfigOnSearchRequestStart: forwardModifyAggConfigOnSearchRequestStart(
|
||||
|
@ -89,5 +85,3 @@ const parentPipelineAggHelper = {
|
|||
return subAgg ? subAgg.type.getFormat(subAgg) : new (FieldFormat.from(identity))();
|
||||
},
|
||||
};
|
||||
|
||||
export { parentPipelineAggHelper, parentPipelineType };
|
||||
|
|
|
@ -43,14 +43,14 @@ const metricAggFilter: string[] = [
|
|||
];
|
||||
const bucketAggFilter: string[] = [];
|
||||
|
||||
const siblingPipelineType = i18n.translate(
|
||||
export const siblingPipelineType = i18n.translate(
|
||||
'data.search.aggs.metrics.siblingPipelineAggregationsSubtypeTitle',
|
||||
{
|
||||
defaultMessage: 'Sibling pipeline aggregations',
|
||||
}
|
||||
);
|
||||
|
||||
const siblingPipelineAggHelper = {
|
||||
export const siblingPipelineAggHelper = {
|
||||
subtype: siblingPipelineType,
|
||||
params() {
|
||||
return [
|
||||
|
@ -59,11 +59,9 @@ const siblingPipelineAggHelper = {
|
|||
type: 'agg',
|
||||
allowedAggs: bucketAggFilter,
|
||||
default: null,
|
||||
makeAgg(agg: IMetricAggConfig, state: any) {
|
||||
state = state || { type: 'date_histogram' };
|
||||
makeAgg(agg: IMetricAggConfig, state = { type: 'date_histogram' }) {
|
||||
const orderAgg = agg.aggConfigs.createAggConfig(state, { addToAggConfigs: false });
|
||||
orderAgg.id = agg.id + '-bucket';
|
||||
|
||||
return orderAgg;
|
||||
},
|
||||
modifyAggConfigOnSearchRequestStart: forwardModifyAggConfigOnSearchRequestStart(
|
||||
|
@ -76,11 +74,9 @@ const siblingPipelineAggHelper = {
|
|||
type: 'agg',
|
||||
allowedAggs: metricAggFilter,
|
||||
default: null,
|
||||
makeAgg(agg: IMetricAggConfig, state: any) {
|
||||
state = state || { type: 'count' };
|
||||
makeAgg(agg: IMetricAggConfig, state = { type: 'count' }) {
|
||||
const orderAgg = agg.aggConfigs.createAggConfig(state, { addToAggConfigs: false });
|
||||
orderAgg.id = agg.id + '-metric';
|
||||
|
||||
return orderAgg;
|
||||
},
|
||||
modifyAggConfigOnSearchRequestStart: forwardModifyAggConfigOnSearchRequestStart(
|
||||
|
@ -98,5 +94,3 @@ const siblingPipelineAggHelper = {
|
|||
: new (FieldFormat.from(identity))();
|
||||
},
|
||||
};
|
||||
|
||||
export { siblingPipelineAggHelper, siblingPipelineType };
|
||||
|
|
|
@ -25,21 +25,28 @@ import {
|
|||
import { AggConfigs, IAggConfigs } from '../agg_configs';
|
||||
import { mockAggTypesRegistry } from '../test_helpers';
|
||||
import { METRIC_TYPES } from './metric_agg_types';
|
||||
import { FieldFormatsStart } from '../../../field_formats';
|
||||
import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
|
||||
import { notificationServiceMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { InternalStartServices } from '../../../types';
|
||||
|
||||
describe('AggTypesMetricsPercentileRanksProvider class', function() {
|
||||
let aggConfigs: IAggConfigs;
|
||||
const aggTypesDependencies: PercentileRanksMetricAggDependencies = {
|
||||
getInternalStartServices: () =>
|
||||
(({
|
||||
fieldFormats: fieldFormatsServiceMock.createStartContract(),
|
||||
notifications: notificationServiceMock.createStartContract(),
|
||||
} as unknown) as InternalStartServices),
|
||||
};
|
||||
let fieldFormats: FieldFormatsStart;
|
||||
let aggTypesDependencies: PercentileRanksMetricAggDependencies;
|
||||
|
||||
beforeEach(() => {
|
||||
fieldFormats = fieldFormatsServiceMock.createStartContract();
|
||||
fieldFormats.getDefaultInstance = (() => ({
|
||||
convert: (t?: string) => t,
|
||||
})) as any;
|
||||
aggTypesDependencies = {
|
||||
getInternalStartServices: () =>
|
||||
(({
|
||||
fieldFormats,
|
||||
notifications: notificationServiceMock.createStartContract(),
|
||||
} as unknown) as InternalStartServices),
|
||||
};
|
||||
const typesRegistry = mockAggTypesRegistry([getPercentileRanksMetricAgg(aggTypesDependencies)]);
|
||||
const field = {
|
||||
name: 'bytes',
|
||||
|
@ -61,12 +68,7 @@ describe('AggTypesMetricsPercentileRanksProvider class', function() {
|
|||
type: METRIC_TYPES.PERCENTILE_RANKS,
|
||||
schema: 'metric',
|
||||
params: {
|
||||
field: {
|
||||
displayName: 'bytes',
|
||||
format: {
|
||||
convert: jest.fn(x => x),
|
||||
},
|
||||
},
|
||||
field: 'bytes',
|
||||
customLabel: 'my custom field label',
|
||||
values: [5000, 10000],
|
||||
},
|
||||
|
|
|
@ -61,12 +61,7 @@ describe('AggTypesMetricsPercentilesProvider class', () => {
|
|||
type: METRIC_TYPES.PERCENTILES,
|
||||
schema: 'metric',
|
||||
params: {
|
||||
field: {
|
||||
displayName: 'bytes',
|
||||
format: {
|
||||
convert: jest.fn(x => `${x}th`),
|
||||
},
|
||||
},
|
||||
field: 'bytes',
|
||||
customLabel: 'prince',
|
||||
percents: [95],
|
||||
},
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { AggConfig, IAggConfig } from '../agg_config';
|
||||
import { AggConfig, IAggConfig, AggConfigSerialized } from '../agg_config';
|
||||
import { BaseParamType } from './base';
|
||||
|
||||
export class AggParamType<TAggConfig extends IAggConfig = IAggConfig> extends BaseParamType<
|
||||
TAggConfig
|
||||
> {
|
||||
makeAgg: (agg: TAggConfig, state?: any) => TAggConfig;
|
||||
makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig;
|
||||
allowedAggs: string[] = [];
|
||||
|
||||
constructor(config: Record<string, any>) {
|
||||
|
@ -42,17 +42,25 @@ export class AggParamType<TAggConfig extends IAggConfig = IAggConfig> extends Ba
|
|||
}
|
||||
if (!config.serialize) {
|
||||
this.serialize = (agg: TAggConfig) => {
|
||||
return agg.toJSON();
|
||||
return agg.serialize();
|
||||
};
|
||||
}
|
||||
if (!config.deserialize) {
|
||||
this.deserialize = (state: unknown, agg?: TAggConfig): TAggConfig => {
|
||||
this.deserialize = (state: AggConfigSerialized, agg?: TAggConfig): TAggConfig => {
|
||||
if (!agg) {
|
||||
throw new Error('aggConfig was not provided to AggParamType deserialize function');
|
||||
}
|
||||
return this.makeAgg(agg, state);
|
||||
};
|
||||
}
|
||||
if (!config.toExpressionAst) {
|
||||
this.toExpressionAst = (agg: TAggConfig) => {
|
||||
if (!agg || !agg.toExpressionAst) {
|
||||
throw new Error('aggConfig was not provided to AggParamType toExpressionAst function');
|
||||
}
|
||||
return agg.toExpressionAst();
|
||||
};
|
||||
}
|
||||
|
||||
this.makeAgg = config.makeAgg;
|
||||
this.valueType = AggConfig;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { ExpressionAstFunction } from 'src/plugins/expressions/public';
|
||||
import { IAggConfigs } from '../agg_configs';
|
||||
import { IAggConfig } from '../agg_config';
|
||||
import { FetchOptions } from '../../fetch';
|
||||
|
@ -37,6 +38,7 @@ export class BaseParamType<TAggConfig extends IAggConfig = IAggConfig> {
|
|||
) => void;
|
||||
serialize: (value: any, aggConfig?: TAggConfig) => any;
|
||||
deserialize: (value: any, aggConfig?: TAggConfig) => any;
|
||||
toExpressionAst?: (value: any) => ExpressionAstFunction | undefined;
|
||||
options: any[];
|
||||
valueType?: any;
|
||||
|
||||
|
@ -77,6 +79,7 @@ export class BaseParamType<TAggConfig extends IAggConfig = IAggConfig> {
|
|||
this.write = config.write || defaultWrite;
|
||||
this.serialize = config.serialize;
|
||||
this.deserialize = config.deserialize;
|
||||
this.toExpressionAst = config.toExpressionAst;
|
||||
this.options = config.options;
|
||||
this.modifyAggConfigOnSearchRequestStart =
|
||||
config.modifyAggConfigOnSearchRequestStart || function() {};
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 { mapValues } from 'lodash';
|
||||
import {
|
||||
AnyExpressionFunctionDefinition,
|
||||
ExpressionFunctionDefinition,
|
||||
ExecutionContext,
|
||||
} from '../../../../../../plugins/expressions/public';
|
||||
|
||||
/**
|
||||
* Takes a function spec and passes in default args,
|
||||
* overriding with any provided args.
|
||||
*
|
||||
* Similar to the test helper used in Expressions & Canvas,
|
||||
* however in this case we are ignoring the input & execution
|
||||
* context, as they are not applicable to the agg type
|
||||
* expression functions.
|
||||
*/
|
||||
export const functionWrapper = <T extends AnyExpressionFunctionDefinition>(spec: T) => {
|
||||
const defaultArgs = mapValues(spec.args, argSpec => argSpec.default);
|
||||
return (
|
||||
args: T extends ExpressionFunctionDefinition<
|
||||
infer Name,
|
||||
infer Input,
|
||||
infer Arguments,
|
||||
infer Output,
|
||||
infer Context
|
||||
>
|
||||
? Arguments
|
||||
: never
|
||||
) => spec.fn(null, { ...defaultArgs, ...args }, {} as ExecutionContext);
|
||||
};
|
|
@ -17,5 +17,6 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { functionWrapper } from './function_wrapper';
|
||||
export { mockAggTypesRegistry } from './mock_agg_types_registry';
|
||||
export { mockDataServices } from './mock_data_services';
|
||||
|
|
|
@ -19,21 +19,23 @@
|
|||
|
||||
import { IndexPattern } from '../../index_patterns';
|
||||
import {
|
||||
AggConfig,
|
||||
AggConfigSerialized,
|
||||
AggConfigs,
|
||||
AggParamsTerms,
|
||||
AggType,
|
||||
aggTypeFieldFilters,
|
||||
AggTypesRegistrySetup,
|
||||
AggTypesRegistryStart,
|
||||
AggConfig,
|
||||
AggConfigs,
|
||||
CreateAggConfigParams,
|
||||
FieldParamType,
|
||||
getCalculateAutoTimeExpression,
|
||||
MetricAggType,
|
||||
aggTypeFieldFilters,
|
||||
parentPipelineAggHelper,
|
||||
siblingPipelineAggHelper,
|
||||
} from './';
|
||||
|
||||
export { IAggConfig } from './agg_config';
|
||||
export { IAggConfig, AggConfigSerialized } from './agg_config';
|
||||
export { CreateAggConfigParams, IAggConfigs } from './agg_configs';
|
||||
export { IAggType } from './agg_type';
|
||||
export { AggParam, AggParamOption } from './agg_params';
|
||||
|
@ -70,3 +72,25 @@ export interface SearchAggsStart {
|
|||
) => InstanceType<typeof AggConfigs>;
|
||||
types: AggTypesRegistryStart;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface AggExpressionType {
|
||||
type: 'agg_type';
|
||||
value: AggConfigSerialized;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export type AggExpressionFunctionArgs<
|
||||
Name extends keyof AggParamsMapping
|
||||
> = AggParamsMapping[Name] & Pick<AggConfigSerialized, 'id' | 'enabled' | 'schema'>;
|
||||
|
||||
/**
|
||||
* A global list of the param interfaces for each agg type.
|
||||
* For now this is internal, but eventually we will probably
|
||||
* want to make it public.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export interface AggParamsMapping {
|
||||
terms: AggParamsTerms;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ export const serializeAggConfig = (aggConfig: IAggConfig): KibanaDatatableColumn
|
|||
return {
|
||||
type: aggConfig.type.name,
|
||||
indexPatternId: aggConfig.getIndexPattern().id,
|
||||
aggConfigParams: aggConfig.toJSON().params,
|
||||
aggConfigParams: aggConfig.serialize().params,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -18,9 +18,10 @@
|
|||
*/
|
||||
|
||||
import { coreMock } from '../../../../core/public/mocks';
|
||||
import { CoreSetup } from '../../../../core/public';
|
||||
import { expressionsPluginMock } from '../../../../plugins/expressions/public/mocks';
|
||||
|
||||
import { SearchService } from './search_service';
|
||||
import { CoreSetup } from '../../../../core/public';
|
||||
|
||||
describe('Search service', () => {
|
||||
let searchService: SearchService;
|
||||
|
@ -35,6 +36,7 @@ describe('Search service', () => {
|
|||
it('exposes proper contract', async () => {
|
||||
const setup = searchService.setup(mockCoreSetup, {
|
||||
packageInfo: { version: '8' },
|
||||
expressions: expressionsPluginMock.createSetupContract(),
|
||||
} as any);
|
||||
expect(setup).toHaveProperty('registerSearchStrategyProvider');
|
||||
});
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import { Plugin, CoreSetup, CoreStart, PackageInfo } from '../../../../core/public';
|
||||
import { ExpressionsSetup } from '../../../../plugins/expressions/public';
|
||||
|
||||
import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy';
|
||||
import {
|
||||
|
@ -37,6 +38,7 @@ import { GetInternalStartServicesFn } from '../types';
|
|||
import { SearchInterceptor } from './search_interceptor';
|
||||
import {
|
||||
getAggTypes,
|
||||
getAggTypesFunctions,
|
||||
AggType,
|
||||
AggTypesRegistry,
|
||||
AggConfig,
|
||||
|
@ -52,9 +54,10 @@ import { FieldFormatsStart } from '../field_formats';
|
|||
import { ISearchGeneric } from './i_search';
|
||||
|
||||
interface SearchServiceSetupDependencies {
|
||||
expressions: ExpressionsSetup;
|
||||
getInternalStartServices: GetInternalStartServicesFn;
|
||||
packageInfo: PackageInfo;
|
||||
query: QuerySetup;
|
||||
getInternalStartServices: GetInternalStartServicesFn;
|
||||
}
|
||||
|
||||
interface SearchServiceStartDependencies {
|
||||
|
@ -97,22 +100,27 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
|
||||
public setup(
|
||||
core: CoreSetup,
|
||||
{ packageInfo, query, getInternalStartServices }: SearchServiceSetupDependencies
|
||||
{ expressions, packageInfo, query, getInternalStartServices }: SearchServiceSetupDependencies
|
||||
): ISearchSetup {
|
||||
this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo);
|
||||
this.registerSearchStrategyProvider(SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider);
|
||||
this.registerSearchStrategyProvider(ES_SEARCH_STRATEGY, esSearchStrategyProvider);
|
||||
|
||||
const aggTypesSetup = this.aggTypesRegistry.setup();
|
||||
|
||||
// register each agg type
|
||||
const aggTypes = getAggTypes({
|
||||
query,
|
||||
uiSettings: core.uiSettings,
|
||||
getInternalStartServices,
|
||||
});
|
||||
|
||||
aggTypes.buckets.forEach(b => aggTypesSetup.registerBucket(b));
|
||||
aggTypes.metrics.forEach(m => aggTypesSetup.registerMetric(m));
|
||||
|
||||
// register expression functions for each agg type
|
||||
const aggFunctions = getAggTypesFunctions();
|
||||
aggFunctions.forEach(fn => expressions.registerFunction(fn));
|
||||
|
||||
return {
|
||||
aggs: {
|
||||
calculateAutoTimeExpression: getCalculateAutoTimeExpression(core.uiSettings),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue