diff --git a/.ci/Jenkinsfile_security_cypress b/.ci/Jenkinsfile_security_cypress
new file mode 100644
index 0000000000000..bdfef18024b78
--- /dev/null
+++ b/.ci/Jenkinsfile_security_cypress
@@ -0,0 +1,21 @@
+#!/bin/groovy
+
+library 'kibana-pipeline-library'
+kibanaLibrary.load()
+
+kibanaPipeline(timeoutMinutes: 180) {
+ slackNotifications.onFailure(
+ disabled: !params.NOTIFY_ON_FAILURE,
+ channel: '#security-solution-slack-testing'
+ ) {
+ catchError {
+ workers.base(size: 's', ramDisk: false) {
+ kibanaPipeline.bash('test/scripts/jenkins_security_solution_cypress.sh', 'Execute Security Solution Cypress Tests')
+ }
+ }
+ }
+
+ if (params.NOTIFY_ON_FAILURE) {
+ kibanaPipeline.sendMail(to: 'gloria.delatorre@elastic.co')
+ }
+}
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 6863b91858ff6..1f076e3c84001 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -8,7 +8,7 @@
/x-pack/plugins/lens/ @elastic/kibana-app
/x-pack/plugins/graph/ @elastic/kibana-app
/src/plugins/dashboard/ @elastic/kibana-app
-/src/plugins/dashboard/**/*.scss @elastic/kibana-core-ui
+/src/plugins/dashboard/**/*.scss @elastic/kibana-core-ui-designers
/src/plugins/discover/ @elastic/kibana-app
/src/plugins/input_control_vis/ @elastic/kibana-app
/src/plugins/kibana_legacy/ @elastic/kibana-app
@@ -70,7 +70,7 @@
# Canvas
/x-pack/plugins/canvas/ @elastic/kibana-canvas
-/x-pack/plugins/canvas/**/*.scss @elastic/kibana-core-ui
+/x-pack/plugins/canvas/**/*.scss @elastic/kibana-core-ui-designers
/x-pack/test/functional/apps/canvas/ @elastic/kibana-canvas
# Core UI
@@ -80,7 +80,7 @@
/src/plugins/home/server/services/ @elastic/kibana-core-ui
# Exclude tutorial resources folder for now because they are not owned by Kibana app and most will move out soon
/src/legacy/core_plugins/kibana/public/home/*.ts @elastic/kibana-core-ui
-/src/legacy/core_plugins/kibana/public/home/**/*.scss @elastic/kibana-core-ui
+/src/legacy/core_plugins/kibana/public/home/**/*.scss @elastic/kibana-core-ui-designers
/src/legacy/core_plugins/kibana/public/home/np_ready/ @elastic/kibana-core-ui
# Observability UIs
@@ -165,14 +165,14 @@
# Security
/src/core/server/csp/ @elastic/kibana-security @elastic/kibana-platform
/x-pack/legacy/plugins/security/ @elastic/kibana-security
-/x-pack/legacy/plugins/security/**/*.scss @elastic/kibana-core-ui
+/x-pack/legacy/plugins/security/**/*.scss @elastic/kibana-core-ui-designers
/x-pack/legacy/plugins/spaces/ @elastic/kibana-security
-/x-pack/legacy/plugins/spaces/**/*.scss @elastic/kibana-core-ui
+/x-pack/legacy/plugins/spaces/**/*.scss @elastic/kibana-core-ui-designers
/x-pack/plugins/spaces/ @elastic/kibana-security
-/x-pack/plugins/spaces/**/*.scss @elastic/kibana-core-ui
+/x-pack/plugins/spaces/**/*.scss @elastic/kibana-core-ui-designers
/x-pack/plugins/encrypted_saved_objects/ @elastic/kibana-security
/x-pack/plugins/security/ @elastic/kibana-security
-/x-pack/plugins/security/**/*.scss @elastic/kibana-core-ui
+/x-pack/plugins/security/**/*.scss @elastic/kibana-core-ui-designers
/x-pack/test/api_integration/apis/security/ @elastic/kibana-security
# Kibana Localization
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.baseformatterspublic.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.baseformatterspublic.md
index 25f046983cbce..1aa9f460c4fac 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.baseformatterspublic.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.baseformatterspublic.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-baseFormattersPublic: (import("../../common").FieldFormatInstanceType | typeof DateNanosFormat | typeof DateFormat)[]
+baseFormattersPublic: (import("../../common").FieldFormatInstanceType | typeof DateFormat | typeof DateNanosFormat)[]
```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md
index 6c8f7fbdb170b..22dc92c275670 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md
@@ -14,7 +14,7 @@ search: {
intervalOptions: ({
display: string;
val: string;
- enabled(agg: import("./search/aggs/buckets/bucket_agg_type").IBucketAggConfig): boolean | "" | undefined;
+ enabled(agg: import("../common").IBucketAggConfig): boolean | "" | undefined;
} | {
display: string;
val: string;
@@ -23,9 +23,9 @@ search: {
InvalidEsIntervalFormatError: typeof InvalidEsIntervalFormatError;
Ipv4Address: typeof Ipv4Address;
isDateHistogramBucketAggConfig: typeof isDateHistogramBucketAggConfig;
- isNumberType: (agg: import("./search").AggConfig) => boolean;
- isStringType: (agg: import("./search").AggConfig) => boolean;
- isType: (...types: string[]) => (agg: import("./search").AggConfig) => boolean;
+ isNumberType: (agg: import("../common").AggConfig) => boolean;
+ isStringType: (agg: import("../common").AggConfig) => boolean;
+ isType: (...types: string[]) => (agg: import("../common").AggConfig) => boolean;
isValidEsInterval: typeof isValidEsInterval;
isValidInterval: typeof isValidInterval;
parentPipelineType: string;
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggconfigoptions.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggconfigoptions.md
new file mode 100644
index 0000000000000..effb2e798ad6f
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggconfigoptions.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggConfigOptions](./kibana-plugin-plugins-data-server.aggconfigoptions.md)
+
+## AggConfigOptions type
+
+Signature:
+
+```typescript
+export declare type AggConfigOptions = Assign;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.agggrouplabels.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.agggrouplabels.md
new file mode 100644
index 0000000000000..cf0caee6ac33e
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.agggrouplabels.md
@@ -0,0 +1,15 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggGroupLabels](./kibana-plugin-plugins-data-server.agggrouplabels.md)
+
+## AggGroupLabels variable
+
+Signature:
+
+```typescript
+AggGroupLabels: {
+ buckets: string;
+ metrics: string;
+ none: string;
+}
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.agggroupname.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.agggroupname.md
new file mode 100644
index 0000000000000..403294eba1367
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.agggroupname.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggGroupName](./kibana-plugin-plugins-data-server.agggroupname.md)
+
+## AggGroupName type
+
+Signature:
+
+```typescript
+export declare type AggGroupName = $Values;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.agggroupnames.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.agggroupnames.md
new file mode 100644
index 0000000000000..11d194723c521
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.agggroupnames.md
@@ -0,0 +1,15 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggGroupNames](./kibana-plugin-plugins-data-server.agggroupnames.md)
+
+## AggGroupNames variable
+
+Signature:
+
+```typescript
+AggGroupNames: Readonly<{
+ Buckets: "buckets";
+ Metrics: "metrics";
+ None: "none";
+}>
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparam.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparam.md
new file mode 100644
index 0000000000000..893501666b9a0
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparam.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggParam](./kibana-plugin-plugins-data-server.aggparam.md)
+
+## AggParam type
+
+Signature:
+
+```typescript
+export declare type AggParam = BaseParamType;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamoption.display.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamoption.display.md
new file mode 100644
index 0000000000000..1030056e16afe
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamoption.display.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggParamOption](./kibana-plugin-plugins-data-server.aggparamoption.md) > [display](./kibana-plugin-plugins-data-server.aggparamoption.display.md)
+
+## AggParamOption.display property
+
+Signature:
+
+```typescript
+display: string;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamoption.enabled.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamoption.enabled.md
new file mode 100644
index 0000000000000..8b1fcc4a1bbd0
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamoption.enabled.md
@@ -0,0 +1,22 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggParamOption](./kibana-plugin-plugins-data-server.aggparamoption.md) > [enabled](./kibana-plugin-plugins-data-server.aggparamoption.enabled.md)
+
+## AggParamOption.enabled() method
+
+Signature:
+
+```typescript
+enabled?(agg: AggConfig): boolean;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| agg | AggConfig
| |
+
+Returns:
+
+`boolean`
+
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamoption.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamoption.md
new file mode 100644
index 0000000000000..a7ddcf395cab4
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamoption.md
@@ -0,0 +1,25 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggParamOption](./kibana-plugin-plugins-data-server.aggparamoption.md)
+
+## AggParamOption interface
+
+Signature:
+
+```typescript
+export interface AggParamOption
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [display](./kibana-plugin-plugins-data-server.aggparamoption.display.md) | string
| |
+| [val](./kibana-plugin-plugins-data-server.aggparamoption.val.md) | string
| |
+
+## Methods
+
+| Method | Description |
+| --- | --- |
+| [enabled(agg)](./kibana-plugin-plugins-data-server.aggparamoption.enabled.md) | |
+
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamoption.val.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamoption.val.md
new file mode 100644
index 0000000000000..2c87c91c294d9
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamoption.val.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggParamOption](./kibana-plugin-plugins-data-server.aggparamoption.md) > [val](./kibana-plugin-plugins-data-server.aggparamoption.val.md)
+
+## AggParamOption.val property
+
+Signature:
+
+```typescript
+val: string;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamtype._constructor_.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamtype._constructor_.md
new file mode 100644
index 0000000000000..2e1b16855987e
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamtype._constructor_.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggParamType](./kibana-plugin-plugins-data-server.aggparamtype.md) > [(constructor)](./kibana-plugin-plugins-data-server.aggparamtype._constructor_.md)
+
+## AggParamType.(constructor)
+
+Constructs a new instance of the `AggParamType` class
+
+Signature:
+
+```typescript
+constructor(config: Record);
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| config | Record<string, any>
| |
+
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamtype.allowedaggs.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamtype.allowedaggs.md
new file mode 100644
index 0000000000000..36179a9ce3569
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamtype.allowedaggs.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggParamType](./kibana-plugin-plugins-data-server.aggparamtype.md) > [allowedAggs](./kibana-plugin-plugins-data-server.aggparamtype.allowedaggs.md)
+
+## AggParamType.allowedAggs property
+
+Signature:
+
+```typescript
+allowedAggs: string[];
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamtype.makeagg.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamtype.makeagg.md
new file mode 100644
index 0000000000000..bd5d2fca77659
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamtype.makeagg.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggParamType](./kibana-plugin-plugins-data-server.aggparamtype.md) > [makeAgg](./kibana-plugin-plugins-data-server.aggparamtype.makeagg.md)
+
+## AggParamType.makeAgg property
+
+Signature:
+
+```typescript
+makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamtype.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamtype.md
new file mode 100644
index 0000000000000..00c1906dd880b
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.aggparamtype.md
@@ -0,0 +1,25 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [AggParamType](./kibana-plugin-plugins-data-server.aggparamtype.md)
+
+## AggParamType class
+
+Signature:
+
+```typescript
+export declare class AggParamType extends BaseParamType
+```
+
+## Constructors
+
+| Constructor | Modifiers | Description |
+| --- | --- | --- |
+| [(constructor)(config)](./kibana-plugin-plugins-data-server.aggparamtype._constructor_.md) | | Constructs a new instance of the AggParamType
class |
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [allowedAggs](./kibana-plugin-plugins-data-server.aggparamtype.allowedaggs.md) | | string[]
| |
+| [makeAgg](./kibana-plugin-plugins-data-server.aggparamtype.makeagg.md) | | (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig
| |
+
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.bucket_types.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.bucket_types.md
new file mode 100644
index 0000000000000..568e435754545
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.bucket_types.md
@@ -0,0 +1,28 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [BUCKET\_TYPES](./kibana-plugin-plugins-data-server.bucket_types.md)
+
+## BUCKET\_TYPES enum
+
+Signature:
+
+```typescript
+export declare enum BUCKET_TYPES
+```
+
+## Enumeration Members
+
+| Member | Value | Description |
+| --- | --- | --- |
+| DATE\_HISTOGRAM | "date_histogram"
| |
+| DATE\_RANGE | "date_range"
| |
+| FILTER | "filter"
| |
+| FILTERS | "filters"
| |
+| GEOHASH\_GRID | "geohash_grid"
| |
+| GEOTILE\_GRID | "geotile_grid"
| |
+| HISTOGRAM | "histogram"
| |
+| IP\_RANGE | "ip_range"
| |
+| RANGE | "range"
| |
+| SIGNIFICANT\_TERMS | "significant_terms"
| |
+| TERMS | "terms"
| |
+
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iaggconfig.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iaggconfig.md
new file mode 100644
index 0000000000000..261b6e0b3bac1
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iaggconfig.md
@@ -0,0 +1,15 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IAggConfig](./kibana-plugin-plugins-data-server.iaggconfig.md)
+
+## IAggConfig type
+
+ AggConfig
+
+ This class represents an aggregation, which is displayed in the left-hand nav of the Visualize app.
+
+Signature:
+
+```typescript
+export declare type IAggConfig = AggConfig;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iaggtype.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iaggtype.md
new file mode 100644
index 0000000000000..d5868e1b0917e
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iaggtype.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IAggType](./kibana-plugin-plugins-data-server.iaggtype.md)
+
+## IAggType type
+
+Signature:
+
+```typescript
+export declare type IAggType = AggType;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldparamtype.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldparamtype.md
new file mode 100644
index 0000000000000..4937245647f4e
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldparamtype.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IFieldParamType](./kibana-plugin-plugins-data-server.ifieldparamtype.md)
+
+## IFieldParamType type
+
+Signature:
+
+```typescript
+export declare type IFieldParamType = FieldParamType;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.imetricaggtype.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.imetricaggtype.md
new file mode 100644
index 0000000000000..ae779c2b1510f
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.imetricaggtype.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IMetricAggType](./kibana-plugin-plugins-data-server.imetricaggtype.md)
+
+## IMetricAggType type
+
+Signature:
+
+```typescript
+export declare type IMetricAggType = MetricAggType;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.aggs.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.aggs.md
new file mode 100644
index 0000000000000..86bd4ab694e11
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.aggs.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchSetup](./kibana-plugin-plugins-data-server.isearchsetup.md) > [aggs](./kibana-plugin-plugins-data-server.isearchsetup.aggs.md)
+
+## ISearchSetup.aggs property
+
+Signature:
+
+```typescript
+aggs: AggsSetup;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md
index d9749bc44f45a..e5b11a0b997ea 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md
@@ -14,6 +14,7 @@ export interface ISearchSetup
| Property | Type | Description |
| --- | --- | --- |
+| [aggs](./kibana-plugin-plugins-data-server.isearchsetup.aggs.md) | AggsSetup
| |
| [registerSearchStrategy](./kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md) | (name: string, strategy: ISearchStrategy) => void
| Extension point exposed for other plugins to register their own search strategies. |
| [usage](./kibana-plugin-plugins-data-server.isearchsetup.usage.md) | SearchUsage
| Used internally for telemetry |
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.aggs.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.aggs.md
new file mode 100644
index 0000000000000..8da429a07708c
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.aggs.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchStart](./kibana-plugin-plugins-data-server.isearchstart.md) > [aggs](./kibana-plugin-plugins-data-server.isearchstart.aggs.md)
+
+## ISearchStart.aggs property
+
+Signature:
+
+```typescript
+aggs: AggsStart;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md
index 308ce3cb568bc..3762da963d4d9 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md
@@ -14,6 +14,7 @@ export interface ISearchStart
| Property | Type | Description |
| --- | --- | --- |
+| [aggs](./kibana-plugin-plugins-data-server.isearchstart.aggs.md) | AggsStart
| |
| [getSearchStrategy](./kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md) | (name: string) => ISearchStrategy
| Get other registered search strategies. For example, if a new strategy needs to use the already-registered ES search strategy, it can use this function to accomplish that. |
| [search](./kibana-plugin-plugins-data-server.isearchstart.search.md) | (context: RequestHandlerContext, request: IKibanaSearchRequest, options: ISearchOptions) => Promise<IKibanaSearchResponse>
| |
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md
index f472064c87755..0292e08063fbb 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md
@@ -8,15 +8,19 @@
| Class | Description |
| --- | --- |
+| [AggParamType](./kibana-plugin-plugins-data-server.aggparamtype.md) | |
| [IndexPatternsFetcher](./kibana-plugin-plugins-data-server.indexpatternsfetcher.md) | |
+| [OptionedParamType](./kibana-plugin-plugins-data-server.optionedparamtype.md) | |
| [Plugin](./kibana-plugin-plugins-data-server.plugin.md) | |
## Enumerations
| Enumeration | Description |
| --- | --- |
+| [BUCKET\_TYPES](./kibana-plugin-plugins-data-server.bucket_types.md) | |
| [ES\_FIELD\_TYPES](./kibana-plugin-plugins-data-server.es_field_types.md) | \* |
| [KBN\_FIELD\_TYPES](./kibana-plugin-plugins-data-server.kbn_field_types.md) | \* |
+| [METRIC\_TYPES](./kibana-plugin-plugins-data-server.metric_types.md) | |
## Functions
@@ -33,6 +37,7 @@
| Interface | Description |
| --- | --- |
+| [AggParamOption](./kibana-plugin-plugins-data-server.aggparamoption.md) | |
| [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) | |
| [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) | |
| [Filter](./kibana-plugin-plugins-data-server.filter.md) | |
@@ -48,17 +53,22 @@
| [ISearchStart](./kibana-plugin-plugins-data-server.isearchstart.md) | |
| [ISearchStrategy](./kibana-plugin-plugins-data-server.isearchstrategy.md) | Search strategy interface contains a search method that takes in a request and returns a promise that resolves to a response. |
| [KueryNode](./kibana-plugin-plugins-data-server.kuerynode.md) | |
+| [OptionedValueProp](./kibana-plugin-plugins-data-server.optionedvalueprop.md) | |
| [PluginSetup](./kibana-plugin-plugins-data-server.pluginsetup.md) | |
| [PluginStart](./kibana-plugin-plugins-data-server.pluginstart.md) | |
| [Query](./kibana-plugin-plugins-data-server.query.md) | |
| [RefreshInterval](./kibana-plugin-plugins-data-server.refreshinterval.md) | |
| [SearchUsage](./kibana-plugin-plugins-data-server.searchusage.md) | |
+| [TabbedAggColumn](./kibana-plugin-plugins-data-server.tabbedaggcolumn.md) | \* |
+| [TabbedTable](./kibana-plugin-plugins-data-server.tabbedtable.md) | \* |
| [TimeRange](./kibana-plugin-plugins-data-server.timerange.md) | |
## Variables
| Variable | Description |
| --- | --- |
+| [AggGroupLabels](./kibana-plugin-plugins-data-server.agggrouplabels.md) | |
+| [AggGroupNames](./kibana-plugin-plugins-data-server.agggroupnames.md) | |
| [castEsToKbnFieldTypeName](./kibana-plugin-plugins-data-server.castestokbnfieldtypename.md) | Get the KbnFieldType name for an esType string |
| [config](./kibana-plugin-plugins-data-server.config.md) | |
| [esFilters](./kibana-plugin-plugins-data-server.esfilters.md) | |
@@ -73,8 +83,16 @@
| Type Alias | Description |
| --- | --- |
+| [AggConfigOptions](./kibana-plugin-plugins-data-server.aggconfigoptions.md) | |
+| [AggGroupName](./kibana-plugin-plugins-data-server.agggroupname.md) | |
+| [AggParam](./kibana-plugin-plugins-data-server.aggparam.md) | |
| [EsaggsExpressionFunctionDefinition](./kibana-plugin-plugins-data-server.esaggsexpressionfunctiondefinition.md) | |
| [FieldFormatsGetConfigFn](./kibana-plugin-plugins-data-server.fieldformatsgetconfigfn.md) | |
+| [IAggConfig](./kibana-plugin-plugins-data-server.iaggconfig.md) | AggConfig This class represents an aggregation, which is displayed in the left-hand nav of the Visualize app. |
+| [IAggType](./kibana-plugin-plugins-data-server.iaggtype.md) | |
| [IFieldFormatsRegistry](./kibana-plugin-plugins-data-server.ifieldformatsregistry.md) | |
+| [IFieldParamType](./kibana-plugin-plugins-data-server.ifieldparamtype.md) | |
+| [IMetricAggType](./kibana-plugin-plugins-data-server.imetricaggtype.md) | |
| [ParsedInterval](./kibana-plugin-plugins-data-server.parsedinterval.md) | |
+| [TabbedAggRow](./kibana-plugin-plugins-data-server.tabbedaggrow.md) | \* |
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.metric_types.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.metric_types.md
new file mode 100644
index 0000000000000..49df98b6d70a1
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.metric_types.md
@@ -0,0 +1,38 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [METRIC\_TYPES](./kibana-plugin-plugins-data-server.metric_types.md)
+
+## METRIC\_TYPES enum
+
+Signature:
+
+```typescript
+export declare enum METRIC_TYPES
+```
+
+## Enumeration Members
+
+| Member | Value | Description |
+| --- | --- | --- |
+| AVG | "avg"
| |
+| AVG\_BUCKET | "avg_bucket"
| |
+| CARDINALITY | "cardinality"
| |
+| COUNT | "count"
| |
+| CUMULATIVE\_SUM | "cumulative_sum"
| |
+| DERIVATIVE | "derivative"
| |
+| GEO\_BOUNDS | "geo_bounds"
| |
+| GEO\_CENTROID | "geo_centroid"
| |
+| MAX | "max"
| |
+| MAX\_BUCKET | "max_bucket"
| |
+| MEDIAN | "median"
| |
+| MIN | "min"
| |
+| MIN\_BUCKET | "min_bucket"
| |
+| MOVING\_FN | "moving_avg"
| |
+| PERCENTILE\_RANKS | "percentile_ranks"
| |
+| PERCENTILES | "percentiles"
| |
+| SERIAL\_DIFF | "serial_diff"
| |
+| STD\_DEV | "std_dev"
| |
+| SUM | "sum"
| |
+| SUM\_BUCKET | "sum_bucket"
| |
+| TOP\_HITS | "top_hits"
| |
+
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedparamtype._constructor_.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedparamtype._constructor_.md
new file mode 100644
index 0000000000000..3b2fd2218709a
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedparamtype._constructor_.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [OptionedParamType](./kibana-plugin-plugins-data-server.optionedparamtype.md) > [(constructor)](./kibana-plugin-plugins-data-server.optionedparamtype._constructor_.md)
+
+## OptionedParamType.(constructor)
+
+Constructs a new instance of the `OptionedParamType` class
+
+Signature:
+
+```typescript
+constructor(config: Record);
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| config | Record<string, any>
| |
+
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedparamtype.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedparamtype.md
new file mode 100644
index 0000000000000..6bf2ef4baa915
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedparamtype.md
@@ -0,0 +1,24 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [OptionedParamType](./kibana-plugin-plugins-data-server.optionedparamtype.md)
+
+## OptionedParamType class
+
+Signature:
+
+```typescript
+export declare class OptionedParamType extends BaseParamType
+```
+
+## Constructors
+
+| Constructor | Modifiers | Description |
+| --- | --- | --- |
+| [(constructor)(config)](./kibana-plugin-plugins-data-server.optionedparamtype._constructor_.md) | | Constructs a new instance of the OptionedParamType
class |
+
+## Properties
+
+| Property | Modifiers | Type | Description |
+| --- | --- | --- | --- |
+| [options](./kibana-plugin-plugins-data-server.optionedparamtype.options.md) | | OptionedValueProp[]
| |
+
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedparamtype.options.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedparamtype.options.md
new file mode 100644
index 0000000000000..868619ad5a9e0
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedparamtype.options.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [OptionedParamType](./kibana-plugin-plugins-data-server.optionedparamtype.md) > [options](./kibana-plugin-plugins-data-server.optionedparamtype.options.md)
+
+## OptionedParamType.options property
+
+Signature:
+
+```typescript
+options: OptionedValueProp[];
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.disabled.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.disabled.md
new file mode 100644
index 0000000000000..e0a21a8727614
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.disabled.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [OptionedValueProp](./kibana-plugin-plugins-data-server.optionedvalueprop.md) > [disabled](./kibana-plugin-plugins-data-server.optionedvalueprop.disabled.md)
+
+## OptionedValueProp.disabled property
+
+Signature:
+
+```typescript
+disabled?: boolean;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.iscompatible.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.iscompatible.md
new file mode 100644
index 0000000000000..de3ecc0b97a64
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.iscompatible.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [OptionedValueProp](./kibana-plugin-plugins-data-server.optionedvalueprop.md) > [isCompatible](./kibana-plugin-plugins-data-server.optionedvalueprop.iscompatible.md)
+
+## OptionedValueProp.isCompatible property
+
+Signature:
+
+```typescript
+isCompatible: (agg: IAggConfig) => boolean;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.md
new file mode 100644
index 0000000000000..ef2440035c83b
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [OptionedValueProp](./kibana-plugin-plugins-data-server.optionedvalueprop.md)
+
+## OptionedValueProp interface
+
+Signature:
+
+```typescript
+export interface OptionedValueProp
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [disabled](./kibana-plugin-plugins-data-server.optionedvalueprop.disabled.md) | boolean
| |
+| [isCompatible](./kibana-plugin-plugins-data-server.optionedvalueprop.iscompatible.md) | (agg: IAggConfig) => boolean
| |
+| [text](./kibana-plugin-plugins-data-server.optionedvalueprop.text.md) | string
| |
+| [value](./kibana-plugin-plugins-data-server.optionedvalueprop.value.md) | string
| |
+
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.text.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.text.md
new file mode 100644
index 0000000000000..0a2b3ac708038
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.text.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [OptionedValueProp](./kibana-plugin-plugins-data-server.optionedvalueprop.md) > [text](./kibana-plugin-plugins-data-server.optionedvalueprop.text.md)
+
+## OptionedValueProp.text property
+
+Signature:
+
+```typescript
+text: string;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.value.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.value.md
new file mode 100644
index 0000000000000..76618558d0479
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.optionedvalueprop.value.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [OptionedValueProp](./kibana-plugin-plugins-data-server.optionedvalueprop.md) > [value](./kibana-plugin-plugins-data-server.optionedvalueprop.value.md)
+
+## OptionedValueProp.value property
+
+Signature:
+
+```typescript
+value: string;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md
index a6fdfdf6891c8..18fca3d2c8a66 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md
@@ -7,10 +7,10 @@
Signature:
```typescript
-setup(core: CoreSetup
diff --git a/src/plugins/dashboard/public/application/dashboard_empty_screen.test.tsx b/src/plugins/dashboard/public/application/dashboard_empty_screen.test.tsx
index 933475d354cfa..0a49e524d3350 100644
--- a/src/plugins/dashboard/public/application/dashboard_empty_screen.test.tsx
+++ b/src/plugins/dashboard/public/application/dashboard_empty_screen.test.tsx
@@ -19,7 +19,6 @@
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { DashboardEmptyScreen, DashboardEmptyScreenProps } from './dashboard_empty_screen';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import { coreMock } from '../../../../core/public/mocks';
diff --git a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.test.tsx b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.test.tsx
index 1e07c610b0ef2..60395bce678c2 100644
--- a/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.test.tsx
+++ b/src/plugins/dashboard/public/application/embeddable/viewport/dashboard_viewport.test.tsx
@@ -17,7 +17,6 @@
* under the License.
*/
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import React from 'react';
import { skip } from 'rxjs/operators';
diff --git a/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx b/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx
index 6eb85faeea014..24075e0a634ba 100644
--- a/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx
+++ b/src/plugins/dashboard/public/application/tests/dashboard_container.test.tsx
@@ -17,7 +17,6 @@
* under the License.
*/
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import React from 'react';
import { mount } from 'enzyme';
diff --git a/src/plugins/data/common/field_formats/converters/source.ts b/src/plugins/data/common/field_formats/converters/source.ts
index f00261e00971a..9c81bb011e127 100644
--- a/src/plugins/data/common/field_formats/converters/source.ts
+++ b/src/plugins/data/common/field_formats/converters/source.ts
@@ -22,7 +22,7 @@ import { shortenDottedString } from '../../utils';
import { KBN_FIELD_TYPES } from '../../kbn_field_types/types';
import { FieldFormat } from '../field_format';
import { TextContextTypeConvert, HtmlContextTypeConvert, FIELD_FORMAT_IDS } from '../types';
-import { UI_SETTINGS } from '../../';
+import { UI_SETTINGS } from '../../constants';
/**
* Remove all of the whitespace between html tags
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 84bedd2f9dee0..4b847ebc358d7 100644
--- a/src/plugins/data/common/field_formats/field_formats_registry.ts
+++ b/src/plugins/data/common/field_formats/field_formats_registry.ts
@@ -33,7 +33,7 @@ import { baseFormatters } from './constants/base_formatters';
import { FieldFormat } from './field_format';
import { SerializedFieldFormat } from '../../../expressions/common/types';
import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../types';
-import { UI_SETTINGS } from '../';
+import { UI_SETTINGS } from '../constants';
export class FieldFormatsRegistry {
protected fieldFormats: Map = new Map();
diff --git a/src/plugins/data/common/field_formats/mocks.ts b/src/plugins/data/common/field_formats/mocks.ts
index 9bbaefe2d146a..ddf9cf246ec7d 100644
--- a/src/plugins/data/common/field_formats/mocks.ts
+++ b/src/plugins/data/common/field_formats/mocks.ts
@@ -24,6 +24,7 @@ export const fieldFormatsMock: IFieldFormatsRegistry = {
getByFieldType: jest.fn(),
getDefaultConfig: jest.fn(),
getDefaultInstance: jest.fn().mockImplementation(() => ({
+ convert: jest.fn().mockImplementation((t: string) => t),
getConverterFor: jest.fn().mockImplementation(() => (t: string) => t),
})) as any,
getDefaultInstanceCacheResolver: jest.fn(),
diff --git a/src/plugins/data/common/index.ts b/src/plugins/data/common/index.ts
index ca6bc965d48c5..bc7080e7d450b 100644
--- a/src/plugins/data/common/index.ts
+++ b/src/plugins/data/common/index.ts
@@ -25,7 +25,5 @@ export * from './index_patterns';
export * from './kbn_field_types';
export * from './query';
export * from './search';
-export * from './search/aggs';
-export * from './search/expressions';
export * from './types';
export * from './utils';
diff --git a/src/plugins/data/common/index_patterns/fields/field_list.ts b/src/plugins/data/common/index_patterns/fields/field_list.ts
index 172da9f9ca43f..34bd69230a2e4 100644
--- a/src/plugins/data/common/index_patterns/fields/field_list.ts
+++ b/src/plugins/data/common/index_patterns/fields/field_list.ts
@@ -18,10 +18,11 @@
*/
import { findIndex } from 'lodash';
-import { IFieldType, shortenDottedString } from '../../../common';
+import { IFieldType } from './types';
import { IndexPatternField } from './index_pattern_field';
import { OnNotification, FieldSpec } from '../types';
import { IndexPattern } from '../index_patterns';
+import { shortenDottedString } from '../../utils';
type FieldMap = Map;
diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts
index 679de103f8019..965f1a7f63065 100644
--- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts
+++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts
@@ -18,13 +18,10 @@
*/
import { i18n } from '@kbn/i18n';
-import {
- IFieldType,
- KbnFieldType,
- getKbnFieldType,
- KBN_FIELD_TYPES,
- FieldFormat,
-} from '../../../common';
+import { KbnFieldType, getKbnFieldType } from '../../kbn_field_types';
+import { KBN_FIELD_TYPES } from '../../kbn_field_types/types';
+import { FieldFormat } from '../../field_formats';
+import { IFieldType } from './types';
import { OnNotification, FieldSpec } from '../types';
import { IndexPattern } from '../index_patterns';
diff --git a/src/plugins/data/public/search/aggs/agg_config.test.ts b/src/plugins/data/common/search/aggs/agg_config.test.ts
similarity index 98%
rename from src/plugins/data/public/search/aggs/agg_config.test.ts
rename to src/plugins/data/common/search/aggs/agg_config.test.ts
index f9279e06d14a9..a443eacee731c 100644
--- a/src/plugins/data/public/search/aggs/agg_config.test.ts
+++ b/src/plugins/data/common/search/aggs/agg_config.test.ts
@@ -25,7 +25,8 @@ import { AggType } from './agg_type';
import { AggTypesRegistryStart } from './agg_types_registry';
import { mockAggTypesRegistry } from './test_helpers';
import { MetricAggType } from './metrics/metric_agg_type';
-import { IndexPattern, IIndexPatternFieldList } from '../../index_patterns';
+import { IndexPattern } from '../../index_patterns/index_patterns/index_pattern';
+import { IIndexPatternFieldList } from '../../index_patterns/fields';
describe('AggConfig', () => {
let indexPattern: IndexPattern;
@@ -622,7 +623,7 @@ describe('AggConfig', () => {
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');
+ const range = typesRegistry.get('range') as MetricAggType;
range.expressionName = 'aggRange';
const rangesParam = range.params.find((p) => p.name === 'ranges');
rangesParam!.toExpressionAst = (val: any) => ({
diff --git a/src/plugins/data/public/search/aggs/agg_config.ts b/src/plugins/data/common/search/aggs/agg_config.ts
similarity index 99%
rename from src/plugins/data/public/search/aggs/agg_config.ts
rename to src/plugins/data/common/search/aggs/agg_config.ts
index 31618eac18e98..b5747ce7bb9bd 100644
--- a/src/plugins/data/public/search/aggs/agg_config.ts
+++ b/src/plugins/data/common/search/aggs/agg_config.ts
@@ -20,16 +20,17 @@
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
import { Assign, Ensure } from '@kbn/utility-types';
+
+import { FetchOptions, ISearchSource } from 'src/plugins/data/public';
import {
ExpressionAstFunction,
ExpressionAstArgument,
SerializedFieldFormat,
} from 'src/plugins/expressions/common';
+
import { IAggType } from './agg_type';
import { writeParams } from './agg_params';
import { IAggConfigs } from './agg_configs';
-import { FetchOptions } from '../fetch';
-import { ISearchSource } from '../search_source';
type State = string | number | boolean | null | undefined | SerializableState;
diff --git a/src/plugins/data/public/search/aggs/agg_configs.test.ts b/src/plugins/data/common/search/aggs/agg_configs.test.ts
similarity index 98%
rename from src/plugins/data/public/search/aggs/agg_configs.test.ts
rename to src/plugins/data/common/search/aggs/agg_configs.test.ts
index ff0cc3341929e..803ccc70b98a7 100644
--- a/src/plugins/data/public/search/aggs/agg_configs.test.ts
+++ b/src/plugins/data/common/search/aggs/agg_configs.test.ts
@@ -22,8 +22,9 @@ import { AggConfig } from './agg_config';
import { AggConfigs } from './agg_configs';
import { AggTypesRegistryStart } from './agg_types_registry';
import { mockAggTypesRegistry } from './test_helpers';
-import { IndexPatternField, IndexPattern } from '../../index_patterns';
-import { stubIndexPattern, stubIndexPatternWithFields } from '../../../public/stubs';
+import type { IndexPatternField } from '../../index_patterns';
+import { IndexPattern } from '../../index_patterns/index_patterns/index_pattern';
+import { stubIndexPattern, stubIndexPatternWithFields } from '../../../common/stubs';
describe('AggConfigs', () => {
let indexPattern: IndexPattern;
diff --git a/src/plugins/data/public/search/aggs/agg_configs.ts b/src/plugins/data/common/search/aggs/agg_configs.ts
similarity index 98%
rename from src/plugins/data/public/search/aggs/agg_configs.ts
rename to src/plugins/data/common/search/aggs/agg_configs.ts
index b272dfd3c7468..203eda3a907ee 100644
--- a/src/plugins/data/public/search/aggs/agg_configs.ts
+++ b/src/plugins/data/common/search/aggs/agg_configs.ts
@@ -20,13 +20,12 @@
import _ from 'lodash';
import { Assign } from '@kbn/utility-types';
+import { FetchOptions, ISearchSource } from 'src/plugins/data/public';
import { AggConfig, AggConfigSerialized, IAggConfig } from './agg_config';
import { IAggType } from './agg_type';
import { AggTypesRegistryStart } from './agg_types_registry';
import { AggGroupNames } from './agg_groups';
-import { IndexPattern } from '../../index_patterns';
-import { ISearchSource } from '../search_source';
-import { FetchOptions } from '../fetch';
+import { IndexPattern } from '../../index_patterns/index_patterns/index_pattern';
import { TimeRange } from '../../../common';
function removeParentAggs(obj: any) {
diff --git a/src/plugins/data/public/search/aggs/agg_groups.ts b/src/plugins/data/common/search/aggs/agg_groups.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/agg_groups.ts
rename to src/plugins/data/common/search/aggs/agg_groups.ts
diff --git a/src/plugins/data/public/search/aggs/agg_params.test.ts b/src/plugins/data/common/search/aggs/agg_params.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/agg_params.test.ts
rename to src/plugins/data/common/search/aggs/agg_params.test.ts
diff --git a/src/plugins/data/public/search/aggs/agg_params.ts b/src/plugins/data/common/search/aggs/agg_params.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/agg_params.ts
rename to src/plugins/data/common/search/aggs/agg_params.ts
diff --git a/src/plugins/data/public/search/aggs/agg_type.test.ts b/src/plugins/data/common/search/aggs/agg_type.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/agg_type.test.ts
rename to src/plugins/data/common/search/aggs/agg_type.test.ts
diff --git a/src/plugins/data/public/search/aggs/agg_type.ts b/src/plugins/data/common/search/aggs/agg_type.ts
similarity index 99%
rename from src/plugins/data/public/search/aggs/agg_type.ts
rename to src/plugins/data/common/search/aggs/agg_type.ts
index de7ca48e71d57..0ba2bb66e7758 100644
--- a/src/plugins/data/public/search/aggs/agg_type.ts
+++ b/src/plugins/data/common/search/aggs/agg_type.ts
@@ -20,6 +20,7 @@
import { constant, noop, identity } from 'lodash';
import { i18n } from '@kbn/i18n';
+import { ISearchSource } from 'src/plugins/data/public';
import { SerializedFieldFormat } from 'src/plugins/expressions/common';
import type { RequestAdapter } from 'src/plugins/inspector/common';
@@ -28,7 +29,6 @@ import { AggConfig } from './agg_config';
import { IAggConfigs } from './agg_configs';
import { BaseParamType } from './param_types/base';
import { AggParamType } from './param_types/agg';
-import { ISearchSource } from '../search_source';
export interface AggTypeConfig<
TAggConfig extends AggConfig = AggConfig,
diff --git a/src/plugins/data/public/search/aggs/agg_types.ts b/src/plugins/data/common/search/aggs/agg_types.ts
similarity index 66%
rename from src/plugins/data/public/search/aggs/agg_types.ts
rename to src/plugins/data/common/search/aggs/agg_types.ts
index 2820ae495f318..8565de13aed5b 100644
--- a/src/plugins/data/public/search/aggs/agg_types.ts
+++ b/src/plugins/data/common/search/aggs/agg_types.ts
@@ -17,9 +17,9 @@
* under the License.
*/
-import { IUiSettingsClient } from 'src/core/public';
-import { TimeRange, TimeRangeBounds } from '../../../common';
-import { GetInternalStartServicesFn } from '../../types';
+import { FieldFormatsStartCommon } from '../../field_formats';
+import { BUCKET_TYPES } from './buckets';
+import { METRIC_TYPES } from './metrics';
import { getCountMetricAgg } from './metrics/count';
import { getAvgMetricAgg } from './metrics/avg';
@@ -39,7 +39,7 @@ 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 { getDateHistogramBucketAgg, CalculateBoundsFn } from './buckets/date_histogram';
import { getHistogramBucketAgg } from './buckets/histogram';
import { getRangeBucketAgg } from './buckets/range';
import { getDateRangeBucketAgg } from './buckets/date_range';
@@ -55,52 +55,50 @@ import { getBucketAvgMetricAgg } from './metrics/bucket_avg';
import { getBucketMinMetricAgg } from './metrics/bucket_min';
import { getBucketMaxMetricAgg } from './metrics/bucket_max';
+/** @internal */
export interface AggTypesDependencies {
- calculateBounds: (timeRange: TimeRange) => TimeRangeBounds;
- getInternalStartServices: GetInternalStartServicesFn;
- uiSettings: IUiSettingsClient;
+ calculateBounds: CalculateBoundsFn;
+ getConfig: (key: string) => T;
+ getFieldFormatsStart: () => Pick;
+ isDefaultTimezone: () => boolean;
}
-export const getAggTypes = ({
- calculateBounds,
- getInternalStartServices,
- uiSettings,
-}: AggTypesDependencies) => ({
+export const getAggTypes = () => ({
metrics: [
- getCountMetricAgg(),
- getAvgMetricAgg(),
- getSumMetricAgg(),
- getMedianMetricAgg(),
- getMinMetricAgg(),
- getMaxMetricAgg(),
- getStdDeviationMetricAgg(),
- getCardinalityMetricAgg(),
- getPercentilesMetricAgg(),
- getPercentileRanksMetricAgg({ getInternalStartServices }),
- getTopHitMetricAgg(),
- getDerivativeMetricAgg(),
- getCumulativeSumMetricAgg(),
- getMovingAvgMetricAgg(),
- getSerialDiffMetricAgg(),
- getBucketAvgMetricAgg(),
- getBucketSumMetricAgg(),
- getBucketMinMetricAgg(),
- getBucketMaxMetricAgg(),
- getGeoBoundsMetricAgg(),
- getGeoCentroidMetricAgg(),
+ { name: METRIC_TYPES.COUNT, fn: getCountMetricAgg },
+ { name: METRIC_TYPES.AVG, fn: getAvgMetricAgg },
+ { name: METRIC_TYPES.SUM, fn: getSumMetricAgg },
+ { name: METRIC_TYPES.MEDIAN, fn: getMedianMetricAgg },
+ { name: METRIC_TYPES.MIN, fn: getMinMetricAgg },
+ { name: METRIC_TYPES.MAX, fn: getMaxMetricAgg },
+ { name: METRIC_TYPES.STD_DEV, fn: getStdDeviationMetricAgg },
+ { name: METRIC_TYPES.CARDINALITY, fn: getCardinalityMetricAgg },
+ { name: METRIC_TYPES.PERCENTILES, fn: getPercentilesMetricAgg },
+ { name: METRIC_TYPES.PERCENTILE_RANKS, fn: getPercentileRanksMetricAgg },
+ { name: METRIC_TYPES.TOP_HITS, fn: getTopHitMetricAgg },
+ { name: METRIC_TYPES.DERIVATIVE, fn: getDerivativeMetricAgg },
+ { name: METRIC_TYPES.CUMULATIVE_SUM, fn: getCumulativeSumMetricAgg },
+ { name: METRIC_TYPES.MOVING_FN, fn: getMovingAvgMetricAgg },
+ { name: METRIC_TYPES.SERIAL_DIFF, fn: getSerialDiffMetricAgg },
+ { name: METRIC_TYPES.AVG_BUCKET, fn: getBucketAvgMetricAgg },
+ { name: METRIC_TYPES.SUM_BUCKET, fn: getBucketSumMetricAgg },
+ { name: METRIC_TYPES.MIN_BUCKET, fn: getBucketMinMetricAgg },
+ { name: METRIC_TYPES.MAX_BUCKET, fn: getBucketMaxMetricAgg },
+ { name: METRIC_TYPES.GEO_BOUNDS, fn: getGeoBoundsMetricAgg },
+ { name: METRIC_TYPES.GEO_CENTROID, fn: getGeoCentroidMetricAgg },
],
buckets: [
- getDateHistogramBucketAgg({ calculateBounds, uiSettings }),
- getHistogramBucketAgg({ uiSettings, getInternalStartServices }),
- getRangeBucketAgg({ getInternalStartServices }),
- getDateRangeBucketAgg({ uiSettings }),
- getIpRangeBucketAgg(),
- getTermsBucketAgg(),
- getFilterBucketAgg(),
- getFiltersBucketAgg({ uiSettings }),
- getSignificantTermsBucketAgg(),
- getGeoHashBucketAgg(),
- getGeoTitleBucketAgg(),
+ { name: BUCKET_TYPES.DATE_HISTOGRAM, fn: getDateHistogramBucketAgg },
+ { name: BUCKET_TYPES.HISTOGRAM, fn: getHistogramBucketAgg },
+ { name: BUCKET_TYPES.RANGE, fn: getRangeBucketAgg },
+ { name: BUCKET_TYPES.DATE_RANGE, fn: getDateRangeBucketAgg },
+ { name: BUCKET_TYPES.IP_RANGE, fn: getIpRangeBucketAgg },
+ { name: BUCKET_TYPES.TERMS, fn: getTermsBucketAgg },
+ { name: BUCKET_TYPES.FILTER, fn: getFilterBucketAgg },
+ { name: BUCKET_TYPES.FILTERS, fn: getFiltersBucketAgg },
+ { name: BUCKET_TYPES.SIGNIFICANT_TERMS, fn: getSignificantTermsBucketAgg },
+ { name: BUCKET_TYPES.GEOHASH_GRID, fn: getGeoHashBucketAgg },
+ { name: BUCKET_TYPES.GEOTILE_GRID, fn: getGeoTitleBucketAgg },
],
});
diff --git a/src/plugins/data/public/search/aggs/agg_types_registry.test.ts b/src/plugins/data/common/search/aggs/agg_types_registry.test.ts
similarity index 54%
rename from src/plugins/data/public/search/aggs/agg_types_registry.test.ts
rename to src/plugins/data/common/search/aggs/agg_types_registry.test.ts
index 58d1a07d965e2..df3dddfcd9c6f 100644
--- a/src/plugins/data/public/search/aggs/agg_types_registry.test.ts
+++ b/src/plugins/data/common/search/aggs/agg_types_registry.test.ts
@@ -17,21 +17,17 @@
* under the License.
*/
-import {
- AggTypesRegistry,
- AggTypesRegistrySetup,
- AggTypesRegistryStart,
-} from './agg_types_registry';
+import { AggTypesRegistry, AggTypesRegistrySetup } from './agg_types_registry';
import { BucketAggType } from './buckets/bucket_agg_type';
import { MetricAggType } from './metrics/metric_agg_type';
-const bucketType = { name: 'terms', type: 'bucket' } as BucketAggType;
-const metricType = { name: 'count', type: 'metric' } as MetricAggType;
+const bucketType = () => ({ name: 'terms', type: 'buckets' } as BucketAggType);
+const metricType = () => ({ name: 'count', type: 'metrics' } as MetricAggType);
describe('AggTypesRegistry', () => {
let registry: AggTypesRegistry;
let setup: AggTypesRegistrySetup;
- let start: AggTypesRegistryStart;
+ let start: ReturnType;
beforeEach(() => {
registry = new AggTypesRegistry();
@@ -40,49 +36,53 @@ describe('AggTypesRegistry', () => {
});
it('registerBucket adds new buckets', () => {
- setup.registerBucket(bucketType);
- expect(start.getBuckets()).toEqual([bucketType]);
+ setup.registerBucket('terms', bucketType);
+ expect(start.getAll().buckets).toEqual([bucketType]);
});
it('registerBucket throws error when registering duplicate bucket', () => {
expect(() => {
- setup.registerBucket(bucketType);
- setup.registerBucket(bucketType);
+ setup.registerBucket('terms', bucketType);
+ setup.registerBucket('terms', bucketType);
}).toThrow(/already been registered with name: terms/);
+
+ const fooBucket = () => ({ name: 'foo', type: 'buckets' } as BucketAggType);
+ const fooMetric = () => ({ name: 'foo', type: 'metrics' } as MetricAggType);
+ expect(() => {
+ setup.registerBucket('foo', fooBucket);
+ setup.registerMetric('foo', fooMetric);
+ }).toThrow(/already been registered with name: foo/);
});
it('registerMetric adds new metrics', () => {
- setup.registerMetric(metricType);
- expect(start.getMetrics()).toEqual([metricType]);
+ setup.registerMetric('count', metricType);
+ expect(start.getAll().metrics).toEqual([metricType]);
});
it('registerMetric throws error when registering duplicate metric', () => {
expect(() => {
- setup.registerMetric(metricType);
- setup.registerMetric(metricType);
+ setup.registerMetric('count', metricType);
+ setup.registerMetric('count', metricType);
}).toThrow(/already been registered with name: count/);
+
+ const fooBucket = () => ({ name: 'foo', type: 'buckets' } as BucketAggType);
+ const fooMetric = () => ({ name: 'foo', type: 'metrics' } as MetricAggType);
+ expect(() => {
+ setup.registerMetric('foo', fooMetric);
+ setup.registerBucket('foo', fooBucket);
+ }).toThrow(/already been registered with name: foo/);
});
it('gets either buckets or metrics by id', () => {
- setup.registerBucket(bucketType);
- setup.registerMetric(metricType);
+ setup.registerBucket('terms', bucketType);
+ setup.registerMetric('count', metricType);
expect(start.get('terms')).toEqual(bucketType);
expect(start.get('count')).toEqual(metricType);
});
- it('getBuckets retrieves only buckets', () => {
- setup.registerBucket(bucketType);
- expect(start.getBuckets()).toEqual([bucketType]);
- });
-
- it('getMetrics retrieves only metrics', () => {
- setup.registerMetric(metricType);
- expect(start.getMetrics()).toEqual([metricType]);
- });
-
it('getAll returns all buckets and metrics', () => {
- setup.registerBucket(bucketType);
- setup.registerMetric(metricType);
+ setup.registerBucket('terms', bucketType);
+ setup.registerMetric('count', metricType);
expect(start.getAll()).toEqual({
buckets: [bucketType],
metrics: [metricType],
diff --git a/src/plugins/data/public/search/aggs/agg_types_registry.ts b/src/plugins/data/common/search/aggs/agg_types_registry.ts
similarity index 57%
rename from src/plugins/data/public/search/aggs/agg_types_registry.ts
rename to src/plugins/data/common/search/aggs/agg_types_registry.ts
index 5a0c58120d810..ce22fa840bafa 100644
--- a/src/plugins/data/public/search/aggs/agg_types_registry.ts
+++ b/src/plugins/data/common/search/aggs/agg_types_registry.ts
@@ -19,9 +19,21 @@
import { BucketAggType } from './buckets/bucket_agg_type';
import { MetricAggType } from './metrics/metric_agg_type';
+import { AggTypesDependencies } from './agg_types';
export type AggTypesRegistrySetup = ReturnType;
-export type AggTypesRegistryStart = ReturnType;
+/**
+ * AggsCommonStart returns the _unitialized_ agg type providers, but in our
+ * real start contract we will need to return the initialized versions.
+ * So we need to provide the correct typings so they can be overwritten
+ * on client/server.
+ *
+ * @internal
+ */
+export interface AggTypesRegistryStart {
+ get: (id: string) => BucketAggType | MetricAggType;
+ getAll: () => { buckets: Array>; metrics: Array> };
+}
export class AggTypesRegistry {
private readonly bucketAggs = new Map();
@@ -29,17 +41,27 @@ export class AggTypesRegistry {
setup = () => {
return {
- registerBucket: >(type: T): void => {
- const { name } = type;
- if (this.bucketAggs.get(name)) {
- throw new Error(`Bucket agg has already been registered with name: ${name}`);
+ registerBucket: <
+ N extends string,
+ T extends (deps: AggTypesDependencies) => BucketAggType
+ >(
+ name: N,
+ type: T
+ ): void => {
+ if (this.bucketAggs.get(name) || this.metricAggs.get(name)) {
+ throw new Error(`Agg has already been registered with name: ${name}`);
}
this.bucketAggs.set(name, type);
},
- registerMetric: >(type: T): void => {
- const { name } = type;
- if (this.metricAggs.get(name)) {
- throw new Error(`Metric agg has already been registered with name: ${name}`);
+ registerMetric: <
+ N extends string,
+ T extends (deps: AggTypesDependencies) => MetricAggType
+ >(
+ name: N,
+ type: T
+ ): void => {
+ if (this.bucketAggs.get(name) || this.metricAggs.get(name)) {
+ throw new Error(`Agg has already been registered with name: ${name}`);
}
this.metricAggs.set(name, type);
},
@@ -51,12 +73,6 @@ export class AggTypesRegistry {
get: (name: string) => {
return this.bucketAggs.get(name) || this.metricAggs.get(name);
},
- getBuckets: () => {
- return Array.from(this.bucketAggs.values());
- },
- getMetrics: () => {
- return Array.from(this.metricAggs.values());
- },
getAll: () => {
return {
buckets: Array.from(this.bucketAggs.values()),
diff --git a/src/plugins/data/common/search/aggs/aggs_service.test.ts b/src/plugins/data/common/search/aggs/aggs_service.test.ts
new file mode 100644
index 0000000000000..bcf2101704c80
--- /dev/null
+++ b/src/plugins/data/common/search/aggs/aggs_service.test.ts
@@ -0,0 +1,217 @@
+/*
+ * 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 {
+ AggsCommonService,
+ AggsCommonSetupDependencies,
+ AggsCommonStartDependencies,
+} from './aggs_service';
+import { AggTypesDependencies, getAggTypes } from './agg_types';
+import { BucketAggType } from './buckets/bucket_agg_type';
+import { MetricAggType } from './metrics/metric_agg_type';
+
+describe('Aggs service', () => {
+ let service: AggsCommonService;
+ let setupDeps: AggsCommonSetupDependencies;
+ let startDeps: AggsCommonStartDependencies;
+ const aggTypesDependencies: AggTypesDependencies = {
+ calculateBounds: jest.fn(),
+ getFieldFormatsStart: jest.fn(),
+ getConfig: jest.fn(),
+ isDefaultTimezone: () => true,
+ };
+
+ beforeEach(() => {
+ service = new AggsCommonService();
+ setupDeps = {
+ registerFunction: jest.fn(),
+ };
+ startDeps = {
+ getConfig: jest.fn(),
+ };
+ });
+
+ describe('setup()', () => {
+ test('exposes proper contract', () => {
+ const setup = service.setup(setupDeps);
+ expect(Object.keys(setup).length).toBe(1);
+ expect(setup).toHaveProperty('types');
+ });
+
+ test('instantiates a new registry', () => {
+ const a = new AggsCommonService();
+ const b = new AggsCommonService();
+ const bSetupDeps = {
+ registerFunction: jest.fn(),
+ };
+
+ const aSetup = a.setup(setupDeps);
+ aSetup.types.registerBucket(
+ 'foo',
+ () => ({ name: 'foo', type: 'buckets' } as BucketAggType)
+ );
+ const aStart = a.start(startDeps);
+ expect(aStart.types.getAll().buckets.map((t) => t(aggTypesDependencies).name))
+ .toMatchInlineSnapshot(`
+ Array [
+ "date_histogram",
+ "histogram",
+ "range",
+ "date_range",
+ "ip_range",
+ "terms",
+ "filter",
+ "filters",
+ "significant_terms",
+ "geohash_grid",
+ "geotile_grid",
+ "foo",
+ ]
+ `);
+ expect(aStart.types.getAll().metrics.map((t) => t(aggTypesDependencies).name))
+ .toMatchInlineSnapshot(`
+ Array [
+ "count",
+ "avg",
+ "sum",
+ "median",
+ "min",
+ "max",
+ "std_dev",
+ "cardinality",
+ "percentiles",
+ "percentile_ranks",
+ "top_hits",
+ "derivative",
+ "cumulative_sum",
+ "moving_avg",
+ "serial_diff",
+ "avg_bucket",
+ "sum_bucket",
+ "min_bucket",
+ "max_bucket",
+ "geo_bounds",
+ "geo_centroid",
+ ]
+ `);
+
+ b.setup(bSetupDeps);
+ const bStart = b.start(startDeps);
+ expect(bStart.types.getAll().buckets.map((t) => t(aggTypesDependencies).name))
+ .toMatchInlineSnapshot(`
+ Array [
+ "date_histogram",
+ "histogram",
+ "range",
+ "date_range",
+ "ip_range",
+ "terms",
+ "filter",
+ "filters",
+ "significant_terms",
+ "geohash_grid",
+ "geotile_grid",
+ ]
+ `);
+ expect(bStart.types.getAll().metrics.map((t) => t(aggTypesDependencies).name))
+ .toMatchInlineSnapshot(`
+ Array [
+ "count",
+ "avg",
+ "sum",
+ "median",
+ "min",
+ "max",
+ "std_dev",
+ "cardinality",
+ "percentiles",
+ "percentile_ranks",
+ "top_hits",
+ "derivative",
+ "cumulative_sum",
+ "moving_avg",
+ "serial_diff",
+ "avg_bucket",
+ "sum_bucket",
+ "min_bucket",
+ "max_bucket",
+ "geo_bounds",
+ "geo_centroid",
+ ]
+ `);
+ });
+
+ test('registers default agg types', () => {
+ service.setup(setupDeps);
+ const start = service.start(startDeps);
+
+ const aggTypes = getAggTypes();
+ expect(start.types.getAll().buckets.length).toBe(aggTypes.buckets.length);
+ expect(start.types.getAll().metrics.length).toBe(aggTypes.metrics.length);
+ });
+
+ test('merges default agg types with types registered during setup', () => {
+ const setup = service.setup(setupDeps);
+ setup.types.registerBucket(
+ 'foo',
+ () => ({ name: 'foo', type: 'buckets' } as BucketAggType)
+ );
+ setup.types.registerMetric(
+ 'bar',
+ () => ({ name: 'bar', type: 'metrics' } as MetricAggType)
+ );
+ const start = service.start(startDeps);
+
+ const aggTypes = getAggTypes();
+ expect(start.types.getAll().buckets.length).toBe(aggTypes.buckets.length + 1);
+ expect(start.types.getAll().buckets.some((t) => t(aggTypesDependencies).name === 'foo')).toBe(
+ true
+ );
+ expect(start.types.getAll().metrics.length).toBe(aggTypes.metrics.length + 1);
+ expect(start.types.getAll().metrics.some((t) => t(aggTypesDependencies).name === 'bar')).toBe(
+ true
+ );
+ });
+
+ test('registers all agg type expression functions', () => {
+ service.setup(setupDeps);
+ const aggTypes = getAggTypes();
+ expect(setupDeps.registerFunction).toHaveBeenCalledTimes(
+ aggTypes.buckets.length + aggTypes.metrics.length
+ );
+ });
+ });
+
+ describe('start()', () => {
+ test('exposes proper contract', () => {
+ const start = service.start(startDeps);
+ expect(Object.keys(start).length).toBe(3);
+ expect(start).toHaveProperty('calculateAutoTimeExpression');
+ expect(start).toHaveProperty('createAggConfigs');
+ expect(start).toHaveProperty('types');
+ });
+
+ test('types registry returns uninitialized type providers', () => {
+ service.setup(setupDeps);
+ const start = service.start(startDeps);
+ expect(typeof start.types.get('terms')).toBe('function');
+ expect(start.types.get('terms')(aggTypesDependencies).name).toBe('terms');
+ });
+ });
+});
diff --git a/src/plugins/data/common/search/aggs/aggs_service.ts b/src/plugins/data/common/search/aggs/aggs_service.ts
new file mode 100644
index 0000000000000..59c54fcce6838
--- /dev/null
+++ b/src/plugins/data/common/search/aggs/aggs_service.ts
@@ -0,0 +1,92 @@
+/*
+ * 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 { ExpressionsServiceSetup } from 'src/plugins/expressions/common';
+import { UI_SETTINGS } from '../../../common';
+import {
+ AggConfigs,
+ AggTypesRegistry,
+ getAggTypes,
+ getAggTypesFunctions,
+ getCalculateAutoTimeExpression,
+} from './';
+import { AggsCommonSetup, AggsCommonStart } from './types';
+
+/** @internal */
+export const aggsRequiredUiSettings = [
+ 'dateFormat',
+ 'dateFormat:scaled',
+ 'dateFormat:tz',
+ UI_SETTINGS.HISTOGRAM_BAR_TARGET,
+ UI_SETTINGS.HISTOGRAM_MAX_BARS,
+ UI_SETTINGS.SEARCH_QUERY_LANGUAGE,
+ UI_SETTINGS.QUERY_ALLOW_LEADING_WILDCARDS,
+ UI_SETTINGS.QUERY_STRING_OPTIONS,
+ UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX,
+];
+
+/** @internal */
+export interface AggsCommonSetupDependencies {
+ registerFunction: ExpressionsServiceSetup['registerFunction'];
+}
+
+/** @internal */
+export interface AggsCommonStartDependencies {
+ getConfig: (key: string) => T;
+}
+
+/**
+ * The aggs service provides a means of modeling and manipulating the various
+ * Elasticsearch aggregations supported by Kibana, providing the ability to
+ * output the correct DSL when you are ready to send your request to ES.
+ */
+export class AggsCommonService {
+ private readonly aggTypesRegistry = new AggTypesRegistry();
+
+ public setup({ registerFunction }: AggsCommonSetupDependencies): AggsCommonSetup {
+ const aggTypesSetup = this.aggTypesRegistry.setup();
+
+ // register each agg type
+ const aggTypes = getAggTypes();
+ aggTypes.buckets.forEach(({ name, fn }) => aggTypesSetup.registerBucket(name, fn));
+ aggTypes.metrics.forEach(({ name, fn }) => aggTypesSetup.registerMetric(name, fn));
+
+ // register expression functions for each agg type
+ const aggFunctions = getAggTypesFunctions();
+ aggFunctions.forEach((fn) => registerFunction(fn));
+
+ return {
+ types: aggTypesSetup,
+ };
+ }
+
+ public start({ getConfig }: AggsCommonStartDependencies): AggsCommonStart {
+ const aggTypesStart = this.aggTypesRegistry.start();
+
+ return {
+ calculateAutoTimeExpression: getCalculateAutoTimeExpression(getConfig),
+ createAggConfigs: (indexPattern, configStates = [], schemas) => {
+ return new AggConfigs(indexPattern, configStates, {
+ typesRegistry: aggTypesStart,
+ });
+ },
+ types: aggTypesStart,
+ };
+ }
+}
diff --git a/src/plugins/data/public/search/aggs/buckets/_interval_options.ts b/src/plugins/data/common/search/aggs/buckets/_interval_options.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/_interval_options.ts
rename to src/plugins/data/common/search/aggs/buckets/_interval_options.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.test.ts b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.test.ts
rename to src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.ts b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.ts
rename to src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/bucket_agg_type.ts b/src/plugins/data/common/search/aggs/buckets/bucket_agg_type.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/bucket_agg_type.ts
rename to src/plugins/data/common/search/aggs/buckets/bucket_agg_type.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/bucket_agg_types.ts b/src/plugins/data/common/search/aggs/buckets/bucket_agg_types.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/bucket_agg_types.ts
rename to src/plugins/data/common/search/aggs/buckets/bucket_agg_types.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/date_histogram.test.ts
similarity index 87%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/date_histogram.test.ts
index 24a17b60566cc..143d549836900 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts
+++ b/src/plugins/data/common/search/aggs/buckets/create_filter/date_histogram.test.ts
@@ -22,32 +22,17 @@ import { createFilterDateHistogram } from './date_histogram';
import { intervalOptions } from '../_interval_options';
import { AggConfigs } from '../../agg_configs';
import { mockAggTypesRegistry } from '../../test_helpers';
-import {
- getDateHistogramBucketAgg,
- DateHistogramBucketAggDependencies,
- IBucketDateHistogramAggConfig,
-} from '../date_histogram';
+import { IBucketDateHistogramAggConfig } from '../date_histogram';
import { BUCKET_TYPES } from '../bucket_agg_types';
import { RangeFilter } from '../../../../../common';
-import { coreMock } from '../../../../../../../core/public/mocks';
describe('AggConfig Filters', () => {
describe('date_histogram', () => {
- let aggTypesDependencies: DateHistogramBucketAggDependencies;
let agg: IBucketDateHistogramAggConfig;
let filter: RangeFilter;
let bucketStart: any;
let field: any;
- beforeEach(() => {
- const { uiSettings } = coreMock.createSetup();
-
- aggTypesDependencies = {
- calculateBounds: jest.fn(),
- uiSettings,
- };
- });
-
const init = (interval: string = 'auto', duration: any = moment.duration(15, 'minutes')) => {
field = {
name: 'date',
@@ -71,7 +56,7 @@ describe('AggConfig Filters', () => {
},
],
{
- typesRegistry: mockAggTypesRegistry([getDateHistogramBucketAgg(aggTypesDependencies)]),
+ typesRegistry: mockAggTypesRegistry(),
}
);
const bucketKey = 1422579600000;
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/date_histogram.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/date_histogram.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/date_range.test.ts
similarity index 78%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/date_range.test.ts
index c272c037c5927..8def27f1d8ee5 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts
+++ b/src/plugins/data/common/search/aggs/buckets/create_filter/date_range.test.ts
@@ -18,31 +18,18 @@
*/
import moment from 'moment';
-import { getDateRangeBucketAgg, DateRangeBucketAggDependencies } from '../date_range';
import { createFilterDateRange } from './date_range';
-import { FieldFormatsGetConfigFn } from '../../../../../common';
-import { DateFormat } from '../../../../field_formats';
import { AggConfigs } from '../../agg_configs';
import { mockAggTypesRegistry } from '../../test_helpers';
import { BUCKET_TYPES } from '../bucket_agg_types';
import { IBucketAggConfig } from '../bucket_agg_type';
-import { coreMock } from '../../../../../../../core/public/mocks';
describe('AggConfig Filters', () => {
describe('Date range', () => {
- let aggTypesDependencies: DateRangeBucketAggDependencies;
-
- beforeEach(() => {
- const { uiSettings } = coreMock.createSetup();
-
- aggTypesDependencies = { uiSettings };
- });
-
- const getConfig = (() => {}) as FieldFormatsGetConfigFn;
const getAggConfigs = () => {
const field = {
name: '@timestamp',
- format: new DateFormat({}, getConfig),
+ format: {},
};
const indexPattern = {
@@ -66,7 +53,7 @@ describe('AggConfig Filters', () => {
},
],
{
- typesRegistry: mockAggTypesRegistry([getDateRangeBucketAgg(aggTypesDependencies)]),
+ typesRegistry: mockAggTypesRegistry(),
}
);
};
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/date_range.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/date_range.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/date_range.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/filters.test.ts
similarity index 83%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/filters.test.ts
index ff66d80c6d8d0..aec99bd00af55 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts
+++ b/src/plugins/data/common/search/aggs/buckets/create_filter/filters.test.ts
@@ -17,23 +17,13 @@
* under the License.
*/
-import { getFiltersBucketAgg, FiltersBucketAggDependencies } from '../filters';
import { createFilterFilters } from './filters';
import { AggConfigs } from '../../agg_configs';
import { mockAggTypesRegistry } from '../../test_helpers';
import { IBucketAggConfig } from '../bucket_agg_type';
-import { coreMock } from '../../../../../../../core/public/mocks';
describe('AggConfig Filters', () => {
describe('filters', () => {
- let aggTypesDependencies: FiltersBucketAggDependencies;
-
- beforeEach(() => {
- const { uiSettings } = coreMock.createSetup();
-
- aggTypesDependencies = { uiSettings };
- });
-
const getAggConfigs = () => {
const field = {
name: 'bytes',
@@ -63,7 +53,7 @@ describe('AggConfig Filters', () => {
},
],
{
- typesRegistry: mockAggTypesRegistry([getFiltersBucketAgg(aggTypesDependencies)]),
+ typesRegistry: mockAggTypesRegistry(),
}
);
};
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/filters.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/filters.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/histogram.test.ts
similarity index 77%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/histogram.test.ts
index 3f9f5dd5672f0..b57d530ef40e8 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts
+++ b/src/plugins/data/common/search/aggs/buckets/create_filter/histogram.test.ts
@@ -17,25 +17,14 @@
* under the License.
*/
-import { createFilterHistogram } from './histogram';
+import { BytesFormat, FieldFormatsGetConfigFn } from '../../../../../common/field_formats';
import { AggConfigs } from '../../agg_configs';
-import { mockAggTypesRegistry } from '../../test_helpers';
+import { mockAggTypesRegistry, mockGetFieldFormatsStart } from '../../test_helpers';
import { BUCKET_TYPES } from '../bucket_agg_types';
import { IBucketAggConfig } from '../bucket_agg_type';
-import { BytesFormat, FieldFormatsGetConfigFn } from '../../../../../common';
-import { GetInternalStartServicesFn, InternalStartServices } from '../../../../types';
-import { FieldFormatsStart } from '../../../../field_formats';
-import { fieldFormatsServiceMock } from '../../../../field_formats/mocks';
+import { createFilterHistogram } from './histogram';
describe('AggConfig Filters', () => {
- let getInternalStartServices: GetInternalStartServicesFn;
- let fieldFormats: FieldFormatsStart;
-
- beforeEach(() => {
- fieldFormats = fieldFormatsServiceMock.createStartContract();
- getInternalStartServices = () => (({ fieldFormats } as unknown) as InternalStartServices);
- });
-
describe('histogram', () => {
const getConfig = (() => {}) as FieldFormatsGetConfigFn;
const getAggConfigs = () => {
@@ -72,12 +61,12 @@ describe('AggConfig Filters', () => {
test('should return an range filter for histogram', () => {
const aggConfigs = getAggConfigs();
- const filter = createFilterHistogram(getInternalStartServices)(
+ const filter = createFilterHistogram(mockGetFieldFormatsStart)(
aggConfigs.aggs[0] as IBucketAggConfig,
'2048'
);
- expect(fieldFormats.deserialize).toHaveBeenCalledTimes(1);
+ expect(mockGetFieldFormatsStart().deserialize).toHaveBeenCalledTimes(1);
expect(filter).toHaveProperty('meta');
expect(filter.meta).toHaveProperty('index', '1234');
expect(filter).toHaveProperty('range');
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/histogram.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/histogram.ts
similarity index 80%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/histogram.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/histogram.ts
index f3626bc9130ad..4684b1640cd82 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/histogram.ts
+++ b/src/plugins/data/common/search/aggs/buckets/create_filter/histogram.ts
@@ -17,14 +17,16 @@
* under the License.
*/
-import { IBucketAggConfig } from '../bucket_agg_type';
import { buildRangeFilter, RangeFilterParams } from '../../../../../common';
-import { GetInternalStartServicesFn } from '../../../../types';
+import { AggTypesDependencies } from '../../agg_types';
+import { IBucketAggConfig } from '../bucket_agg_type';
/** @internal */
-export const createFilterHistogram = (getInternalStartServices: GetInternalStartServicesFn) => {
+export const createFilterHistogram = (
+ getFieldFormatsStart: AggTypesDependencies['getFieldFormatsStart']
+) => {
return (aggConfig: IBucketAggConfig, key: string) => {
- const { fieldFormats } = getInternalStartServices();
+ const { deserialize } = getFieldFormatsStart();
const value = parseInt(key, 10);
const params: RangeFilterParams = { gte: value, lt: value + aggConfig.params.interval };
@@ -32,7 +34,7 @@ export const createFilterHistogram = (getInternalStartServices: GetInternalStart
aggConfig.params.field,
params,
aggConfig.getIndexPattern(),
- fieldFormats.deserialize(aggConfig.toSerializedFieldFormat()).convert(key)
+ deserialize(aggConfig.toSerializedFieldFormat()).convert(key)
);
};
};
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/ip_range.test.ts
similarity index 96%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/ip_range.test.ts
index 852685a505afd..9f823001aac8c 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts
+++ b/src/plugins/data/common/search/aggs/buckets/create_filter/ip_range.test.ts
@@ -17,7 +17,6 @@
* under the License.
*/
-import { getIpRangeBucketAgg } from '../ip_range';
import { createFilterIpRange } from './ip_range';
import { AggConfigs, CreateAggConfigParams } from '../../agg_configs';
import { mockAggTypesRegistry } from '../../test_helpers';
@@ -27,7 +26,7 @@ import { IBucketAggConfig } from '../bucket_agg_type';
describe('AggConfig Filters', () => {
describe('IP range', () => {
- const typesRegistry = mockAggTypesRegistry([getIpRangeBucketAgg()]);
+ const typesRegistry = mockAggTypesRegistry();
const getAggConfigs = (aggs: CreateAggConfigParams[]) => {
const field = {
name: 'ip',
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/ip_range.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/ip_range.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/range.test.ts
similarity index 73%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/range.test.ts
index faffad3beb8c1..30af970f55aa9 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts
+++ b/src/plugins/data/common/search/aggs/buckets/create_filter/range.test.ts
@@ -17,31 +17,15 @@
* under the License.
*/
-import { getRangeBucketAgg } from '../range';
-import { createFilterRange } from './range';
-import { BytesFormat, FieldFormatsGetConfigFn } from '../../../../../common';
+import { BytesFormat, FieldFormatsGetConfigFn } from '../../../../../common/field_formats';
import { AggConfigs } from '../../agg_configs';
-import { mockAggTypesRegistry } from '../../test_helpers';
-import { BUCKET_TYPES } from '../bucket_agg_types';
+import { mockAggTypesRegistry, mockGetFieldFormatsStart } from '../../test_helpers';
import { IBucketAggConfig } from '../bucket_agg_type';
-import { FieldFormatsStart } from '../../../../field_formats';
-import { fieldFormatsServiceMock } from '../../../../field_formats/mocks';
-import { GetInternalStartServicesFn, InternalStartServices } from '../../../../types';
+import { BUCKET_TYPES } from '../bucket_agg_types';
+import { createFilterRange } from './range';
describe('AggConfig Filters', () => {
describe('range', () => {
- let getInternalStartServices: GetInternalStartServicesFn;
- let fieldFormats: FieldFormatsStart;
-
- beforeEach(() => {
- fieldFormats = fieldFormatsServiceMock.createStartContract();
-
- getInternalStartServices = () =>
- (({
- fieldFormats,
- } as unknown) as InternalStartServices);
- });
-
const getConfig = (() => {}) as FieldFormatsGetConfigFn;
const getAggConfigs = () => {
const field = {
@@ -72,14 +56,14 @@ describe('AggConfig Filters', () => {
},
],
{
- typesRegistry: mockAggTypesRegistry([getRangeBucketAgg({ getInternalStartServices })]),
+ typesRegistry: mockAggTypesRegistry(),
}
);
};
test('should return a range filter for range agg', () => {
const aggConfigs = getAggConfigs();
- const filter = createFilterRange(getInternalStartServices)(
+ const filter = createFilterRange(mockGetFieldFormatsStart)(
aggConfigs.aggs[0] as IBucketAggConfig,
{
gte: 1024,
@@ -87,7 +71,7 @@ describe('AggConfig Filters', () => {
}
);
- expect(fieldFormats.deserialize).toHaveBeenCalledTimes(1);
+ expect(mockGetFieldFormatsStart().deserialize).toHaveBeenCalledTimes(1);
expect(filter).toHaveProperty('range');
expect(filter).toHaveProperty('meta');
expect(filter.meta).toHaveProperty('index', '1234');
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/range.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/range.ts
similarity index 78%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/range.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/range.ts
index f9db2973af136..8dea33a450c5d 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/range.ts
+++ b/src/plugins/data/common/search/aggs/buckets/create_filter/range.ts
@@ -17,19 +17,21 @@
* under the License.
*/
-import { IBucketAggConfig } from '../bucket_agg_type';
import { buildRangeFilter } from '../../../../../common';
-import { GetInternalStartServicesFn } from '../../../../types';
+import { AggTypesDependencies } from '../../agg_types';
+import { IBucketAggConfig } from '../bucket_agg_type';
/** @internal */
-export const createFilterRange = (getInternalStartServices: GetInternalStartServicesFn) => {
+export const createFilterRange = (
+ getFieldFormatsStart: AggTypesDependencies['getFieldFormatsStart']
+) => {
return (aggConfig: IBucketAggConfig, params: any) => {
- const { fieldFormats } = getInternalStartServices();
+ const { deserialize } = getFieldFormatsStart();
return buildRangeFilter(
aggConfig.params.field,
params,
aggConfig.getIndexPattern(),
- fieldFormats.deserialize(aggConfig.toSerializedFieldFormat()).convert(params)
+ deserialize(aggConfig.toSerializedFieldFormat()).convert(params)
);
};
};
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/terms.test.ts
similarity index 97%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/terms.test.ts
index 1c165f0d29ab6..c3c661296e1cf 100644
--- a/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts
+++ b/src/plugins/data/common/search/aggs/buckets/create_filter/terms.test.ts
@@ -17,7 +17,6 @@
* under the License.
*/
-import { getTermsBucketAgg } from '../terms';
import { createFilterTerms } from './terms';
import { AggConfigs, CreateAggConfigParams } from '../../agg_configs';
import { mockAggTypesRegistry } from '../../test_helpers';
@@ -43,7 +42,7 @@ describe('AggConfig Filters', () => {
};
return new AggConfigs(indexPattern, aggs, {
- typesRegistry: mockAggTypesRegistry([getTermsBucketAgg()]),
+ typesRegistry: mockAggTypesRegistry(),
});
};
diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/terms.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/terms.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/create_filter/terms.ts
rename to src/plugins/data/common/search/aggs/buckets/create_filter/terms.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/date_histogram.ts b/src/plugins/data/common/search/aggs/buckets/date_histogram.ts
similarity index 92%
rename from src/plugins/data/public/search/aggs/buckets/date_histogram.ts
rename to src/plugins/data/common/search/aggs/buckets/date_histogram.ts
index fa1725eccbd28..fdf9c456b3876 100644
--- a/src/plugins/data/public/search/aggs/buckets/date_histogram.ts
+++ b/src/plugins/data/common/search/aggs/buckets/date_histogram.ts
@@ -20,27 +20,23 @@
import { get, noop, find, every } from 'lodash';
import moment from 'moment-timezone';
import { i18n } from '@kbn/i18n';
-import { IUiSettingsClient } from 'src/core/public';
-import { TimeBuckets } from './lib/time_buckets';
+import { KBN_FIELD_TYPES, TimeRange, TimeRangeBounds, UI_SETTINGS } from '../../../../common';
+
+import { intervalOptions } from './_interval_options';
+import { createFilterDateHistogram } from './create_filter/date_histogram';
import { BucketAggType, IBucketAggConfig } from './bucket_agg_type';
import { BUCKET_TYPES } from './bucket_agg_types';
-import { createFilterDateHistogram } from './create_filter/date_histogram';
-import { intervalOptions } from './_interval_options';
+import { ExtendedBounds } from './lib/extended_bounds';
+import { TimeBuckets } from './lib/time_buckets';
+
import { writeParams } from '../agg_params';
import { isMetricAggType } from '../metrics/metric_agg_type';
-
-import {
- dateHistogramInterval,
- KBN_FIELD_TYPES,
- TimeRange,
- TimeRangeBounds,
- UI_SETTINGS,
-} from '../../../../common';
import { BaseAggParams } from '../types';
-import { ExtendedBounds } from './lib/extended_bounds';
+import { dateHistogramInterval } from '../utils';
-type CalculateBoundsFn = (timeRange: TimeRange) => TimeRangeBounds;
+/** @internal */
+export type CalculateBoundsFn = (timeRange: TimeRange) => TimeRangeBounds;
const updateTimeBuckets = (
agg: IBucketDateHistogramAggConfig,
@@ -58,7 +54,8 @@ const updateTimeBuckets = (
export interface DateHistogramBucketAggDependencies {
calculateBounds: CalculateBoundsFn;
- uiSettings: IUiSettingsClient;
+ isDefaultTimezone: () => boolean;
+ getConfig: (key: string) => T;
}
export interface IBucketDateHistogramAggConfig extends IBucketAggConfig {
@@ -84,7 +81,8 @@ export interface AggParamsDateHistogram extends BaseAggParams {
export const getDateHistogramBucketAgg = ({
calculateBounds,
- uiSettings,
+ isDefaultTimezone,
+ getConfig,
}: DateHistogramBucketAggDependencies) =>
new BucketAggType({
name: BUCKET_TYPES.DATE_HISTOGRAM,
@@ -122,10 +120,10 @@ export const getDateHistogramBucketAgg = ({
if (buckets) return buckets;
buckets = new TimeBuckets({
- 'histogram:maxBars': uiSettings.get(UI_SETTINGS.HISTOGRAM_MAX_BARS),
- 'histogram:barTarget': uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET),
- dateFormat: uiSettings.get('dateFormat'),
- 'dateFormat:scaled': uiSettings.get('dateFormat:scaled'),
+ 'histogram:maxBars': getConfig(UI_SETTINGS.HISTOGRAM_MAX_BARS),
+ 'histogram:barTarget': getConfig(UI_SETTINGS.HISTOGRAM_BAR_TARGET),
+ dateFormat: getConfig('dateFormat'),
+ 'dateFormat:scaled': getConfig('dateFormat:scaled'),
});
updateTimeBuckets(this, calculateBounds, buckets);
@@ -252,10 +250,9 @@ export const getDateHistogramBucketAgg = ({
}
if (!tz) {
// If the index pattern typeMeta data, didn't had a time zone assigned for the selected field use the configured tz
- const isDefaultTimezone = uiSettings.isDefault('dateFormat:tz');
const detectedTimezone = moment.tz.guess();
const tzOffset = moment().format('Z');
- tz = isDefaultTimezone ? detectedTimezone || tzOffset : uiSettings.get('dateFormat:tz');
+ tz = isDefaultTimezone() ? detectedTimezone || tzOffset : getConfig('dateFormat:tz');
}
output.params.time_zone = tz;
},
diff --git a/src/plugins/data/public/search/aggs/buckets/date_histogram_fn.test.ts b/src/plugins/data/common/search/aggs/buckets/date_histogram_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/date_histogram_fn.test.ts
rename to src/plugins/data/common/search/aggs/buckets/date_histogram_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/date_histogram_fn.ts b/src/plugins/data/common/search/aggs/buckets/date_histogram_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/date_histogram_fn.ts
rename to src/plugins/data/common/search/aggs/buckets/date_histogram_fn.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/date_range.test.ts b/src/plugins/data/common/search/aggs/buckets/date_range.test.ts
similarity index 84%
rename from src/plugins/data/public/search/aggs/buckets/date_range.test.ts
rename to src/plugins/data/common/search/aggs/buckets/date_range.test.ts
index 69515dfee87fe..66f8e269cd38d 100644
--- a/src/plugins/data/public/search/aggs/buckets/date_range.test.ts
+++ b/src/plugins/data/common/search/aggs/buckets/date_range.test.ts
@@ -17,19 +17,20 @@
* under the License.
*/
-import { coreMock } from '../../../../../../../src/core/public/mocks';
-import { getDateRangeBucketAgg, DateRangeBucketAggDependencies } from './date_range';
import { AggConfigs } from '../agg_configs';
-import { mockAggTypesRegistry } from '../test_helpers';
+import { AggTypesDependencies } from '../agg_types';
+import { mockAggTypesRegistry, mockAggTypesDependencies } from '../test_helpers';
import { BUCKET_TYPES } from './bucket_agg_types';
describe('date_range params', () => {
- let aggTypesDependencies: DateRangeBucketAggDependencies;
+ let aggTypesDependencies: AggTypesDependencies;
beforeEach(() => {
- const { uiSettings } = coreMock.createSetup();
-
- aggTypesDependencies = { uiSettings };
+ aggTypesDependencies = {
+ ...mockAggTypesDependencies,
+ getConfig: jest.fn(),
+ isDefaultTimezone: jest.fn().mockReturnValue(false),
+ };
});
const getAggConfigs = (params: Record = {}, hasIncludeTypeMeta: boolean = true) => {
@@ -68,7 +69,7 @@ describe('date_range params', () => {
},
],
{
- typesRegistry: mockAggTypesRegistry([getDateRangeBucketAgg(aggTypesDependencies)]),
+ typesRegistry: mockAggTypesRegistry(aggTypesDependencies),
}
);
};
@@ -108,10 +109,7 @@ describe('date_range params', () => {
test('should use the Kibana time_zone if no parameter specified', () => {
aggTypesDependencies = {
...aggTypesDependencies,
- uiSettings: {
- ...aggTypesDependencies.uiSettings,
- get: () => 'kibanaTimeZone' as any,
- },
+ getConfig: () => 'kibanaTimeZone' as any,
};
const aggConfigs = getAggConfigs(
diff --git a/src/plugins/data/public/search/aggs/buckets/date_range.ts b/src/plugins/data/common/search/aggs/buckets/date_range.ts
similarity index 88%
rename from src/plugins/data/public/search/aggs/buckets/date_range.ts
rename to src/plugins/data/common/search/aggs/buckets/date_range.ts
index 8c576023f0239..eda35a77afa5f 100644
--- a/src/plugins/data/public/search/aggs/buckets/date_range.ts
+++ b/src/plugins/data/common/search/aggs/buckets/date_range.ts
@@ -20,14 +20,13 @@
import { get } from 'lodash';
import moment from 'moment-timezone';
import { i18n } from '@kbn/i18n';
-import { IUiSettingsClient } from 'src/core/public';
import { BUCKET_TYPES } from './bucket_agg_types';
import { BucketAggType, IBucketAggConfig } from './bucket_agg_type';
import { createFilterDateRange } from './create_filter/date_range';
import { DateRangeKey } from './lib/date_range';
-import { KBN_FIELD_TYPES } from '../../../../common';
+import { KBN_FIELD_TYPES } from '../../../../common/kbn_field_types/types';
import { BaseAggParams } from '../types';
const dateRangeTitle = i18n.translate('data.search.aggs.buckets.dateRangeTitle', {
@@ -35,7 +34,8 @@ const dateRangeTitle = i18n.translate('data.search.aggs.buckets.dateRangeTitle',
});
export interface DateRangeBucketAggDependencies {
- uiSettings: IUiSettingsClient;
+ isDefaultTimezone: () => boolean;
+ getConfig: (key: string) => T;
}
export interface AggParamsDateRange extends BaseAggParams {
@@ -44,7 +44,10 @@ export interface AggParamsDateRange extends BaseAggParams {
time_zone?: string;
}
-export const getDateRangeBucketAgg = ({ uiSettings }: DateRangeBucketAggDependencies) =>
+export const getDateRangeBucketAgg = ({
+ isDefaultTimezone,
+ getConfig,
+}: DateRangeBucketAggDependencies) =>
new BucketAggType({
name: BUCKET_TYPES.DATE_RANGE,
title: dateRangeTitle,
@@ -100,9 +103,8 @@ export const getDateRangeBucketAgg = ({ uiSettings }: DateRangeBucketAggDependen
if (!tz) {
const detectedTimezone = moment.tz.guess();
const tzOffset = moment().format('Z');
- const isDefaultTimezone = uiSettings.isDefault('dateFormat:tz');
- tz = isDefaultTimezone ? detectedTimezone || tzOffset : uiSettings.get('dateFormat:tz');
+ tz = isDefaultTimezone() ? detectedTimezone || tzOffset : getConfig('dateFormat:tz');
}
output.params.time_zone = tz;
},
diff --git a/src/plugins/data/public/search/aggs/buckets/date_range_fn.test.ts b/src/plugins/data/common/search/aggs/buckets/date_range_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/date_range_fn.test.ts
rename to src/plugins/data/common/search/aggs/buckets/date_range_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/date_range_fn.ts b/src/plugins/data/common/search/aggs/buckets/date_range_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/date_range_fn.ts
rename to src/plugins/data/common/search/aggs/buckets/date_range_fn.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/filter.ts b/src/plugins/data/common/search/aggs/buckets/filter.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/filter.ts
rename to src/plugins/data/common/search/aggs/buckets/filter.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/filter_fn.test.ts b/src/plugins/data/common/search/aggs/buckets/filter_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/filter_fn.test.ts
rename to src/plugins/data/common/search/aggs/buckets/filter_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/filter_fn.ts b/src/plugins/data/common/search/aggs/buckets/filter_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/filter_fn.ts
rename to src/plugins/data/common/search/aggs/buckets/filter_fn.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/filters.test.ts b/src/plugins/data/common/search/aggs/buckets/filters.test.ts
similarity index 93%
rename from src/plugins/data/public/search/aggs/buckets/filters.test.ts
rename to src/plugins/data/common/search/aggs/buckets/filters.test.ts
index bcb82b5f99649..f745b4537131a 100644
--- a/src/plugins/data/public/search/aggs/buckets/filters.test.ts
+++ b/src/plugins/data/common/search/aggs/buckets/filters.test.ts
@@ -18,20 +18,20 @@
*/
import { Query } from '../../../../common';
-import { coreMock } from '../../../../../../../src/core/public/mocks';
import { AggConfigs } from '../agg_configs';
-import { mockAggTypesRegistry } from '../test_helpers';
+import { AggTypesDependencies } from '../agg_types';
+import { mockAggTypesRegistry, mockAggTypesDependencies } from '../test_helpers';
import { BUCKET_TYPES } from './bucket_agg_types';
-import { getFiltersBucketAgg, FiltersBucketAggDependencies } from './filters';
describe('Filters Agg', () => {
- let aggTypesDependencies: FiltersBucketAggDependencies;
+ let aggTypesDependencies: AggTypesDependencies;
beforeEach(() => {
jest.resetAllMocks();
- const { uiSettings } = coreMock.createSetup();
-
- aggTypesDependencies = { uiSettings };
+ aggTypesDependencies = {
+ ...mockAggTypesDependencies,
+ getConfig: jest.fn(),
+ };
});
describe('order agg editor UI', () => {
@@ -61,7 +61,7 @@ describe('Filters Agg', () => {
},
],
{
- typesRegistry: mockAggTypesRegistry([getFiltersBucketAgg(aggTypesDependencies)]),
+ typesRegistry: mockAggTypesRegistry(aggTypesDependencies),
}
);
};
@@ -218,7 +218,7 @@ describe('Filters Agg', () => {
});
test('works with leading wildcards if allowed', () => {
- aggTypesDependencies.uiSettings.get = (s: any) =>
+ aggTypesDependencies.getConfig = (s: any) =>
s === 'query:allowLeadingWildcards' ? true : s;
const aggConfigs = getAggConfigs({
diff --git a/src/plugins/data/public/search/aggs/buckets/filters.ts b/src/plugins/data/common/search/aggs/buckets/filters.ts
similarity index 90%
rename from src/plugins/data/public/search/aggs/buckets/filters.ts
rename to src/plugins/data/common/search/aggs/buckets/filters.ts
index cd4ed721fda77..7310fa08b68e0 100644
--- a/src/plugins/data/public/search/aggs/buckets/filters.ts
+++ b/src/plugins/data/common/search/aggs/buckets/filters.ts
@@ -19,7 +19,6 @@
import { i18n } from '@kbn/i18n';
import { size, transform, cloneDeep } from 'lodash';
-import { IUiSettingsClient } from 'src/core/public';
import { createFilterFilters } from './create_filter/filters';
import { toAngularJSON } from '../utils';
@@ -41,7 +40,7 @@ interface FilterValue {
}
export interface FiltersBucketAggDependencies {
- uiSettings: IUiSettingsClient;
+ getConfig: (key: string) => any;
}
export interface AggParamsFilters extends Omit {
@@ -51,7 +50,7 @@ export interface AggParamsFilters extends Omit {
}>;
}
-export const getFiltersBucketAgg = ({ uiSettings }: FiltersBucketAggDependencies) =>
+export const getFiltersBucketAgg = ({ getConfig }: FiltersBucketAggDependencies) =>
new BucketAggType({
name: BUCKET_TYPES.FILTERS,
title: filtersTitle,
@@ -60,9 +59,9 @@ export const getFiltersBucketAgg = ({ uiSettings }: FiltersBucketAggDependencies
params: [
{
name: 'filters',
- default: [
+ default: () => [
{
- input: { query: '', language: uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE) },
+ input: { query: '', language: getConfig(UI_SETTINGS.SEARCH_QUERY_LANGUAGE) },
label: '',
},
],
@@ -80,7 +79,7 @@ export const getFiltersBucketAgg = ({ uiSettings }: FiltersBucketAggDependencies
return;
}
- const esQueryConfigs = getEsQueryConfig(uiSettings);
+ const esQueryConfigs = getEsQueryConfig({ get: getConfig });
const query = buildEsQuery(aggConfig.getIndexPattern(), [input], [], esQueryConfigs);
if (!query) {
diff --git a/src/plugins/data/public/search/aggs/buckets/filters_fn.test.ts b/src/plugins/data/common/search/aggs/buckets/filters_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/filters_fn.test.ts
rename to src/plugins/data/common/search/aggs/buckets/filters_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/filters_fn.ts b/src/plugins/data/common/search/aggs/buckets/filters_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/filters_fn.ts
rename to src/plugins/data/common/search/aggs/buckets/filters_fn.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts b/src/plugins/data/common/search/aggs/buckets/geo_hash.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts
rename to src/plugins/data/common/search/aggs/buckets/geo_hash.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/geo_hash.ts b/src/plugins/data/common/search/aggs/buckets/geo_hash.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/geo_hash.ts
rename to src/plugins/data/common/search/aggs/buckets/geo_hash.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/geo_hash_fn.test.ts b/src/plugins/data/common/search/aggs/buckets/geo_hash_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/geo_hash_fn.test.ts
rename to src/plugins/data/common/search/aggs/buckets/geo_hash_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/geo_hash_fn.ts b/src/plugins/data/common/search/aggs/buckets/geo_hash_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/geo_hash_fn.ts
rename to src/plugins/data/common/search/aggs/buckets/geo_hash_fn.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/geo_tile.ts b/src/plugins/data/common/search/aggs/buckets/geo_tile.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/geo_tile.ts
rename to src/plugins/data/common/search/aggs/buckets/geo_tile.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/geo_tile_fn.test.ts b/src/plugins/data/common/search/aggs/buckets/geo_tile_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/geo_tile_fn.test.ts
rename to src/plugins/data/common/search/aggs/buckets/geo_tile_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/geo_tile_fn.ts b/src/plugins/data/common/search/aggs/buckets/geo_tile_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/geo_tile_fn.ts
rename to src/plugins/data/common/search/aggs/buckets/geo_tile_fn.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/histogram.test.ts b/src/plugins/data/common/search/aggs/buckets/histogram.test.ts
similarity index 90%
rename from src/plugins/data/public/search/aggs/buckets/histogram.test.ts
rename to src/plugins/data/common/search/aggs/buckets/histogram.test.ts
index 6ac77f207d9ce..3727747984d3e 100644
--- a/src/plugins/data/public/search/aggs/buckets/histogram.test.ts
+++ b/src/plugins/data/common/search/aggs/buckets/histogram.test.ts
@@ -17,33 +17,18 @@
* under the License.
*/
-import { coreMock } from '../../../../../../../src/core/public/mocks';
import { AggConfigs } from '../agg_configs';
-import { mockAggTypesRegistry } from '../test_helpers';
+import { mockAggTypesRegistry, mockAggTypesDependencies } from '../test_helpers';
+import { AggTypesDependencies } from '../agg_types';
import { BUCKET_TYPES } from './bucket_agg_types';
-import {
- IBucketHistogramAggConfig,
- getHistogramBucketAgg,
- AutoBounds,
- HistogramBucketAggDependencies,
-} from './histogram';
+import { IBucketHistogramAggConfig, getHistogramBucketAgg, AutoBounds } from './histogram';
import { BucketAggType } from './bucket_agg_type';
-import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
-import { InternalStartServices } from '../../../types';
describe('Histogram Agg', () => {
- let aggTypesDependencies: HistogramBucketAggDependencies;
+ let aggTypesDependencies: AggTypesDependencies;
beforeEach(() => {
- const { uiSettings } = coreMock.createSetup();
-
- aggTypesDependencies = {
- uiSettings,
- getInternalStartServices: () =>
- (({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- } as unknown) as InternalStartServices),
- };
+ aggTypesDependencies = { ...mockAggTypesDependencies };
});
const getAggConfigs = (params: Record) => {
@@ -72,7 +57,7 @@ describe('Histogram Agg', () => {
},
],
{
- typesRegistry: mockAggTypesRegistry([getHistogramBucketAgg(aggTypesDependencies)]),
+ typesRegistry: mockAggTypesRegistry(aggTypesDependencies),
}
);
};
@@ -167,10 +152,7 @@ describe('Histogram Agg', () => {
) => {
aggTypesDependencies = {
...aggTypesDependencies,
- uiSettings: {
- ...aggTypesDependencies.uiSettings,
- get: () => maxBars as any,
- },
+ getConfig: () => maxBars as any,
};
const aggConfigs = getAggConfigs({
diff --git a/src/plugins/data/public/search/aggs/buckets/histogram.ts b/src/plugins/data/common/search/aggs/buckets/histogram.ts
similarity index 93%
rename from src/plugins/data/public/search/aggs/buckets/histogram.ts
rename to src/plugins/data/common/search/aggs/buckets/histogram.ts
index 500b6eab75d77..2b263013e55a2 100644
--- a/src/plugins/data/public/search/aggs/buckets/histogram.ts
+++ b/src/plugins/data/common/search/aggs/buckets/histogram.ts
@@ -19,14 +19,14 @@
import { get } from 'lodash';
import { i18n } from '@kbn/i18n';
-import { IUiSettingsClient } from 'src/core/public';
+
+import { KBN_FIELD_TYPES, UI_SETTINGS } from '../../../../common';
+import { AggTypesDependencies } from '../agg_types';
+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 { KBN_FIELD_TYPES, UI_SETTINGS } from '../../../../common';
-import { GetInternalStartServicesFn } from '../../../types';
-import { BaseAggParams } from '../types';
import { ExtendedBounds } from './lib/extended_bounds';
export interface AutoBounds {
@@ -35,8 +35,8 @@ export interface AutoBounds {
}
export interface HistogramBucketAggDependencies {
- uiSettings: IUiSettingsClient;
- getInternalStartServices: GetInternalStartServicesFn;
+ getConfig: (key: string) => T;
+ getFieldFormatsStart: AggTypesDependencies['getFieldFormatsStart'];
}
export interface IBucketHistogramAggConfig extends IBucketAggConfig {
@@ -54,8 +54,8 @@ export interface AggParamsHistogram extends BaseAggParams {
}
export const getHistogramBucketAgg = ({
- uiSettings,
- getInternalStartServices,
+ getConfig,
+ getFieldFormatsStart,
}: HistogramBucketAggDependencies) =>
new BucketAggType({
name: BUCKET_TYPES.HISTOGRAM,
@@ -66,7 +66,7 @@ export const getHistogramBucketAgg = ({
makeLabel(aggConfig) {
return aggConfig.getFieldDisplayName();
},
- createFilter: createFilterHistogram(getInternalStartServices),
+ createFilter: createFilterHistogram(getFieldFormatsStart),
decorateAggConfig() {
let autoBounds: AutoBounds;
@@ -154,8 +154,8 @@ export const getHistogramBucketAgg = ({
const range = autoBounds.max - autoBounds.min;
const bars = range / interval;
- if (bars > uiSettings.get(UI_SETTINGS.HISTOGRAM_MAX_BARS)) {
- const minInterval = range / uiSettings.get(UI_SETTINGS.HISTOGRAM_MAX_BARS);
+ if (bars > getConfig(UI_SETTINGS.HISTOGRAM_MAX_BARS)) {
+ const minInterval = range / getConfig(UI_SETTINGS.HISTOGRAM_MAX_BARS);
// Round interval by order of magnitude to provide clean intervals
// Always round interval up so there will always be less buckets than histogram:maxBars
diff --git a/src/plugins/data/public/search/aggs/buckets/histogram_fn.test.ts b/src/plugins/data/common/search/aggs/buckets/histogram_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/histogram_fn.test.ts
rename to src/plugins/data/common/search/aggs/buckets/histogram_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/histogram_fn.ts b/src/plugins/data/common/search/aggs/buckets/histogram_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/histogram_fn.ts
rename to src/plugins/data/common/search/aggs/buckets/histogram_fn.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/index.ts b/src/plugins/data/common/search/aggs/buckets/index.ts
similarity index 97%
rename from src/plugins/data/public/search/aggs/buckets/index.ts
rename to src/plugins/data/common/search/aggs/buckets/index.ts
index 7036cc7785db7..b16242e519872 100644
--- a/src/plugins/data/public/search/aggs/buckets/index.ts
+++ b/src/plugins/data/common/search/aggs/buckets/index.ts
@@ -18,6 +18,7 @@
*/
export * from './_interval_options';
+export * from './bucket_agg_type';
export * from './bucket_agg_types';
export * from './histogram';
export * from './date_histogram';
diff --git a/src/plugins/data/public/search/aggs/buckets/ip_range.ts b/src/plugins/data/common/search/aggs/buckets/ip_range.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/ip_range.ts
rename to src/plugins/data/common/search/aggs/buckets/ip_range.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/ip_range_fn.test.ts b/src/plugins/data/common/search/aggs/buckets/ip_range_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/ip_range_fn.test.ts
rename to src/plugins/data/common/search/aggs/buckets/ip_range_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/ip_range_fn.ts b/src/plugins/data/common/search/aggs/buckets/ip_range_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/ip_range_fn.ts
rename to src/plugins/data/common/search/aggs/buckets/ip_range_fn.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/lib/cidr_mask.test.ts b/src/plugins/data/common/search/aggs/buckets/lib/cidr_mask.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/lib/cidr_mask.test.ts
rename to src/plugins/data/common/search/aggs/buckets/lib/cidr_mask.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/lib/cidr_mask.ts b/src/plugins/data/common/search/aggs/buckets/lib/cidr_mask.ts
similarity index 97%
rename from src/plugins/data/public/search/aggs/buckets/lib/cidr_mask.ts
rename to src/plugins/data/common/search/aggs/buckets/lib/cidr_mask.ts
index 57a7b378f305f..e4f6ab1e8da3c 100644
--- a/src/plugins/data/public/search/aggs/buckets/lib/cidr_mask.ts
+++ b/src/plugins/data/common/search/aggs/buckets/lib/cidr_mask.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { Ipv4Address } from '../../../../../common';
+import { Ipv4Address } from '../../utils';
const NUM_BITS = 32;
diff --git a/src/plugins/data/public/search/aggs/buckets/lib/date_range.ts b/src/plugins/data/common/search/aggs/buckets/lib/date_range.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/lib/date_range.ts
rename to src/plugins/data/common/search/aggs/buckets/lib/date_range.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/lib/extended_bounds.ts b/src/plugins/data/common/search/aggs/buckets/lib/extended_bounds.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/lib/extended_bounds.ts
rename to src/plugins/data/common/search/aggs/buckets/lib/extended_bounds.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/lib/geo_point.ts b/src/plugins/data/common/search/aggs/buckets/lib/geo_point.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/lib/geo_point.ts
rename to src/plugins/data/common/search/aggs/buckets/lib/geo_point.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/lib/ip_range.ts b/src/plugins/data/common/search/aggs/buckets/lib/ip_range.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/lib/ip_range.ts
rename to src/plugins/data/common/search/aggs/buckets/lib/ip_range.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/calc_auto_interval.test.ts b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/lib/time_buckets/calc_auto_interval.test.ts
rename to src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts
rename to src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts
similarity index 97%
rename from src/plugins/data/public/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts
rename to src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts
index 3e7d315a0a42a..0ef2c571ca8fa 100644
--- a/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts
+++ b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_es_interval.ts
@@ -20,7 +20,7 @@
import moment from 'moment';
import dateMath, { Unit } from '@elastic/datemath';
-import { parseEsInterval } from '../../../../../../common';
+import { parseEsInterval } from '../../../utils';
const unitsDesc = dateMath.unitsDesc;
const largeMax = unitsDesc.indexOf('M');
diff --git a/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/index.ts b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/index.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/lib/time_buckets/index.ts
rename to src/plugins/data/common/search/aggs/buckets/lib/time_buckets/index.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/time_buckets.test.ts b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/lib/time_buckets/time_buckets.test.ts
rename to src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/time_buckets.ts b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts
similarity index 99%
rename from src/plugins/data/public/search/aggs/buckets/lib/time_buckets/time_buckets.ts
rename to src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts
index 017f646258c01..6402a6e83ead9 100644
--- a/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/time_buckets.ts
+++ b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/time_buckets.ts
@@ -20,7 +20,7 @@
import { isString, isObject as isObjectLodash, isPlainObject, sortBy } from 'lodash';
import moment, { Moment } from 'moment';
-import { parseInterval } from '../../../../../../common';
+import { parseInterval } from '../../../utils';
import { TimeRangeBounds } from '../../../../../query';
import { calcAutoIntervalLessThan, calcAutoIntervalNear } from './calc_auto_interval';
import {
diff --git a/src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts b/src/plugins/data/common/search/aggs/buckets/migrate_include_exclude_format.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts
rename to src/plugins/data/common/search/aggs/buckets/migrate_include_exclude_format.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/range.test.ts b/src/plugins/data/common/search/aggs/buckets/range.test.ts
similarity index 79%
rename from src/plugins/data/public/search/aggs/buckets/range.test.ts
rename to src/plugins/data/common/search/aggs/buckets/range.test.ts
index f7c61a638158c..b23b03db6a9ec 100644
--- a/src/plugins/data/public/search/aggs/buckets/range.test.ts
+++ b/src/plugins/data/common/search/aggs/buckets/range.test.ts
@@ -17,26 +17,12 @@
* under the License.
*/
-import { getRangeBucketAgg, RangeBucketAggDependencies } from './range';
import { AggConfigs } from '../agg_configs';
import { mockAggTypesRegistry } from '../test_helpers';
import { BUCKET_TYPES } from './bucket_agg_types';
-import { FieldFormatsGetConfigFn, NumberFormat } from '../../../../common';
-import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
-import { InternalStartServices } from '../../../types';
+import { FieldFormatsGetConfigFn, NumberFormat } from '../../../../common/field_formats';
describe('Range Agg', () => {
- let aggTypesDependencies: RangeBucketAggDependencies;
-
- beforeEach(() => {
- aggTypesDependencies = {
- getInternalStartServices: () =>
- (({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- } as unknown) as InternalStartServices),
- };
- });
-
const getConfig = (() => {}) as FieldFormatsGetConfigFn;
const getAggConfigs = () => {
const field = {
@@ -74,7 +60,7 @@ describe('Range Agg', () => {
},
],
{
- typesRegistry: mockAggTypesRegistry([getRangeBucketAgg(aggTypesDependencies)]),
+ typesRegistry: mockAggTypesRegistry(),
}
);
};
diff --git a/src/plugins/data/public/search/aggs/buckets/range.ts b/src/plugins/data/common/search/aggs/buckets/range.ts
similarity index 90%
rename from src/plugins/data/public/search/aggs/buckets/range.ts
rename to src/plugins/data/common/search/aggs/buckets/range.ts
index 9f54f9fd0704e..91a357b635950 100644
--- a/src/plugins/data/public/search/aggs/buckets/range.ts
+++ b/src/plugins/data/common/search/aggs/buckets/range.ts
@@ -18,20 +18,22 @@
*/
import { i18n } from '@kbn/i18n';
-import { BucketAggType } from './bucket_agg_type';
+
import { KBN_FIELD_TYPES } from '../../../../common';
+import { AggTypesDependencies } from '../agg_types';
+import { BaseAggParams } from '../types';
+
+import { BucketAggType } from './bucket_agg_type';
import { RangeKey } from './range_key';
import { createFilterRange } from './create_filter/range';
import { BUCKET_TYPES } from './bucket_agg_types';
-import { GetInternalStartServicesFn } from '../../../types';
-import { BaseAggParams } from '../types';
const rangeTitle = i18n.translate('data.search.aggs.buckets.rangeTitle', {
defaultMessage: 'Range',
});
export interface RangeBucketAggDependencies {
- getInternalStartServices: GetInternalStartServicesFn;
+ getFieldFormatsStart: AggTypesDependencies['getFieldFormatsStart'];
}
export interface AggParamsRange extends BaseAggParams {
@@ -42,13 +44,13 @@ export interface AggParamsRange extends BaseAggParams {
}>;
}
-export const getRangeBucketAgg = ({ getInternalStartServices }: RangeBucketAggDependencies) => {
+export const getRangeBucketAgg = ({ getFieldFormatsStart }: RangeBucketAggDependencies) => {
const keyCaches = new WeakMap();
return new BucketAggType({
name: BUCKET_TYPES.RANGE,
title: rangeTitle,
- createFilter: createFilterRange(getInternalStartServices),
+ createFilter: createFilterRange(getFieldFormatsStart),
makeLabel(aggConfig) {
return i18n.translate('data.search.aggs.aggTypesLabel', {
defaultMessage: '{fieldName} ranges',
diff --git a/src/plugins/data/public/search/aggs/buckets/range_fn.test.ts b/src/plugins/data/common/search/aggs/buckets/range_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/range_fn.test.ts
rename to src/plugins/data/common/search/aggs/buckets/range_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/range_fn.ts b/src/plugins/data/common/search/aggs/buckets/range_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/range_fn.ts
rename to src/plugins/data/common/search/aggs/buckets/range_fn.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/range_key.ts b/src/plugins/data/common/search/aggs/buckets/range_key.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/range_key.ts
rename to src/plugins/data/common/search/aggs/buckets/range_key.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts b/src/plugins/data/common/search/aggs/buckets/significant_terms.test.ts
similarity index 95%
rename from src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts
rename to src/plugins/data/common/search/aggs/buckets/significant_terms.test.ts
index f13fafc2b17e6..e6c7bbee72a72 100644
--- a/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts
+++ b/src/plugins/data/common/search/aggs/buckets/significant_terms.test.ts
@@ -20,7 +20,6 @@
import { AggConfigs, IAggConfigs } from '../agg_configs';
import { mockAggTypesRegistry } from '../test_helpers';
import { BUCKET_TYPES } from './bucket_agg_types';
-import { getSignificantTermsBucketAgg } from './significant_terms';
describe('Significant Terms Agg', () => {
describe('order agg editor UI', () => {
@@ -51,7 +50,7 @@ describe('Significant Terms Agg', () => {
},
],
{
- typesRegistry: mockAggTypesRegistry([getSignificantTermsBucketAgg()]),
+ typesRegistry: mockAggTypesRegistry(),
}
);
};
diff --git a/src/plugins/data/public/search/aggs/buckets/significant_terms.ts b/src/plugins/data/common/search/aggs/buckets/significant_terms.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/significant_terms.ts
rename to src/plugins/data/common/search/aggs/buckets/significant_terms.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/significant_terms_fn.test.ts b/src/plugins/data/common/search/aggs/buckets/significant_terms_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/significant_terms_fn.test.ts
rename to src/plugins/data/common/search/aggs/buckets/significant_terms_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/significant_terms_fn.ts b/src/plugins/data/common/search/aggs/buckets/significant_terms_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/significant_terms_fn.ts
rename to src/plugins/data/common/search/aggs/buckets/significant_terms_fn.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/terms.test.ts b/src/plugins/data/common/search/aggs/buckets/terms.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/terms.test.ts
rename to src/plugins/data/common/search/aggs/buckets/terms.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/terms.ts b/src/plugins/data/common/search/aggs/buckets/terms.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/terms.ts
rename to src/plugins/data/common/search/aggs/buckets/terms.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts b/src/plugins/data/common/search/aggs/buckets/terms_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/terms_fn.test.ts
rename to src/plugins/data/common/search/aggs/buckets/terms_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/buckets/terms_fn.ts b/src/plugins/data/common/search/aggs/buckets/terms_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/buckets/terms_fn.ts
rename to src/plugins/data/common/search/aggs/buckets/terms_fn.ts
diff --git a/src/plugins/data/public/search/aggs/index.test.ts b/src/plugins/data/common/search/aggs/index.test.ts
similarity index 63%
rename from src/plugins/data/public/search/aggs/index.test.ts
rename to src/plugins/data/common/search/aggs/index.test.ts
index 73068326ca97e..1fdc28d1c7839 100644
--- a/src/plugins/data/public/search/aggs/index.test.ts
+++ b/src/plugins/data/common/search/aggs/index.test.ts
@@ -17,42 +17,34 @@
* under the License.
*/
-import { coreMock } from '../../../../../../src/core/public/mocks';
import { getAggTypes } from './index';
+import { mockGetFieldFormatsStart } from './test_helpers';
import { isBucketAggType } from './buckets/bucket_agg_type';
import { isMetricAggType } from './metrics/metric_agg_type';
-import { FieldFormatsStart } from '../../field_formats';
-import { InternalStartServices } from '../../types';
describe('AggTypesComponent', () => {
- const coreSetup = coreMock.createSetup();
- const coreStart = coreMock.createSetup();
-
- const aggTypes = getAggTypes({
- calculateBounds: jest.fn(),
- getInternalStartServices: () =>
- (({
- notifications: coreStart.notifications,
- fieldFormats: {} as FieldFormatsStart,
- } as unknown) as InternalStartServices),
- uiSettings: coreSetup.uiSettings,
- });
-
+ const aggTypes = getAggTypes();
const { buckets, metrics } = aggTypes;
+ const aggTypesDependencies = {
+ calculateBounds: jest.fn(),
+ getConfig: jest.fn(),
+ getFieldFormatsStart: mockGetFieldFormatsStart,
+ isDefaultTimezone: jest.fn().mockReturnValue(true),
+ };
describe('bucket aggs', () => {
test('all extend BucketAggType', () => {
- buckets.forEach((bucketAgg) => {
- expect(isBucketAggType(bucketAgg)).toBeTruthy();
+ buckets.forEach(({ fn }) => {
+ expect(isBucketAggType(fn(aggTypesDependencies))).toBeTruthy();
});
});
});
describe('metric aggs', () => {
test('all extend MetricAggType', () => {
- metrics.forEach((metricAgg) => {
- expect(isMetricAggType(metricAgg)).toBeTruthy();
+ metrics.forEach(({ fn }) => {
+ expect(isMetricAggType(fn(aggTypesDependencies))).toBeTruthy();
});
});
});
diff --git a/src/plugins/data/common/search/aggs/index.ts b/src/plugins/data/common/search/aggs/index.ts
index 7a5b7d509c940..1c4ae27e49530 100644
--- a/src/plugins/data/common/search/aggs/index.ts
+++ b/src/plugins/data/common/search/aggs/index.ts
@@ -17,5 +17,15 @@
* under the License.
*/
-export * from './date_interval_utils';
-export * from './ipv4_address';
+export * from './agg_config';
+export * from './agg_configs';
+export * from './agg_groups';
+export * from './agg_type';
+export * from './agg_types';
+export * from './agg_types_registry';
+export * from './aggs_service';
+export * from './buckets';
+export * from './metrics';
+export * from './param_types';
+export * from './types';
+export * from './utils';
diff --git a/src/plugins/data/public/search/aggs/metrics/avg.ts b/src/plugins/data/common/search/aggs/metrics/avg.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/avg.ts
rename to src/plugins/data/common/search/aggs/metrics/avg.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/avg_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/avg_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/avg_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/avg_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/avg_fn.ts b/src/plugins/data/common/search/aggs/metrics/avg_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/avg_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/avg_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_avg.ts b/src/plugins/data/common/search/aggs/metrics/bucket_avg.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/bucket_avg.ts
rename to src/plugins/data/common/search/aggs/metrics/bucket_avg.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/bucket_avg_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/bucket_avg_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.ts b/src/plugins/data/common/search/aggs/metrics/bucket_avg_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/bucket_avg_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/bucket_avg_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_max.ts b/src/plugins/data/common/search/aggs/metrics/bucket_max.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/bucket_max.ts
rename to src/plugins/data/common/search/aggs/metrics/bucket_max.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/bucket_max_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/bucket_max_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/bucket_max_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_max_fn.ts b/src/plugins/data/common/search/aggs/metrics/bucket_max_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/bucket_max_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/bucket_max_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_min.ts b/src/plugins/data/common/search/aggs/metrics/bucket_min.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/bucket_min.ts
rename to src/plugins/data/common/search/aggs/metrics/bucket_min.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/bucket_min_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/bucket_min_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/bucket_min_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_min_fn.ts b/src/plugins/data/common/search/aggs/metrics/bucket_min_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/bucket_min_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/bucket_min_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_sum.ts b/src/plugins/data/common/search/aggs/metrics/bucket_sum.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/bucket_sum.ts
rename to src/plugins/data/common/search/aggs/metrics/bucket_sum.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/bucket_sum_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/bucket_sum_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.ts b/src/plugins/data/common/search/aggs/metrics/bucket_sum_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/bucket_sum_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/bucket_sum_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/cardinality.ts b/src/plugins/data/common/search/aggs/metrics/cardinality.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/cardinality.ts
rename to src/plugins/data/common/search/aggs/metrics/cardinality.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/cardinality_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/cardinality_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/cardinality_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/cardinality_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/cardinality_fn.ts b/src/plugins/data/common/search/aggs/metrics/cardinality_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/cardinality_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/cardinality_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/count.ts b/src/plugins/data/common/search/aggs/metrics/count.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/count.ts
rename to src/plugins/data/common/search/aggs/metrics/count.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/count_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/count_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/count_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/count_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/count_fn.ts b/src/plugins/data/common/search/aggs/metrics/count_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/count_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/count_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts b/src/plugins/data/common/search/aggs/metrics/cumulative_sum.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts
rename to src/plugins/data/common/search/aggs/metrics/cumulative_sum.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/cumulative_sum_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/cumulative_sum_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.ts b/src/plugins/data/common/search/aggs/metrics/cumulative_sum_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/cumulative_sum_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/cumulative_sum_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/derivative.ts b/src/plugins/data/common/search/aggs/metrics/derivative.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/derivative.ts
rename to src/plugins/data/common/search/aggs/metrics/derivative.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/derivative_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/derivative_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/derivative_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/derivative_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/derivative_fn.ts b/src/plugins/data/common/search/aggs/metrics/derivative_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/derivative_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/derivative_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/geo_bounds.ts b/src/plugins/data/common/search/aggs/metrics/geo_bounds.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/geo_bounds.ts
rename to src/plugins/data/common/search/aggs/metrics/geo_bounds.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/geo_bounds_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/geo_bounds_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.ts b/src/plugins/data/common/search/aggs/metrics/geo_bounds_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/geo_bounds_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/geo_bounds_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/geo_centroid.ts b/src/plugins/data/common/search/aggs/metrics/geo_centroid.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/geo_centroid.ts
rename to src/plugins/data/common/search/aggs/metrics/geo_centroid.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/geo_centroid_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/geo_centroid_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.ts b/src/plugins/data/common/search/aggs/metrics/geo_centroid_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/geo_centroid_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/geo_centroid_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/index.ts b/src/plugins/data/common/search/aggs/metrics/index.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/index.ts
rename to src/plugins/data/common/search/aggs/metrics/index.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/lib/get_response_agg_config_class.ts b/src/plugins/data/common/search/aggs/metrics/lib/get_response_agg_config_class.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/lib/get_response_agg_config_class.ts
rename to src/plugins/data/common/search/aggs/metrics/lib/get_response_agg_config_class.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/lib/make_nested_label.test.ts b/src/plugins/data/common/search/aggs/metrics/lib/make_nested_label.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/lib/make_nested_label.test.ts
rename to src/plugins/data/common/search/aggs/metrics/lib/make_nested_label.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/lib/make_nested_label.ts b/src/plugins/data/common/search/aggs/metrics/lib/make_nested_label.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/lib/make_nested_label.ts
rename to src/plugins/data/common/search/aggs/metrics/lib/make_nested_label.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/lib/nested_agg_helpers.ts b/src/plugins/data/common/search/aggs/metrics/lib/nested_agg_helpers.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/lib/nested_agg_helpers.ts
rename to src/plugins/data/common/search/aggs/metrics/lib/nested_agg_helpers.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/lib/ordinal_suffix.test.ts b/src/plugins/data/common/search/aggs/metrics/lib/ordinal_suffix.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/lib/ordinal_suffix.test.ts
rename to src/plugins/data/common/search/aggs/metrics/lib/ordinal_suffix.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/lib/ordinal_suffix.ts b/src/plugins/data/common/search/aggs/metrics/lib/ordinal_suffix.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/lib/ordinal_suffix.ts
rename to src/plugins/data/common/search/aggs/metrics/lib/ordinal_suffix.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts b/src/plugins/data/common/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts
rename to src/plugins/data/common/search/aggs/metrics/lib/parent_pipeline_agg_helper.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_writer.ts b/src/plugins/data/common/search/aggs/metrics/lib/parent_pipeline_agg_writer.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/lib/parent_pipeline_agg_writer.ts
rename to src/plugins/data/common/search/aggs/metrics/lib/parent_pipeline_agg_writer.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts b/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts
rename to src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_helper.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts b/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts
rename to src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/max.ts b/src/plugins/data/common/search/aggs/metrics/max.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/max.ts
rename to src/plugins/data/common/search/aggs/metrics/max.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/max_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/max_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/max_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/max_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/max_fn.ts b/src/plugins/data/common/search/aggs/metrics/max_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/max_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/max_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/median.test.ts b/src/plugins/data/common/search/aggs/metrics/median.test.ts
similarity index 94%
rename from src/plugins/data/public/search/aggs/metrics/median.test.ts
rename to src/plugins/data/common/search/aggs/metrics/median.test.ts
index 22d907330e2a3..f3f2d157ebafc 100644
--- a/src/plugins/data/public/search/aggs/metrics/median.test.ts
+++ b/src/plugins/data/common/search/aggs/metrics/median.test.ts
@@ -17,7 +17,6 @@
* under the License.
*/
-import { getMedianMetricAgg } from './median';
import { AggConfigs, IAggConfigs } from '../agg_configs';
import { mockAggTypesRegistry } from '../test_helpers';
import { METRIC_TYPES } from './metric_agg_types';
@@ -26,7 +25,7 @@ describe('AggTypeMetricMedianProvider class', () => {
let aggConfigs: IAggConfigs;
beforeEach(() => {
- const typesRegistry = mockAggTypesRegistry([getMedianMetricAgg()]);
+ const typesRegistry = mockAggTypesRegistry();
const field = {
name: 'bytes',
};
diff --git a/src/plugins/data/public/search/aggs/metrics/median.ts b/src/plugins/data/common/search/aggs/metrics/median.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/median.ts
rename to src/plugins/data/common/search/aggs/metrics/median.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/median_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/median_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/median_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/median_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/median_fn.ts b/src/plugins/data/common/search/aggs/metrics/median_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/median_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/median_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/metric_agg_type.ts b/src/plugins/data/common/search/aggs/metrics/metric_agg_type.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/metric_agg_type.ts
rename to src/plugins/data/common/search/aggs/metrics/metric_agg_type.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/metric_agg_types.ts b/src/plugins/data/common/search/aggs/metrics/metric_agg_types.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/metric_agg_types.ts
rename to src/plugins/data/common/search/aggs/metrics/metric_agg_types.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/min.ts b/src/plugins/data/common/search/aggs/metrics/min.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/min.ts
rename to src/plugins/data/common/search/aggs/metrics/min.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/min_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/min_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/min_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/min_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/min_fn.ts b/src/plugins/data/common/search/aggs/metrics/min_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/min_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/min_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/moving_avg.ts b/src/plugins/data/common/search/aggs/metrics/moving_avg.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/moving_avg.ts
rename to src/plugins/data/common/search/aggs/metrics/moving_avg.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/moving_avg_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/moving_avg_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/moving_avg_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/moving_avg_fn.ts b/src/plugins/data/common/search/aggs/metrics/moving_avg_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/moving_avg_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/moving_avg_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts b/src/plugins/data/common/search/aggs/metrics/parent_pipeline.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts
rename to src/plugins/data/common/search/aggs/metrics/parent_pipeline.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts b/src/plugins/data/common/search/aggs/metrics/percentile_ranks.test.ts
similarity index 77%
rename from src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts
rename to src/plugins/data/common/search/aggs/metrics/percentile_ranks.test.ts
index 348aecc23243a..970daf5b62458 100644
--- a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts
+++ b/src/plugins/data/common/search/aggs/metrics/percentile_ranks.test.ts
@@ -23,29 +23,16 @@ import {
PercentileRanksMetricAggDependencies,
} from './percentile_ranks';
import { AggConfigs, IAggConfigs } from '../agg_configs';
-import { mockAggTypesRegistry } from '../test_helpers';
+import { mockAggTypesRegistry, mockGetFieldFormatsStart } from '../test_helpers';
import { METRIC_TYPES } from './metric_agg_types';
-import { FieldFormatsStart } from '../../../field_formats';
-import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
-import { InternalStartServices } from '../../../types';
describe('AggTypesMetricsPercentileRanksProvider class', function () {
let aggConfigs: IAggConfigs;
- let fieldFormats: FieldFormatsStart;
let aggTypesDependencies: PercentileRanksMetricAggDependencies;
beforeEach(() => {
- fieldFormats = fieldFormatsServiceMock.createStartContract();
- fieldFormats.getDefaultInstance = (() => ({
- convert: (t?: string) => t,
- })) as any;
- aggTypesDependencies = {
- getInternalStartServices: () =>
- (({
- fieldFormats,
- } as unknown) as InternalStartServices),
- };
- const typesRegistry = mockAggTypesRegistry([getPercentileRanksMetricAgg(aggTypesDependencies)]);
+ aggTypesDependencies = { getFieldFormatsStart: mockGetFieldFormatsStart };
+ const typesRegistry = mockAggTypesRegistry();
const field = {
name: 'bytes',
};
diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts b/src/plugins/data/common/search/aggs/metrics/percentile_ranks.ts
similarity index 86%
rename from src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts
rename to src/plugins/data/common/search/aggs/metrics/percentile_ranks.ts
index 3c0be229f1bbd..664cc1ad02ada 100644
--- a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts
+++ b/src/plugins/data/common/search/aggs/metrics/percentile_ranks.ts
@@ -18,13 +18,15 @@
*/
import { i18n } from '@kbn/i18n';
+
+import { KBN_FIELD_TYPES } from '../../../../common';
+import { AggTypesDependencies } from '../agg_types';
+import { BaseAggParams } from '../types';
+
import { MetricAggType } from './metric_agg_type';
import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_response_agg_config_class';
import { getPercentileValue } from './percentiles_get_value';
import { METRIC_TYPES } from './metric_agg_types';
-import { KBN_FIELD_TYPES } from '../../../../common';
-import { GetInternalStartServicesFn } from '../../../types';
-import { BaseAggParams } from '../types';
export interface AggParamsPercentileRanks extends BaseAggParams {
field: string;
@@ -35,16 +37,17 @@ export interface AggParamsPercentileRanks extends BaseAggParams {
export type IPercentileRanksAggConfig = IResponseAggConfig;
export interface PercentileRanksMetricAggDependencies {
- getInternalStartServices: GetInternalStartServicesFn;
+ getFieldFormatsStart: AggTypesDependencies['getFieldFormatsStart'];
}
-const getValueProps = (getInternalStartServices: GetInternalStartServicesFn) => {
+const getValueProps = (
+ getFieldFormatsStart: PercentileRanksMetricAggDependencies['getFieldFormatsStart']
+) => {
return {
makeLabel(this: IPercentileRanksAggConfig) {
- const { fieldFormats } = getInternalStartServices();
+ const { getDefaultInstance } = getFieldFormatsStart();
const field = this.getField();
- const format =
- (field && field.format) || fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.NUMBER);
+ const format = (field && field.format) || getDefaultInstance(KBN_FIELD_TYPES.NUMBER);
const customLabel = this.getParam('customLabel');
const label = customLabel || this.getFieldDisplayName();
@@ -57,7 +60,7 @@ const getValueProps = (getInternalStartServices: GetInternalStartServicesFn) =>
};
export const getPercentileRanksMetricAgg = ({
- getInternalStartServices,
+ getFieldFormatsStart,
}: PercentileRanksMetricAggDependencies) => {
return new MetricAggType({
name: METRIC_TYPES.PERCENTILE_RANKS,
@@ -87,10 +90,7 @@ export const getPercentileRanksMetricAgg = ({
},
],
getResponseAggs(agg) {
- const ValueAggConfig = getResponseAggConfigClass(
- agg,
- getValueProps(getInternalStartServices)
- );
+ const ValueAggConfig = getResponseAggConfigClass(agg, getValueProps(getFieldFormatsStart));
const values = agg.getParam('values');
return values.map((value: any) => new ValueAggConfig(value));
diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/percentile_ranks_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/percentile_ranks_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.ts b/src/plugins/data/common/search/aggs/metrics/percentile_ranks_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/percentile_ranks_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/percentile_ranks_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts b/src/plugins/data/common/search/aggs/metrics/percentiles.test.ts
similarity index 96%
rename from src/plugins/data/public/search/aggs/metrics/percentiles.test.ts
rename to src/plugins/data/common/search/aggs/metrics/percentiles.test.ts
index a44c0e5075ef9..10e98df5a4eeb 100644
--- a/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts
+++ b/src/plugins/data/common/search/aggs/metrics/percentiles.test.ts
@@ -26,7 +26,7 @@ describe('AggTypesMetricsPercentilesProvider class', () => {
let aggConfigs: IAggConfigs;
beforeEach(() => {
- const typesRegistry = mockAggTypesRegistry([getPercentilesMetricAgg()]);
+ const typesRegistry = mockAggTypesRegistry();
const field = {
name: 'bytes',
};
diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles.ts b/src/plugins/data/common/search/aggs/metrics/percentiles.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/percentiles.ts
rename to src/plugins/data/common/search/aggs/metrics/percentiles.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/percentiles_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/percentiles_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/percentiles_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles_fn.ts b/src/plugins/data/common/search/aggs/metrics/percentiles_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/percentiles_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/percentiles_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles_get_value.ts b/src/plugins/data/common/search/aggs/metrics/percentiles_get_value.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/percentiles_get_value.ts
rename to src/plugins/data/common/search/aggs/metrics/percentiles_get_value.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/serial_diff.ts b/src/plugins/data/common/search/aggs/metrics/serial_diff.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/serial_diff.ts
rename to src/plugins/data/common/search/aggs/metrics/serial_diff.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/serial_diff_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/serial_diff_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/serial_diff_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/serial_diff_fn.ts b/src/plugins/data/common/search/aggs/metrics/serial_diff_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/serial_diff_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/serial_diff_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts b/src/plugins/data/common/search/aggs/metrics/sibling_pipeline.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts
rename to src/plugins/data/common/search/aggs/metrics/sibling_pipeline.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts b/src/plugins/data/common/search/aggs/metrics/std_deviation.test.ts
similarity index 97%
rename from src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts
rename to src/plugins/data/common/search/aggs/metrics/std_deviation.test.ts
index c3efe95f44a56..f2f30fcde42eb 100644
--- a/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts
+++ b/src/plugins/data/common/search/aggs/metrics/std_deviation.test.ts
@@ -23,7 +23,7 @@ import { mockAggTypesRegistry } from '../test_helpers';
import { METRIC_TYPES } from './metric_agg_types';
describe('AggTypeMetricStandardDeviationProvider class', () => {
- const typesRegistry = mockAggTypesRegistry([getStdDeviationMetricAgg()]);
+ const typesRegistry = mockAggTypesRegistry();
const getAggConfigs = (customLabel?: string) => {
const field = {
name: 'memory',
diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation.ts b/src/plugins/data/common/search/aggs/metrics/std_deviation.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/std_deviation.ts
rename to src/plugins/data/common/search/aggs/metrics/std_deviation.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/std_deviation_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/std_deviation_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/std_deviation_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation_fn.ts b/src/plugins/data/common/search/aggs/metrics/std_deviation_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/std_deviation_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/std_deviation_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/sum.ts b/src/plugins/data/common/search/aggs/metrics/sum.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/sum.ts
rename to src/plugins/data/common/search/aggs/metrics/sum.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/sum_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/sum_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/sum_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/sum_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/sum_fn.ts b/src/plugins/data/common/search/aggs/metrics/sum_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/sum_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/sum_fn.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts b/src/plugins/data/common/search/aggs/metrics/top_hit.test.ts
similarity index 99%
rename from src/plugins/data/public/search/aggs/metrics/top_hit.test.ts
rename to src/plugins/data/common/search/aggs/metrics/top_hit.test.ts
index c2434df3ae53c..c0cbfb33c842b 100644
--- a/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts
+++ b/src/plugins/data/common/search/aggs/metrics/top_hit.test.ts
@@ -36,7 +36,7 @@ describe('Top hit metric', () => {
fieldType = KBN_FIELD_TYPES.NUMBER,
size = 1,
}: any) => {
- const typesRegistry = mockAggTypesRegistry([getTopHitMetricAgg()]);
+ const typesRegistry = mockAggTypesRegistry();
const field = {
name: fieldName,
displayName: fieldName,
diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit.ts b/src/plugins/data/common/search/aggs/metrics/top_hit.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/top_hit.ts
rename to src/plugins/data/common/search/aggs/metrics/top_hit.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit_fn.test.ts b/src/plugins/data/common/search/aggs/metrics/top_hit_fn.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/top_hit_fn.test.ts
rename to src/plugins/data/common/search/aggs/metrics/top_hit_fn.test.ts
diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit_fn.ts b/src/plugins/data/common/search/aggs/metrics/top_hit_fn.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/metrics/top_hit_fn.ts
rename to src/plugins/data/common/search/aggs/metrics/top_hit_fn.ts
diff --git a/src/plugins/data/public/search/aggs/param_types/agg.ts b/src/plugins/data/common/search/aggs/param_types/agg.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/param_types/agg.ts
rename to src/plugins/data/common/search/aggs/param_types/agg.ts
diff --git a/src/plugins/data/public/search/aggs/param_types/base.ts b/src/plugins/data/common/search/aggs/param_types/base.ts
similarity index 96%
rename from src/plugins/data/public/search/aggs/param_types/base.ts
rename to src/plugins/data/common/search/aggs/param_types/base.ts
index 1ba8a75e98cbe..3a12a9a54500f 100644
--- a/src/plugins/data/public/search/aggs/param_types/base.ts
+++ b/src/plugins/data/common/search/aggs/param_types/base.ts
@@ -17,11 +17,10 @@
* under the License.
*/
+import { FetchOptions, ISearchSource } from 'src/plugins/data/public';
import { ExpressionAstFunction } from 'src/plugins/expressions/common';
import { IAggConfigs } from '../agg_configs';
import { IAggConfig } from '../agg_config';
-import { FetchOptions } from '../../fetch';
-import { ISearchSource } from '../../search_source';
export class BaseParamType {
name: string;
diff --git a/src/plugins/data/public/search/aggs/param_types/field.test.ts b/src/plugins/data/common/search/aggs/param_types/field.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/param_types/field.test.ts
rename to src/plugins/data/common/search/aggs/param_types/field.test.ts
diff --git a/src/plugins/data/public/search/aggs/param_types/field.ts b/src/plugins/data/common/search/aggs/param_types/field.ts
similarity index 96%
rename from src/plugins/data/public/search/aggs/param_types/field.ts
rename to src/plugins/data/common/search/aggs/param_types/field.ts
index 7c00bc668a39f..492294bdf4e5f 100644
--- a/src/plugins/data/public/search/aggs/param_types/field.ts
+++ b/src/plugins/data/common/search/aggs/param_types/field.ts
@@ -22,8 +22,8 @@ import { IAggConfig } from '../agg_config';
import { SavedObjectNotFound } from '../../../../../../plugins/kibana_utils/common';
import { BaseParamType } from './base';
import { propFilter } from '../utils';
-import { isNestedField, KBN_FIELD_TYPES } from '../../../../common';
-import { IndexPatternField } from '../../../index_patterns';
+import { KBN_FIELD_TYPES } from '../../../kbn_field_types/types';
+import { isNestedField, IndexPatternField } from '../../../index_patterns/fields';
const filterByType = propFilter('type');
diff --git a/src/plugins/data/public/search/aggs/param_types/index.ts b/src/plugins/data/common/search/aggs/param_types/index.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/param_types/index.ts
rename to src/plugins/data/common/search/aggs/param_types/index.ts
diff --git a/src/plugins/data/public/search/aggs/param_types/json.test.ts b/src/plugins/data/common/search/aggs/param_types/json.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/param_types/json.test.ts
rename to src/plugins/data/common/search/aggs/param_types/json.test.ts
diff --git a/src/plugins/data/public/search/aggs/param_types/json.ts b/src/plugins/data/common/search/aggs/param_types/json.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/param_types/json.ts
rename to src/plugins/data/common/search/aggs/param_types/json.ts
diff --git a/src/plugins/data/public/search/aggs/param_types/optioned.test.ts b/src/plugins/data/common/search/aggs/param_types/optioned.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/param_types/optioned.test.ts
rename to src/plugins/data/common/search/aggs/param_types/optioned.test.ts
diff --git a/src/plugins/data/public/search/aggs/param_types/optioned.ts b/src/plugins/data/common/search/aggs/param_types/optioned.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/param_types/optioned.ts
rename to src/plugins/data/common/search/aggs/param_types/optioned.ts
diff --git a/src/plugins/data/public/search/aggs/param_types/string.test.ts b/src/plugins/data/common/search/aggs/param_types/string.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/param_types/string.test.ts
rename to src/plugins/data/common/search/aggs/param_types/string.test.ts
diff --git a/src/plugins/data/public/search/aggs/param_types/string.ts b/src/plugins/data/common/search/aggs/param_types/string.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/param_types/string.ts
rename to src/plugins/data/common/search/aggs/param_types/string.ts
diff --git a/src/plugins/data/public/search/aggs/test_helpers/function_wrapper.ts b/src/plugins/data/common/search/aggs/test_helpers/function_wrapper.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/test_helpers/function_wrapper.ts
rename to src/plugins/data/common/search/aggs/test_helpers/function_wrapper.ts
diff --git a/src/plugins/data/public/search/aggs/test_helpers/index.ts b/src/plugins/data/common/search/aggs/test_helpers/index.ts
similarity index 86%
rename from src/plugins/data/public/search/aggs/test_helpers/index.ts
rename to src/plugins/data/common/search/aggs/test_helpers/index.ts
index d47317d8b4725..30f315f276741 100644
--- a/src/plugins/data/public/search/aggs/test_helpers/index.ts
+++ b/src/plugins/data/common/search/aggs/test_helpers/index.ts
@@ -17,5 +17,5 @@
* under the License.
*/
-export { functionWrapper } from './function_wrapper';
-export { mockAggTypesRegistry } from './mock_agg_types_registry';
+export * from './function_wrapper';
+export * from './mock_agg_types_registry';
diff --git a/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts b/src/plugins/data/common/search/aggs/test_helpers/mock_agg_types_registry.ts
similarity index 52%
rename from src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts
rename to src/plugins/data/common/search/aggs/test_helpers/mock_agg_types_registry.ts
index 4a0820c349b5f..14631a9d53055 100644
--- a/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts
+++ b/src/plugins/data/common/search/aggs/test_helpers/mock_agg_types_registry.ts
@@ -17,17 +17,14 @@
* under the License.
*/
-import { coreMock } from '../../../../../../../src/core/public/mocks';
+import { fieldFormatsMock } from '../../../field_formats/mocks';
+
import { AggTypesRegistry, AggTypesRegistryStart } from '../agg_types_registry';
-import { getAggTypes } from '../agg_types';
-import { BucketAggType } from '../buckets/bucket_agg_type';
-import { MetricAggType } from '../metrics/metric_agg_type';
-import { fieldFormatsServiceMock } from '../../../field_formats/mocks';
-import { InternalStartServices } from '../../../types';
+import { AggTypesDependencies, getAggTypes } from '../agg_types';
import { TimeBucketsConfig } from '../buckets/lib/time_buckets/time_buckets';
// Mocked uiSettings shared among aggs unit tests
-const mockUiSettings = jest.fn().mockImplementation((key: string) => {
+const mockGetConfig = jest.fn().mockImplementation((key: string) => {
const config: TimeBucketsConfig = {
'histogram:maxBars': 4,
'histogram:barTarget': 3,
@@ -44,6 +41,23 @@ const mockUiSettings = jest.fn().mockImplementation((key: string) => {
return config[key] ?? key;
});
+/** @internal */
+export function mockGetFieldFormatsStart() {
+ const { deserialize, getDefaultInstance } = fieldFormatsMock;
+ return {
+ deserialize,
+ getDefaultInstance,
+ };
+}
+
+/** @internal */
+export const mockAggTypesDependencies: AggTypesDependencies = {
+ calculateBounds: jest.fn(),
+ getFieldFormatsStart: mockGetFieldFormatsStart,
+ getConfig: mockGetConfig,
+ isDefaultTimezone: () => true,
+};
+
/**
* Testing utility which creates a new instance of AggTypesRegistry,
* registers the provided agg types, and returns AggTypesRegistry.start()
@@ -56,36 +70,37 @@ const mockUiSettings = jest.fn().mockImplementation((key: string) => {
*
* @internal
*/
-export function mockAggTypesRegistry | MetricAggType>(
- types?: T[]
-): AggTypesRegistryStart {
+export function mockAggTypesRegistry(deps?: AggTypesDependencies): AggTypesRegistryStart {
const registry = new AggTypesRegistry();
+ const initializedAggTypes = new Map();
const registrySetup = registry.setup();
- if (types) {
- types.forEach((type) => {
- if (type instanceof BucketAggType) {
- registrySetup.registerBucket(type);
- } else if (type instanceof MetricAggType) {
- registrySetup.registerMetric(type);
- }
- });
- } else {
- const coreSetup = coreMock.createSetup();
- coreSetup.uiSettings.get = mockUiSettings;
+ const aggTypes = getAggTypes();
+
+ aggTypes.buckets.forEach(({ name, fn }) => registrySetup.registerBucket(name, fn));
+ aggTypes.metrics.forEach(({ name, fn }) => registrySetup.registerMetric(name, fn));
- const aggTypes = getAggTypes({
- calculateBounds: jest.fn(),
- getInternalStartServices: () =>
- (({
- fieldFormats: fieldFormatsServiceMock.createStartContract(),
- } as unknown) as InternalStartServices),
- uiSettings: coreSetup.uiSettings,
- });
+ const registryStart = registry.start();
- aggTypes.buckets.forEach((type) => registrySetup.registerBucket(type));
- aggTypes.metrics.forEach((type) => registrySetup.registerMetric(type));
- }
+ // initialize each agg type and store in memory
+ registryStart.getAll().buckets.forEach((type) => {
+ const agg = type(deps ?? mockAggTypesDependencies);
+ initializedAggTypes.set(agg.name, agg);
+ });
+ registryStart.getAll().metrics.forEach((type) => {
+ const agg = type(deps ?? mockAggTypesDependencies);
+ initializedAggTypes.set(agg.name, agg);
+ });
- return registry.start();
+ return {
+ get: (name: string) => {
+ return initializedAggTypes.get(name);
+ },
+ getAll: () => {
+ return {
+ buckets: Array.from(initializedAggTypes.values()).filter((agg) => agg.type === 'buckets'),
+ metrics: Array.from(initializedAggTypes.values()).filter((agg) => agg.type === 'metrics'),
+ };
+ },
+ };
}
diff --git a/src/plugins/data/common/search/aggs/types.ts b/src/plugins/data/common/search/aggs/types.ts
new file mode 100644
index 0000000000000..dabd653463d4f
--- /dev/null
+++ b/src/plugins/data/common/search/aggs/types.ts
@@ -0,0 +1,157 @@
+/*
+ * 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 { Assign } from '@kbn/utility-types';
+import { IndexPattern } from '../../index_patterns/index_patterns/index_pattern';
+import {
+ AggConfigSerialized,
+ AggConfigs,
+ AggParamsRange,
+ AggParamsIpRange,
+ AggParamsDateRange,
+ AggParamsFilter,
+ AggParamsFilters,
+ AggParamsSignificantTerms,
+ AggParamsGeoTile,
+ AggParamsGeoHash,
+ AggParamsTerms,
+ AggParamsAvg,
+ AggParamsCardinality,
+ AggParamsGeoBounds,
+ AggParamsGeoCentroid,
+ AggParamsMax,
+ AggParamsMedian,
+ AggParamsMin,
+ AggParamsStdDeviation,
+ AggParamsSum,
+ AggParamsBucketAvg,
+ AggParamsBucketMax,
+ AggParamsBucketMin,
+ AggParamsBucketSum,
+ AggParamsCumulativeSum,
+ AggParamsDerivative,
+ AggParamsMovingAvg,
+ AggParamsPercentileRanks,
+ AggParamsPercentiles,
+ AggParamsSerialDiff,
+ AggParamsTopHit,
+ AggParamsHistogram,
+ AggParamsDateHistogram,
+ AggTypesRegistry,
+ AggTypesRegistrySetup,
+ AggTypesRegistryStart,
+ CreateAggConfigParams,
+ getCalculateAutoTimeExpression,
+ METRIC_TYPES,
+ BUCKET_TYPES,
+} from './';
+
+export { IAggConfig, AggConfigSerialized } from './agg_config';
+export { CreateAggConfigParams, IAggConfigs } from './agg_configs';
+export { IAggType } from './agg_type';
+export { AggParam, AggParamOption } from './agg_params';
+export { IFieldParamType } from './param_types';
+export { IMetricAggType } from './metrics/metric_agg_type';
+export { DateRangeKey } from './buckets/lib/date_range';
+export { IpRangeKey } from './buckets/lib/ip_range';
+export { OptionedValueProp } from './param_types/optioned';
+
+/** @internal */
+export interface AggsCommonSetup {
+ types: AggTypesRegistrySetup;
+}
+
+/** @internal */
+export interface AggsCommonStart {
+ calculateAutoTimeExpression: ReturnType;
+ createAggConfigs: (
+ indexPattern: IndexPattern,
+ configStates?: CreateAggConfigParams[],
+ schemas?: Record
+ ) => InstanceType;
+ types: ReturnType;
+}
+
+/**
+ * AggsStart represents the actual external contract as AggsCommonStart
+ * is only used internally. The difference is that AggsStart includes the
+ * typings for the registry with initialized agg types.
+ *
+ * @internal
+ */
+export type AggsStart = Assign;
+
+/** @internal */
+export interface BaseAggParams {
+ json?: string;
+ customLabel?: string;
+}
+
+/** @internal */
+export interface AggExpressionType {
+ type: 'agg_type';
+ value: AggConfigSerialized;
+}
+
+/** @internal */
+export type AggExpressionFunctionArgs<
+ Name extends keyof AggParamsMapping
+> = AggParamsMapping[Name] & Pick;
+
+/**
+ * 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 {
+ [BUCKET_TYPES.RANGE]: AggParamsRange;
+ [BUCKET_TYPES.IP_RANGE]: AggParamsIpRange;
+ [BUCKET_TYPES.DATE_RANGE]: AggParamsDateRange;
+ [BUCKET_TYPES.FILTER]: AggParamsFilter;
+ [BUCKET_TYPES.FILTERS]: AggParamsFilters;
+ [BUCKET_TYPES.SIGNIFICANT_TERMS]: AggParamsSignificantTerms;
+ [BUCKET_TYPES.GEOTILE_GRID]: AggParamsGeoTile;
+ [BUCKET_TYPES.GEOHASH_GRID]: AggParamsGeoHash;
+ [BUCKET_TYPES.HISTOGRAM]: AggParamsHistogram;
+ [BUCKET_TYPES.DATE_HISTOGRAM]: AggParamsDateHistogram;
+ [BUCKET_TYPES.TERMS]: AggParamsTerms;
+ [METRIC_TYPES.AVG]: AggParamsAvg;
+ [METRIC_TYPES.CARDINALITY]: AggParamsCardinality;
+ [METRIC_TYPES.COUNT]: BaseAggParams;
+ [METRIC_TYPES.GEO_BOUNDS]: AggParamsGeoBounds;
+ [METRIC_TYPES.GEO_CENTROID]: AggParamsGeoCentroid;
+ [METRIC_TYPES.MAX]: AggParamsMax;
+ [METRIC_TYPES.MEDIAN]: AggParamsMedian;
+ [METRIC_TYPES.MIN]: AggParamsMin;
+ [METRIC_TYPES.STD_DEV]: AggParamsStdDeviation;
+ [METRIC_TYPES.SUM]: AggParamsSum;
+ [METRIC_TYPES.AVG_BUCKET]: AggParamsBucketAvg;
+ [METRIC_TYPES.MAX_BUCKET]: AggParamsBucketMax;
+ [METRIC_TYPES.MIN_BUCKET]: AggParamsBucketMin;
+ [METRIC_TYPES.SUM_BUCKET]: AggParamsBucketSum;
+ [METRIC_TYPES.CUMULATIVE_SUM]: AggParamsCumulativeSum;
+ [METRIC_TYPES.DERIVATIVE]: AggParamsDerivative;
+ [METRIC_TYPES.MOVING_FN]: AggParamsMovingAvg;
+ [METRIC_TYPES.PERCENTILE_RANKS]: AggParamsPercentileRanks;
+ [METRIC_TYPES.PERCENTILES]: AggParamsPercentiles;
+ [METRIC_TYPES.SERIAL_DIFF]: AggParamsSerialDiff;
+ [METRIC_TYPES.TOP_HITS]: AggParamsTopHit;
+}
diff --git a/src/plugins/data/public/search/aggs/utils/calculate_auto_time_expression.ts b/src/plugins/data/common/search/aggs/utils/calculate_auto_time_expression.ts
similarity index 71%
rename from src/plugins/data/public/search/aggs/utils/calculate_auto_time_expression.ts
rename to src/plugins/data/common/search/aggs/utils/calculate_auto_time_expression.ts
index 30fcdd9d83a38..622e8101f34ab 100644
--- a/src/plugins/data/public/search/aggs/utils/calculate_auto_time_expression.ts
+++ b/src/plugins/data/common/search/aggs/utils/calculate_auto_time_expression.ts
@@ -17,11 +17,12 @@
* under the License.
*/
import moment from 'moment';
-import { IUiSettingsClient } from 'src/core/public';
+import { UI_SETTINGS } from '../../../../common/constants';
+import { TimeRange } from '../../../../common/query';
import { TimeBuckets } from '../buckets/lib/time_buckets';
-import { toAbsoluteDates, TimeRange, UI_SETTINGS } from '../../../../common';
+import { toAbsoluteDates } from './date_interval_utils';
-export function getCalculateAutoTimeExpression(uiSettings: IUiSettingsClient) {
+export function getCalculateAutoTimeExpression(getConfig: (key: string) => any) {
return function calculateAutoTimeExpression(range: TimeRange) {
const dates = toAbsoluteDates(range);
if (!dates) {
@@ -29,10 +30,10 @@ export function getCalculateAutoTimeExpression(uiSettings: IUiSettingsClient) {
}
const buckets = new TimeBuckets({
- 'histogram:maxBars': uiSettings.get(UI_SETTINGS.HISTOGRAM_MAX_BARS),
- 'histogram:barTarget': uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET),
- dateFormat: uiSettings.get('dateFormat'),
- 'dateFormat:scaled': uiSettings.get('dateFormat:scaled'),
+ 'histogram:maxBars': getConfig(UI_SETTINGS.HISTOGRAM_MAX_BARS),
+ 'histogram:barTarget': getConfig(UI_SETTINGS.HISTOGRAM_BAR_TARGET),
+ dateFormat: getConfig('dateFormat'),
+ 'dateFormat:scaled': getConfig('dateFormat:scaled'),
});
buckets.setInterval('auto');
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/date_histogram_interval.test.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/date_histogram_interval.test.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/date_histogram_interval.test.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/date_histogram_interval.test.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/date_histogram_interval.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/date_histogram_interval.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/date_histogram_interval.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/date_histogram_interval.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/index.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/index.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/index.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/index.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/invalid_es_calendar_interval_error.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/invalid_es_calendar_interval_error.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/invalid_es_calendar_interval_error.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/invalid_es_calendar_interval_error.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/invalid_es_interval_format_error.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/invalid_es_interval_format_error.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/invalid_es_interval_format_error.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/invalid_es_interval_format_error.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/is_valid_es_interval.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/is_valid_es_interval.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/is_valid_es_interval.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/is_valid_es_interval.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/is_valid_interval.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/is_valid_interval.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/is_valid_interval.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/is_valid_interval.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/least_common_interval.test.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/least_common_interval.test.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/least_common_interval.test.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/least_common_interval.test.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/least_common_interval.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/least_common_interval.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/least_common_interval.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/least_common_interval.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/least_common_multiple.test.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/least_common_multiple.test.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/least_common_multiple.test.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/least_common_multiple.test.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/least_common_multiple.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/least_common_multiple.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/least_common_multiple.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/least_common_multiple.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/parse_es_interval.test.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_es_interval.test.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/parse_es_interval.test.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_es_interval.test.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/parse_es_interval.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_es_interval.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/parse_es_interval.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_es_interval.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/parse_interval.test.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.test.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/parse_interval.test.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.test.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/parse_interval.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/date_interval_utils/parse_interval.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/parse_interval.ts
diff --git a/src/plugins/data/common/search/aggs/date_interval_utils/to_absolute_dates.ts b/src/plugins/data/common/search/aggs/utils/date_interval_utils/to_absolute_dates.ts
similarity index 95%
rename from src/plugins/data/common/search/aggs/date_interval_utils/to_absolute_dates.ts
rename to src/plugins/data/common/search/aggs/utils/date_interval_utils/to_absolute_dates.ts
index 98d752a72e28a..99809e06df38f 100644
--- a/src/plugins/data/common/search/aggs/date_interval_utils/to_absolute_dates.ts
+++ b/src/plugins/data/common/search/aggs/utils/date_interval_utils/to_absolute_dates.ts
@@ -18,7 +18,7 @@
*/
import dateMath from '@elastic/datemath';
-import { TimeRange } from '../../../../common';
+import { TimeRange } from '../../../../../common';
export function toAbsoluteDates(range: TimeRange) {
const fromDate = dateMath.parse(range.from);
diff --git a/src/plugins/data/public/search/aggs/utils/get_format_with_aggs.test.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts
similarity index 95%
rename from src/plugins/data/public/search/aggs/utils/get_format_with_aggs.test.ts
rename to src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts
index 3b440bc50c93b..20d8cfc105e49 100644
--- a/src/plugins/data/public/search/aggs/utils/get_format_with_aggs.test.ts
+++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts
@@ -19,9 +19,8 @@
import { identity } from 'lodash';
-import { SerializedFieldFormat } from '../../../../../expressions/common/types';
-import { FieldFormat } from '../../../../common';
-import { IFieldFormat } from '../../../../public';
+import { SerializedFieldFormat } from 'src/plugins/expressions/common/types';
+import { FieldFormat, IFieldFormat } from '../../../../common';
import { getFormatWithAggs } from './get_format_with_aggs';
diff --git a/src/plugins/data/public/search/aggs/utils/get_format_with_aggs.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts
similarity index 95%
rename from src/plugins/data/public/search/aggs/utils/get_format_with_aggs.ts
rename to src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts
index e0db249c7cf86..01369206ab3cf 100644
--- a/src/plugins/data/public/search/aggs/utils/get_format_with_aggs.ts
+++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts
@@ -19,9 +19,12 @@
import { i18n } from '@kbn/i18n';
-import { SerializedFieldFormat } from '../../../../../expressions/common/types';
-import { FieldFormat } from '../../../../common';
-import { FieldFormatsContentType, IFieldFormat } from '../../../../public';
+import { SerializedFieldFormat } from 'src/plugins/expressions/common/types';
+import {
+ FieldFormat,
+ FieldFormatsContentType,
+ IFieldFormat,
+} from '../../../../common/field_formats';
import { convertDateRangeToString, DateRangeKey } from '../buckets/lib/date_range';
import { convertIPRangeToString, IpRangeKey } from '../buckets/lib/ip_range';
diff --git a/src/plugins/data/public/search/aggs/utils/get_parsed_value.ts b/src/plugins/data/common/search/aggs/utils/get_parsed_value.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/utils/get_parsed_value.ts
rename to src/plugins/data/common/search/aggs/utils/get_parsed_value.ts
diff --git a/src/plugins/data/public/search/aggs/utils/index.ts b/src/plugins/data/common/search/aggs/utils/index.ts
similarity index 93%
rename from src/plugins/data/public/search/aggs/utils/index.ts
rename to src/plugins/data/common/search/aggs/utils/index.ts
index 5a889ee9ead9d..99ce44207d80d 100644
--- a/src/plugins/data/public/search/aggs/utils/index.ts
+++ b/src/plugins/data/common/search/aggs/utils/index.ts
@@ -18,6 +18,8 @@
*/
export * from './calculate_auto_time_expression';
+export * from './date_interval_utils';
export * from './get_format_with_aggs';
+export * from './ipv4_address';
export * from './prop_filter';
export * from './to_angular_json';
diff --git a/src/plugins/data/common/search/aggs/ipv4_address.test.ts b/src/plugins/data/common/search/aggs/utils/ipv4_address.test.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/ipv4_address.test.ts
rename to src/plugins/data/common/search/aggs/utils/ipv4_address.test.ts
diff --git a/src/plugins/data/common/search/aggs/ipv4_address.ts b/src/plugins/data/common/search/aggs/utils/ipv4_address.ts
similarity index 100%
rename from src/plugins/data/common/search/aggs/ipv4_address.ts
rename to src/plugins/data/common/search/aggs/utils/ipv4_address.ts
diff --git a/src/plugins/data/public/search/aggs/utils/prop_filter.test.ts b/src/plugins/data/common/search/aggs/utils/prop_filter.test.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/utils/prop_filter.test.ts
rename to src/plugins/data/common/search/aggs/utils/prop_filter.test.ts
diff --git a/src/plugins/data/public/search/aggs/utils/prop_filter.ts b/src/plugins/data/common/search/aggs/utils/prop_filter.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/utils/prop_filter.ts
rename to src/plugins/data/common/search/aggs/utils/prop_filter.ts
diff --git a/src/plugins/data/public/search/aggs/utils/to_angular_json.ts b/src/plugins/data/common/search/aggs/utils/to_angular_json.ts
similarity index 100%
rename from src/plugins/data/public/search/aggs/utils/to_angular_json.ts
rename to src/plugins/data/common/search/aggs/utils/to_angular_json.ts
diff --git a/src/plugins/data/common/search/expressions/index.ts b/src/plugins/data/common/search/expressions/index.ts
index f1a39a8383629..25839a805d8c5 100644
--- a/src/plugins/data/common/search/expressions/index.ts
+++ b/src/plugins/data/common/search/expressions/index.ts
@@ -18,3 +18,4 @@
*/
export * from './esaggs';
+export * from './utils';
diff --git a/src/plugins/data/public/search/expressions/utils/courier_inspector_stats.ts b/src/plugins/data/common/search/expressions/utils/courier_inspector_stats.ts
similarity index 98%
rename from src/plugins/data/public/search/expressions/utils/courier_inspector_stats.ts
rename to src/plugins/data/common/search/expressions/utils/courier_inspector_stats.ts
index c933e8cd3e961..d41f02d2728d5 100644
--- a/src/plugins/data/public/search/expressions/utils/courier_inspector_stats.ts
+++ b/src/plugins/data/common/search/expressions/utils/courier_inspector_stats.ts
@@ -26,8 +26,8 @@
import { i18n } from '@kbn/i18n';
import { SearchResponse } from 'elasticsearch';
+import { ISearchSource } from 'src/plugins/data/public';
import { RequestStatistics } from 'src/plugins/inspector/common';
-import { ISearchSource } from '../../search_source';
/** @public */
export function getRequestInspectorStats(searchSource: ISearchSource) {
diff --git a/src/plugins/data/common/search/expressions/utils/index.ts b/src/plugins/data/common/search/expressions/utils/index.ts
new file mode 100644
index 0000000000000..75c1809770c78
--- /dev/null
+++ b/src/plugins/data/common/search/expressions/utils/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 './courier_inspector_stats';
diff --git a/src/plugins/data/common/search/index.ts b/src/plugins/data/common/search/index.ts
index 45daf16d10c6d..557ab64079d16 100644
--- a/src/plugins/data/common/search/index.ts
+++ b/src/plugins/data/common/search/index.ts
@@ -17,10 +17,13 @@
* under the License.
*/
-import { ES_SEARCH_STRATEGY } from './es_search';
-
-export { IKibanaSearchResponse, IKibanaSearchRequest } from './types';
+export * from './aggs';
+export * from './es_search';
+export * from './expressions';
+export * from './tabify';
+export * from './types';
+import { ES_SEARCH_STRATEGY } from './es_search';
export const DEFAULT_SEARCH_STRATEGY = ES_SEARCH_STRATEGY;
export {
diff --git a/src/plugins/data/public/search/tabify/buckets.test.ts b/src/plugins/data/common/search/tabify/buckets.test.ts
similarity index 100%
rename from src/plugins/data/public/search/tabify/buckets.test.ts
rename to src/plugins/data/common/search/tabify/buckets.test.ts
diff --git a/src/plugins/data/public/search/tabify/buckets.ts b/src/plugins/data/common/search/tabify/buckets.ts
similarity index 100%
rename from src/plugins/data/public/search/tabify/buckets.ts
rename to src/plugins/data/common/search/tabify/buckets.ts
diff --git a/src/plugins/data/public/search/tabify/get_columns.test.ts b/src/plugins/data/common/search/tabify/get_columns.test.ts
similarity index 100%
rename from src/plugins/data/public/search/tabify/get_columns.test.ts
rename to src/plugins/data/common/search/tabify/get_columns.test.ts
diff --git a/src/plugins/data/public/search/tabify/get_columns.ts b/src/plugins/data/common/search/tabify/get_columns.ts
similarity index 100%
rename from src/plugins/data/public/search/tabify/get_columns.ts
rename to src/plugins/data/common/search/tabify/get_columns.ts
diff --git a/src/plugins/data/public/search/tabify/index.ts b/src/plugins/data/common/search/tabify/index.ts
similarity index 100%
rename from src/plugins/data/public/search/tabify/index.ts
rename to src/plugins/data/common/search/tabify/index.ts
diff --git a/src/plugins/data/public/search/tabify/response_writer.test.ts b/src/plugins/data/common/search/tabify/response_writer.test.ts
similarity index 100%
rename from src/plugins/data/public/search/tabify/response_writer.test.ts
rename to src/plugins/data/common/search/tabify/response_writer.test.ts
diff --git a/src/plugins/data/public/search/tabify/response_writer.ts b/src/plugins/data/common/search/tabify/response_writer.ts
similarity index 100%
rename from src/plugins/data/public/search/tabify/response_writer.ts
rename to src/plugins/data/common/search/tabify/response_writer.ts
diff --git a/src/plugins/data/public/search/tabify/tabify.test.ts b/src/plugins/data/common/search/tabify/tabify.test.ts
similarity index 98%
rename from src/plugins/data/public/search/tabify/tabify.test.ts
rename to src/plugins/data/common/search/tabify/tabify.test.ts
index 0a1e99c8bb200..6b9d520b11436 100644
--- a/src/plugins/data/public/search/tabify/tabify.test.ts
+++ b/src/plugins/data/common/search/tabify/tabify.test.ts
@@ -18,7 +18,7 @@
*/
import { tabifyAggResponse } from './tabify';
-import { IndexPattern } from '../../index_patterns';
+import { IndexPattern } from '../../index_patterns/index_patterns/index_pattern';
import { AggConfigs, IAggConfig, IAggConfigs } from '../aggs';
import { mockAggTypesRegistry } from '../aggs/test_helpers';
import { metricOnly, threeTermBuckets } from 'fixtures/fake_hierarchical_data';
diff --git a/src/plugins/data/public/search/tabify/tabify.ts b/src/plugins/data/common/search/tabify/tabify.ts
similarity index 100%
rename from src/plugins/data/public/search/tabify/tabify.ts
rename to src/plugins/data/common/search/tabify/tabify.ts
diff --git a/src/plugins/data/public/search/tabify/types.ts b/src/plugins/data/common/search/tabify/types.ts
similarity index 100%
rename from src/plugins/data/public/search/tabify/types.ts
rename to src/plugins/data/common/search/tabify/types.ts
diff --git a/src/plugins/data/public/field_formats/utils/deserialize.ts b/src/plugins/data/public/field_formats/utils/deserialize.ts
index 26baa5fdeb1e4..7595a443bf8f0 100644
--- a/src/plugins/data/public/field_formats/utils/deserialize.ts
+++ b/src/plugins/data/public/field_formats/utils/deserialize.ts
@@ -23,9 +23,9 @@ import { SerializedFieldFormat } from '../../../../expressions/common/types';
import { FieldFormat } from '../../../common';
import { FormatFactory } from '../../../common/field_formats/utils';
+import { getFormatWithAggs } from '../../../common/search/aggs';
import { DataPublicPluginStart, IFieldFormat } from '../../../public';
import { getUiSettings } from '../../../public/services';
-import { getFormatWithAggs } from '../../search/aggs/utils';
const getConfig = (key: string, defaultOverride?: any): any =>
getUiSettings().get(key, defaultOverride);
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index f036d5f30a0e2..d35069207ee84 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -294,15 +294,6 @@ import {
propFilter,
siblingPipelineType,
termsAggFilter,
- // expressions utils
- getRequestInspectorStats,
- getResponseInspectorStats,
- // tabify
- tabifyAggResponse,
- tabifyGetColumns,
-} from './search';
-
-import {
dateHistogramInterval,
InvalidEsCalendarIntervalError,
InvalidEsIntervalFormatError,
@@ -312,10 +303,14 @@ import {
parseEsInterval,
parseInterval,
toAbsoluteDates,
+ // expressions utils
+ getRequestInspectorStats,
+ getResponseInspectorStats,
+ // tabify
+ tabifyAggResponse,
+ tabifyGetColumns,
} from '../common';
-export { EsaggsExpressionFunctionDefinition, ParsedInterval } from '../common';
-
export {
// aggs
AggGroupLabels,
@@ -326,6 +321,7 @@ export {
AggParamType,
AggConfigOptions,
BUCKET_TYPES,
+ EsaggsExpressionFunctionDefinition,
IAggConfig,
IAggConfigs,
IAggType,
@@ -334,35 +330,39 @@ export {
METRIC_TYPES,
OptionedParamType,
OptionedValueProp,
+ ParsedInterval,
+ // tabify
+ TabbedAggColumn,
+ TabbedAggRow,
+ TabbedTable,
+} from '../common';
+
+export {
// search
ES_SEARCH_STRATEGY,
+ EsQuerySortValue,
+ extractSearchSourceReferences,
+ FetchOptions,
getEsPreference,
- ISearch,
- ISearchOptions,
- ISearchGeneric,
- IEsSearchResponse,
+ getSearchParamsFromRequest,
IEsSearchRequest,
- IKibanaSearchResponse,
+ IEsSearchResponse,
IKibanaSearchRequest,
- SearchRequest,
- SearchResponse,
- SearchError,
+ IKibanaSearchResponse,
+ injectSearchSourceReferences,
+ ISearch,
+ ISearchGeneric,
+ ISearchOptions,
ISearchSource,
parseSearchSourceJSON,
- injectSearchSourceReferences,
- getSearchParamsFromRequest,
- extractSearchSourceReferences,
- SearchSourceFields,
- EsQuerySortValue,
- SortDirection,
- FetchOptions,
- // tabify
- TabbedAggColumn,
- TabbedAggRow,
- TabbedTable,
+ RequestTimeoutError,
+ SearchError,
SearchInterceptor,
SearchInterceptorDeps,
- RequestTimeoutError,
+ SearchRequest,
+ SearchResponse,
+ SearchSourceFields,
+ SortDirection,
} from './search';
// Search namespace
diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts
index 3fc1e6454829d..78e40cfedd906 100644
--- a/src/plugins/data/public/mocks.ts
+++ b/src/plugins/data/public/mocks.ts
@@ -79,7 +79,7 @@ const createStartContract = (): Start => {
};
export { createSearchSourceMock } from './search/mocks';
-export { getCalculateAutoTimeExpression } from './search/aggs';
+export { getCalculateAutoTimeExpression } from '../common/search/aggs';
export const dataPluginMock = {
createSetupContract,
diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts
index e950434b287a7..e6a48794d8b0f 100644
--- a/src/plugins/data/public/plugin.ts
+++ b/src/plugins/data/public/plugin.ts
@@ -33,7 +33,6 @@ import {
DataPublicPluginStart,
DataSetupDependencies,
DataStartDependencies,
- InternalStartServices,
DataPublicPluginEnhancements,
} from './types';
import { AutocompleteService } from './autocomplete';
@@ -120,17 +119,6 @@ export class DataPublicPlugin
): DataPublicPluginSetup {
const startServices = createStartServicesGetter(core.getStartServices);
- const getInternalStartServices = (): InternalStartServices => {
- const { core: coreStart, self } = startServices();
- return {
- fieldFormats: self.fieldFormats,
- notifications: coreStart.notifications,
- uiSettings: coreStart.uiSettings,
- searchService: self.search,
- injectedMetadata: coreStart.injectedMetadata,
- };
- };
-
expressions.registerFunction(esaggs);
expressions.registerFunction(indexPatternLoad);
@@ -158,10 +146,9 @@ export class DataPublicPlugin
);
const searchService = this.searchService.setup(core, {
- expressions,
usageCollection,
- getInternalStartServices,
packageInfo: this.packageInfo,
+ registerFunction: expressions.registerFunction,
});
return {
@@ -210,7 +197,7 @@ export class DataPublicPlugin
});
setQueryService(query);
- const search = this.searchService.start(core, { indexPatterns });
+ const search = this.searchService.start(core, { fieldFormats, indexPatterns });
setSearchService(search);
uiActions.addTriggerAction(
@@ -247,5 +234,7 @@ export class DataPublicPlugin
public stop() {
this.autocomplete.clearProviders();
+ this.queryService.stop();
+ this.searchService.stop();
}
}
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index a61334905e9f5..744376403e1a1 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -26,10 +26,12 @@ import { EuiGlobalToastListToast } from '@elastic/eui';
import { ExclusiveUnion } from '@elastic/eui';
import { ExpressionAstFunction } from 'src/plugins/expressions/common';
import { ExpressionsSetup } from 'src/plugins/expressions/public';
+import { FetchOptions as FetchOptions_2 } from 'src/plugins/data/public';
import { History } from 'history';
import { Href } from 'history';
import { IconType } from '@elastic/eui';
import { InjectedIntl } from '@kbn/i18n/react';
+import { ISearchSource as ISearchSource_2 } from 'src/plugins/data/public';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import { IUiSettingsClient } from 'src/core/public';
import { IUiSettingsClient as IUiSettingsClient_3 } from 'kibana/public';
@@ -153,12 +155,12 @@ export interface ApplyGlobalFilterActionContext {
timeFieldName?: string;
}
-// Warning: (ae-forgotten-export) The symbol "DateNanosFormat" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "DateFormat" needs to be exported by the entry point index.d.ts
+// Warning: (ae-forgotten-export) The symbol "DateNanosFormat" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "baseFormattersPublic" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export const baseFormattersPublic: (import("../../common").FieldFormatInstanceType | typeof DateNanosFormat | typeof DateFormat)[];
+export const baseFormattersPublic: (import("../../common").FieldFormatInstanceType | typeof DateFormat | typeof DateNanosFormat)[];
// Warning: (ae-missing-release-tag) "BUCKET_TYPES" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -1653,7 +1655,7 @@ export const search: {
intervalOptions: ({
display: string;
val: string;
- enabled(agg: import("./search/aggs/buckets/bucket_agg_type").IBucketAggConfig): boolean | "" | undefined;
+ enabled(agg: import("../common").IBucketAggConfig): boolean | "" | undefined;
} | {
display: string;
val: string;
@@ -1662,9 +1664,9 @@ export const search: {
InvalidEsIntervalFormatError: typeof InvalidEsIntervalFormatError;
Ipv4Address: typeof Ipv4Address;
isDateHistogramBucketAggConfig: typeof isDateHistogramBucketAggConfig;
- isNumberType: (agg: import("./search").AggConfig) => boolean;
- isStringType: (agg: import("./search").AggConfig) => boolean;
- isType: (...types: string[]) => (agg: import("./search").AggConfig) => boolean;
+ isNumberType: (agg: import("../common").AggConfig) => boolean;
+ isStringType: (agg: import("../common").AggConfig) => boolean;
+ isType: (...types: string[]) => (agg: import("../common").AggConfig) => boolean;
isValidEsInterval: typeof isValidEsInterval;
isValidInterval: typeof isValidInterval;
parentPipelineType: string;
diff --git a/src/plugins/data/public/search/aggs/aggs_service.test.ts b/src/plugins/data/public/search/aggs/aggs_service.test.ts
new file mode 100644
index 0000000000000..db25dfb300d11
--- /dev/null
+++ b/src/plugins/data/public/search/aggs/aggs_service.test.ts
@@ -0,0 +1,182 @@
+/*
+ * 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 { BehaviorSubject, Subscription } from 'rxjs';
+
+import { coreMock } from '../../../../../core/public/mocks';
+import { expressionsPluginMock } from '../../../../../plugins/expressions/public/mocks';
+import { BucketAggType, getAggTypes, MetricAggType } from '../../../common';
+import { fieldFormatsServiceMock } from '../../field_formats/mocks';
+
+import {
+ AggsService,
+ AggsSetupDependencies,
+ AggsStartDependencies,
+ createGetConfig,
+} from './aggs_service';
+
+const { uiSettings } = coreMock.createSetup();
+
+describe('AggsService - public', () => {
+ let service: AggsService;
+ let setupDeps: AggsSetupDependencies;
+ let startDeps: AggsStartDependencies;
+
+ beforeEach(() => {
+ service = new AggsService();
+ setupDeps = {
+ registerFunction: expressionsPluginMock.createSetupContract().registerFunction,
+ uiSettings,
+ };
+ startDeps = {
+ fieldFormats: fieldFormatsServiceMock.createStartContract(),
+ uiSettings,
+ };
+ });
+
+ describe('setup()', () => {
+ test('exposes proper contract', () => {
+ const setup = service.setup(setupDeps);
+ expect(Object.keys(setup).length).toBe(1);
+ expect(setup).toHaveProperty('types');
+ });
+
+ test('registers default agg types', () => {
+ service.setup(setupDeps);
+ const start = service.start(startDeps);
+ expect(start.types.getAll().buckets.length).toBe(11);
+ expect(start.types.getAll().metrics.length).toBe(21);
+ });
+
+ test('registers custom agg types', () => {
+ const setup = service.setup(setupDeps);
+ setup.types.registerBucket(
+ 'foo',
+ () => ({ name: 'foo', type: 'buckets' } as BucketAggType)
+ );
+ setup.types.registerMetric(
+ 'bar',
+ () => ({ name: 'bar', type: 'metrics' } as MetricAggType)
+ );
+
+ const start = service.start(startDeps);
+ expect(start.types.getAll().buckets.length).toBe(12);
+ expect(start.types.getAll().buckets.some(({ name }) => name === 'foo')).toBe(true);
+ expect(start.types.getAll().metrics.length).toBe(22);
+ expect(start.types.getAll().metrics.some(({ name }) => name === 'bar')).toBe(true);
+ });
+ });
+
+ describe('start()', () => {
+ test('exposes proper contract', () => {
+ const start = service.start(startDeps);
+ expect(Object.keys(start).length).toBe(3);
+ expect(start).toHaveProperty('calculateAutoTimeExpression');
+ expect(start).toHaveProperty('createAggConfigs');
+ expect(start).toHaveProperty('types');
+ });
+
+ test('types registry returns initialized agg types', () => {
+ service.setup(setupDeps);
+ const start = service.start(startDeps);
+
+ expect(start.types.get('terms').name).toBe('terms');
+ });
+
+ test('registers default agg types', () => {
+ service.setup(setupDeps);
+ const start = service.start(startDeps);
+
+ const aggTypes = getAggTypes();
+ expect(start.types.getAll().buckets.length).toBe(aggTypes.buckets.length);
+ expect(start.types.getAll().metrics.length).toBe(aggTypes.metrics.length);
+ });
+
+ test('merges default agg types with types registered during setup', () => {
+ const setup = service.setup(setupDeps);
+ setup.types.registerBucket(
+ 'foo',
+ () => ({ name: 'foo', type: 'buckets' } as BucketAggType)
+ );
+ setup.types.registerMetric(
+ 'bar',
+ () => ({ name: 'bar', type: 'metrics' } as MetricAggType)
+ );
+
+ const start = service.start(startDeps);
+
+ const aggTypes = getAggTypes();
+ expect(start.types.getAll().buckets.length).toBe(aggTypes.buckets.length + 1);
+ expect(start.types.getAll().buckets.some(({ name }) => name === 'foo')).toBe(true);
+ expect(start.types.getAll().metrics.length).toBe(aggTypes.metrics.length + 1);
+ expect(start.types.getAll().metrics.some(({ name }) => name === 'bar')).toBe(true);
+ });
+ });
+
+ describe('createGetConfig()', () => {
+ let fooSubject$: BehaviorSubject;
+ let barSubject$: BehaviorSubject;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+
+ fooSubject$ = new BehaviorSubject('fooVal');
+ barSubject$ = new BehaviorSubject('barVal');
+
+ uiSettings.get$.mockImplementation((key: string) => {
+ const mockSettings: Record = {
+ foo: () => fooSubject$,
+ bar: () => barSubject$,
+ };
+ return mockSettings[key] ? mockSettings[key]() : undefined;
+ });
+ });
+
+ test('returns a function to get the value of the provided setting', () => {
+ const requiredSettings = ['foo', 'bar'];
+ const subscriptions: Subscription[] = [];
+ const getConfig = createGetConfig(uiSettings, requiredSettings, subscriptions);
+
+ expect(getConfig('foo')).toBe('fooVal');
+ expect(getConfig('bar')).toBe('barVal');
+ });
+
+ test('does not return values for settings that are not explicitly declared', () => {
+ const requiredSettings = ['foo', 'bar'];
+ const subscriptions: Subscription[] = [];
+ const getConfig = createGetConfig(uiSettings, requiredSettings, subscriptions);
+
+ expect(subscriptions.length).toBe(2);
+ expect(getConfig('baz')).toBe(undefined);
+ });
+
+ test('provides latest value for each setting', () => {
+ const requiredSettings = ['foo', 'bar'];
+ const subscriptions: Subscription[] = [];
+ const getConfig = createGetConfig(uiSettings, requiredSettings, subscriptions);
+
+ expect(getConfig('foo')).toBe('fooVal');
+ fooSubject$.next('fooVal2');
+ expect(getConfig('foo')).toBe('fooVal2');
+ expect(getConfig('foo')).toBe('fooVal2');
+ fooSubject$.next('fooVal3');
+ expect(getConfig('foo')).toBe('fooVal3');
+ });
+ });
+});
diff --git a/src/plugins/data/public/search/aggs/aggs_service.ts b/src/plugins/data/public/search/aggs/aggs_service.ts
new file mode 100644
index 0000000000000..d535f97fefdf8
--- /dev/null
+++ b/src/plugins/data/public/search/aggs/aggs_service.ts
@@ -0,0 +1,152 @@
+/*
+ * 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 { Subscription } from 'rxjs';
+
+import { IUiSettingsClient } from 'src/core/public';
+import { ExpressionsServiceSetup } from 'src/plugins/expressions/common';
+import {
+ aggsRequiredUiSettings,
+ AggsCommonStartDependencies,
+ AggsCommonService,
+ AggConfigs,
+ AggTypesDependencies,
+ calculateBounds,
+ TimeRange,
+} from '../../../common';
+import { FieldFormatsStart } from '../../field_formats';
+import { getForceNow } from '../../query/timefilter/lib/get_force_now';
+import { AggsSetup, AggsStart } from './types';
+
+/**
+ * Aggs needs synchronous access to specific uiSettings. Since settings can change
+ * without a page refresh, we create a cache that subscribes to changes from
+ * uiSettings.get$ and keeps everything up-to-date.
+ *
+ * @internal
+ */
+export function createGetConfig(
+ uiSettings: IUiSettingsClient,
+ requiredSettings: string[],
+ subscriptions: Subscription[]
+): AggsCommonStartDependencies['getConfig'] {
+ const settingsCache: Record = {};
+
+ requiredSettings.forEach((setting) => {
+ subscriptions.push(
+ uiSettings.get$(setting).subscribe((value) => {
+ settingsCache[setting] = value;
+ })
+ );
+ });
+
+ return (key) => settingsCache[key];
+}
+
+/** @internal */
+export interface AggsSetupDependencies {
+ registerFunction: ExpressionsServiceSetup['registerFunction'];
+ uiSettings: IUiSettingsClient;
+}
+
+/** @internal */
+export interface AggsStartDependencies {
+ fieldFormats: FieldFormatsStart;
+ uiSettings: IUiSettingsClient;
+}
+
+/**
+ * The aggs service provides a means of modeling and manipulating the various
+ * Elasticsearch aggregations supported by Kibana, providing the ability to
+ * output the correct DSL when you are ready to send your request to ES.
+ */
+export class AggsService {
+ private readonly aggsCommonService = new AggsCommonService();
+ private readonly initializedAggTypes = new Map();
+ private getConfig?: AggsCommonStartDependencies['getConfig'];
+ private subscriptions: Subscription[] = [];
+
+ /**
+ * getForceNow uses window.location, so we must have a separate implementation
+ * of calculateBounds on the client and the server.
+ */
+ private calculateBounds = (timeRange: TimeRange) =>
+ calculateBounds(timeRange, { forceNow: getForceNow() });
+
+ public setup({ registerFunction, uiSettings }: AggsSetupDependencies): AggsSetup {
+ this.getConfig = createGetConfig(uiSettings, aggsRequiredUiSettings, this.subscriptions);
+
+ return this.aggsCommonService.setup({ registerFunction });
+ }
+
+ public start({ fieldFormats, uiSettings }: AggsStartDependencies): AggsStart {
+ const { calculateAutoTimeExpression, types } = this.aggsCommonService.start({
+ getConfig: this.getConfig!,
+ });
+
+ const aggTypesDependencies: AggTypesDependencies = {
+ calculateBounds: this.calculateBounds,
+ getConfig: this.getConfig!,
+ getFieldFormatsStart: () => ({
+ deserialize: fieldFormats.deserialize,
+ getDefaultInstance: fieldFormats.getDefaultInstance,
+ }),
+ isDefaultTimezone: () => uiSettings.isDefault('dateFormat:tz'),
+ };
+
+ // initialize each agg type and store in memory
+ types.getAll().buckets.forEach((type) => {
+ const agg = type(aggTypesDependencies);
+ this.initializedAggTypes.set(agg.name, agg);
+ });
+ types.getAll().metrics.forEach((type) => {
+ const agg = type(aggTypesDependencies);
+ this.initializedAggTypes.set(agg.name, agg);
+ });
+
+ const typesRegistry = {
+ get: (name: string) => {
+ return this.initializedAggTypes.get(name);
+ },
+ getAll: () => {
+ return {
+ buckets: Array.from(this.initializedAggTypes.values()).filter(
+ (agg) => agg.type === 'buckets'
+ ),
+ metrics: Array.from(this.initializedAggTypes.values()).filter(
+ (agg) => agg.type === 'metrics'
+ ),
+ };
+ },
+ };
+
+ return {
+ calculateAutoTimeExpression,
+ createAggConfigs: (indexPattern, configStates = [], schemas) => {
+ return new AggConfigs(indexPattern, configStates, { typesRegistry });
+ },
+ types: typesRegistry,
+ };
+ }
+
+ public stop() {
+ this.subscriptions.forEach((s) => s.unsubscribe());
+ this.subscriptions = [];
+ }
+}
diff --git a/src/plugins/data/public/search/aggs/index.ts b/src/plugins/data/public/search/aggs/index.ts
index 1139d9c7ff722..77d97c426260c 100644
--- a/src/plugins/data/public/search/aggs/index.ts
+++ b/src/plugins/data/public/search/aggs/index.ts
@@ -17,14 +17,5 @@
* under the License.
*/
-export * from './agg_config';
-export * from './agg_configs';
-export * from './agg_groups';
-export * from './agg_type';
-export * from './agg_types';
-export * from './agg_types_registry';
-export * from './buckets';
-export * from './metrics';
-export * from './param_types';
+export * from './aggs_service';
export * from './types';
-export * from './utils';
diff --git a/src/plugins/data/public/search/aggs/mocks.ts b/src/plugins/data/public/search/aggs/mocks.ts
index 2cad646067292..ca13343777e63 100644
--- a/src/plugins/data/public/search/aggs/mocks.ts
+++ b/src/plugins/data/public/search/aggs/mocks.ts
@@ -17,15 +17,17 @@
* under the License.
*/
-import { coreMock } from '../../../../../../src/core/public/mocks';
import {
AggConfigs,
AggTypesRegistrySetup,
AggTypesRegistryStart,
getCalculateAutoTimeExpression,
-} from './';
-import { SearchAggsSetup, SearchAggsStart } from './types';
-import { mockAggTypesRegistry } from './test_helpers';
+} from '../../../common';
+import { AggsSetup, AggsStart } from './types';
+
+import { mockAggTypesRegistry } from '../../../common/search/aggs/test_helpers';
+
+const getConfig = jest.fn();
const aggTypeBaseParamMock = () => ({
name: 'some_param',
@@ -53,21 +55,18 @@ export const aggTypesRegistrySetupMock = (): AggTypesRegistrySetup => ({
export const aggTypesRegistryStartMock = (): AggTypesRegistryStart => ({
get: jest.fn().mockImplementation(aggTypeConfigMock),
- getBuckets: jest.fn().mockImplementation(() => [aggTypeConfigMock()]),
- getMetrics: jest.fn().mockImplementation(() => [aggTypeConfigMock()]),
getAll: jest.fn().mockImplementation(() => ({
buckets: [aggTypeConfigMock()],
metrics: [aggTypeConfigMock()],
})),
});
-export const searchAggsSetupMock = (): SearchAggsSetup => ({
- calculateAutoTimeExpression: getCalculateAutoTimeExpression(coreMock.createSetup().uiSettings),
+export const searchAggsSetupMock = (): AggsSetup => ({
types: aggTypesRegistrySetupMock(),
});
-export const searchAggsStartMock = (): SearchAggsStart => ({
- calculateAutoTimeExpression: getCalculateAutoTimeExpression(coreMock.createStart().uiSettings),
+export const searchAggsStartMock = (): AggsStart => ({
+ calculateAutoTimeExpression: getCalculateAutoTimeExpression(getConfig),
createAggConfigs: jest.fn().mockImplementation((indexPattern, configStates = [], schemas) => {
return new AggConfigs(indexPattern, configStates, {
typesRegistry: mockAggTypesRegistry(),
diff --git a/src/plugins/data/public/search/aggs/types.ts b/src/plugins/data/public/search/aggs/types.ts
index a784bfaada4c7..38be541973ce9 100644
--- a/src/plugins/data/public/search/aggs/types.ts
+++ b/src/plugins/data/public/search/aggs/types.ts
@@ -17,131 +17,7 @@
* under the License.
*/
-import { IndexPattern } from '../../index_patterns';
-import {
- AggConfigSerialized,
- AggConfigs,
- AggParamsRange,
- AggParamsIpRange,
- AggParamsDateRange,
- AggParamsFilter,
- AggParamsFilters,
- AggParamsSignificantTerms,
- AggParamsGeoTile,
- AggParamsGeoHash,
- AggParamsTerms,
- AggParamsAvg,
- AggParamsCardinality,
- AggParamsGeoBounds,
- AggParamsGeoCentroid,
- AggParamsMax,
- AggParamsMedian,
- AggParamsMin,
- AggParamsStdDeviation,
- AggParamsSum,
- AggParamsBucketAvg,
- AggParamsBucketMax,
- AggParamsBucketMin,
- AggParamsBucketSum,
- AggParamsCumulativeSum,
- AggParamsDerivative,
- AggParamsMovingAvg,
- AggParamsPercentileRanks,
- AggParamsPercentiles,
- AggParamsSerialDiff,
- AggParamsTopHit,
- AggParamsHistogram,
- AggParamsDateHistogram,
- AggTypesRegistrySetup,
- AggTypesRegistryStart,
- CreateAggConfigParams,
- getCalculateAutoTimeExpression,
- METRIC_TYPES,
- BUCKET_TYPES,
-} from './';
+import { AggsCommonSetup } from '../../../common';
-export { IAggConfig, AggConfigSerialized } from './agg_config';
-export { CreateAggConfigParams, IAggConfigs } from './agg_configs';
-export { IAggType } from './agg_type';
-export { AggParam, AggParamOption } from './agg_params';
-export { IFieldParamType } from './param_types';
-export { IMetricAggType } from './metrics/metric_agg_type';
-export { DateRangeKey } from './buckets/lib/date_range';
-export { IpRangeKey } from './buckets/lib/ip_range';
-export { OptionedValueProp } from './param_types/optioned';
-
-/** @internal */
-export interface SearchAggsSetup {
- calculateAutoTimeExpression: ReturnType;
- types: AggTypesRegistrySetup;
-}
-
-/** @internal */
-export interface SearchAggsStart {
- calculateAutoTimeExpression: ReturnType;
- createAggConfigs: (
- indexPattern: IndexPattern,
- configStates?: CreateAggConfigParams[],
- schemas?: Record
- ) => InstanceType;
- types: AggTypesRegistryStart;
-}
-
-/** @internal */
-export interface BaseAggParams {
- json?: string;
- customLabel?: string;
-}
-
-/** @internal */
-export interface AggExpressionType {
- type: 'agg_type';
- value: AggConfigSerialized;
-}
-
-/** @internal */
-export type AggExpressionFunctionArgs<
- Name extends keyof AggParamsMapping
-> = AggParamsMapping[Name] & Pick;
-
-/**
- * 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 {
- [BUCKET_TYPES.RANGE]: AggParamsRange;
- [BUCKET_TYPES.IP_RANGE]: AggParamsIpRange;
- [BUCKET_TYPES.DATE_RANGE]: AggParamsDateRange;
- [BUCKET_TYPES.FILTER]: AggParamsFilter;
- [BUCKET_TYPES.FILTERS]: AggParamsFilters;
- [BUCKET_TYPES.SIGNIFICANT_TERMS]: AggParamsSignificantTerms;
- [BUCKET_TYPES.GEOTILE_GRID]: AggParamsGeoTile;
- [BUCKET_TYPES.GEOHASH_GRID]: AggParamsGeoHash;
- [BUCKET_TYPES.HISTOGRAM]: AggParamsHistogram;
- [BUCKET_TYPES.DATE_HISTOGRAM]: AggParamsDateHistogram;
- [BUCKET_TYPES.TERMS]: AggParamsTerms;
- [METRIC_TYPES.AVG]: AggParamsAvg;
- [METRIC_TYPES.CARDINALITY]: AggParamsCardinality;
- [METRIC_TYPES.COUNT]: BaseAggParams;
- [METRIC_TYPES.GEO_BOUNDS]: AggParamsGeoBounds;
- [METRIC_TYPES.GEO_CENTROID]: AggParamsGeoCentroid;
- [METRIC_TYPES.MAX]: AggParamsMax;
- [METRIC_TYPES.MEDIAN]: AggParamsMedian;
- [METRIC_TYPES.MIN]: AggParamsMin;
- [METRIC_TYPES.STD_DEV]: AggParamsStdDeviation;
- [METRIC_TYPES.SUM]: AggParamsSum;
- [METRIC_TYPES.AVG_BUCKET]: AggParamsBucketAvg;
- [METRIC_TYPES.MAX_BUCKET]: AggParamsBucketMax;
- [METRIC_TYPES.MIN_BUCKET]: AggParamsBucketMin;
- [METRIC_TYPES.SUM_BUCKET]: AggParamsBucketSum;
- [METRIC_TYPES.CUMULATIVE_SUM]: AggParamsCumulativeSum;
- [METRIC_TYPES.DERIVATIVE]: AggParamsDerivative;
- [METRIC_TYPES.MOVING_FN]: AggParamsMovingAvg;
- [METRIC_TYPES.PERCENTILE_RANKS]: AggParamsPercentileRanks;
- [METRIC_TYPES.PERCENTILES]: AggParamsPercentiles;
- [METRIC_TYPES.SERIAL_DIFF]: AggParamsSerialDiff;
- [METRIC_TYPES.TOP_HITS]: AggParamsTopHit;
-}
+export type AggsSetup = AggsCommonSetup;
+export { AggsStart } from '../../../common';
diff --git a/src/plugins/data/public/search/expressions/build_tabular_inspector_data.ts b/src/plugins/data/public/search/expressions/build_tabular_inspector_data.ts
index 75a4464a8e61e..7eff6f25fd828 100644
--- a/src/plugins/data/public/search/expressions/build_tabular_inspector_data.ts
+++ b/src/plugins/data/public/search/expressions/build_tabular_inspector_data.ts
@@ -19,8 +19,8 @@
import { set } from '@elastic/safer-lodash-set';
import { FormattedData } from '../../../../../plugins/inspector/public';
+import { TabbedTable } from '../../../common';
import { FormatFactory } from '../../../common/field_formats/utils';
-import { TabbedTable } from '../tabify';
import { createFilter } from './create_filter';
/**
diff --git a/src/plugins/data/public/search/expressions/create_filter.test.ts b/src/plugins/data/public/search/expressions/create_filter.test.ts
index a7fd67983cb92..7968c80628531 100644
--- a/src/plugins/data/public/search/expressions/create_filter.test.ts
+++ b/src/plugins/data/public/search/expressions/create_filter.test.ts
@@ -17,11 +17,17 @@
* under the License.
*/
+import {
+ AggConfigs,
+ IAggConfig,
+ TabbedTable,
+ isRangeFilter,
+ BytesFormat,
+ FieldFormatsGetConfigFn,
+} from '../../../common';
+import { mockAggTypesRegistry } from '../../../common/search/aggs/test_helpers';
+
import { createFilter } from './create_filter';
-import { AggConfigs, IAggConfig } from '../aggs';
-import { TabbedTable } from '../tabify';
-import { isRangeFilter, BytesFormat, FieldFormatsGetConfigFn } from '../../../common';
-import { mockAggTypesRegistry } from '../aggs/test_helpers';
describe('createFilter', () => {
let table: TabbedTable;
diff --git a/src/plugins/data/public/search/expressions/create_filter.ts b/src/plugins/data/public/search/expressions/create_filter.ts
index 94d84380e03df..09200c2e17b31 100644
--- a/src/plugins/data/public/search/expressions/create_filter.ts
+++ b/src/plugins/data/public/search/expressions/create_filter.ts
@@ -17,9 +17,7 @@
* under the License.
*/
-import { IAggConfig } from '../aggs';
-import { TabbedTable } from '../tabify';
-import { Filter } from '../../../common';
+import { Filter, IAggConfig, TabbedTable } from '../../../common';
const getOtherBucketFilterTerms = (table: TabbedTable, columnIndex: number, rowIndex: number) => {
if (rowIndex === -1) {
diff --git a/src/plugins/data/public/search/expressions/esaggs.ts b/src/plugins/data/public/search/expressions/esaggs.ts
index 690f6b1df11c3..50fbb114b39fd 100644
--- a/src/plugins/data/public/search/expressions/esaggs.ts
+++ b/src/plugins/data/public/search/expressions/esaggs.ts
@@ -19,15 +19,11 @@
import { get, hasIn } from 'lodash';
import { i18n } from '@kbn/i18n';
-
import { KibanaDatatable, KibanaDatatableColumn } from 'src/plugins/expressions/public';
import { calculateObjectHash } from '../../../../../plugins/kibana_utils/public';
import { PersistedState } from '../../../../../plugins/visualizations/public';
import { Adapters } from '../../../../../plugins/inspector/public';
-import { IAggConfigs } from '../aggs';
-import { ISearchSource } from '../search_source';
-import { tabifyAggResponse } from '../tabify';
import {
calculateBounds,
EsaggsExpressionFunctionDefinition,
@@ -38,6 +34,13 @@ import {
Query,
TimeRange,
} from '../../../common';
+import {
+ getRequestInspectorStats,
+ getResponseInspectorStats,
+ IAggConfigs,
+ tabifyAggResponse,
+} from '../../../common/search';
+
import { FilterManager } from '../../query';
import {
getFieldFormats,
@@ -45,8 +48,9 @@ import {
getQueryService,
getSearchService,
} from '../../services';
+import { ISearchSource } from '../search_source';
import { buildTabularInspectorData } from './build_tabular_inspector_data';
-import { getRequestInspectorStats, getResponseInspectorStats, serializeAggConfig } from './utils';
+import { serializeAggConfig } from './utils';
export interface RequestHandlerParams {
searchSource: ISearchSource;
diff --git a/src/plugins/data/public/search/expressions/utils/index.ts b/src/plugins/data/public/search/expressions/utils/index.ts
index 0fd51f3e158a6..094536fc18437 100644
--- a/src/plugins/data/public/search/expressions/utils/index.ts
+++ b/src/plugins/data/public/search/expressions/utils/index.ts
@@ -17,5 +17,4 @@
* under the License.
*/
-export * from './courier_inspector_stats';
export * from './serialize_agg_config';
diff --git a/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts b/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts
index 78b4935077d10..6ba323b65783f 100644
--- a/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts
+++ b/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts
@@ -18,7 +18,7 @@
*/
import { KibanaDatatableColumnMeta } from '../../../../../../plugins/expressions/public';
-import { IAggConfig } from '../../aggs';
+import { IAggConfig } from '../../../../common';
import { IndexPattern } from '../../../index_patterns';
import { getSearchService } from '../../../../public/services';
diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts
index ae028df31e401..32bcd8a279036 100644
--- a/src/plugins/data/public/search/index.ts
+++ b/src/plugins/data/public/search/index.ts
@@ -17,9 +17,7 @@
* under the License.
*/
-export * from './aggs';
export * from './expressions';
-export * from './tabify';
export {
ISearch,
diff --git a/src/plugins/data/public/search/search_service.test.ts b/src/plugins/data/public/search/search_service.test.ts
index f0a017847e06a..e6897a16a353a 100644
--- a/src/plugins/data/public/search/search_service.test.ts
+++ b/src/plugins/data/public/search/search_service.test.ts
@@ -19,9 +19,8 @@
import { coreMock } from '../../../../core/public/mocks';
import { CoreSetup, CoreStart } from '../../../../core/public';
-import { expressionsPluginMock } from '../../../../plugins/expressions/public/mocks';
-import { SearchService } from './search_service';
+import { SearchService, SearchServiceSetupDependencies } from './search_service';
describe('Search service', () => {
let searchService: SearchService;
@@ -36,11 +35,12 @@ describe('Search service', () => {
describe('setup()', () => {
it('exposes proper contract', async () => {
- const setup = searchService.setup(mockCoreSetup, {
+ const setup = searchService.setup(mockCoreSetup, ({
packageInfo: { version: '8' },
- expressions: expressionsPluginMock.createSetupContract(),
- } as any);
+ registerFunction: jest.fn(),
+ } as unknown) as SearchServiceSetupDependencies);
expect(setup).toHaveProperty('aggs');
+ expect(setup).toHaveProperty('usageCollector');
expect(setup).toHaveProperty('__enhance');
});
});
@@ -50,6 +50,7 @@ describe('Search service', () => {
const start = searchService.start(mockCoreStart, {
indexPatterns: {},
} as any);
+ expect(start).toHaveProperty('aggs');
expect(start).toHaveProperty('search');
});
});
diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts
index 4c94925b66d6e..bd9c1b1253fe2 100644
--- a/src/plugins/data/public/search/search_service.ts
+++ b/src/plugins/data/public/search/search_service.ts
@@ -17,80 +17,43 @@
* under the License.
*/
-import { Plugin, CoreSetup, CoreStart, PackageInfo } from '../../../../core/public';
+import { Plugin, CoreSetup, CoreStart, PackageInfo } from 'src/core/public';
import { ISearchSetup, ISearchStart, SearchEnhancements } from './types';
-import { ExpressionsSetup } from '../../../../plugins/expressions/public';
import { createSearchSource, SearchSource, SearchSourceDependencies } from './search_source';
import { getEsClient, LegacyApiCaller } from './legacy';
-import { getForceNow } from '../query/timefilter/lib/get_force_now';
-import { calculateBounds, TimeRange } from '../../common/query';
-
+import { AggsService, AggsSetupDependencies, AggsStartDependencies } from './aggs';
import { IndexPatternsContract } from '../index_patterns/index_patterns';
-import { GetInternalStartServicesFn } from '../types';
import { ISearchInterceptor, SearchInterceptor } from './search_interceptor';
-import {
- getAggTypes,
- getAggTypesFunctions,
- AggTypesRegistry,
- AggConfigs,
- getCalculateAutoTimeExpression,
-} from './aggs';
import { ISearchGeneric } from './types';
import { SearchUsageCollector, createUsageCollector } from './collectors';
import { UsageCollectionSetup } from '../../../usage_collection/public';
-interface SearchServiceSetupDependencies {
- expressions: ExpressionsSetup;
- usageCollection?: UsageCollectionSetup;
- getInternalStartServices: GetInternalStartServicesFn;
+/** @internal */
+export interface SearchServiceSetupDependencies {
packageInfo: PackageInfo;
+ registerFunction: AggsSetupDependencies['registerFunction'];
+ usageCollection?: UsageCollectionSetup;
}
-interface SearchServiceStartDependencies {
+/** @internal */
+export interface SearchServiceStartDependencies {
+ fieldFormats: AggsStartDependencies['fieldFormats'];
indexPatterns: IndexPatternsContract;
}
export class SearchService implements Plugin {
private esClient?: LegacyApiCaller;
- private readonly aggTypesRegistry = new AggTypesRegistry();
+ private readonly aggsService = new AggsService();
private searchInterceptor!: ISearchInterceptor;
private usageCollector?: SearchUsageCollector;
- /**
- * getForceNow uses window.location, so we must have a separate implementation
- * of calculateBounds on the client and the server.
- */
- private calculateBounds = (timeRange: TimeRange) =>
- calculateBounds(timeRange, { forceNow: getForceNow() });
-
public setup(
core: CoreSetup,
- {
- expressions,
- usageCollection,
- packageInfo,
- getInternalStartServices,
- }: SearchServiceSetupDependencies
+ { packageInfo, registerFunction, usageCollection }: SearchServiceSetupDependencies
): ISearchSetup {
this.usageCollector = createUsageCollector(core, usageCollection);
this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo);
-
- const aggTypesSetup = this.aggTypesRegistry.setup();
-
- // register each agg type
- const aggTypes = getAggTypes({
- calculateBounds: this.calculateBounds,
- getInternalStartServices,
- uiSettings: core.uiSettings,
- });
- 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));
-
/**
* A global object that intercepts all searches and provides convenience methods for cancelling
* all pending search requests, as well as getting the number of pending search requests.
@@ -109,20 +72,21 @@ export class SearchService implements Plugin {
);
return {
+ aggs: this.aggsService.setup({
+ registerFunction,
+ uiSettings: core.uiSettings,
+ }),
usageCollector: this.usageCollector!,
__enhance: (enhancements: SearchEnhancements) => {
this.searchInterceptor = enhancements.searchInterceptor;
},
- aggs: {
- calculateAutoTimeExpression: getCalculateAutoTimeExpression(core.uiSettings),
- types: aggTypesSetup,
- },
};
}
- public start(core: CoreStart, dependencies: SearchServiceStartDependencies): ISearchStart {
- const aggTypesStart = this.aggTypesRegistry.start();
-
+ public start(
+ { application, http, injectedMetadata, notifications, uiSettings }: CoreStart,
+ { fieldFormats, indexPatterns }: SearchServiceStartDependencies
+ ): ISearchStart {
const search: ISearchGeneric = (request, options) => {
return this.searchInterceptor.search(request, options);
};
@@ -132,25 +96,17 @@ export class SearchService implements Plugin {
};
const searchSourceDependencies: SearchSourceDependencies = {
- uiSettings: core.uiSettings,
- injectedMetadata: core.injectedMetadata,
+ uiSettings,
+ injectedMetadata,
search,
legacySearch,
};
return {
- aggs: {
- calculateAutoTimeExpression: getCalculateAutoTimeExpression(core.uiSettings),
- createAggConfigs: (indexPattern, configStates = [], schemas) => {
- return new AggConfigs(indexPattern, configStates, {
- typesRegistry: aggTypesStart,
- });
- },
- types: aggTypesStart,
- },
+ aggs: this.aggsService.start({ fieldFormats, uiSettings }),
search,
searchSource: {
- create: createSearchSource(dependencies.indexPatterns, searchSourceDependencies),
+ create: createSearchSource(indexPatterns, searchSourceDependencies),
createEmpty: () => {
return new SearchSource({}, searchSourceDependencies);
},
@@ -159,5 +115,7 @@ export class SearchService implements Plugin {
};
}
- public stop() {}
+ public stop() {
+ this.aggsService.stop();
+ }
}
diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts
index d85d4c4e5c935..d1a4437943402 100644
--- a/src/plugins/data/public/search/types.ts
+++ b/src/plugins/data/public/search/types.ts
@@ -19,11 +19,11 @@
import { Observable } from 'rxjs';
import { PackageInfo } from 'kibana/server';
-import { SearchAggsSetup, SearchAggsStart } from './aggs';
import { LegacyApiCaller } from './legacy/es_client';
import { ISearchInterceptor } from './search_interceptor';
import { ISearchSource, SearchSourceFields } from './search_source';
import { SearchUsageCollector } from './collectors';
+import { AggsSetup, AggsSetupDependencies, AggsStartDependencies, AggsStart } from './aggs';
import {
IKibanaSearchRequest,
IKibanaSearchResponse,
@@ -31,9 +31,7 @@ import {
IEsSearchResponse,
} from '../../common/search';
import { IndexPatternsContract } from '../../common/index_patterns/index_patterns';
-import { ExpressionsSetup } from '../../../expressions/public';
import { UsageCollectionSetup } from '../../../usage_collection/public';
-import { GetInternalStartServicesFn } from '../types';
export interface ISearchOptions {
signal?: AbortSignal;
@@ -62,7 +60,7 @@ export interface SearchEnhancements {
* point.
*/
export interface ISearchSetup {
- aggs: SearchAggsSetup;
+ aggs: AggsSetup;
usageCollector?: SearchUsageCollector;
/**
* @internal
@@ -71,7 +69,7 @@ export interface ISearchSetup {
}
export interface ISearchStart {
- aggs: SearchAggsStart;
+ aggs: AggsStart;
search: ISearchGeneric;
searchSource: {
create: (fields?: SearchSourceFields) => Promise;
@@ -86,13 +84,15 @@ export interface ISearchStart {
export { SEARCH_EVENT_TYPE } from './collectors';
+/** @internal */
export interface SearchServiceSetupDependencies {
- expressions: ExpressionsSetup;
- usageCollection?: UsageCollectionSetup;
- getInternalStartServices: GetInternalStartServicesFn;
packageInfo: PackageInfo;
+ registerFunction: AggsSetupDependencies['registerFunction'];
+ usageCollection?: UsageCollectionSetup;
}
+/** @internal */
export interface SearchServiceStartDependencies {
+ fieldFormats: AggsStartDependencies['fieldFormats'];
indexPatterns: IndexPatternsContract;
}
diff --git a/src/plugins/data/public/types.ts b/src/plugins/data/public/types.ts
index c39b7d355d495..bffc10642eb47 100644
--- a/src/plugins/data/public/types.ts
+++ b/src/plugins/data/public/types.ts
@@ -82,15 +82,3 @@ export interface IDataPluginServices extends Partial {
storage: IStorageWrapper;
data: DataPublicPluginStart;
}
-
-/** @internal **/
-export interface InternalStartServices {
- readonly fieldFormats: FieldFormatsStart;
- readonly notifications: CoreStart['notifications'];
- readonly uiSettings: CoreStart['uiSettings'];
- readonly searchService: DataPublicPluginStart['search'];
- readonly injectedMetadata: CoreStart['injectedMetadata'];
-}
-
-/** @internal **/
-export type GetInternalStartServicesFn = () => InternalStartServices;
diff --git a/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.test.tsx b/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.test.tsx
index 18b1237895f79..aee8d1f4eac4d 100644
--- a/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.test.tsx
+++ b/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.test.tsx
@@ -22,7 +22,6 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { ShardFailureOpenModalButton } from './shard_failure_open_modal_button';
import { shardFailureRequest } from './__mocks__/shard_failure_request';
import { shardFailureResponse } from './__mocks__/shard_failure_response';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
describe('ShardFailureOpenModalButton', () => {
diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts
index 73ed88850d787..c3b06992dba0e 100644
--- a/src/plugins/data/server/index.ts
+++ b/src/plugins/data/server/index.ts
@@ -150,6 +150,16 @@ export {
*/
import {
+ // aggs
+ CidrMask,
+ intervalOptions,
+ isNumberType,
+ isStringType,
+ isType,
+ parentPipelineType,
+ propFilter,
+ siblingPipelineType,
+ termsAggFilter,
dateHistogramInterval,
InvalidEsCalendarIntervalError,
InvalidEsIntervalFormatError,
@@ -159,13 +169,41 @@ import {
parseEsInterval,
parseInterval,
toAbsoluteDates,
+ // expressions utils
+ getRequestInspectorStats,
+ getResponseInspectorStats,
+ // tabify
+ tabifyAggResponse,
+ tabifyGetColumns,
} from '../common';
export {
+ // aggs
+ AggGroupLabels,
+ AggGroupName,
+ AggGroupNames,
+ AggParam,
+ AggParamOption,
+ AggParamType,
+ AggConfigOptions,
+ BUCKET_TYPES,
EsaggsExpressionFunctionDefinition,
+ IAggConfig,
+ IAggConfigs,
+ IAggType,
+ IFieldParamType,
+ IMetricAggType,
+ METRIC_TYPES,
+ OptionedParamType,
+ OptionedValueProp,
ParsedInterval,
+ // search
IEsSearchRequest,
IEsSearchResponse,
+ // tabify
+ TabbedAggColumn,
+ TabbedAggRow,
+ TabbedTable,
} from '../common';
export {
@@ -182,16 +220,29 @@ export {
// Search namespace
export const search = {
aggs: {
+ CidrMask,
dateHistogramInterval,
+ intervalOptions,
InvalidEsCalendarIntervalError,
InvalidEsIntervalFormatError,
Ipv4Address,
+ isNumberType,
+ isStringType,
+ isType,
isValidEsInterval,
isValidInterval,
+ parentPipelineType,
parseEsInterval,
parseInterval,
+ propFilter,
+ siblingPipelineType,
+ termsAggFilter,
toAbsoluteDates,
},
+ getRequestInspectorStats,
+ getResponseInspectorStats,
+ tabifyAggResponse,
+ tabifyGetColumns,
};
/**
diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts
index 61d8e566d2d2b..5163bfcb17d40 100644
--- a/src/plugins/data/server/plugin.ts
+++ b/src/plugins/data/server/plugin.ts
@@ -17,13 +17,8 @@
* under the License.
*/
-import {
- PluginInitializerContext,
- CoreSetup,
- CoreStart,
- Plugin,
- Logger,
-} from '../../../core/server';
+import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from 'src/core/server';
+import { ExpressionsServerSetup } from 'src/plugins/expressions/server';
import { ConfigSchema } from '../config';
import { IndexPatternsService, IndexPatternsServiceStart } from './index_patterns';
import { ISearchSetup, ISearchStart } from './search';
@@ -48,10 +43,21 @@ export interface DataPluginStart {
}
export interface DataPluginSetupDependencies {
+ expressions: ExpressionsServerSetup;
usageCollection?: UsageCollectionSetup;
}
-export class DataServerPlugin implements Plugin {
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface DataPluginStartDependencies {}
+
+export class DataServerPlugin
+ implements
+ Plugin<
+ DataPluginSetup,
+ DataPluginStart,
+ DataPluginSetupDependencies,
+ DataPluginStartDependencies
+ > {
private readonly searchService: SearchService;
private readonly scriptsService: ScriptsService;
private readonly kqlTelemetryService: KqlTelemetryService;
@@ -70,8 +76,8 @@ export class DataServerPlugin implements Plugin,
- { usageCollection }: DataPluginSetupDependencies
+ core: CoreSetup,
+ { expressions, usageCollection }: DataPluginSetupDependencies
) {
this.indexPatterns.setup(core);
this.scriptsService.setup(core);
@@ -82,7 +88,10 @@ export class DataServerPlugin implements Plugin {
+ let service: AggsService;
+ let setupDeps: AggsSetupDependencies;
+ let startDeps: AggsStartDependencies;
+
+ beforeEach(() => {
+ service = new AggsService();
+ setupDeps = {
+ registerFunction: expressionsPluginMock.createSetupContract().registerFunction,
+ };
+ startDeps = {
+ fieldFormats: createFieldFormatsStartMock(),
+ uiSettings,
+ };
+ });
+
+ describe('setup()', () => {
+ test('exposes proper contract', () => {
+ const setup = service.setup(setupDeps);
+ expect(Object.keys(setup).length).toBe(1);
+ expect(setup).toHaveProperty('types');
+ });
+ });
+
+ describe('start()', () => {
+ test('exposes proper contract', async () => {
+ service.setup(setupDeps);
+ const start = service.start(startDeps);
+
+ expect(Object.keys(start).length).toBe(1);
+ expect(start).toHaveProperty('asScopedToClient');
+
+ const contract = await start.asScopedToClient(
+ savedObjects.getScopedClient({} as KibanaRequest)
+ );
+ expect(contract).toHaveProperty('calculateAutoTimeExpression');
+ expect(contract).toHaveProperty('createAggConfigs');
+ expect(contract).toHaveProperty('types');
+ });
+
+ test('types registry returns initialized agg types', async () => {
+ service.setup(setupDeps);
+ const start = await service
+ .start(startDeps)
+ .asScopedToClient(savedObjects.getScopedClient({} as KibanaRequest));
+
+ expect(start.types.get('terms').name).toBe('terms');
+ });
+
+ test('registers default agg types', async () => {
+ service.setup(setupDeps);
+ const start = await service
+ .start(startDeps)
+ .asScopedToClient(savedObjects.getScopedClient({} as KibanaRequest));
+
+ const aggTypes = getAggTypes();
+ expect(start.types.getAll().buckets.length).toBe(aggTypes.buckets.length);
+ expect(start.types.getAll().metrics.length).toBe(aggTypes.metrics.length);
+ });
+
+ test('merges default agg types with types registered during setup', async () => {
+ const setup = service.setup(setupDeps);
+ setup.types.registerBucket(
+ 'foo',
+ () => ({ name: 'foo', type: 'buckets' } as BucketAggType)
+ );
+ setup.types.registerMetric(
+ 'bar',
+ () => ({ name: 'bar', type: 'metrics' } as MetricAggType)
+ );
+
+ const start = await service
+ .start(startDeps)
+ .asScopedToClient(savedObjects.getScopedClient({} as KibanaRequest));
+
+ const aggTypes = getAggTypes();
+ expect(start.types.getAll().buckets.length).toBe(aggTypes.buckets.length + 1);
+ expect(start.types.getAll().buckets.some(({ name }) => name === 'foo')).toBe(true);
+ expect(start.types.getAll().metrics.length).toBe(aggTypes.metrics.length + 1);
+ expect(start.types.getAll().metrics.some(({ name }) => name === 'bar')).toBe(true);
+ });
+ });
+});
diff --git a/src/plugins/data/server/search/aggs/aggs_service.ts b/src/plugins/data/server/search/aggs/aggs_service.ts
new file mode 100644
index 0000000000000..3e5cd8adb44a6
--- /dev/null
+++ b/src/plugins/data/server/search/aggs/aggs_service.ts
@@ -0,0 +1,122 @@
+/*
+ * 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 { pick } from 'lodash';
+
+import { UiSettingsServiceStart, SavedObjectsClientContract } from 'src/core/server';
+import { ExpressionsServiceSetup } from 'src/plugins/expressions/common';
+import {
+ AggsCommonService,
+ AggConfigs,
+ AggTypesDependencies,
+ aggsRequiredUiSettings,
+ calculateBounds,
+ TimeRange,
+} from '../../../common';
+import { FieldFormatsStart } from '../../field_formats';
+import { AggsSetup, AggsStart } from './types';
+
+/** @internal */
+export interface AggsSetupDependencies {
+ registerFunction: ExpressionsServiceSetup['registerFunction'];
+}
+
+/** @internal */
+export interface AggsStartDependencies {
+ fieldFormats: FieldFormatsStart;
+ uiSettings: UiSettingsServiceStart;
+}
+
+/**
+ * The aggs service provides a means of modeling and manipulating the various
+ * Elasticsearch aggregations supported by Kibana, providing the ability to
+ * output the correct DSL when you are ready to send your request to ES.
+ */
+export class AggsService {
+ private readonly aggsCommonService = new AggsCommonService();
+
+ /**
+ * getForceNow uses window.location on the client, so we must have a
+ * separate implementation of calculateBounds on the server.
+ */
+ private calculateBounds = (timeRange: TimeRange) => calculateBounds(timeRange, {});
+
+ public setup({ registerFunction }: AggsSetupDependencies): AggsSetup {
+ return this.aggsCommonService.setup({ registerFunction });
+ }
+
+ public start({ fieldFormats, uiSettings }: AggsStartDependencies): AggsStart {
+ return {
+ asScopedToClient: async (savedObjectsClient: SavedObjectsClientContract) => {
+ const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient);
+ const formats = await fieldFormats.fieldFormatServiceFactory(uiSettingsClient);
+
+ // cache ui settings, only including items which are explicitly needed by aggs
+ const uiSettingsCache = pick(await uiSettingsClient.getAll(), aggsRequiredUiSettings);
+ const getConfig = (key: string): T => {
+ return uiSettingsCache[key];
+ };
+
+ const { calculateAutoTimeExpression, types } = this.aggsCommonService.start({ getConfig });
+
+ const aggTypesDependencies: AggTypesDependencies = {
+ calculateBounds: this.calculateBounds,
+ getConfig,
+ getFieldFormatsStart: () => ({
+ deserialize: formats.deserialize,
+ getDefaultInstance: formats.getDefaultInstance,
+ }),
+ /**
+ * Date histogram and date range need to know whether we are using the
+ * default timezone, but `isDefault` is not currently offered on the
+ * server, so we need to manually check for the default value.
+ */
+ isDefaultTimezone: () => getConfig('dateFormat:tz') === 'Browser',
+ };
+
+ const typesRegistry = {
+ get: (name: string) => {
+ const type = types.get(name);
+ if (!type) {
+ return;
+ }
+ return type(aggTypesDependencies);
+ },
+ getAll: () => {
+ return {
+ // initialize each agg type on the fly
+ buckets: types.getAll().buckets.map((type) => type(aggTypesDependencies)),
+ metrics: types.getAll().metrics.map((type) => type(aggTypesDependencies)),
+ };
+ },
+ };
+
+ return {
+ calculateAutoTimeExpression,
+ createAggConfigs: (indexPattern, configStates = [], schemas) => {
+ return new AggConfigs(indexPattern, configStates, { typesRegistry });
+ },
+ types: typesRegistry,
+ };
+ },
+ };
+ }
+
+ public stop() {}
+}
diff --git a/src/plugins/data/server/search/aggs/index.ts b/src/plugins/data/server/search/aggs/index.ts
new file mode 100644
index 0000000000000..77d97c426260c
--- /dev/null
+++ b/src/plugins/data/server/search/aggs/index.ts
@@ -0,0 +1,21 @@
+/*
+ * 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 './aggs_service';
+export * from './types';
diff --git a/src/plugins/data/server/search/aggs/mocks.ts b/src/plugins/data/server/search/aggs/mocks.ts
new file mode 100644
index 0000000000000..b50e22fe87b7c
--- /dev/null
+++ b/src/plugins/data/server/search/aggs/mocks.ts
@@ -0,0 +1,81 @@
+/*
+ * 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 {
+ AggConfigs,
+ AggTypesRegistrySetup,
+ AggTypesRegistryStart,
+ AggsCommonStart,
+ getCalculateAutoTimeExpression,
+} from '../../../common';
+import { AggsSetup, AggsStart } from './types';
+
+import { mockAggTypesRegistry } from '../../../common/search/aggs/test_helpers';
+
+const getConfig = jest.fn();
+
+const aggTypeBaseParamMock = () => ({
+ name: 'some_param',
+ type: 'some_param_type',
+ displayName: 'some_agg_type_param',
+ required: false,
+ advanced: false,
+ default: {},
+ write: jest.fn(),
+ serialize: jest.fn().mockImplementation(() => {}),
+ deserialize: jest.fn().mockImplementation(() => {}),
+ options: [],
+});
+
+const aggTypeConfigMock = () => ({
+ name: 'some_name',
+ title: 'some_title',
+ params: [aggTypeBaseParamMock()],
+});
+
+export const aggTypesRegistrySetupMock = (): AggTypesRegistrySetup => ({
+ registerBucket: jest.fn(),
+ registerMetric: jest.fn(),
+});
+
+export const aggTypesRegistryStartMock = (): AggTypesRegistryStart => ({
+ get: jest.fn().mockImplementation(aggTypeConfigMock),
+ getAll: jest.fn().mockImplementation(() => ({
+ buckets: [aggTypeConfigMock()],
+ metrics: [aggTypeConfigMock()],
+ })),
+});
+
+export const searchAggsSetupMock = (): AggsSetup => ({
+ types: aggTypesRegistrySetupMock(),
+});
+
+const commonStartMock = (): AggsCommonStart => ({
+ calculateAutoTimeExpression: getCalculateAutoTimeExpression(getConfig),
+ createAggConfigs: jest.fn().mockImplementation((indexPattern, configStates = [], schemas) => {
+ return new AggConfigs(indexPattern, configStates, {
+ typesRegistry: mockAggTypesRegistry(),
+ });
+ }),
+ types: mockAggTypesRegistry(),
+});
+
+export const searchAggsStartMock = (): AggsStart => ({
+ asScopedToClient: jest.fn().mockResolvedValue(commonStartMock()),
+});
diff --git a/src/plugins/data/server/search/aggs/types.ts b/src/plugins/data/server/search/aggs/types.ts
new file mode 100644
index 0000000000000..1b21d948b25d9
--- /dev/null
+++ b/src/plugins/data/server/search/aggs/types.ts
@@ -0,0 +1,27 @@
+/*
+ * 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 { SavedObjectsClientContract } from 'src/core/server';
+import { AggsCommonSetup, AggsStart as Start } from '../../../common';
+
+export type AggsSetup = AggsCommonSetup;
+
+export interface AggsStart {
+ asScopedToClient: (savedObjectsClient: SavedObjectsClientContract) => Promise;
+}
diff --git a/src/plugins/data/server/search/index.ts b/src/plugins/data/server/search/index.ts
index cea2714671f0b..4a3990621ca39 100644
--- a/src/plugins/data/server/search/index.ts
+++ b/src/plugins/data/server/search/index.ts
@@ -22,3 +22,5 @@ export { ISearchStrategy, ISearchOptions, ISearchSetup, ISearchStart } from './t
export { getDefaultSearchParams, getTotalLoaded } from './es_search';
export { usageProvider, SearchUsage } from './collectors';
+
+export * from './aggs';
diff --git a/src/plugins/data/server/search/mocks.ts b/src/plugins/data/server/search/mocks.ts
index b210df3c55db9..578a170f468bf 100644
--- a/src/plugins/data/server/search/mocks.ts
+++ b/src/plugins/data/server/search/mocks.ts
@@ -17,14 +17,19 @@
* under the License.
*/
-export function createSearchSetupMock() {
+import { ISearchSetup, ISearchStart } from './types';
+import { searchAggsSetupMock, searchAggsStartMock } from './aggs/mocks';
+
+export function createSearchSetupMock(): jest.Mocked {
return {
+ aggs: searchAggsSetupMock(),
registerSearchStrategy: jest.fn(),
};
}
-export function createSearchStartMock() {
+export function createSearchStartMock(): jest.Mocked {
return {
+ aggs: searchAggsStartMock(),
getSearchStrategy: jest.fn(),
search: jest.fn(),
};
diff --git a/src/plugins/data/server/search/search_service.test.ts b/src/plugins/data/server/search/search_service.test.ts
index be00b7409fe4a..030f37d0f7c46 100644
--- a/src/plugins/data/server/search/search_service.test.ts
+++ b/src/plugins/data/server/search/search_service.test.ts
@@ -17,15 +17,18 @@
* under the License.
*/
+import { CoreSetup, CoreStart } from '../../../../core/server';
import { coreMock } from '../../../../core/server/mocks';
-import { SearchService } from './search_service';
-import { CoreSetup } from '../../../../core/server';
import { DataPluginStart } from '../plugin';
+import { createFieldFormatsStartMock } from '../field_formats/mocks';
+
+import { SearchService, SearchServiceSetupDependencies } from './search_service';
describe('Search service', () => {
let plugin: SearchService;
let mockCoreSetup: MockedKeys>;
+ let mockCoreStart: MockedKeys;
beforeEach(() => {
const mockLogger: any = {
@@ -33,19 +36,27 @@ describe('Search service', () => {
};
plugin = new SearchService(coreMock.createPluginInitializerContext({}), mockLogger);
mockCoreSetup = coreMock.createSetup();
+ mockCoreStart = coreMock.createStart();
});
describe('setup()', () => {
it('exposes proper contract', async () => {
- const setup = plugin.setup(mockCoreSetup, {});
+ const setup = plugin.setup(mockCoreSetup, ({
+ packageInfo: { version: '8' },
+ registerFunction: jest.fn(),
+ } as unknown) as SearchServiceSetupDependencies);
+ expect(setup).toHaveProperty('aggs');
expect(setup).toHaveProperty('registerSearchStrategy');
});
});
describe('start()', () => {
it('exposes proper contract', async () => {
- const setup = plugin.start();
- expect(setup).toHaveProperty('getSearchStrategy');
+ const start = plugin.start(mockCoreStart, {
+ fieldFormats: createFieldFormatsStartMock(),
+ });
+ expect(start).toHaveProperty('aggs');
+ expect(start).toHaveProperty('getSearchStrategy');
});
});
});
diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts
index 9dc47369567af..a8b1cdd608a84 100644
--- a/src/plugins/data/server/search/search_service.ts
+++ b/src/plugins/data/server/search/search_service.ts
@@ -18,13 +18,18 @@
*/
import {
+ CoreSetup,
+ CoreStart,
+ Logger,
Plugin,
PluginInitializerContext,
- CoreSetup,
RequestHandlerContext,
- Logger,
} from '../../../../core/server';
import { ISearchSetup, ISearchStart, ISearchStrategy } from './types';
+
+import { AggsService, AggsSetupDependencies } from './aggs';
+
+import { FieldFormatsStart } from '../field_formats';
import { registerSearchRoute } from './routes';
import { ES_SEARCH_STRATEGY, esSearchStrategyProvider } from './es_search';
import { DataPluginStart } from '../plugin';
@@ -38,7 +43,19 @@ interface StrategyMap {
[name: string]: ISearchStrategy;
}
+/** @internal */
+export interface SearchServiceSetupDependencies {
+ registerFunction: AggsSetupDependencies['registerFunction'];
+ usageCollection?: UsageCollectionSetup;
+}
+
+/** @internal */
+export interface SearchServiceStartDependencies {
+ fieldFormats: FieldFormatsStart;
+}
+
export class SearchService implements Plugin {
+ private readonly aggsService = new AggsService();
private searchStrategies: StrategyMap = {};
constructor(
@@ -48,7 +65,7 @@ export class SearchService implements Plugin {
public setup(
core: CoreSetup,
- { usageCollection }: { usageCollection?: UsageCollectionSetup }
+ { registerFunction, usageCollection }: SearchServiceSetupDependencies
): ISearchSetup {
const usage = usageCollection ? usageProvider(core) : undefined;
@@ -68,7 +85,11 @@ export class SearchService implements Plugin {
registerSearchRoute(core);
- return { registerSearchStrategy: this.registerSearchStrategy, usage };
+ return {
+ aggs: this.aggsService.setup({ registerFunction }),
+ registerSearchStrategy: this.registerSearchStrategy,
+ usage,
+ };
}
private search(
@@ -83,8 +104,12 @@ export class SearchService implements Plugin {
);
}
- public start(): ISearchStart {
+ public start(
+ { uiSettings }: CoreStart,
+ { fieldFormats }: SearchServiceStartDependencies
+ ): ISearchStart {
return {
+ aggs: this.aggsService.start({ fieldFormats, uiSettings }),
getSearchStrategy: this.getSearchStrategy,
search: (
context: RequestHandlerContext,
@@ -96,7 +121,9 @@ export class SearchService implements Plugin {
};
}
- public stop() {}
+ public stop() {
+ this.aggsService.stop();
+ }
private registerSearchStrategy = (name: string, strategy: ISearchStrategy) => {
this.logger.info(`Register strategy ${name}`);
diff --git a/src/plugins/data/server/search/types.ts b/src/plugins/data/server/search/types.ts
index 76afd7e8c951c..fe54975d76624 100644
--- a/src/plugins/data/server/search/types.ts
+++ b/src/plugins/data/server/search/types.ts
@@ -19,6 +19,7 @@
import { RequestHandlerContext } from '../../../../core/server';
import { IKibanaSearchResponse, IKibanaSearchRequest } from '../../common/search';
+import { AggsSetup, AggsStart } from './aggs';
import { SearchUsage } from './collectors/usage';
import { IEsSearchRequest, IEsSearchResponse } from './es_search';
@@ -31,6 +32,7 @@ export interface ISearchOptions {
}
export interface ISearchSetup {
+ aggs: AggsSetup;
/**
* Extension point exposed for other plugins to register their own search
* strategies.
@@ -44,6 +46,7 @@ export interface ISearchSetup {
}
export interface ISearchStart {
+ aggs: AggsStart;
/**
* Get other registered search strategies. For example, if a new strategy needs to use the
* already-registered ES search strategy, it can use this function to accomplish that.
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index 37d569a4bf9fe..9c8a79f27a9db 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -4,7 +4,9 @@
```ts
+import { $Values } from '@kbn/utility-types';
import { ApiResponse } from '@elastic/elasticsearch/lib/Transport';
+import { Assign } from '@kbn/utility-types';
import Boom from 'boom';
import { BulkIndexDocumentsParams } from 'elasticsearch';
import { CatAliasesParams } from 'elasticsearch';
@@ -22,7 +24,6 @@ import { CatTasksParams } from 'elasticsearch';
import { CatThreadPoolParams } from 'elasticsearch';
import { ClearScrollParams } from 'elasticsearch';
import { Client } from 'elasticsearch';
-import { ClientOptions } from '@elastic/elasticsearch';
import { ClusterAllocationExplainParams } from 'elasticsearch';
import { ClusterGetSettingsParams } from 'elasticsearch';
import { ClusterHealthParams } from 'elasticsearch';
@@ -31,19 +32,23 @@ import { ClusterPutSettingsParams } from 'elasticsearch';
import { ClusterRerouteParams } from 'elasticsearch';
import { ClusterStateParams } from 'elasticsearch';
import { ClusterStatsParams } from 'elasticsearch';
-import { ConfigOptions } from 'elasticsearch';
+import { CoreSetup } from 'src/core/server';
import { CoreSetup as CoreSetup_2 } from 'kibana/server';
+import { CoreStart } from 'src/core/server';
import { CountParams } from 'elasticsearch';
import { CreateDocumentParams } from 'elasticsearch';
import { DeleteDocumentByQueryParams } from 'elasticsearch';
import { DeleteDocumentParams } from 'elasticsearch';
import { DeleteScriptParams } from 'elasticsearch';
import { DeleteTemplateParams } from 'elasticsearch';
-import { DetailedPeerCertificate } from 'tls';
import { Duration } from 'moment';
+import { Ensure } from '@kbn/utility-types';
import { ErrorToastOptions } from 'src/core/public/notifications';
import { ExistsParams } from 'elasticsearch';
import { ExplainParams } from 'elasticsearch';
+import { ExpressionAstFunction } from 'src/plugins/expressions/common';
+import { ExpressionsServerSetup } from 'src/plugins/expressions/server';
+import { FetchOptions } from 'src/plugins/data/public';
import { FieldStatsParams } from 'elasticsearch';
import { GenericParams } from 'elasticsearch';
import { GetParams } from 'elasticsearch';
@@ -94,13 +99,15 @@ import { IngestDeletePipelineParams } from 'elasticsearch';
import { IngestGetPipelineParams } from 'elasticsearch';
import { IngestPutPipelineParams } from 'elasticsearch';
import { IngestSimulateParams } from 'elasticsearch';
+import { ISearchSource } from 'src/plugins/data/public';
import { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { KibanaConfigType as KibanaConfigType_2 } from 'src/core/server/kibana_config';
-import { KibanaRequest as KibanaRequest_2 } from 'kibana/server';
+import { KibanaRequest } from 'kibana/server';
import { LegacyAPICaller as LegacyAPICaller_2 } from 'kibana/server';
import { Logger as Logger_2 } from 'kibana/server';
import { MGetParams } from 'elasticsearch';
import { MGetResponse } from 'elasticsearch';
+import { Moment } from 'moment';
import moment from 'moment';
import { MSearchParams } from 'elasticsearch';
import { MSearchResponse } from 'elasticsearch';
@@ -109,27 +116,26 @@ import { MTermVectorsParams } from 'elasticsearch';
import { NodesHotThreadsParams } from 'elasticsearch';
import { NodesInfoParams } from 'elasticsearch';
import { NodesStatsParams } from 'elasticsearch';
-import { ObjectType } from '@kbn/config-schema';
import { Observable } from 'rxjs';
-import { PeerCertificate } from 'tls';
import { PingParams } from 'elasticsearch';
+import { Plugin as Plugin_2 } from 'src/core/server';
+import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/server';
import { PutScriptParams } from 'elasticsearch';
import { PutTemplateParams } from 'elasticsearch';
import { RecursiveReadonly } from '@kbn/utility-types';
import { ReindexParams } from 'elasticsearch';
import { ReindexRethrottleParams } from 'elasticsearch';
import { RenderSearchTemplateParams } from 'elasticsearch';
-import { Request } from 'hapi';
-import { ResponseObject } from 'hapi';
-import { ResponseToolkit } from 'hapi';
-import { SavedObject as SavedObject_2 } from 'src/core/server';
-import { SchemaTypeError } from '@kbn/config-schema';
+import { RequestAdapter } from 'src/plugins/inspector/common';
+import { RequestStatistics } from 'src/plugins/inspector/common';
+import { SavedObject } from 'src/core/server';
+import { SavedObjectsClientContract as SavedObjectsClientContract_2 } from 'src/core/server';
import { ScrollParams } from 'elasticsearch';
import { SearchParams } from 'elasticsearch';
import { SearchResponse } from 'elasticsearch';
import { SearchShardsParams } from 'elasticsearch';
import { SearchTemplateParams } from 'elasticsearch';
-import { ShallowPromise } from '@kbn/utility-types';
+import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common';
import { ShardsResponse } from 'elasticsearch';
import { SnapshotCreateParams } from 'elasticsearch';
import { SnapshotCreateRepositoryParams } from 'elasticsearch';
@@ -140,7 +146,6 @@ import { SnapshotGetRepositoryParams } from 'elasticsearch';
import { SnapshotRestoreParams } from 'elasticsearch';
import { SnapshotStatusParams } from 'elasticsearch';
import { SnapshotVerifyRepositoryParams } from 'elasticsearch';
-import { Stream } from 'stream';
import { SuggestParams } from 'elasticsearch';
import { TasksCancelParams } from 'elasticsearch';
import { TasksGetParams } from 'elasticsearch';
@@ -156,7 +161,96 @@ import { Unit } from '@elastic/datemath';
import { UnwrapPromiseOrReturn } from '@kbn/utility-types';
import { UpdateDocumentByQueryParams } from 'elasticsearch';
import { UpdateDocumentParams } from 'elasticsearch';
-import { Url } from 'url';
+
+// 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 type AggConfigOptions = Assign;
+
+// Warning: (ae-missing-release-tag) "AggGroupLabels" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const AggGroupLabels: {
+ buckets: string;
+ metrics: string;
+ none: string;
+};
+
+// Warning: (ae-missing-release-tag) "AggGroupName" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export type AggGroupName = $Values;
+
+// Warning: (ae-missing-release-tag) "AggGroupNames" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const AggGroupNames: Readonly<{
+ Buckets: "buckets";
+ Metrics: "metrics";
+ None: "none";
+}>;
+
+// Warning: (ae-forgotten-export) The symbol "BaseParamType" needs to be exported by the entry point index.d.ts
+// Warning: (ae-missing-release-tag) "AggParam" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export type AggParam = BaseParamType;
+
+// Warning: (ae-missing-release-tag) "AggParamOption" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export interface AggParamOption {
+ // (undocumented)
+ display: string;
+ // Warning: (ae-forgotten-export) The symbol "AggConfig" needs to be exported by the entry point index.d.ts
+ //
+ // (undocumented)
+ enabled?(agg: AggConfig): boolean;
+ // (undocumented)
+ val: string;
+}
+
+// Warning: (ae-missing-release-tag) "AggParamType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export class AggParamType extends BaseParamType {
+ constructor(config: Record);
+ // (undocumented)
+ allowedAggs: string[];
+ // (undocumented)
+ makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig;
+}
+
+// Warning: (ae-missing-release-tag) "BUCKET_TYPES" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export enum BUCKET_TYPES {
+ // (undocumented)
+ DATE_HISTOGRAM = "date_histogram",
+ // (undocumented)
+ DATE_RANGE = "date_range",
+ // (undocumented)
+ FILTER = "filter",
+ // (undocumented)
+ FILTERS = "filters",
+ // (undocumented)
+ GEOHASH_GRID = "geohash_grid",
+ // (undocumented)
+ GEOTILE_GRID = "geotile_grid",
+ // (undocumented)
+ HISTOGRAM = "histogram",
+ // (undocumented)
+ IP_RANGE = "ip_range",
+ // (undocumented)
+ RANGE = "range",
+ // (undocumented)
+ SIGNIFICANT_TERMS = "significant_terms",
+ // (undocumented)
+ TERMS = "terms"
+}
// Warning: (ae-missing-release-tag) "castEsToKbnFieldTypeName" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -369,6 +463,22 @@ export function getTotalLoaded({ total, failed, successful }: ShardsResponse): {
loaded: number;
};
+// Warning: (ae-missing-release-tag) "IAggConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public
+export type IAggConfig = AggConfig;
+
+// Warning: (ae-forgotten-export) The symbol "AggConfigs" needs to be exported by the entry point index.d.ts
+//
+// @internal
+export type IAggConfigs = AggConfigs;
+
+// Warning: (ae-forgotten-export) The symbol "AggType" needs to be exported by the entry point index.d.ts
+// Warning: (ae-missing-release-tag) "IAggType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export type IAggType = AggType;
+
// Warning: (ae-forgotten-export) The symbol "IKibanaSearchRequest" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "IEsSearchRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -398,6 +508,12 @@ export interface IEsSearchResponse extends IKibanaSearchResponse {
// @public (undocumented)
export type IFieldFormatsRegistry = PublicMethodsOf;
+// Warning: (ae-forgotten-export) The symbol "FieldParamType" needs to be exported by the entry point index.d.ts
+// Warning: (ae-missing-release-tag) "IFieldParamType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export type IFieldParamType = FieldParamType;
+
// Warning: (ae-missing-release-tag) "IFieldSubType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -479,6 +595,12 @@ export interface IIndexPattern {
type?: string;
}
+// Warning: (ae-forgotten-export) The symbol "MetricAggType" needs to be exported by the entry point index.d.ts
+// Warning: (ae-missing-release-tag) "IMetricAggType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export type IMetricAggType = MetricAggType;
+
// Warning: (ae-missing-release-tag) "IndexPatternAttributes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public @deprecated
@@ -561,6 +683,10 @@ export interface ISearchOptions {
//
// @public (undocumented)
export interface ISearchSetup {
+ // Warning: (ae-forgotten-export) The symbol "AggsSetup" needs to be exported by the entry point index.d.ts
+ //
+ // (undocumented)
+ aggs: AggsSetup;
registerSearchStrategy: (name: string, strategy: ISearchStrategy) => void;
usage?: SearchUsage;
}
@@ -569,6 +695,10 @@ export interface ISearchSetup {
//
// @public (undocumented)
export interface ISearchStart {
+ // Warning: (ae-forgotten-export) The symbol "AggsStart" needs to be exported by the entry point index.d.ts
+ //
+ // (undocumented)
+ aggs: AggsStart;
getSearchStrategy: (name: string) => ISearchStrategy;
// Warning: (ae-forgotten-export) The symbol "RequestHandlerContext" needs to be exported by the entry point index.d.ts
//
@@ -632,6 +762,77 @@ export interface KueryNode {
type: keyof NodeTypes;
}
+// Warning: (ae-missing-release-tag) "METRIC_TYPES" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export enum METRIC_TYPES {
+ // (undocumented)
+ AVG = "avg",
+ // (undocumented)
+ AVG_BUCKET = "avg_bucket",
+ // (undocumented)
+ CARDINALITY = "cardinality",
+ // (undocumented)
+ COUNT = "count",
+ // (undocumented)
+ CUMULATIVE_SUM = "cumulative_sum",
+ // (undocumented)
+ DERIVATIVE = "derivative",
+ // (undocumented)
+ GEO_BOUNDS = "geo_bounds",
+ // (undocumented)
+ GEO_CENTROID = "geo_centroid",
+ // (undocumented)
+ MAX = "max",
+ // (undocumented)
+ MAX_BUCKET = "max_bucket",
+ // (undocumented)
+ MEDIAN = "median",
+ // (undocumented)
+ MIN = "min",
+ // (undocumented)
+ MIN_BUCKET = "min_bucket",
+ // (undocumented)
+ MOVING_FN = "moving_avg",
+ // (undocumented)
+ PERCENTILE_RANKS = "percentile_ranks",
+ // (undocumented)
+ PERCENTILES = "percentiles",
+ // (undocumented)
+ SERIAL_DIFF = "serial_diff",
+ // (undocumented)
+ STD_DEV = "std_dev",
+ // (undocumented)
+ SUM = "sum",
+ // (undocumented)
+ SUM_BUCKET = "sum_bucket",
+ // (undocumented)
+ TOP_HITS = "top_hits"
+}
+
+// Warning: (ae-missing-release-tag) "OptionedParamType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export class OptionedParamType extends BaseParamType {
+ constructor(config: Record);
+ // (undocumented)
+ options: OptionedValueProp[];
+}
+
+// Warning: (ae-missing-release-tag) "OptionedValueProp" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export interface OptionedValueProp {
+ // (undocumented)
+ disabled?: boolean;
+ // (undocumented)
+ isCompatible: (agg: IAggConfig) => boolean;
+ // (undocumented)
+ text: string;
+ // (undocumented)
+ value: string;
+}
+
// Warning: (ae-forgotten-export) The symbol "parseEsInterval" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "ParsedInterval" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -643,25 +844,20 @@ export type ParsedInterval = ReturnType;
// @public (undocumented)
export function parseInterval(interval: string): moment.Duration | null;
-// Warning: (ae-forgotten-export) The symbol "Plugin" needs to be exported by the entry point index.d.ts
+// 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)
-export class Plugin implements Plugin_2 {
- // Warning: (ae-forgotten-export) The symbol "PluginInitializerContext" needs to be exported by the entry point index.d.ts
- constructor(initializerContext: PluginInitializerContext);
- // Warning: (ae-forgotten-export) The symbol "CoreSetup" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "DataPluginSetupDependencies" needs to be exported by the entry point index.d.ts
- //
+export class Plugin implements Plugin_2 {
+ constructor(initializerContext: PluginInitializerContext_2);
// (undocumented)
- setup(core: CoreSetup, { usageCollection }: DataPluginSetupDependencies): {
+ setup(core: CoreSetup, { expressions, usageCollection }: DataPluginSetupDependencies): {
search: ISearchSetup;
fieldFormats: {
- register: (customFieldFormat: import("../common").FieldFormatInstanceType) => number;
+ register: (customFieldFormat: import("../public").FieldFormatInstanceType) => number;
};
};
- // Warning: (ae-forgotten-export) The symbol "CoreStart" needs to be exported by the entry point index.d.ts
- //
// (undocumented)
start(core: CoreStart): {
search: ISearchStart;
@@ -676,6 +872,8 @@ export class Plugin implements Plugin_2 {
stop(): void;
}
+// Warning: (ae-forgotten-export) The symbol "PluginInitializerContext" needs to be exported by the entry point index.d.ts
+//
// @public
export function plugin(initializerContext: PluginInitializerContext): Plugin;
@@ -734,16 +932,36 @@ export interface RefreshInterval {
// @public (undocumented)
export const search: {
aggs: {
+ CidrMask: typeof CidrMask;
dateHistogramInterval: typeof dateHistogramInterval;
+ intervalOptions: ({
+ display: string;
+ val: string;
+ enabled(agg: import("../common").IBucketAggConfig): boolean | "" | undefined;
+ } | {
+ display: string;
+ val: string;
+ })[];
InvalidEsCalendarIntervalError: typeof InvalidEsCalendarIntervalError;
InvalidEsIntervalFormatError: typeof InvalidEsIntervalFormatError;
Ipv4Address: typeof Ipv4Address;
+ isNumberType: (agg: import("../common").AggConfig) => boolean;
+ isStringType: (agg: import("../common").AggConfig) => boolean;
+ isType: (...types: string[]) => (agg: import("../common").AggConfig) => boolean;
isValidEsInterval: typeof isValidEsInterval;
isValidInterval: typeof isValidInterval;
+ parentPipelineType: string;
parseEsInterval: typeof parseEsInterval;
parseInterval: typeof parseInterval;
+ propFilter: typeof propFilter;
+ siblingPipelineType: string;
+ termsAggFilter: string[];
toAbsoluteDates: typeof toAbsoluteDates;
};
+ getRequestInspectorStats: typeof getRequestInspectorStats;
+ getResponseInspectorStats: typeof getResponseInspectorStats;
+ tabifyAggResponse: typeof tabifyAggResponse;
+ tabifyGetColumns: typeof tabifyGetColumns;
};
// Warning: (ae-missing-release-tag) "SearchUsage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -761,6 +979,27 @@ export interface SearchUsage {
// @public (undocumented)
export function shouldReadFieldFromDocValues(aggregatable: boolean, esType: string): boolean;
+// @public (undocumented)
+export interface TabbedAggColumn {
+ // (undocumented)
+ aggConfig: IAggConfig;
+ // (undocumented)
+ id: string;
+ // (undocumented)
+ name: string;
+}
+
+// @public (undocumented)
+export type TabbedAggRow = Record;
+
+// @public (undocumented)
+export interface TabbedTable {
+ // (undocumented)
+ columns: TabbedAggColumn[];
+ // (undocumented)
+ rows: TabbedAggRow[];
+}
+
// Warning: (ae-missing-release-tag) "TimeRange" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -836,13 +1075,19 @@ export function usageProvider(core: CoreSetup_2): SearchUsage;
// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:127:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:127:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:185:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:186:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:187:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:188:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:189:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:190:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/server/index.ts:193:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:221:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:221:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:221:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:221:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:223:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:224:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:233:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:234:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:235:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:239:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:240:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:244:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
+// src/plugins/data/server/index.ts:247:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
// (No @packageDocumentation comment for this package)
diff --git a/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar.test.tsx b/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar.test.tsx
index 8976f8ea3c107..ab7adba193d87 100644
--- a/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar.test.tsx
+++ b/src/plugins/discover/public/application/angular/context/components/action_bar/action_bar.test.tsx
@@ -20,7 +20,6 @@
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { ActionBar, ActionBarProps } from './action_bar';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE } from '../../query_parameters/constants';
diff --git a/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx
index 524161c77cbf8..2cd829d89f78e 100644
--- a/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx
+++ b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx
@@ -20,7 +20,6 @@
import React from 'react';
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
import { ToolBarPagerButtons } from './tool_bar_pager_buttons';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
test('it renders ToolBarPagerButtons', () => {
diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.test.tsx b/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.test.tsx
index b201bea26503e..224e249a274cd 100644
--- a/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.test.tsx
+++ b/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header.test.tsx
@@ -20,7 +20,6 @@
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { TableHeader } from './table_header';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import { SortOrder } from './helpers';
import { IndexPattern, IFieldType } from '../../../../../kibana_services';
diff --git a/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx b/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx
index 1c9439bc34e58..1cc8247957512 100644
--- a/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx
+++ b/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx
@@ -22,7 +22,6 @@ import { ReactWrapper } from 'enzyme';
import { ContextErrorMessage } from './context_error_message';
// @ts-ignore
import { FAILURE_REASONS, LOADING_STATUS } from '../../angular/context/query';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
describe('loading spinner', function () {
diff --git a/src/plugins/discover/public/application/components/doc/doc.test.tsx b/src/plugins/discover/public/application/components/doc/doc.test.tsx
index 0bc621714c70f..c9fa551f61aca 100644
--- a/src/plugins/discover/public/application/components/doc/doc.test.tsx
+++ b/src/plugins/discover/public/application/components/doc/doc.test.tsx
@@ -20,7 +20,6 @@ import React from 'react';
import { act } from 'react-dom/test-utils';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { ReactWrapper } from 'enzyme';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import { Doc, DocProps } from './doc';
diff --git a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx
index 3710ce72533db..9115915690324 100644
--- a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx
+++ b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.test.tsx
@@ -19,7 +19,6 @@
import React from 'react';
import { mount, shallow } from 'enzyme';
import { DocViewer } from './doc_viewer';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import { getDocViewsRegistry } from '../../../kibana_services';
import { DocViewRenderProps } from '../../doc_views/doc_views_types';
diff --git a/src/plugins/discover/public/application/components/hits_counter/hits_counter.test.tsx b/src/plugins/discover/public/application/components/hits_counter/hits_counter.test.tsx
index 84ad19dbddcbf..c2eb4f08cf549 100644
--- a/src/plugins/discover/public/application/components/hits_counter/hits_counter.test.tsx
+++ b/src/plugins/discover/public/application/components/hits_counter/hits_counter.test.tsx
@@ -20,7 +20,6 @@ import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { ReactWrapper } from 'enzyme';
import { HitsCounter, HitsCounterProps } from './hits_counter';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
describe('hits counter', function () {
diff --git a/src/plugins/discover/public/application/components/loading_spinner/loading_spinner.test.tsx b/src/plugins/discover/public/application/components/loading_spinner/loading_spinner.test.tsx
index 3321ac764a05b..e996da5fe0e3c 100644
--- a/src/plugins/discover/public/application/components/loading_spinner/loading_spinner.test.tsx
+++ b/src/plugins/discover/public/application/components/loading_spinner/loading_spinner.test.tsx
@@ -20,7 +20,6 @@ import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { ReactWrapper } from 'enzyme';
import { LoadingSpinner } from './loading_spinner';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
describe('loading spinner', function () {
diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx
index 3f12a8c0fa769..e1abbfd7657d0 100644
--- a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx
+++ b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx
@@ -18,7 +18,6 @@
*/
import React from 'react';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
// @ts-ignore
import StubIndexPattern from 'test_utils/stub_index_pattern';
diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_search.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_search.test.tsx
index 654df5bfa9ee9..625d6833406eb 100644
--- a/src/plugins/discover/public/application/components/sidebar/discover_field_search.test.tsx
+++ b/src/plugins/discover/public/application/components/sidebar/discover_field_search.test.tsx
@@ -19,7 +19,6 @@
import React, { EventHandler, MouseEvent as ReactMouseEvent } from 'react';
import { act } from 'react-dom/test-utils';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import { DiscoverFieldSearch, Props } from './discover_field_search';
import { EuiButtonGroupProps, EuiPopover } from '@elastic/eui';
diff --git a/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.test.tsx
index 24e6f5993a0a5..a1b231c2d4479 100644
--- a/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.test.tsx
+++ b/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.test.tsx
@@ -24,7 +24,7 @@ import { ShallowWrapper } from 'enzyme';
import { ChangeIndexPattern } from './change_indexpattern';
import { SavedObject } from 'kibana/server';
import { DiscoverIndexPattern } from './discover_index_pattern';
-import { EuiSelectable, EuiSelectableList } from '@elastic/eui';
+import { EuiSelectable } from '@elastic/eui';
import { IIndexPattern } from 'src/plugins/data/public';
const indexPattern = {
@@ -57,7 +57,7 @@ function getIndexPatternPickerList(instance: ShallowWrapper) {
}
function getIndexPatternPickerOptions(instance: ShallowWrapper) {
- return getIndexPatternPickerList(instance).dive().find(EuiSelectableList).prop('options');
+ return getIndexPatternPickerList(instance).prop('options');
}
function selectIndexPatternPickerOption(instance: ShallowWrapper, selectedLabel: string) {
diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx
index 90ade60d2073d..9572f35641d69 100644
--- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx
+++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx
@@ -19,7 +19,6 @@
import _ from 'lodash';
import { ReactWrapper } from 'enzyme';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
// @ts-ignore
import StubIndexPattern from 'test_utils/stub_index_pattern';
diff --git a/src/plugins/discover/public/application/components/skip_bottom_button/skip_bottom_button.test.tsx b/src/plugins/discover/public/application/components/skip_bottom_button/skip_bottom_button.test.tsx
index bf417f9f1890b..fdb0ff973dcf0 100644
--- a/src/plugins/discover/public/application/components/skip_bottom_button/skip_bottom_button.test.tsx
+++ b/src/plugins/discover/public/application/components/skip_bottom_button/skip_bottom_button.test.tsx
@@ -20,8 +20,6 @@ import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { ReactWrapper } from 'enzyme';
import { SkipBottomButton, SkipBottomButtonProps } from './skip_bottom_button';
-// @ts-ignore
-import { findTestSubject } from '@elastic/eui/lib/test';
describe('Skip to Bottom Button', function () {
let props: SkipBottomButtonProps;
diff --git a/src/plugins/discover/public/application/components/table/table.test.tsx b/src/plugins/discover/public/application/components/table/table.test.tsx
index 29659b3969365..5b840a25d8beb 100644
--- a/src/plugins/discover/public/application/components/table/table.test.tsx
+++ b/src/plugins/discover/public/application/components/table/table.test.tsx
@@ -18,7 +18,6 @@
*/
import React from 'react';
import { mount } from 'enzyme';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import { DocViewTable } from './table';
import { indexPatterns, IndexPattern } from '../../../../../data/public';
diff --git a/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx b/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx
index 964f94ca9d9b2..a4c10e749d868 100644
--- a/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx
+++ b/src/plugins/discover/public/application/components/timechart_header/timechart_header.test.tsx
@@ -21,7 +21,6 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { ReactWrapper } from 'enzyme';
import { TimechartHeader, TimechartHeaderProps } from './timechart_header';
import { EuiIconTip } from '@elastic/eui';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
describe('timechart header', function () {
diff --git a/src/plugins/embeddable/public/lib/actions/apply_filter_action.test.ts b/src/plugins/embeddable/public/lib/actions/apply_filter_action.test.ts
index 636ce3e623c5b..88c1a5917e609 100644
--- a/src/plugins/embeddable/public/lib/actions/apply_filter_action.test.ts
+++ b/src/plugins/embeddable/public/lib/actions/apply_filter_action.test.ts
@@ -19,6 +19,7 @@
import { createFilterAction } from './apply_filter_action';
import { expectErrorAsync } from '../../tests/helpers';
+import { defaultTrigger } from '../../../../ui_actions/public/triggers';
test('has ACTION_APPLY_FILTER type and id', () => {
const action = createFilterAction();
@@ -51,6 +52,7 @@ describe('isCompatible()', () => {
}),
} as any,
filters: [],
+ trigger: defaultTrigger,
});
expect(result).toBe(true);
});
@@ -66,6 +68,7 @@ describe('isCompatible()', () => {
}),
} as any,
filters: [],
+ trigger: defaultTrigger,
});
expect(result).toBe(false);
});
@@ -119,6 +122,7 @@ describe('execute()', () => {
await action.execute({
embeddable,
filters: ['FILTER' as any],
+ trigger: defaultTrigger,
});
expect(root.updateInput).toHaveBeenCalledTimes(1);
diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_root.test.tsx b/src/plugins/embeddable/public/lib/embeddables/embeddable_root.test.tsx
index 131069909dd2a..cb900884fde97 100644
--- a/src/plugins/embeddable/public/lib/embeddables/embeddable_root.test.tsx
+++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_root.test.tsx
@@ -20,7 +20,6 @@ import React from 'react';
import { HelloWorldEmbeddable } from '../../../../../../examples/embeddable_examples/public';
import { EmbeddableRoot } from './embeddable_root';
import { mount } from 'enzyme';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
test('EmbeddableRoot renders an embeddable', async () => {
diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx
index 341a51d7348b2..fcf79c1d6b211 100644
--- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx
+++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx
@@ -21,7 +21,6 @@ import React from 'react';
import { mount } from 'enzyme';
import { nextTick } from 'test_utils/enzyme_helpers';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import { I18nProvider } from '@kbn/i18n/react';
import { CONTEXT_MENU_TRIGGER } from '../triggers';
diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
index cb02ffc470e95..d8659680dceb9 100644
--- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
+++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
@@ -30,6 +30,7 @@ import {
PANEL_BADGE_TRIGGER,
PANEL_NOTIFICATION_TRIGGER,
EmbeddableContext,
+ contextMenuTrigger,
} from '../triggers';
import { IEmbeddable, EmbeddableOutput, EmbeddableError } from '../embeddables/i_embeddable';
import { ViewMode } from '../types';
@@ -311,7 +312,11 @@ export class EmbeddablePanel extends React.Component {
const sortedActions = [...regularActions, ...extraActions].sort(sortByOrderField);
return await buildContextMenuForActions({
- actions: sortedActions.map((action) => [action, { embeddable: this.props.embeddable }]),
+ actions: sortedActions.map((action) => ({
+ action,
+ context: { embeddable: this.props.embeddable },
+ trigger: contextMenuTrigger,
+ })),
closeMenu: this.closeMyContextMenuPanel,
});
};
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx
index d8def3147e52c..0361939fd07e6 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx
@@ -31,6 +31,7 @@ import { ContactCardEmbeddable } from '../../../../test_samples';
import { esFilters, Filter } from '../../../../../../../../plugins/data/public';
import { EmbeddableStart } from '../../../../../plugin';
import { embeddablePluginMock } from '../../../../../mocks';
+import { defaultTrigger } from '../../../../../../../ui_actions/public/triggers';
const { setup, doStart } = embeddablePluginMock.createInstance();
setup.registerEmbeddableFactory(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory());
@@ -85,7 +86,9 @@ test('Is not compatible when container is in view mode', async () => {
() => null
);
container.updateInput({ viewMode: ViewMode.VIEW });
- expect(await addPanelAction.isCompatible({ embeddable: container })).toBe(false);
+ expect(
+ await addPanelAction.isCompatible({ embeddable: container, trigger: defaultTrigger })
+ ).toBe(false);
});
test('Is not compatible when embeddable is not a container', async () => {
@@ -94,7 +97,7 @@ test('Is not compatible when embeddable is not a container', async () => {
test('Is compatible when embeddable is a parent and in edit mode', async () => {
container.updateInput({ viewMode: ViewMode.EDIT });
- expect(await action.isCompatible({ embeddable: container })).toBe(true);
+ expect(await action.isCompatible({ embeddable: container, trigger: defaultTrigger })).toBe(true);
});
test('Execute throws an error when called with an embeddable that is not a container', async () => {
@@ -108,6 +111,7 @@ test('Execute throws an error when called with an embeddable that is not a conta
},
{} as any
),
+ trigger: defaultTrigger,
} as any);
}
await expect(check()).rejects.toThrow(Error);
@@ -116,6 +120,7 @@ test('Execute does not throw an error when called with a compatible container',
container.updateInput({ viewMode: ViewMode.EDIT });
await action.execute({
embeddable: container,
+ trigger: defaultTrigger,
});
});
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts
index f3a483bb4bda4..63575273bbf62 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts
@@ -17,7 +17,7 @@
* under the License.
*/
import { i18n } from '@kbn/i18n';
-import { Action } from 'src/plugins/ui_actions/public';
+import { Action, ActionExecutionContext } from 'src/plugins/ui_actions/public';
import { NotificationsStart, OverlayStart } from 'src/core/public';
import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin';
import { ViewMode } from '../../../../types';
@@ -52,12 +52,14 @@ export class AddPanelAction implements Action {
return 'plusInCircleFilled';
}
- public async isCompatible({ embeddable }: ActionContext) {
+ public async isCompatible(context: ActionExecutionContext) {
+ const { embeddable } = context;
return embeddable.getIsContainer() && embeddable.getInput().viewMode === ViewMode.EDIT;
}
- public async execute({ embeddable }: ActionContext) {
- if (!embeddable.getIsContainer() || !(await this.isCompatible({ embeddable }))) {
+ public async execute(context: ActionExecutionContext) {
+ const { embeddable } = context;
+ if (!embeddable.getIsContainer() || !(await this.isCompatible(context))) {
throw new Error('Context is incompatible');
}
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx
index 34a176400dbb9..95aee3d9cb335 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.test.tsx
@@ -30,7 +30,6 @@ import { ContainerInput } from '../../../../containers';
import { mountWithIntl as mount } from 'test_utils/enzyme_helpers';
import { ReactWrapper } from 'enzyme';
import { coreMock } from '../../../../../../../../core/public/mocks';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import { embeddablePluginMock } from '../../../../../mocks';
@@ -125,7 +124,7 @@ test('selecting embeddable in "Create new ..." list calls createNewEmbeddable()'
notifications={core.notifications}
SavedObjectFinder={(props) => }
/>
- ) as ReactWrapper;
+ ) as ReactWrapper;
const spy = jest.fn();
component.instance().createNewEmbeddable = spy;
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.test.ts b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.test.ts
index 6fddcbc84faf7..dbfa55a4e0f13 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.test.ts
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.test.ts
@@ -18,8 +18,6 @@
*/
import { Container, isErrorEmbeddable } from '../../../..';
-// @ts-ignore
-import { findTestSubject } from '@elastic/eui/lib/test';
import { nextTick } from 'test_utils/enzyme_helpers';
import { CustomizePanelTitleAction } from './customize_panel_action';
import {
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx
index 2f086a3fb2c0c..5d7daaa7217ed 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx
@@ -30,7 +30,7 @@ import React from 'react';
import { Action } from 'src/plugins/ui_actions/public';
import { PanelOptionsMenu } from './panel_options_menu';
import { IEmbeddable } from '../../embeddables';
-import { EmbeddableContext } from '../../triggers';
+import { EmbeddableContext, panelBadgeTrigger, panelNotificationTrigger } from '../../triggers';
export interface PanelHeaderProps {
title?: string;
@@ -49,11 +49,11 @@ function renderBadges(badges: Array>, embeddable: IEmb
badge.execute({ embeddable })}
- onClickAriaLabel={badge.getDisplayName({ embeddable })}
+ iconType={badge.getIconType({ embeddable, trigger: panelBadgeTrigger })}
+ onClick={() => badge.execute({ embeddable, trigger: panelBadgeTrigger })}
+ onClickAriaLabel={badge.getDisplayName({ embeddable, trigger: panelBadgeTrigger })}
>
- {badge.getDisplayName({ embeddable })}
+ {badge.getDisplayName({ embeddable, trigger: panelBadgeTrigger })}
));
}
@@ -70,14 +70,17 @@ function renderNotifications(
data-test-subj={`embeddablePanelNotification-${notification.id}`}
key={notification.id}
style={{ marginTop: '4px', marginRight: '4px' }}
- onClick={() => notification.execute(context)}
+ onClick={() => notification.execute({ ...context, trigger: panelNotificationTrigger })}
>
- {notification.getDisplayName(context)}
+ {notification.getDisplayName({ ...context, trigger: panelNotificationTrigger })}
);
if (notification.getDisplayNameTooltip) {
- const tooltip = notification.getDisplayNameTooltip(context);
+ const tooltip = notification.getDisplayNameTooltip({
+ ...context,
+ trigger: panelNotificationTrigger,
+ });
if (tooltip) {
badge = (
diff --git a/src/plugins/embeddable/public/lib/test_samples/actions/say_hello_action.tsx b/src/plugins/embeddable/public/lib/test_samples/actions/say_hello_action.tsx
index 0612b838a6ce7..968caf67b1826 100644
--- a/src/plugins/embeddable/public/lib/test_samples/actions/say_hello_action.tsx
+++ b/src/plugins/embeddable/public/lib/test_samples/actions/say_hello_action.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
-import { ActionByType, IncompatibleActionError, ActionType } from '../../ui_actions';
+import { IncompatibleActionError, ActionType, ActionDefinitionByType } from '../../ui_actions';
import { EmbeddableInput, Embeddable, EmbeddableOutput, IEmbeddable } from '../../embeddables';
// Casting to ActionType is a hack - in a real situation use
@@ -42,7 +42,7 @@ export interface SayHelloActionContext {
message?: string;
}
-export class SayHelloAction implements ActionByType {
+export class SayHelloAction implements ActionDefinitionByType {
public readonly type = SAY_HELLO_ACTION;
public readonly id = SAY_HELLO_ACTION;
diff --git a/src/plugins/embeddable/public/tests/apply_filter_action.test.ts b/src/plugins/embeddable/public/tests/apply_filter_action.test.ts
index 9d765c9906443..f8c4a4a7e4b72 100644
--- a/src/plugins/embeddable/public/tests/apply_filter_action.test.ts
+++ b/src/plugins/embeddable/public/tests/apply_filter_action.test.ts
@@ -31,6 +31,7 @@ import {
FilterableEmbeddableInput,
} from '../lib/test_samples';
import { esFilters } from '../../../data/public';
+import { applyFilterTrigger } from '../../../ui_actions/public';
test('ApplyFilterAction applies the filter to the root of the container tree', async () => {
const { doStart, setup } = testPlugin();
@@ -85,7 +86,7 @@ test('ApplyFilterAction applies the filter to the root of the container tree', a
query: { match: { extension: { query: 'foo' } } },
};
- await applyFilterAction.execute({ embeddable, filters: [filter] });
+ await applyFilterAction.execute({ embeddable, filters: [filter], trigger: applyFilterTrigger });
expect(root.getInput().filters.length).toBe(1);
expect(node1.getInput().filters.length).toBe(1);
expect(embeddable.getInput().filters.length).toBe(1);
diff --git a/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx b/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx
index e094afe528498..d12a55dd827e6 100644
--- a/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx
+++ b/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx
@@ -17,7 +17,6 @@
* under the License.
*/
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import * as React from 'react';
import { Container, isErrorEmbeddable } from '../lib';
diff --git a/src/plugins/home/public/application/components/tutorial/__snapshots__/saved_objects_installer.test.js.snap b/src/plugins/home/public/application/components/tutorial/__snapshots__/saved_objects_installer.test.js.snap
index 1e7b3d5c6284c..9a9e055d54d2f 100644
--- a/src/plugins/home/public/application/components/tutorial/__snapshots__/saved_objects_installer.test.js.snap
+++ b/src/plugins/home/public/application/components/tutorial/__snapshots__/saved_objects_installer.test.js.snap
@@ -245,22 +245,41 @@ exports[`bulkCreate should display error message when bulkCreate request fails 1
isLoading={false}
onClick={[Function]}
>
-
+
+
+ Load Kibana objects
+
+
+
+
+
@@ -565,22 +584,41 @@ exports[`bulkCreate should display success message when bulkCreate is successful
isLoading={false}
onClick={[Function]}
>
-
+
+
+ Load Kibana objects
+
+
+
+
+
diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/header/__snapshots__/header.test.tsx.snap b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/header/__snapshots__/header.test.tsx.snap
index 6261ea2c90793..5218ebd1b4ad4 100644
--- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/header/__snapshots__/header.test.tsx.snap
+++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/header/__snapshots__/header.test.tsx.snap
@@ -33,7 +33,7 @@ exports[`Header should render normally 1`] = `
type="button"
>
+
diff --git a/src/plugins/kibana_utils/public/state_management/url/format.ts b/src/plugins/kibana_utils/public/state_management/url/format.ts
index 2912b665ff014..4497e509bc86b 100644
--- a/src/plugins/kibana_utils/public/state_management/url/format.ts
+++ b/src/plugins/kibana_utils/public/state_management/url/format.ts
@@ -22,6 +22,23 @@ import { stringify, ParsedQuery } from 'query-string';
import { parseUrl, parseUrlHash } from './parse';
import { url as urlUtils } from '../../../common';
+export function replaceUrlQuery(
+ rawUrl: string,
+ queryReplacer: (query: ParsedQuery) => ParsedQuery
+) {
+ const url = parseUrl(rawUrl);
+ const newQuery = queryReplacer(url.query || {});
+ const searchQueryString = stringify(urlUtils.encodeQuery(newQuery), {
+ sort: false,
+ encode: false,
+ });
+ if (!url.search && !searchQueryString) return rawUrl; // nothing to change. return original url
+ return formatUrl({
+ ...url,
+ search: searchQueryString,
+ });
+}
+
export function replaceUrlHashQuery(
rawUrl: string,
queryReplacer: (query: ParsedQuery) => ParsedQuery
diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts
index a8c3aab2202d1..3d25134cd178d 100644
--- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts
+++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts
@@ -78,6 +78,27 @@ describe('kbn_url_storage', () => {
const retrievedState2 = getStateFromKbnUrl('_s', newUrl);
expect(retrievedState2).toEqual(state2);
});
+
+ it('should set query to url with storeInHashQuery: false', () => {
+ let newUrl = setStateToKbnUrl(
+ '_a',
+ { tab: 'other' },
+ { useHash: false, storeInHashQuery: false },
+ 'http://localhost:5601/oxf/app/kibana/yourApp'
+ );
+ expect(newUrl).toMatchInlineSnapshot(
+ `"http://localhost:5601/oxf/app/kibana/yourApp?_a=(tab:other)"`
+ );
+ newUrl = setStateToKbnUrl(
+ '_b',
+ { f: 'test', i: '', l: '' },
+ { useHash: false, storeInHashQuery: false },
+ newUrl
+ );
+ expect(newUrl).toMatchInlineSnapshot(
+ `"http://localhost:5601/oxf/app/kibana/yourApp?_a=(tab:other)&_b=(f:test,i:'',l:'')"`
+ );
+ });
});
describe('urlControls', () => {
diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts
index fefd5f668c6b3..a3b220f911504 100644
--- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts
+++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts
@@ -22,7 +22,7 @@ import { stringify } from 'query-string';
import { createBrowserHistory, History } from 'history';
import { decodeState, encodeState } from '../state_encoder';
import { getCurrentUrl, parseUrl, parseUrlHash } from './parse';
-import { replaceUrlHashQuery } from './format';
+import { replaceUrlHashQuery, replaceUrlQuery } from './format';
import { url as urlUtils } from '../../../common';
/**
@@ -84,10 +84,14 @@ export function getStateFromKbnUrl(
export function setStateToKbnUrl(
key: string,
state: State,
- { useHash = false }: { useHash: boolean } = { useHash: false },
+ { useHash = false, storeInHashQuery = true }: { useHash: boolean; storeInHashQuery?: boolean } = {
+ useHash: false,
+ storeInHashQuery: true,
+ },
rawUrl = window.location.href
): string {
- return replaceUrlHashQuery(rawUrl, (query) => {
+ const replacer = storeInHashQuery ? replaceUrlHashQuery : replaceUrlQuery;
+ return replacer(rawUrl, (query) => {
const encoded = encodeState(state, useHash);
return {
...query,
diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_data.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_data.tsx
index a1653c5289255..992a2fcf0c89d 100644
--- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_data.tsx
+++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_data.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
-import { ButtonIconSide } from '@elastic/eui';
+import { EuiButtonProps } from '@elastic/eui';
export type TopNavMenuAction = (anchorElement: HTMLElement) => void;
@@ -32,7 +32,7 @@ export interface TopNavMenuData {
tooltip?: string | (() => string | undefined);
emphasize?: boolean;
iconType?: string;
- iconSide?: ButtonIconSide;
+ iconSide?: EuiButtonProps['iconSide'];
}
export interface RegisteredTopNavMenuData extends TopNavMenuData {
diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap
index d56776c2be9d7..f5c2d3efd56f7 100644
--- a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap
+++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap
@@ -58,45 +58,63 @@ exports[`Intro component renders correctly 1`] = `
iconType="eye"
size="s"
>
-
-
-
-
-
-
-
+
- View search
-
-
-
-
+
+
+
+
+
+ View search
+
+
+
+
+
+
@@ -113,45 +131,64 @@ exports[`Intro component renders correctly 1`] = `
onClick={[Function]}
size="s"
>
-
+
+
+
+
+
+ Delete search
+
+
+
+
+
+
diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.test.tsx
index 6256e5fcd49c5..0c7bf64ca011d 100644
--- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.test.tsx
+++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.test.tsx
@@ -19,7 +19,6 @@
import React from 'react';
import { shallowWithI18nProvider, mountWithI18nProvider } from 'test_utils/enzyme_helpers';
-// @ts-expect-error
import { findTestSubject } from '@elastic/eui/lib/test';
import { keys } from '@elastic/eui';
import { httpServiceMock } from '../../../../../../core/public/mocks';
diff --git a/src/plugins/ui_actions/public/actions/action.test.ts b/src/plugins/ui_actions/public/actions/action.test.ts
index f9d696d3ddb5f..1f76223a0d7c4 100644
--- a/src/plugins/ui_actions/public/actions/action.test.ts
+++ b/src/plugins/ui_actions/public/actions/action.test.ts
@@ -17,8 +17,9 @@
* under the License.
*/
-import { createAction } from '../../../ui_actions/public';
+import { ActionExecutionContext, createAction } from '../../../ui_actions/public';
import { ActionType } from '../types';
+import { defaultTrigger } from '../triggers';
const sayHelloAction = createAction({
// Casting to ActionType is a hack - in a real situation use
@@ -29,11 +30,17 @@ const sayHelloAction = createAction({
});
test('action is not compatible based on context', async () => {
- const isCompatible = await sayHelloAction.isCompatible({ amICompatible: false });
+ const isCompatible = await sayHelloAction.isCompatible({
+ amICompatible: false,
+ trigger: defaultTrigger,
+ } as ActionExecutionContext);
expect(isCompatible).toBe(false);
});
test('action is compatible based on context', async () => {
- const isCompatible = await sayHelloAction.isCompatible({ amICompatible: true });
+ const isCompatible = await sayHelloAction.isCompatible({
+ amICompatible: true,
+ trigger: defaultTrigger,
+ } as ActionExecutionContext);
expect(isCompatible).toBe(true);
});
diff --git a/src/plugins/ui_actions/public/actions/action.ts b/src/plugins/ui_actions/public/actions/action.ts
index bc5f36acb8f0c..8005dadd8f5ef 100644
--- a/src/plugins/ui_actions/public/actions/action.ts
+++ b/src/plugins/ui_actions/public/actions/action.ts
@@ -18,13 +18,43 @@
*/
import { UiComponent } from 'src/plugins/kibana_utils/public';
-import { ActionType, ActionContextMapping } from '../types';
+import { ActionType, ActionContextMapping, BaseContext } from '../types';
import { Presentable } from '../util/presentable';
+import { Trigger } from '../triggers';
export type ActionByType = Action;
+export type ActionDefinitionByType = ActionDefinition<
+ ActionContextMapping[T]
+>;
-export interface Action
- extends Partial> {
+/**
+ * During action execution we can provide additional information,
+ * for example, trigger, that caused the action execution
+ */
+export interface ActionExecutionMeta {
+ /**
+ * Trigger that executed the action
+ */
+ trigger: Trigger;
+}
+
+/**
+ * Action methods are executed with Context from trigger + {@link ActionExecutionMeta}
+ */
+export type ActionExecutionContext = Context &
+ ActionExecutionMeta;
+
+/**
+ * Simplified action context for {@link ActionDefinition}
+ * When defining action consumer may use either it's own Context
+ * or an ActionExecutionContext to get access to {@link ActionExecutionMeta} params
+ */
+export type ActionDefinitionContext =
+ | Context
+ | ActionExecutionContext;
+
+export interface Action
+ extends Partial>> {
/**
* Determined the order when there is more than one action matched to a trigger.
* Higher numbers are displayed first.
@@ -44,44 +74,51 @@ export interface Action
/**
* Optional EUI icon type that can be displayed along with the title.
*/
- getIconType(context: Context): string | undefined;
+ getIconType(context: ActionExecutionContext): string | undefined;
/**
* Returns a title to be displayed to the user.
* @param context
*/
- getDisplayName(context: Context): string;
+ getDisplayName(context: ActionExecutionContext): string;
/**
* `UiComponent` to render when displaying this action as a context menu item.
* If not provided, `getDisplayName` will be used instead.
*/
- MenuItem?: UiComponent<{ context: Context }>;
+ MenuItem?: UiComponent<{ context: ActionExecutionContext }>;
/**
* Returns a promise that resolves to true if this action is compatible given the context,
* otherwise resolves to false.
*/
- isCompatible(context: Context): Promise;
+ isCompatible(context: ActionExecutionContext): Promise;
/**
* Executes the action.
*/
- execute(context: Context): Promise;
+ execute(context: ActionExecutionContext): Promise;
+
+ /**
+ * This method should return a link if this item can be clicked on. The link
+ * is used to navigate user if user middle-clicks it or Ctrl + clicks or
+ * right-clicks and selects "Open in new tab".
+ */
+ getHref?(context: ActionExecutionContext): Promise;
/**
* Determines if action should be executed automatically,
* without first showing up in context menu.
* false by default.
*/
- shouldAutoExecute?(context: Context): Promise;
+ shouldAutoExecute?(context: ActionExecutionContext): Promise;
}
/**
* A convenience interface used to register an action.
*/
-export interface ActionDefinition
- extends Partial> {
+export interface ActionDefinition
+ extends Partial>> {
/**
* ID of the action that uniquely identifies this action in the actions registry.
*/
@@ -92,17 +129,30 @@ export interface ActionDefinition
*/
readonly type?: ActionType;
+ /**
+ * Returns a promise that resolves to true if this item is compatible given
+ * the context and should be displayed to user, otherwise resolves to false.
+ */
+ isCompatible?(context: ActionDefinitionContext): Promise;
+
/**
* Executes the action.
*/
- execute(context: Context): Promise;
+ execute(context: ActionDefinitionContext): Promise;
/**
* Determines if action should be executed automatically,
* without first showing up in context menu.
* false by default.
*/
- shouldAutoExecute?(context: Context): Promise;
+ shouldAutoExecute?(context: ActionDefinitionContext): Promise;
+
+ /**
+ * This method should return a link if this item can be clicked on. The link
+ * is used to navigate user if user middle-clicks it or Ctrl + clicks or
+ * right-clicks and selects "Open in new tab".
+ */
+ getHref?(context: ActionDefinitionContext): Promise;
}
export type ActionContext = A extends ActionDefinition ? Context : never;
diff --git a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx
index 7b87a5992a7f5..b44a07273f4a9 100644
--- a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx
+++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx
@@ -23,13 +23,22 @@ import _ from 'lodash';
import { i18n } from '@kbn/i18n';
import { uiToReactComponent } from '../../../kibana_react/public';
import { Action } from '../actions';
+import { Trigger } from '../triggers';
import { BaseContext } from '../types';
export const defaultTitle = i18n.translate('uiActions.actionPanel.title', {
defaultMessage: 'Options',
});
-type ActionWithContext = [Action, Context];
+interface ActionWithContext {
+ action: Action;
+ context: Context;
+
+ /**
+ * Trigger that caused this action
+ */
+ trigger: Trigger;
+}
/**
* Transforms an array of Actions to the shape EuiContextMenuPanel expects.
@@ -66,15 +75,19 @@ async function buildEuiContextMenuPanelItems({
closeMenu: () => void;
}) {
const items: EuiContextMenuPanelItemDescriptor[] = new Array(actions.length);
- const promises = actions.map(async ([action, actionContext], index) => {
- const isCompatible = await action.isCompatible(actionContext);
+ const promises = actions.map(async ({ action, context, trigger }, index) => {
+ const isCompatible = await action.isCompatible({
+ ...context,
+ trigger,
+ });
if (!isCompatible) {
return;
}
items[index] = await convertPanelActionToContextMenuItem({
action,
- actionContext,
+ actionContext: context,
+ trigger,
closeMenu,
});
});
@@ -87,19 +100,30 @@ async function buildEuiContextMenuPanelItems({
async function convertPanelActionToContextMenuItem({
action,
actionContext,
+ trigger,
closeMenu,
}: {
action: Action;
actionContext: Context;
+ trigger: Trigger;
closeMenu: () => void;
}): Promise {
const menuPanelItem: EuiContextMenuPanelItemDescriptor = {
name: action.MenuItem
? React.createElement(uiToReactComponent(action.MenuItem), {
- context: actionContext,
+ context: {
+ ...actionContext,
+ trigger,
+ },
})
- : action.getDisplayName(actionContext),
- icon: action.getIconType(actionContext),
+ : action.getDisplayName({
+ ...actionContext,
+ trigger,
+ }),
+ icon: action.getIconType({
+ ...actionContext,
+ trigger,
+ }),
panel: _.get(action, 'childContextMenuPanel.id'),
'data-test-subj': `embeddablePanelAction-${action.id}`,
};
@@ -114,20 +138,29 @@ async function convertPanelActionToContextMenuItem({
!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) // ignore clicks with modifier keys
) {
event.preventDefault();
- action.execute(actionContext);
+ action.execute({
+ ...actionContext,
+ trigger,
+ });
} else {
// let browser handle navigation
}
} else {
// not a link
- action.execute(actionContext);
+ action.execute({
+ ...actionContext,
+ trigger,
+ });
}
closeMenu();
};
if (action.getHref) {
- const href = await action.getHref(actionContext);
+ const href = await action.getHref({
+ ...actionContext,
+ trigger,
+ });
if (href) {
menuPanelItem.href = href;
}
diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts
index a9b413fb36542..d76ca124ead2c 100644
--- a/src/plugins/ui_actions/public/index.ts
+++ b/src/plugins/ui_actions/public/index.ts
@@ -45,4 +45,9 @@ export {
applyFilterTrigger,
} from './triggers';
export { TriggerContextMapping, TriggerId, ActionContextMapping, ActionType } from './types';
-export { ActionByType } from './actions';
+export {
+ ActionByType,
+ ActionDefinitionByType,
+ ActionExecutionContext,
+ ActionExecutionMeta,
+} from './actions';
diff --git a/src/plugins/ui_actions/public/service/ui_actions_execution_service.ts b/src/plugins/ui_actions/public/service/ui_actions_execution_service.ts
index 7393989672e9d..df89c9c2f70e9 100644
--- a/src/plugins/ui_actions/public/service/ui_actions_execution_service.ts
+++ b/src/plugins/ui_actions/public/service/ui_actions_execution_service.ts
@@ -46,7 +46,7 @@ export class UiActionsExecutionService {
context: BaseContext;
trigger: Trigger;
}): Promise {
- const shouldBatch = !(await action.shouldAutoExecute?.(context)) ?? false;
+ const shouldBatch = !(await action.shouldAutoExecute?.({ ...context, trigger })) ?? false;
const task: ExecuteActionTask = {
action,
context,
@@ -59,7 +59,7 @@ export class UiActionsExecutionService {
} else {
this.pendingTasks.add(task);
try {
- await action.execute(context);
+ await action.execute({ ...context, trigger });
this.pendingTasks.delete(task);
} catch (e) {
this.pendingTasks.delete(task);
@@ -96,9 +96,12 @@ export class UiActionsExecutionService {
}, 0);
}
- private async executeSingleTask({ context, action, defer }: ExecuteActionTask) {
+ private async executeSingleTask({ context, action, defer, trigger }: ExecuteActionTask) {
try {
- await action.execute(context);
+ await action.execute({
+ ...context,
+ trigger,
+ });
defer.resolve();
} catch (e) {
defer.reject(e);
@@ -107,7 +110,11 @@ export class UiActionsExecutionService {
private async executeMultipleActions(tasks: ExecuteActionTask[]) {
const panel = await buildContextMenuForActions({
- actions: tasks.map(({ action, context }) => [action, context]),
+ actions: tasks.map(({ action, context, trigger }) => ({
+ action,
+ context,
+ trigger,
+ })),
title: tasks[0].trigger.title, // title of context menu is title of trigger which originated the chain
closeMenu: () => {
tasks.forEach((t) => t.defer.resolve());
diff --git a/src/plugins/ui_actions/public/service/ui_actions_service.ts b/src/plugins/ui_actions/public/service/ui_actions_service.ts
index 08efffbb6b5a8..6028177964fb7 100644
--- a/src/plugins/ui_actions/public/service/ui_actions_service.ts
+++ b/src/plugins/ui_actions/public/service/ui_actions_service.ts
@@ -142,7 +142,7 @@ export class UiActionsService {
triggerId: T,
// The action can accept partial or no context, but if it needs context not provided
// by this type of trigger, typescript will complain. yay!
- action: Action
+ action: ActionDefinition | Action // TODO: remove `Action` https://github.com/elastic/kibana/issues/74501
): void => {
if (!this.actions.has(action.id)) this.registerAction(action);
this.attachAction(triggerId, action.id);
@@ -178,7 +178,14 @@ export class UiActionsService {
context: TriggerContextMapping[T]
): Promise>> => {
const actions = this.getTriggerActions!(triggerId);
- const isCompatibles = await Promise.all(actions.map((action) => action.isCompatible(context)));
+ const isCompatibles = await Promise.all(
+ actions.map((action) =>
+ action.isCompatible({
+ ...context,
+ trigger: this.getTrigger(triggerId),
+ })
+ )
+ );
return actions.reduce(
(acc: Array>, action, i) =>
isCompatibles[i] ? [...acc, action] : acc,
diff --git a/src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts b/src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts
index 9af46f25b4fec..81120990001e3 100644
--- a/src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts
+++ b/src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts
@@ -82,7 +82,7 @@ test('executes a single action mapped to a trigger', async () => {
jest.runAllTimers();
expect(executeFn).toBeCalledTimes(1);
- expect(executeFn).toBeCalledWith(context);
+ expect(executeFn).toBeCalledWith(expect.objectContaining(context));
});
test('throws an error if there are no compatible actions to execute', async () => {
@@ -202,3 +202,25 @@ test("doesn't show a context menu for auto executable actions", async () => {
expect(openContextMenu).toHaveBeenCalledTimes(0);
});
});
+
+test('passes trigger into execute', async () => {
+ const { setup, doStart } = uiActions;
+ const trigger = {
+ id: 'MY-TRIGGER' as TriggerId,
+ title: 'My trigger',
+ };
+ const action = createTestAction<{ foo: string }>('test', () => true);
+
+ setup.registerTrigger(trigger);
+ setup.addTriggerAction(trigger.id, action);
+
+ const start = doStart();
+
+ const context = { foo: 'bar' };
+ await start.executeTriggerActions('MY-TRIGGER' as TriggerId, context);
+ jest.runAllTimers();
+ expect(executeFn).toBeCalledWith({
+ ...context,
+ trigger,
+ });
+});
diff --git a/src/plugins/vis_type_timeseries/public/application/lib/create_brush_handler.js b/src/plugins/ui_actions/public/triggers/default_trigger.ts
similarity index 76%
rename from src/plugins/vis_type_timeseries/public/application/lib/create_brush_handler.js
rename to src/plugins/ui_actions/public/triggers/default_trigger.ts
index 452e85c6405fe..74be0243bdac5 100644
--- a/src/plugins/vis_type_timeseries/public/application/lib/create_brush_handler.js
+++ b/src/plugins/ui_actions/public/triggers/default_trigger.ts
@@ -17,14 +17,11 @@
* under the License.
*/
-import moment from 'moment';
+import { Trigger } from '.';
-const TIME_MODE = 'absolute';
-
-export const createBrushHandler = (timefilter) => (from, to) => {
- timefilter.setTime({
- from: moment(from).toISOString(),
- to: moment(to).toISOString(),
- mode: TIME_MODE,
- });
+export const DEFAULT_TRIGGER = '';
+export const defaultTrigger: Trigger<''> = {
+ id: DEFAULT_TRIGGER,
+ title: 'Unknown',
+ description: 'Unknown trigger.',
};
diff --git a/src/plugins/ui_actions/public/triggers/index.ts b/src/plugins/ui_actions/public/triggers/index.ts
index a5bf9e1822941..dbc54163c5af5 100644
--- a/src/plugins/ui_actions/public/triggers/index.ts
+++ b/src/plugins/ui_actions/public/triggers/index.ts
@@ -23,3 +23,4 @@ export * from './trigger_internal';
export * from './select_range_trigger';
export * from './value_click_trigger';
export * from './apply_filter_trigger';
+export * from './default_trigger';
diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts
index 5631441cf9a1b..dcf0bfb14d538 100644
--- a/src/plugins/ui_actions/public/types.ts
+++ b/src/plugins/ui_actions/public/types.ts
@@ -19,7 +19,12 @@
import { ActionInternal } from './actions/action_internal';
import { TriggerInternal } from './triggers/trigger_internal';
-import { SELECT_RANGE_TRIGGER, VALUE_CLICK_TRIGGER, APPLY_FILTER_TRIGGER } from './triggers';
+import {
+ SELECT_RANGE_TRIGGER,
+ VALUE_CLICK_TRIGGER,
+ APPLY_FILTER_TRIGGER,
+ DEFAULT_TRIGGER,
+} from './triggers';
import type { RangeSelectContext, ValueClickContext } from '../../embeddable/public';
import type { ApplyGlobalFilterActionContext } from '../../data/public';
@@ -27,8 +32,6 @@ export type TriggerRegistry = Map>;
export type ActionRegistry = Map;
export type TriggerToActionsRegistry = Map;
-const DEFAULT_TRIGGER = '';
-
export type TriggerId = keyof TriggerContextMapping;
export type BaseContext = object;
diff --git a/src/plugins/vis_type_timelion/public/components/chart.tsx b/src/plugins/vis_type_timelion/public/components/chart.tsx
index a8b03bdbc8b7e..15a376d4e9638 100644
--- a/src/plugins/vis_type_timelion/public/components/chart.tsx
+++ b/src/plugins/vis_type_timelion/public/components/chart.tsx
@@ -21,8 +21,10 @@ import React from 'react';
import { Sheet } from '../helpers/timelion_request_handler';
import { Panel } from './panel';
+import { ExprVisAPIEvents } from '../../../visualizations/public';
interface ChartComponentProp {
+ applyFilter: ExprVisAPIEvents['applyFilter'];
interval: string;
renderComplete(): void;
seriesList: Sheet;
diff --git a/src/plugins/vis_type_timelion/public/components/panel.tsx b/src/plugins/vis_type_timelion/public/components/panel.tsx
index f4f1cd84613be..9c30a6b75d6db 100644
--- a/src/plugins/vis_type_timelion/public/components/panel.tsx
+++ b/src/plugins/vis_type_timelion/public/components/panel.tsx
@@ -33,10 +33,12 @@ import {
colors,
Axis,
} from '../helpers/panel_utils';
+
import { Series, Sheet } from '../helpers/timelion_request_handler';
import { tickFormatters } from '../helpers/tick_formatters';
import { generateTicksProvider } from '../helpers/tick_generator';
import { TimelionVisDependencies } from '../plugin';
+import { ExprVisAPIEvents } from '../../../visualizations/public';
interface CrosshairPlot extends jquery.flot.plot {
setCrosshair: (pos: Position) => void;
@@ -44,6 +46,7 @@ interface CrosshairPlot extends jquery.flot.plot {
}
interface PanelProps {
+ applyFilter: ExprVisAPIEvents['applyFilter'];
interval: string;
seriesList: Sheet;
renderComplete(): void;
@@ -72,7 +75,7 @@ const DEBOUNCE_DELAY = 50;
// ensure legend is the same height with or without a caption so legend items do not move around
const emptyCaption = '
';
-function Panel({ interval, seriesList, renderComplete }: PanelProps) {
+function Panel({ interval, seriesList, renderComplete, applyFilter }: PanelProps) {
const kibana = useKibana();
const [chart, setChart] = useState(() => cloneDeep(seriesList.list));
const [canvasElem, setCanvasElem] = useState();
@@ -346,12 +349,21 @@ function Panel({ interval, seriesList, renderComplete }: PanelProps) {
const plotSelectedHandler = useCallback(
(event: JQuery.TriggeredEvent, ranges: Ranges) => {
- kibana.services.timefilter.setTime({
- from: moment(ranges.xaxis.from),
- to: moment(ranges.xaxis.to),
+ applyFilter({
+ timeFieldName: '*',
+ filters: [
+ {
+ range: {
+ '*': {
+ gte: ranges.xaxis.from,
+ lte: ranges.xaxis.to,
+ },
+ },
+ },
+ ],
});
},
- [kibana.services.timefilter]
+ [applyFilter]
);
useEffect(() => {
diff --git a/src/plugins/vis_type_timelion/public/components/timelion_vis.tsx b/src/plugins/vis_type_timelion/public/components/timelion_vis.tsx
index 4bb07fe74ee82..aa594c749b600 100644
--- a/src/plugins/vis_type_timelion/public/components/timelion_vis.tsx
+++ b/src/plugins/vis_type_timelion/public/components/timelion_vis.tsx
@@ -38,6 +38,7 @@ function TimelionVisComponent(props: TimelionVisComponentProp) {
return (
{
+ return [VIS_EVENT_TO_TRIGGER.applyFilter];
+ },
options: {
showIndexSelection: false,
showQueryBar: false,
diff --git a/src/plugins/vis_type_timeseries/public/application/components/color_picker.test.tsx b/src/plugins/vis_type_timeseries/public/application/components/color_picker.test.tsx
index 7c930fa2e2960..ee13408511ab5 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/color_picker.test.tsx
+++ b/src/plugins/vis_type_timeseries/public/application/components/color_picker.test.tsx
@@ -22,7 +22,6 @@ import { ColorPicker, ColorPickerProps } from './color_picker';
import { mount } from 'enzyme';
import { ReactWrapper } from 'enzyme';
import { EuiColorPicker, EuiIconTip } from '@elastic/eui';
-// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
describe('ColorPicker', () => {
diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js b/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js
index 300e70f3ae0c0..50585869862ee 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js
@@ -50,7 +50,7 @@ export class VisEditor extends Component {
visFields: props.visFields,
extractedIndexPatterns: [''],
};
- this.onBrush = createBrushHandler(getDataStart().query.timefilter.timefilter);
+ this.onBrush = createBrushHandler((data) => props.vis.API.events.applyFilter(data));
this.visDataSubject = new Rx.BehaviorSubject(this.props.visData);
this.visData$ = this.visDataSubject.asObservable().pipe(share());
diff --git a/src/plugins/vis_type_timeseries/public/application/lib/create_brush_handler.test.js b/src/plugins/vis_type_timeseries/public/application/lib/create_brush_handler.test.ts
similarity index 58%
rename from src/plugins/vis_type_timeseries/public/application/lib/create_brush_handler.test.js
rename to src/plugins/vis_type_timeseries/public/application/lib/create_brush_handler.test.ts
index 6ae01a384e7ca..a9568b5be9d3f 100644
--- a/src/plugins/vis_type_timeseries/public/application/lib/create_brush_handler.test.js
+++ b/src/plugins/vis_type_timeseries/public/application/lib/create_brush_handler.test.ts
@@ -18,28 +18,31 @@
*/
import { createBrushHandler } from './create_brush_handler';
-import moment from 'moment';
+import { ExprVisAPIEvents } from '../../../../visualizations/public';
describe('brushHandler', () => {
- let mockTimefilter;
- let onBrush;
+ let onBrush: ReturnType;
+ let applyFilter: ExprVisAPIEvents['applyFilter'];
beforeEach(() => {
- mockTimefilter = {
- time: {},
- setTime: function (time) {
- this.time = time;
- },
- };
- onBrush = createBrushHandler(mockTimefilter);
+ applyFilter = jest.fn();
+
+ onBrush = createBrushHandler(applyFilter);
});
- it('returns brushHandler() that updates timefilter', () => {
- const from = '2017-01-01T00:00:00Z';
- const to = '2017-01-01T00:10:00Z';
- onBrush(from, to);
- expect(mockTimefilter.time.from).toEqual(moment(from).toISOString());
- expect(mockTimefilter.time.to).toEqual(moment(to).toISOString());
- expect(mockTimefilter.time.mode).toEqual('absolute');
+ test('returns brushHandler() should updates timefilter through vis.API.events.applyFilter', () => {
+ const gte = '2017-01-01T00:00:00Z';
+ const lte = '2017-01-01T00:10:00Z';
+
+ onBrush(gte, lte);
+
+ expect(applyFilter).toHaveBeenCalledWith({
+ timeFieldName: '*',
+ filters: [
+ {
+ range: { '*': { gte: '2017-01-01T00:00:00Z', lte: '2017-01-01T00:10:00Z' } },
+ },
+ ],
+ });
});
});
diff --git a/src/plugins/vis_type_timeseries/public/application/lib/create_brush_handler.ts b/src/plugins/vis_type_timeseries/public/application/lib/create_brush_handler.ts
new file mode 100644
index 0000000000000..38002c7552952
--- /dev/null
+++ b/src/plugins/vis_type_timeseries/public/application/lib/create_brush_handler.ts
@@ -0,0 +1,39 @@
+/*
+ * 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 { ExprVisAPIEvents } from '../../../../visualizations/public';
+
+export const createBrushHandler = (applyFilter: ExprVisAPIEvents['applyFilter']) => (
+ gte: string,
+ lte: string
+) => {
+ return applyFilter({
+ timeFieldName: '*',
+ filters: [
+ {
+ range: {
+ '*': {
+ gte,
+ lte,
+ },
+ },
+ },
+ ],
+ });
+};
diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js b/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js
index b595979130d3a..3aae1bd64d953 100644
--- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js
+++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/top_n.js
@@ -110,7 +110,9 @@ export class TopN extends Component {
const isPositiveValue = lastValue >= 0;
const intervalLength = TopN.calcDomain(renderMode, min, max);
- const width = 100 * (Math.abs(lastValue) / intervalLength);
+ // if both are 0, the division returns NaN causing unexpected behavior.
+ // For this it defaults to 0
+ const width = 100 * (Math.abs(lastValue) / intervalLength) || 0;
const styles = reactcss(
{
diff --git a/src/plugins/vis_type_timeseries/public/metrics_type.ts b/src/plugins/vis_type_timeseries/public/metrics_type.ts
index 44b0334a37871..d6621870fef67 100644
--- a/src/plugins/vis_type_timeseries/public/metrics_type.ts
+++ b/src/plugins/vis_type_timeseries/public/metrics_type.ts
@@ -25,6 +25,7 @@ import { EditorController } from './application';
// @ts-ignore
import { PANEL_TYPES } from '../common/panel_types';
import { VisEditor } from './application/components/vis_editor_lazy';
+import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';
export const metricsVisDefinition = {
name: 'metrics',
@@ -78,6 +79,9 @@ export const metricsVisDefinition = {
showIndexSelection: false,
},
requestHandler: metricsRequestHandler,
+ getSupportedTriggers: () => {
+ return [VIS_EVENT_TO_TRIGGER.applyFilter];
+ },
inspectorAdapters: {},
responseHandler: 'none',
};
diff --git a/src/plugins/vis_type_vega/public/vega_type.ts b/src/plugins/vis_type_vega/public/vega_type.ts
index d69eb3cfba282..f49816017b684 100644
--- a/src/plugins/vis_type_vega/public/vega_type.ts
+++ b/src/plugins/vis_type_vega/public/vega_type.ts
@@ -27,6 +27,7 @@ import { createVegaRequestHandler } from './vega_request_handler';
import { createVegaVisualization } from './vega_visualization';
import { getDefaultSpec } from './default_spec';
import { createInspectorAdapters } from './vega_inspector';
+import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';
export const createVegaTypeDefinition = (dependencies: VegaVisualizationDependencies) => {
const requestHandler = createVegaRequestHandler(dependencies);
@@ -54,6 +55,9 @@ export const createVegaTypeDefinition = (dependencies: VegaVisualizationDependen
showQueryBar: true,
showFilterBar: true,
},
+ getSupportedTriggers: () => {
+ return [VIS_EVENT_TO_TRIGGER.applyFilter];
+ },
stage: 'experimental',
inspectorAdapters: createInspectorAdapters,
};
diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js
index 4596b47364494..a2a973d232de0 100644
--- a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js
+++ b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js
@@ -63,6 +63,7 @@ export class VegaBaseView {
this._parser = opts.vegaParser;
this._serviceSettings = opts.serviceSettings;
this._filterManager = opts.filterManager;
+ this._applyFilter = opts.applyFilter;
this._timefilter = opts.timefilter;
this._findIndex = opts.findIndex;
this._view = null;
@@ -263,7 +264,8 @@ export class VegaBaseView {
async addFilterHandler(query, index) {
const indexId = await this._findIndex(index);
const filter = esFilters.buildQueryFilter(query, indexId);
- this._filterManager.addFilters(filter);
+
+ this._applyFilter({ filters: [filter] });
}
/**
@@ -298,7 +300,22 @@ export class VegaBaseView {
* @param {number|string|Date} end
*/
setTimeFilterHandler(start, end) {
- this._timefilter.setTime(VegaBaseView._parseTimeRange(start, end));
+ const { from, to, mode } = VegaBaseView._parseTimeRange(start, end);
+
+ this._applyFilter({
+ timeFieldName: '*',
+ filters: [
+ {
+ range: {
+ '*': {
+ mode,
+ gte: from,
+ lte: to,
+ },
+ },
+ },
+ ],
+ });
}
/**
diff --git a/src/plugins/vis_type_vega/public/vega_visualization.js b/src/plugins/vis_type_vega/public/vega_visualization.js
index 1fcb89f04457d..d6db0f9ea239f 100644
--- a/src/plugins/vis_type_vega/public/vega_visualization.js
+++ b/src/plugins/vis_type_vega/public/vega_visualization.js
@@ -106,6 +106,7 @@ export const createVegaVisualization = ({ serviceSettings }) =>
const { timefilter } = this.dataPlugin.query.timefilter;
const vegaViewParams = {
parentEl: this._el,
+ applyFilter: this._vis.API.events.applyFilter,
vegaParser,
serviceSettings,
filterManager,
diff --git a/src/plugins/vis_type_vega/public/vega_visualization.test.js b/src/plugins/vis_type_vega/public/vega_visualization.test.js
index 3e318fa22c195..0912edf9503a6 100644
--- a/src/plugins/vis_type_vega/public/vega_visualization.test.js
+++ b/src/plugins/vis_type_vega/public/vega_visualization.test.js
@@ -105,6 +105,11 @@ describe('VegaVisualizations', () => {
vis = {
type: vegaVisType,
+ API: {
+ events: {
+ applyFilter: jest.fn(),
+ },
+ },
};
});
diff --git a/src/plugins/vis_type_vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap b/src/plugins/vis_type_vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap
index 8a90ff8982732..6e8d018bd8f0e 100644
--- a/src/plugins/vis_type_vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap
+++ b/src/plugins/vis_type_vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap
@@ -2,4 +2,4 @@
exports[`VisLegend Component Legend closed should match the snapshot 1`] = `""`;
-exports[`VisLegend Component Legend open should match the snapshot 1`] = `""`;
+exports[`VisLegend Component Legend open should match the snapshot 1`] = `""`;
diff --git a/src/plugins/visualizations/public/embeddable/events.ts b/src/plugins/visualizations/public/embeddable/events.ts
index 0957895a21403..52cac59fbffaa 100644
--- a/src/plugins/visualizations/public/embeddable/events.ts
+++ b/src/plugins/visualizations/public/embeddable/events.ts
@@ -17,14 +17,20 @@
* under the License.
*/
-import { SELECT_RANGE_TRIGGER, VALUE_CLICK_TRIGGER } from '../../../../plugins/ui_actions/public';
+import {
+ APPLY_FILTER_TRIGGER,
+ SELECT_RANGE_TRIGGER,
+ VALUE_CLICK_TRIGGER,
+} from '../../../../plugins/ui_actions/public';
export interface VisEventToTrigger {
+ ['applyFilter']: typeof APPLY_FILTER_TRIGGER;
['brush']: typeof SELECT_RANGE_TRIGGER;
['filter']: typeof VALUE_CLICK_TRIGGER;
}
export const VIS_EVENT_TO_TRIGGER: VisEventToTrigger = {
+ applyFilter: APPLY_FILTER_TRIGGER,
brush: SELECT_RANGE_TRIGGER,
filter: VALUE_CLICK_TRIGGER,
};
diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
index cc24c0509fbc1..80e577930fa8d 100644
--- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
+++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
@@ -310,12 +310,21 @@ export class VisualizeEmbeddable extends Embeddable void;
brush: (data: any) => void;
+ applyFilter: (data: any) => void;
}
export interface ExprVisAPI {
@@ -83,6 +84,10 @@ export class ExprVis extends EventEmitter {
if (!this.eventsSubject) return;
this.eventsSubject.next({ name: 'brush', data });
},
+ applyFilter: (data: any) => {
+ if (!this.eventsSubject) return;
+ this.eventsSubject.next({ name: 'applyFilter', data });
+ },
},
};
}
diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts
index 2ac53c2c81acc..49cfbe76aa9d0 100644
--- a/src/plugins/visualizations/public/index.ts
+++ b/src/plugins/visualizations/public/index.ts
@@ -51,5 +51,6 @@ export {
VisSavedObject,
VisResponseValue,
} from './types';
+export { ExprVisAPIEvents } from './expressions/vis';
export { VisualizationListItem } from './vis_types/vis_type_alias_registry';
export { VISUALIZE_ENABLE_LABS_SETTING } from '../common/constants';
diff --git a/src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap b/src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap
index 5458c88974572..a27dfa13e743e 100644
--- a/src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap
+++ b/src/plugins/visualizations/public/wizard/__snapshots__/new_vis_modal.test.tsx.snap
@@ -142,11 +142,15 @@ exports[`NewVisModal filter for visualization types should render as expected 1`
class="euiOverlayMask euiOverlayMask--aboveHeader"
>
-
-
-
-
+ lockProps={
+ Object {
+ "allowPinchZoom": undefined,
+ "enabled": false,
+ "inert": undefined,
+ "onMouseDown": [Function],
+ "onTouchStart": [Function],
+ "shards": undefined,
+ "sideCar": [Function],
+ }
}
+ noFocusGuards={false}
onActivation={[Function]}
onDeactivation={[Function]}
persistentFocus={false}
+ returnFocus={true}
+ sideCar={[Function]}
>
-
+
+
-
-
-
-
-
-
-
-
-
-
+ }
+ >
+
-
+
-
-
-
-
-
+
+
-
-
-
-
-
-
- 2 types found
-
-
-
-
+ 2 types found
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Select a visualization type
+
+
+
+
+ Start creating your visualization by selecting a type for that visualization.
+
+
+
+
+
+
+
+
+ }
+ onActivation={[Function]}
+ onDeactivation={[Function]}
+ persistentFocus={false}
+ returnFocus={[Function]}
+ shards={Array []}
+ sideCar={
+ Object {
+ "assignMedium": [Function],
+ "assignSyncMedium": [Function],
+ "options": Object {
+ "async": true,
+ "ssr": false,
+ },
+ "read": [Function],
+ "useMedium": [Function],
+ }
+ }
+ >
+
+
+
+ }
+ onActivation={[Function]}
+ onDeactivation={[Function]}
+ persistentFocus={false}
+ returnFocus={[Function]}
+ shards={Array []}
+ >
+
+
-
-
+ }
+ onActivation={[Function]}
+ onDeactivation={[Function]}
+ persistentFocus={false}
+ returnFocus={[Function]}
+ shards={Array []}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
- Select a visualization type
+ New Visualization
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
+
- Start creating your visualization by selecting a type for that visualization.
-
-
+
+
+
+
+
+
+
+
+ 2 types found
+
+
+
+
+
+ -
+
+ Vis with alias Url
+
+ }
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ role="menuitem"
+ >
+
+
+
+ -
+
+ Vis with search
+
+ }
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ role="menuitem"
+ >
+
+
+
+ -
+
+ Vis Type 1
+
+ }
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ role="menuitem"
+ >
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+ Select a visualization type
+
+
+
+
+
+
+
+
+
+
+
+ Start creating your visualization by selecting a type for that visualization.
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
+ >
+
+
+
+
+
+
+
+
+
+
@@ -1712,11 +2376,15 @@ exports[`NewVisModal should render as expected 1`] = `
class="euiOverlayMask euiOverlayMask--aboveHeader"
>
-
-
-
-
+ lockProps={
+ Object {
+ "allowPinchZoom": undefined,
+ "enabled": false,
+ "inert": undefined,
+ "onMouseDown": [Function],
+ "onTouchStart": [Function],
+ "shards": undefined,
+ "sideCar": [Function],
+ }
}
+ noFocusGuards={false}
onActivation={[Function]}
onDeactivation={[Function]}
persistentFocus={false}
+ returnFocus={true}
+ sideCar={[Function]}
>
-
+
+
+
+
+
+
+
+
+
+
+ Select a visualization type
+
+
+
+
+ Start creating your visualization by selecting a type for that visualization.
+
+
+
+
+
+
+
+
+ }
+ onActivation={[Function]}
+ onDeactivation={[Function]}
+ persistentFocus={false}
+ returnFocus={[Function]}
+ shards={Array []}
+ sideCar={
+ Object {
+ "assignMedium": [Function],
+ "assignSyncMedium": [Function],
+ "options": Object {
+ "async": true,
+ "ssr": false,
+ },
+ "read": [Function],
+ "useMedium": [Function],
+ }
+ }
+ >
+
+
-
-
- Select a visualization type
-
-
-
- Start creating your visualization by selecting a type for that visualization.
-
+
+ Select a visualization type
+
+
+
+
+ Start creating your visualization by selecting a type for that visualization.
+
+
-
- }
- onActivation={[Function]}
- onDeactivation={[Function]}
- persistentFocus={false}
- />
-
-
-
-
-
-
-
-
-
-
-
-
+
-
- New Visualization
-
-
-
-
-
-
-
-
+
+
+ Select a visualization type
+
+
-
-
-
-
+ Start creating your visualization by selecting a type for that visualization.
+
+
+
+
+
+
+
+
+ }
+ onActivation={[Function]}
+ onDeactivation={[Function]}
+ persistentFocus={false}
+ returnFocus={[Function]}
+ shards={Array []}
+ >
+
+
+
+
+
+
+
+
+
+
+ Select a visualization type
+
+
+
+
+ Start creating your visualization by selecting a type for that visualization.
+
+
-
+
-
+
-
-
+ }
+ onActivation={[Function]}
+ onDeactivation={[Function]}
+ persistentFocus={false}
+ returnFocus={[Function]}
+ shards={Array []}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
- Select a visualization type
+ New Visualization
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
+
- Start creating your visualization by selecting a type for that visualization.
-
-
+
+
+
+
+
+
+
+
+
+
+ -
+
+ Vis Type 1
+
+ }
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ role="menuitem"
+ >
+
+
+
+ -
+
+ Vis with alias Url
+
+ }
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ role="menuitem"
+ >
+
+
+
+ -
+
+ Vis with search
+
+ }
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ role="menuitem"
+ >
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+ Select a visualization type
+
+
+
+
+
+
+
+
+
+
+
+ Start creating your visualization by selecting a type for that visualization.
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
-
+ >
+
+
+
+
+
+
+
+
+
+
diff --git a/test/functional/apps/dashboard/dashboard_filter_bar.js b/test/functional/apps/dashboard/dashboard_filter_bar.js
index 273779a42d3f9..cbef274b78e93 100644
--- a/test/functional/apps/dashboard/dashboard_filter_bar.js
+++ b/test/functional/apps/dashboard/dashboard_filter_bar.js
@@ -67,7 +67,7 @@ export default function ({ getService, getPageObjects }) {
it('uses default index pattern on an empty dashboard', async () => {
await testSubjects.click('addFilter');
- await dashboardExpect.fieldSuggestions(['bytes']);
+ await dashboardExpect.fieldSuggestions(['agent']);
await filterBar.ensureFieldEditorModalIsClosed();
});
diff --git a/test/functional/apps/management/_import_objects.js b/test/functional/apps/management/_import_objects.js
index 03db3a2b108f2..5fbeb978f9a1c 100644
--- a/test/functional/apps/management/_import_objects.js
+++ b/test/functional/apps/management/_import_objects.js
@@ -49,12 +49,13 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.savedObjects.checkImportSucceeded();
await PageObjects.savedObjects.clickImportDone();
- // get all the elements in the table, and index them by the 'title' visible text field
- const elements = keyBy(await PageObjects.savedObjects.getElementsInTable(), 'title');
log.debug("check that 'Log Agents' is in table as a visualization");
- expect(elements['Log Agents'].objectType).to.eql('visualization');
+ expect(await PageObjects.savedObjects.getObjectTypeByTitle('Log Agents')).to.eql(
+ 'visualization'
+ );
+
+ await PageObjects.savedObjects.clickRelationshipsByTitle('logstash-*');
- await elements['logstash-*'].relationshipsElement.click();
const flyout = keyBy(await PageObjects.savedObjects.getRelationshipFlyout(), 'title');
log.debug(
"check that 'Shared-Item Visualization AreaChart' shows 'logstash-*' as it's Parent"
@@ -150,8 +151,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should not import saved objects linked to saved searches when saved search index pattern does not exist', async function () {
- const elements = keyBy(await PageObjects.savedObjects.getElementsInTable(), 'title');
- await elements['logstash-*'].checkbox.click();
+ await PageObjects.savedObjects.clickCheckboxByTitle('logstash-*');
await PageObjects.savedObjects.clickDelete();
await PageObjects.savedObjects.importFile(
@@ -182,8 +182,7 @@ export default function ({ getService, getPageObjects }) {
it('should import saved objects with index patterns when index patterns does not exists', async () => {
// First, we need to delete the index pattern
- const elements = keyBy(await PageObjects.savedObjects.getElementsInTable(), 'title');
- await elements['logstash-*'].checkbox.click();
+ await PageObjects.savedObjects.clickCheckboxByTitle('logstash-*');
await PageObjects.savedObjects.clickDelete();
// Then, import the objects
@@ -321,8 +320,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.savedObjects.clickImportDone();
// Second, we need to delete the index pattern
- const elements = keyBy(await PageObjects.savedObjects.getElementsInTable(), 'title');
- await elements['logstash-*'].checkbox.click();
+ await PageObjects.savedObjects.clickCheckboxByTitle('logstash-*');
await PageObjects.savedObjects.clickDelete();
// Last, import a saved object connected to the saved search
@@ -353,8 +351,7 @@ export default function ({ getService, getPageObjects }) {
it('should import saved objects with index patterns when index patterns does not exists', async () => {
// First, we need to delete the index pattern
- const elements = keyBy(await PageObjects.savedObjects.getElementsInTable(), 'title');
- await elements['logstash-*'].checkbox.click();
+ await PageObjects.savedObjects.clickCheckboxByTitle('logstash-*');
await PageObjects.savedObjects.clickDelete();
// Then, import the objects
diff --git a/test/functional/apps/visualize/input_control_vis/chained_controls.js b/test/functional/apps/visualize/input_control_vis/chained_controls.js
index 89cca7dc7827e..e1a58e1da34f3 100644
--- a/test/functional/apps/visualize/input_control_vis/chained_controls.js
+++ b/test/functional/apps/visualize/input_control_vis/chained_controls.js
@@ -39,7 +39,7 @@ export default function ({ getService, getPageObjects }) {
it('should disable child control when parent control is not set', async () => {
const parentControlMenu = await comboBox.getOptionsList('listControlSelect0');
- expect(parentControlMenu.trim().split('\n').join()).to.equal('BD,BR,CN,ID,IN,JP,NG,PK,RU,US');
+ expect(parentControlMenu.trim().split('\n').join()).to.equal('BD,BR,CN,ID,IN,JP,NG,PK,RU');
const childControlInput = await find.byCssSelector('[data-test-subj="inputControl1"] input');
const isDisabled = await childControlInput.getAttribute('disabled');
diff --git a/test/functional/apps/visualize/input_control_vis/dynamic_options.js b/test/functional/apps/visualize/input_control_vis/dynamic_options.js
index b19f64511a23b..400be471be4b6 100644
--- a/test/functional/apps/visualize/input_control_vis/dynamic_options.js
+++ b/test/functional/apps/visualize/input_control_vis/dynamic_options.js
@@ -34,13 +34,13 @@ export default function ({ getService, getPageObjects }) {
it('should fetch new options when string field is filtered', async () => {
const initialOptions = await comboBox.getOptionsList('listControlSelect0');
- expect(initialOptions.trim().split('\n').join()).to.equal('BD,BR,CN,ID,IN,JP,NG,PK,RU,US');
+ expect(initialOptions.trim().split('\n').join()).to.equal('BD,BR,CN,ID,IN,JP,NG,PK,RU');
await comboBox.filterOptionsList('listControlSelect0', 'R');
await PageObjects.header.waitUntilLoadingHasFinished();
const updatedOptions = await comboBox.getOptionsList('listControlSelect0');
- expect(updatedOptions.trim().split('\n').join()).to.equal('AR,BR,FR,GR,IR,KR,RO,RU,RW,TR');
+ expect(updatedOptions.trim().split('\n').join()).to.equal('AR,BR,FR,GR,IR,KR,RO,RU,RW');
});
it('should not fetch new options when non-string is filtered', async () => {
@@ -74,13 +74,13 @@ export default function ({ getService, getPageObjects }) {
it('should fetch new options when string field is filtered', async () => {
const initialOptions = await comboBox.getOptionsList('listControlSelect1');
- expect(initialOptions.trim().split('\n').join()).to.equal('BD,BR,CN,ID,IN,JP,MX,NG,PK,US');
+ expect(initialOptions.trim().split('\n').join()).to.equal('BD,BR,CN,ID,IN,JP,MX,NG,PK');
await comboBox.filterOptionsList('listControlSelect1', 'R');
await PageObjects.header.waitUntilLoadingHasFinished();
const updatedOptions = await comboBox.getOptionsList('listControlSelect1');
- expect(updatedOptions.trim().split('\n').join()).to.equal('AR,BR,FR,GR,IR,KR,RO,RS,RU,TR');
+ expect(updatedOptions.trim().split('\n').join()).to.equal('AR,BR,FR,GR,IR,KR,RO,RS,RU');
});
});
});
diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts
index c558d9e2d8a31..5a224d930ee42 100644
--- a/test/functional/page_objects/discover_page.ts
+++ b/test/functional/page_objects/discover_page.ts
@@ -332,6 +332,7 @@ export function DiscoverPageProvider({ getService, getPageObjects }: FtrProvider
public async selectIndexPattern(indexPattern: string) {
await testSubjects.click('indexPattern-switch-link');
+ await find.setValue('[data-test-subj="indexPattern-switcher"] input', indexPattern);
await find.clickByCssSelector(
`[data-test-subj="indexPattern-switcher"] [title="${indexPattern}"]`
);
diff --git a/test/functional/page_objects/management/saved_objects_page.ts b/test/functional/page_objects/management/saved_objects_page.ts
index 03d21aa4aa52f..ad82ea9b6fbc1 100644
--- a/test/functional/page_objects/management/saved_objects_page.ts
+++ b/test/functional/page_objects/management/saved_objects_page.ts
@@ -17,6 +17,7 @@
* under the License.
*/
+import { keyBy } from 'lodash';
import { map as mapAsync } from 'bluebird';
import { FtrProviderContext } from '../../ftr_provider_context';
@@ -34,6 +35,8 @@ export function SavedObjectsPageProvider({ getService, getPageObjects }: FtrProv
await searchBox.clearValue();
await searchBox.type(objectName);
await searchBox.pressKeys(browser.keys.ENTER);
+ await PageObjects.header.waitUntilLoadingHasFinished();
+ await this.waitTableIsLoaded();
}
async importFile(path: string, overwriteAll = true) {
@@ -99,6 +102,56 @@ export function SavedObjectsPageProvider({ getService, getPageObjects }: FtrProv
});
}
+ async clickRelationshipsByTitle(title: string) {
+ const table = keyBy(await this.getElementsInTable(), 'title');
+ // should we check if table size > 0 and log error if not?
+ if (table[title].menuElement) {
+ log.debug(`we found a context menu element for (${title}) so click it`);
+ await table[title].menuElement?.click();
+ // Wait for context menu to render
+ const menuPanel = await find.byCssSelector('.euiContextMenuPanel');
+ await (await menuPanel.findByTestSubject('savedObjectsTableAction-relationships')).click();
+ } else {
+ log.debug(
+ `we didn't find a menu element so should be a relastionships element for (${title}) to click`
+ );
+ // or the action elements are on the row without the menu
+ await table[title].relationshipsElement?.click();
+ }
+ }
+
+ async clickCopyToSpaceByTitle(title: string) {
+ const table = keyBy(await this.getElementsInTable(), 'title');
+ // should we check if table size > 0 and log error if not?
+ if (table[title].menuElement) {
+ log.debug(`we found a context menu element for (${title}) so click it`);
+ await table[title].menuElement?.click();
+ // Wait for context menu to render
+ const menuPanel = await find.byCssSelector('.euiContextMenuPanel');
+ await (
+ await menuPanel.findByTestSubject('savedObjectsTableAction-copy_saved_objects_to_space')
+ ).click();
+ } else {
+ log.debug(
+ `we didn't find a menu element so should be a "copy to space" element for (${title}) to click`
+ );
+ // or the action elements are on the row without the menu
+ await table[title].copySaveObjectsElement?.click();
+ }
+ }
+
+ async clickCheckboxByTitle(title: string) {
+ const table = keyBy(await this.getElementsInTable(), 'title');
+ // should we check if table size > 0 and log error if not?
+ await table[title].checkbox.click();
+ }
+
+ async getObjectTypeByTitle(title: string) {
+ const table = keyBy(await this.getElementsInTable(), 'title');
+ // should we check if table size > 0 and log error if not?
+ return table[title].objectType;
+ }
+
async getElementsInTable() {
const rows = await testSubjects.findAll('~savedObjectsTableRow');
return mapAsync(rows, async (row) => {
@@ -107,23 +160,45 @@ export function SavedObjectsPageProvider({ getService, getPageObjects }: FtrProv
const objectType = await row.findByTestSubject('objectType');
const titleElement = await row.findByTestSubject('savedObjectsTableRowTitle');
// not all rows have inspect button - Advanced Settings objects don't
- let inspectElement;
- const innerHtml = await row.getAttribute('innerHTML');
- if (innerHtml.includes('Inspect')) {
+ // Advanced Settings has 2 actions,
+ // data-test-subj="savedObjectsTableAction-relationships"
+ // data-test-subj="savedObjectsTableAction-copy_saved_objects_to_space"
+ // Some other objects have the ...
+ // data-test-subj="euiCollapsedItemActionsButton"
+ // Maybe some objects still have the inspect element visible?
+ // !!! Also note that since we don't have spaces on OSS, the actions for the same object can be different depending on OSS or not
+ let menuElement = null;
+ let inspectElement = null;
+ let relationshipsElement = null;
+ let copySaveObjectsElement = null;
+ const actions = await row.findByClassName('euiTableRowCell--hasActions');
+ // getting the innerHTML and checking if it 'includes' a string is faster than a timeout looking for each element
+ const actionsHTML = await actions.getAttribute('innerHTML');
+ if (actionsHTML.includes('euiCollapsedItemActionsButton')) {
+ menuElement = await row.findByTestSubject('euiCollapsedItemActionsButton');
+ }
+ if (actionsHTML.includes('savedObjectsTableAction-inspect')) {
inspectElement = await row.findByTestSubject('savedObjectsTableAction-inspect');
- } else {
- inspectElement = null;
}
- const relationshipsElement = await row.findByTestSubject(
- 'savedObjectsTableAction-relationships'
- );
+ if (actionsHTML.includes('savedObjectsTableAction-relationships')) {
+ relationshipsElement = await row.findByTestSubject(
+ 'savedObjectsTableAction-relationships'
+ );
+ }
+ if (actionsHTML.includes('savedObjectsTableAction-copy_saved_objects_to_space')) {
+ copySaveObjectsElement = await row.findByTestSubject(
+ 'savedObjectsTableAction-copy_saved_objects_to_space'
+ );
+ }
return {
checkbox,
objectType: await objectType.getAttribute('aria-label'),
titleElement,
title: await titleElement.getVisibleText(),
+ menuElement,
inspectElement,
relationshipsElement,
+ copySaveObjectsElement,
};
});
}
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json
index f3e5520a14fe2..b04fbc2bb2136 100644
--- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json
+++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json
@@ -8,7 +8,7 @@
},
"license": "Apache-2.0",
"dependencies": {
- "@elastic/eui": "26.3.1",
+ "@elastic/eui": "27.4.0",
"react": "^16.12.0",
"react-dom": "^16.12.0"
},
diff --git a/test/plugin_functional/plugins/kbn_sample_panel_action/package.json b/test/plugin_functional/plugins/kbn_sample_panel_action/package.json
index b9c5b3bc5b836..e4a66fe47d8f2 100644
--- a/test/plugin_functional/plugins/kbn_sample_panel_action/package.json
+++ b/test/plugin_functional/plugins/kbn_sample_panel_action/package.json
@@ -8,7 +8,7 @@
},
"license": "Apache-2.0",
"dependencies": {
- "@elastic/eui": "26.3.1",
+ "@elastic/eui": "27.4.0",
"react": "^16.12.0"
},
"scripts": {
diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json
index 95fafdf221c64..0e560dd7be7c3 100644
--- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json
+++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json
@@ -8,7 +8,7 @@
},
"license": "Apache-2.0",
"dependencies": {
- "@elastic/eui": "26.3.1",
+ "@elastic/eui": "27.4.0",
"react": "^16.12.0"
},
"scripts": {
diff --git a/vars/kibanaPipeline.groovy b/vars/kibanaPipeline.groovy
index 173c5b7e11764..00668f2ccdaa7 100644
--- a/vars/kibanaPipeline.groovy
+++ b/vars/kibanaPipeline.groovy
@@ -170,7 +170,6 @@ def uploadCoverageArtifacts(prefix, pattern) {
def withGcsArtifactUpload(workerName, closure) {
def uploadPrefix = "kibana-ci-artifacts/jobs/${env.JOB_NAME}/${BUILD_NUMBER}/${workerName}"
def ARTIFACT_PATTERNS = [
- '**/target/public/.kbn-optimizer-cache',
'target/kibana-*',
'target/test-metrics/*',
'target/kibana-security-solution/**/*.png',
@@ -221,7 +220,7 @@ def publishJunit() {
}
}
-def sendMail() {
+def sendMail(Map params = [:]) {
// If the build doesn't have a result set by this point, there haven't been any errors and it can be marked as a success
// The e-mail plugin for the infra e-mail depends upon this being set
currentBuild.result = currentBuild.result ?: 'SUCCESS'
@@ -230,7 +229,7 @@ def sendMail() {
if (buildStatus != 'SUCCESS' && buildStatus != 'ABORTED') {
node('flyweight') {
sendInfraMail()
- sendKibanaMail()
+ sendKibanaMail(params)
}
}
}
@@ -246,12 +245,14 @@ def sendInfraMail() {
}
}
-def sendKibanaMail() {
+def sendKibanaMail(Map params = [:]) {
+ def config = [to: 'build-kibana@elastic.co'] + params
+
catchErrors {
def buildStatus = buildUtils.getBuildStatus()
if(params.NOTIFY_ON_FAILURE && buildStatus != 'SUCCESS' && buildStatus != 'ABORTED') {
emailext(
- to: 'build-kibana@elastic.co',
+ config.to,
subject: "${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - ${buildStatus}",
body: '${SCRIPT,template="groovy-html.template"}',
mimeType: 'text/html',
diff --git a/x-pack/dev-tools/jest/create_jest_config.js b/x-pack/dev-tools/jest/create_jest_config.js
index a0574dbdf36da..0e3f82792a2ba 100644
--- a/x-pack/dev-tools/jest/create_jest_config.js
+++ b/x-pack/dev-tools/jest/create_jest_config.js
@@ -58,6 +58,7 @@ export function createJestConfig({ kibanaDirectory, rootDir, xPackKibanaDirector
`${xPackKibanaDirectory}/dev-tools/jest/setup/setup_test.js`,
`${kibanaDirectory}/src/dev/jest/setup/mocks.js`,
`${kibanaDirectory}/src/dev/jest/setup/react_testing_library.js`,
+ `${kibanaDirectory}/src/dev/jest/setup/default_timeout.js`,
],
testEnvironment: 'jest-environment-jsdom-thirteen',
testMatch: ['**/*.test.{js,mjs,ts,tsx}'],
diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_to_url_drilldown/index.tsx b/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_to_url_drilldown/index.tsx
index 037e017097e53..67599687dd881 100644
--- a/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_to_url_drilldown/index.tsx
+++ b/x-pack/examples/ui_actions_enhanced_examples/public/dashboard_to_url_drilldown/index.tsx
@@ -10,6 +10,7 @@ import { reactToUiComponent } from '../../../../../src/plugins/kibana_react/publ
import { UiActionsEnhancedDrilldownDefinition as Drilldown } from '../../../../plugins/ui_actions_enhanced/public';
import { ChartActionContext } from '../../../../../src/plugins/embeddable/public';
import { CollectConfigProps as CollectConfigPropsBase } from '../../../../../src/plugins/kibana_utils/public';
+import { ActionExecutionContext } from '../../../../../src/plugins/ui_actions/public';
function isValidUrl(url: string) {
try {
@@ -101,7 +102,15 @@ export class DashboardToUrlDrilldown implements Drilldown
return config.url;
};
- public readonly execute = async (config: Config, context: ActionContext) => {
+ public readonly execute = async (
+ config: Config,
+ context: ActionExecutionContext
+ ) => {
+ // Just for showcasing:
+ // we can get trigger a which caused this drilldown execution
+ // eslint-disable-next-line no-console
+ console.log(context.trigger?.id);
+
const url = await this.getHref(config, context);
if (config.openInNewTab) {
diff --git a/x-pack/package.json b/x-pack/package.json
index 2b52646e0f748..57a0b88f8c2a5 100644
--- a/x-pack/package.json
+++ b/x-pack/package.json
@@ -44,9 +44,9 @@
"@storybook/addon-storyshots": "^5.3.19",
"@storybook/react": "^5.3.19",
"@storybook/theming": "^5.3.19",
+ "@testing-library/jest-dom": "^5.8.0",
"@testing-library/react": "^9.3.2",
"@testing-library/react-hooks": "^3.2.1",
- "@testing-library/jest-dom": "^5.8.0",
"@types/angular": "^1.6.56",
"@types/archiver": "^3.1.0",
"@types/base64-js": "^1.2.5",
@@ -72,8 +72,9 @@
"@types/gulp": "^4.0.6",
"@types/hapi__wreck": "^15.0.1",
"@types/he": "^1.1.1",
- "@types/hoist-non-react-statics": "^3.3.1",
"@types/history": "^4.7.3",
+ "@types/hoist-non-react-statics": "^3.3.1",
+ "@types/http-proxy": "^1.17.4",
"@types/jest": "^25.2.3",
"@types/jest-specific-snapshot": "^0.5.4",
"@types/joi": "^13.4.2",
@@ -94,6 +95,7 @@
"@types/object-hash": "^1.3.0",
"@types/papaparse": "^5.0.3",
"@types/pngjs": "^3.3.2",
+ "@types/pretty-ms": "^5.0.0",
"@types/prop-types": "^15.5.3",
"@types/proper-lockfile": "^3.0.1",
"@types/puppeteer": "^1.20.1",
@@ -109,6 +111,7 @@
"@types/redux-actions": "^2.6.1",
"@types/set-value": "^2.0.0",
"@types/sinon": "^7.0.13",
+ "@types/stats-lite": "^2.2.0",
"@types/styled-components": "^5.1.0",
"@types/supertest": "^2.0.5",
"@types/tar-fs": "^1.16.1",
@@ -116,11 +119,9 @@
"@types/tinycolor2": "^1.4.1",
"@types/use-resize-observer": "^6.0.0",
"@types/uuid": "^3.4.4",
+ "@types/webpack-env": "^1.15.2",
"@types/xml-crypto": "^1.4.0",
"@types/xml2js": "^0.4.5",
- "@types/stats-lite": "^2.2.0",
- "@types/pretty-ms": "^5.0.0",
- "@types/webpack-env": "^1.15.2",
"@welldone-software/why-did-you-render": "^4.0.0",
"abab": "^1.0.4",
"autoprefixer": "^9.7.4",
@@ -205,7 +206,7 @@
"@elastic/apm-rum-react": "^1.2.2",
"@elastic/datemath": "5.0.3",
"@elastic/ems-client": "7.9.3",
- "@elastic/eui": "26.3.1",
+ "@elastic/eui": "27.4.0",
"@elastic/filesaver": "1.1.2",
"@elastic/maki": "6.3.0",
"@elastic/node-crypto": "1.2.1",
@@ -227,6 +228,7 @@
"@turf/circle": "6.0.1",
"@turf/distance": "6.0.1",
"@turf/helpers": "6.0.1",
+ "@types/http-proxy-agent": "^2.0.2",
"angular": "^1.8.0",
"angular-resource": "1.8.0",
"angular-sanitize": "1.8.0",
diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/types.ts b/x-pack/plugins/actions/server/builtin_action_types/case/types.ts
index de96864d0b295..1030e3d9c5d8e 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/case/types.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/case/types.ts
@@ -9,6 +9,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { TypeOf } from '@kbn/config-schema';
+import { Logger } from '../../../../../../src/core/server';
import {
ExternalIncidentServiceConfigurationSchema,
@@ -122,7 +123,12 @@ export interface ExternalServiceApi {
export interface CreateExternalServiceBasicArgs {
api: ExternalServiceApi;
- createExternalService: (credentials: ExternalServiceCredentials) => ExternalService;
+ createExternalService: (
+ credentials: ExternalServiceCredentials,
+ logger: Logger,
+ proxySettings?: any
+ ) => ExternalService;
+ logger: Logger;
}
export interface CreateExternalServiceArgs extends CreateExternalServiceBasicArgs {
diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/utils.ts b/x-pack/plugins/actions/server/builtin_action_types/case/utils.ts
index 82dedb09c429e..d895bf386a367 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/case/utils.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/case/utils.ts
@@ -67,6 +67,7 @@ export const mapParams = (
export const createConnectorExecutor = ({
api,
createExternalService,
+ logger,
}: CreateExternalServiceBasicArgs) => async (
execOptions: ActionTypeExecutorOptions<
ExternalIncidentServiceConfiguration,
@@ -83,10 +84,14 @@ export const createConnectorExecutor = ({
actionId,
};
- const externalService = createExternalService({
- config,
- secrets,
- });
+ const externalService = createExternalService(
+ {
+ config,
+ secrets,
+ },
+ logger,
+ execOptions.proxySettings
+ );
if (!api[subAction]) {
throw new Error('[Action][ExternalService] Unsupported subAction type.');
@@ -122,10 +127,11 @@ export const createConnector = ({
validate,
createExternalService,
validationSchema,
+ logger,
}: CreateExternalServiceArgs) => {
return ({
configurationUtilities,
- executor = createConnectorExecutor({ api, createExternalService }),
+ executor = createConnectorExecutor({ api, createExternalService, logger }),
}: CreateActionTypeArgs): ActionType => ({
...config,
validate: {
diff --git a/x-pack/plugins/actions/server/builtin_action_types/email.test.ts b/x-pack/plugins/actions/server/builtin_action_types/email.test.ts
index 195f6db538ae5..62f369816d714 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/email.test.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/email.test.ts
@@ -269,6 +269,7 @@ describe('execute()', () => {
"message": "a message to you",
"subject": "the subject",
},
+ "proxySettings": undefined,
"routing": Object {
"bcc": Array [
"jimmy@example.com",
@@ -326,6 +327,7 @@ describe('execute()', () => {
"message": "a message to you",
"subject": "the subject",
},
+ "proxySettings": undefined,
"routing": Object {
"bcc": Array [
"jimmy@example.com",
diff --git a/x-pack/plugins/actions/server/builtin_action_types/email.ts b/x-pack/plugins/actions/server/builtin_action_types/email.ts
index a51a0432a01e0..e9dc4eea5dcfc 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/email.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/email.ts
@@ -184,6 +184,7 @@ async function executor(
subject: params.subject,
message: params.message,
},
+ proxySettings: execOptions.proxySettings,
};
let result;
diff --git a/x-pack/plugins/actions/server/builtin_action_types/index.ts b/x-pack/plugins/actions/server/builtin_action_types/index.ts
index 80a171cbe624d..3591e05fb3acf 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/index.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/index.ts
@@ -31,9 +31,9 @@ export function registerBuiltInActionTypes({
actionTypeRegistry.register(getIndexActionType({ logger }));
actionTypeRegistry.register(getPagerDutyActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getServerLogActionType({ logger }));
- actionTypeRegistry.register(getSlackActionType({ configurationUtilities }));
+ actionTypeRegistry.register(getSlackActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getWebhookActionType({ logger, configurationUtilities }));
actionTypeRegistry.register(getServiceNowActionType({ logger, configurationUtilities }));
- actionTypeRegistry.register(getJiraActionType({ configurationUtilities }));
- actionTypeRegistry.register(getResilientActionType({ configurationUtilities }));
+ actionTypeRegistry.register(getJiraActionType({ logger, configurationUtilities }));
+ actionTypeRegistry.register(getResilientActionType({ logger, configurationUtilities }));
}
diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts
index a2d7bb5930a75..66be0bad02d7b 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts
@@ -4,21 +4,33 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { Logger } from '../../../../../../src/core/server';
import { createConnector } from '../case/utils';
+import { ActionType } from '../../types';
import { api } from './api';
import { config } from './config';
import { validate } from './validators';
import { createExternalService } from './service';
import { JiraSecretConfiguration, JiraPublicConfiguration } from './schema';
+import { ActionsConfigurationUtilities } from '../../actions_config';
-export const getActionType = createConnector({
- api,
- config,
- validate,
- createExternalService,
- validationSchema: {
- config: JiraPublicConfiguration,
- secrets: JiraSecretConfiguration,
- },
-});
+export function getActionType({
+ logger,
+ configurationUtilities,
+}: {
+ logger: Logger;
+ configurationUtilities: ActionsConfigurationUtilities;
+}): ActionType {
+ return createConnector({
+ api,
+ config,
+ validate,
+ createExternalService,
+ validationSchema: {
+ config: JiraPublicConfiguration,
+ secrets: JiraSecretConfiguration,
+ },
+ logger,
+ })({ configurationUtilities });
+}
diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts
index 3de3926b7d821..547595b4c183f 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts
@@ -9,6 +9,9 @@ import axios from 'axios';
import { createExternalService } from './service';
import * as utils from '../lib/axios_utils';
import { ExternalService } from '../case/types';
+import { Logger } from '../../../../../../src/core/server';
+import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
+const logger = loggingSystemMock.create().get() as jest.Mocked;
jest.mock('axios');
jest.mock('../lib/axios_utils', () => {
@@ -26,10 +29,13 @@ describe('Jira service', () => {
let service: ExternalService;
beforeAll(() => {
- service = createExternalService({
- config: { apiUrl: 'https://siem-kibana.atlassian.net', projectKey: 'CK' },
- secrets: { apiToken: 'token', email: 'elastic@elastic.com' },
- });
+ service = createExternalService(
+ {
+ config: { apiUrl: 'https://siem-kibana.atlassian.net', projectKey: 'CK' },
+ secrets: { apiToken: 'token', email: 'elastic@elastic.com' },
+ },
+ logger
+ );
});
beforeEach(() => {
@@ -39,37 +45,49 @@ describe('Jira service', () => {
describe('createExternalService', () => {
test('throws without url', () => {
expect(() =>
- createExternalService({
- config: { apiUrl: null, projectKey: 'CK' },
- secrets: { apiToken: 'token', email: 'elastic@elastic.com' },
- })
+ createExternalService(
+ {
+ config: { apiUrl: null, projectKey: 'CK' },
+ secrets: { apiToken: 'token', email: 'elastic@elastic.com' },
+ },
+ logger
+ )
).toThrow();
});
test('throws without projectKey', () => {
expect(() =>
- createExternalService({
- config: { apiUrl: 'test.com', projectKey: null },
- secrets: { apiToken: 'token', email: 'elastic@elastic.com' },
- })
+ createExternalService(
+ {
+ config: { apiUrl: 'test.com', projectKey: null },
+ secrets: { apiToken: 'token', email: 'elastic@elastic.com' },
+ },
+ logger
+ )
).toThrow();
});
test('throws without username', () => {
expect(() =>
- createExternalService({
- config: { apiUrl: 'test.com' },
- secrets: { apiToken: '', email: 'elastic@elastic.com' },
- })
+ createExternalService(
+ {
+ config: { apiUrl: 'test.com' },
+ secrets: { apiToken: '', email: 'elastic@elastic.com' },
+ },
+ logger
+ )
).toThrow();
});
test('throws without password', () => {
expect(() =>
- createExternalService({
- config: { apiUrl: 'test.com' },
- secrets: { apiToken: '', email: undefined },
- })
+ createExternalService(
+ {
+ config: { apiUrl: 'test.com' },
+ secrets: { apiToken: '', email: undefined },
+ },
+ logger
+ )
).toThrow();
});
});
@@ -92,6 +110,7 @@ describe('Jira service', () => {
expect(requestMock).toHaveBeenCalledWith({
axios,
url: 'https://siem-kibana.atlassian.net/rest/api/2/issue/1',
+ logger,
});
});
@@ -146,6 +165,7 @@ describe('Jira service', () => {
expect(requestMock).toHaveBeenCalledWith({
axios,
url: 'https://siem-kibana.atlassian.net/rest/api/2/issue',
+ logger,
method: 'post',
data: {
fields: {
@@ -210,6 +230,7 @@ describe('Jira service', () => {
expect(requestMock).toHaveBeenCalledWith({
axios,
+ logger,
method: 'put',
url: 'https://siem-kibana.atlassian.net/rest/api/2/issue/1',
data: { fields: { summary: 'title', description: 'desc' } },
@@ -272,6 +293,7 @@ describe('Jira service', () => {
expect(requestMock).toHaveBeenCalledWith({
axios,
+ logger,
method: 'post',
url: 'https://siem-kibana.atlassian.net/rest/api/2/issue/1/comment',
data: { body: 'comment' },
diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts
index 240b645c3a7dc..aec73cfb375ed 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts
@@ -7,6 +7,7 @@
import axios from 'axios';
import { ExternalServiceCredentials, ExternalService, ExternalServiceParams } from '../case/types';
+import { Logger } from '../../../../../../src/core/server';
import {
JiraPublicConfigurationType,
JiraSecretConfigurationType,
@@ -17,6 +18,7 @@ import {
import * as i18n from './translations';
import { request, getErrorMessage } from '../lib/axios_utils';
+import { ProxySettings } from '../../types';
const VERSION = '2';
const BASE_URL = `rest/api/${VERSION}`;
@@ -25,10 +27,11 @@ const COMMENT_URL = `comment`;
const VIEW_INCIDENT_URL = `browse`;
-export const createExternalService = ({
- config,
- secrets,
-}: ExternalServiceCredentials): ExternalService => {
+export const createExternalService = (
+ { config, secrets }: ExternalServiceCredentials,
+ logger: Logger,
+ proxySettings?: ProxySettings
+): ExternalService => {
const { apiUrl: url, projectKey } = config as JiraPublicConfigurationType;
const { apiToken, email } = secrets as JiraSecretConfigurationType;
@@ -55,6 +58,8 @@ export const createExternalService = ({
const res = await request({
axios: axiosInstance,
url: `${incidentUrl}/${id}`,
+ logger,
+ proxySettings,
});
const { fields, ...rest } = res.data;
@@ -75,10 +80,12 @@ export const createExternalService = ({
const res = await request({
axios: axiosInstance,
url: `${incidentUrl}`,
+ logger,
method: 'post',
data: {
fields: { ...incident, project: { key: projectKey }, issuetype: { name: 'Task' } },
},
+ proxySettings,
});
const updatedIncident = await getIncident(res.data.id);
@@ -102,7 +109,9 @@ export const createExternalService = ({
axios: axiosInstance,
method: 'put',
url: `${incidentUrl}/${incidentId}`,
+ logger,
data: { fields: { ...incident } },
+ proxySettings,
});
const updatedIncident = await getIncident(incidentId);
@@ -129,7 +138,9 @@ export const createExternalService = ({
axios: axiosInstance,
method: 'post',
url: getCommentsURL(incidentId),
+ logger,
data: { body: comment.comment },
+ proxySettings,
});
return {
diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils.test.ts
index 4a52ae60bcdda..844aa6d2de7ed 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils.test.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils.test.ts
@@ -5,7 +5,11 @@
*/
import axios from 'axios';
-import { addTimeZoneToDate, throwIfNotAlive, request, patch, getErrorMessage } from './axios_utils';
+import HttpProxyAgent from 'http-proxy-agent';
+import { Logger } from '../../../../../../src/core/server';
+import { addTimeZoneToDate, request, patch, getErrorMessage } from './axios_utils';
+import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
+const logger = loggingSystemMock.create().get() as jest.Mocked;
jest.mock('axios');
const axiosMock = (axios as unknown) as jest.Mock;
@@ -21,26 +25,6 @@ describe('addTimeZoneToDate', () => {
});
});
-describe('throwIfNotAlive ', () => {
- test('throws correctly when status is invalid', async () => {
- expect(() => {
- throwIfNotAlive(404, 'application/json');
- }).toThrow('Instance is not alive.');
- });
-
- test('throws correctly when content is invalid', () => {
- expect(() => {
- throwIfNotAlive(200, 'application/html');
- }).toThrow('Instance is not alive.');
- });
-
- test('do NOT throws with custom validStatusCodes', async () => {
- expect(() => {
- throwIfNotAlive(404, 'application/json', [404]);
- }).not.toThrow('Instance is not alive.');
- });
-});
-
describe('request', () => {
beforeEach(() => {
axiosMock.mockImplementation(() => ({
@@ -51,9 +35,22 @@ describe('request', () => {
});
test('it fetch correctly with defaults', async () => {
- const res = await request({ axios, url: '/test' });
+ const res = await request({
+ axios,
+ url: '/test',
+ logger,
+ });
- expect(axiosMock).toHaveBeenCalledWith('/test', { method: 'get', data: {} });
+ expect(axiosMock).toHaveBeenCalledWith('/test', {
+ method: 'get',
+ data: {},
+ headers: undefined,
+ httpAgent: undefined,
+ httpsAgent: undefined,
+ params: undefined,
+ proxy: false,
+ validateStatus: undefined,
+ });
expect(res).toEqual({
status: 200,
headers: { 'content-type': 'application/json' },
@@ -61,10 +58,27 @@ describe('request', () => {
});
});
- test('it fetch correctly', async () => {
- const res = await request({ axios, url: '/test', method: 'post', data: { id: '123' } });
+ test('it have been called with proper proxy agent', async () => {
+ const res = await request({
+ axios,
+ url: '/testProxy',
+ logger,
+ proxySettings: {
+ proxyUrl: 'http://localhost:1212',
+ rejectUnauthorizedCertificates: false,
+ },
+ });
- expect(axiosMock).toHaveBeenCalledWith('/test', { method: 'post', data: { id: '123' } });
+ expect(axiosMock).toHaveBeenCalledWith('/testProxy', {
+ method: 'get',
+ data: {},
+ headers: undefined,
+ httpAgent: new HttpProxyAgent('http://localhost:1212'),
+ httpsAgent: new HttpProxyAgent('http://localhost:1212'),
+ params: undefined,
+ proxy: false,
+ validateStatus: undefined,
+ });
expect(res).toEqual({
status: 200,
headers: { 'content-type': 'application/json' },
@@ -72,14 +86,24 @@ describe('request', () => {
});
});
- test('it throws correctly', async () => {
- axiosMock.mockImplementation(() => ({
- status: 404,
+ test('it fetch correctly', async () => {
+ const res = await request({ axios, url: '/test', method: 'post', logger, data: { id: '123' } });
+
+ expect(axiosMock).toHaveBeenCalledWith('/test', {
+ method: 'post',
+ data: { id: '123' },
+ headers: undefined,
+ httpAgent: undefined,
+ httpsAgent: undefined,
+ params: undefined,
+ proxy: false,
+ validateStatus: undefined,
+ });
+ expect(res).toEqual({
+ status: 200,
headers: { 'content-type': 'application/json' },
data: { incidentId: '123' },
- }));
-
- await expect(request({ axios, url: '/test' })).rejects.toThrow();
+ });
});
});
@@ -92,8 +116,17 @@ describe('patch', () => {
});
test('it fetch correctly', async () => {
- await patch({ axios, url: '/test', data: { id: '123' } });
- expect(axiosMock).toHaveBeenCalledWith('/test', { method: 'patch', data: { id: '123' } });
+ await patch({ axios, url: '/test', data: { id: '123' }, logger });
+ expect(axiosMock).toHaveBeenCalledWith('/test', {
+ method: 'patch',
+ data: { id: '123' },
+ headers: undefined,
+ httpAgent: undefined,
+ httpsAgent: undefined,
+ params: undefined,
+ proxy: false,
+ validateStatus: undefined,
+ });
});
});
diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils.ts
index d527cf632bace..e26a3b686179c 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/lib/axios_utils.ts
@@ -4,50 +4,68 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { AxiosInstance, Method, AxiosResponse } from 'axios';
-
-export const throwIfNotAlive = (
- status: number,
- contentType: string,
- validStatusCodes: number[] = [200, 201, 204]
-) => {
- if (!validStatusCodes.includes(status) || !contentType.includes('application/json')) {
- throw new Error('Instance is not alive.');
- }
-};
+import { AxiosInstance, Method, AxiosResponse, AxiosBasicCredentials } from 'axios';
+import { Logger } from '../../../../../../src/core/server';
+import { ProxySettings } from '../../types';
+import { getProxyAgent } from './get_proxy_agent';
export const request = async ({
axios,
url,
+ logger,
method = 'get',
data,
params,
+ proxySettings,
+ headers,
+ validateStatus,
+ auth,
}: {
axios: AxiosInstance;
url: string;
+ logger: Logger;
method?: Method;
data?: T;
params?: unknown;
+ proxySettings?: ProxySettings;
+ headers?: Record | null;
+ validateStatus?: (status: number) => boolean;
+ auth?: AxiosBasicCredentials;
}): Promise => {
- const res = await axios(url, { method, data: data ?? {}, params });
- throwIfNotAlive(res.status, res.headers['content-type']);
- return res;
+ return await axios(url, {
+ method,
+ data: data ?? {},
+ params,
+ auth,
+ // use httpsAgent and embedded proxy: false, to be able to handle fail on invalid certs
+ httpsAgent: proxySettings ? getProxyAgent(proxySettings, logger) : undefined,
+ httpAgent: proxySettings ? getProxyAgent(proxySettings, logger) : undefined,
+ proxy: false, // the same way as it done for IncomingWebhook in
+ headers,
+ validateStatus,
+ });
};
export const patch = async ({
axios,
url,
data,
+ logger,
+ proxySettings,
}: {
axios: AxiosInstance;
url: string;
data: T;
+ logger: Logger;
+ proxySettings?: ProxySettings;
}): Promise => {
return request({
axios,
url,
+ logger,
method: 'patch',
data,
+ proxySettings,
});
};
diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/get_proxy_agent.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/get_proxy_agent.test.ts
new file mode 100644
index 0000000000000..2468fab8c6ac5
--- /dev/null
+++ b/x-pack/plugins/actions/server/builtin_action_types/lib/get_proxy_agent.test.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import HttpProxyAgent from 'http-proxy-agent';
+import { HttpsProxyAgent } from 'https-proxy-agent';
+import { Logger } from '../../../../../../src/core/server';
+import { getProxyAgent } from './get_proxy_agent';
+import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
+const logger = loggingSystemMock.create().get() as jest.Mocked;
+
+describe('getProxyAgent', () => {
+ test('return HttpsProxyAgent for https proxy url', () => {
+ const agent = getProxyAgent(
+ { proxyUrl: 'https://someproxyhost', rejectUnauthorizedCertificates: false },
+ logger
+ );
+ expect(agent instanceof HttpsProxyAgent).toBeTruthy();
+ });
+
+ test('return HttpProxyAgent for http proxy url', () => {
+ const agent = getProxyAgent(
+ { proxyUrl: 'http://someproxyhost', rejectUnauthorizedCertificates: false },
+ logger
+ );
+ expect(agent instanceof HttpProxyAgent).toBeTruthy();
+ });
+});
diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/get_proxy_agent.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/get_proxy_agent.ts
new file mode 100644
index 0000000000000..bb4dadd3a4698
--- /dev/null
+++ b/x-pack/plugins/actions/server/builtin_action_types/lib/get_proxy_agent.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import HttpProxyAgent from 'http-proxy-agent';
+import { HttpsProxyAgent } from 'https-proxy-agent';
+import { Logger } from '../../../../../../src/core/server';
+import { ProxySettings } from '../../types';
+
+export function getProxyAgent(
+ proxySettings: ProxySettings,
+ logger: Logger
+): HttpsProxyAgent | HttpProxyAgent {
+ logger.debug(`Create proxy agent for ${proxySettings.proxyUrl}.`);
+
+ if (/^https/i.test(proxySettings.proxyUrl)) {
+ const proxyUrl = new URL(proxySettings.proxyUrl);
+ return new HttpsProxyAgent({
+ host: proxyUrl.hostname,
+ port: Number(proxyUrl.port),
+ protocol: proxyUrl.protocol,
+ headers: proxySettings.proxyHeaders,
+ // do not fail on invalid certs if value is false
+ rejectUnauthorized: proxySettings.rejectUnauthorizedCertificates,
+ });
+ } else {
+ return new HttpProxyAgent(proxySettings.proxyUrl);
+ }
+}
diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/post_pagerduty.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/post_pagerduty.ts
index 92f88ebe0be22..d78237beb98a1 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/lib/post_pagerduty.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/lib/post_pagerduty.ts
@@ -5,22 +5,34 @@
*/
import axios, { AxiosResponse } from 'axios';
-import { Services } from '../../types';
+import { Logger } from '../../../../../../src/core/server';
+import { Services, ProxySettings } from '../../types';
+import { request } from './axios_utils';
interface PostPagerdutyOptions {
apiUrl: string;
data: unknown;
headers: Record;
services: Services;
+ proxySettings?: ProxySettings;
}
// post an event to pagerduty
-export async function postPagerduty(options: PostPagerdutyOptions): Promise {
- const { apiUrl, data, headers } = options;
- const axiosOptions = {
+export async function postPagerduty(
+ options: PostPagerdutyOptions,
+ logger: Logger
+): Promise {
+ const { apiUrl, data, headers, proxySettings } = options;
+ const axiosInstance = axios.create();
+
+ return await request({
+ axios: axiosInstance,
+ url: apiUrl,
+ method: 'post',
+ logger,
+ data,
+ proxySettings,
headers,
validateStatus: () => true,
- };
-
- return axios.post(apiUrl, data, axiosOptions);
+ });
}
diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts
index 3514bd4257b0f..8287ee944bca9 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts
@@ -12,6 +12,7 @@ import { Logger } from '../../../../../../src/core/server';
import { sendEmail } from './send_email';
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
import nodemailer from 'nodemailer';
+import { ProxySettings } from '../../types';
const createTransportMock = nodemailer.createTransport as jest.Mock;
const sendMailMockResult = { result: 'does not matter' };
@@ -63,6 +64,59 @@ describe('send_email module', () => {
});
test('handles unauthenticated email using not secure host/port', async () => {
+ const sendEmailOptions = getSendEmailOptions(
+ {
+ transport: {
+ host: 'example.com',
+ port: 1025,
+ },
+ },
+ {
+ proxyUrl: 'https://example.com',
+ rejectUnauthorizedCertificates: false,
+ }
+ );
+ delete sendEmailOptions.transport.service;
+ delete sendEmailOptions.transport.user;
+ delete sendEmailOptions.transport.password;
+ const result = await sendEmail(mockLogger, sendEmailOptions);
+ expect(result).toBe(sendMailMockResult);
+ expect(createTransportMock.mock.calls[0]).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "headers": undefined,
+ "host": "example.com",
+ "port": 1025,
+ "proxy": "https://example.com",
+ "secure": false,
+ "tls": Object {
+ "rejectUnauthorized": false,
+ },
+ },
+ ]
+ `);
+ expect(sendMailMock.mock.calls[0]).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "bcc": Array [],
+ "cc": Array [
+ "bob@example.com",
+ "robert@example.com",
+ ],
+ "from": "fred@example.com",
+ "html": "a message
+ ",
+ "subject": "a subject",
+ "text": "a message",
+ "to": Array [
+ "jim@example.com",
+ ],
+ },
+ ]
+ `);
+ });
+
+ test('rejectUnauthorized default setting email using not secure host/port', async () => {
const sendEmailOptions = getSendEmailOptions({
transport: {
host: 'example.com',
@@ -80,9 +134,6 @@ describe('send_email module', () => {
"host": "example.com",
"port": 1025,
"secure": false,
- "tls": Object {
- "rejectUnauthorized": false,
- },
},
]
`);
@@ -161,7 +212,10 @@ describe('send_email module', () => {
});
});
-function getSendEmailOptions({ content = {}, routing = {}, transport = {} } = {}) {
+function getSendEmailOptions(
+ { content = {}, routing = {}, transport = {} } = {},
+ proxySettings?: ProxySettings
+) {
return {
content: {
...content,
@@ -181,5 +235,6 @@ function getSendEmailOptions({ content = {}, routing = {}, transport = {} } = {}
user: 'elastic',
password: 'changeme',
},
+ proxySettings,
};
}
diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts
index 869db34f034ae..a4f32f1880cb5 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts
@@ -6,10 +6,10 @@
// info on nodemailer: https://nodemailer.com/about/
import nodemailer from 'nodemailer';
-
import { default as MarkdownIt } from 'markdown-it';
import { Logger } from '../../../../../../src/core/server';
+import { ProxySettings } from '../../types';
// an email "service" which doesn't actually send, just returns what it would send
export const JSON_TRANSPORT_SERVICE = '__json';
@@ -18,6 +18,7 @@ export interface SendEmailOptions {
transport: Transport;
routing: Routing;
content: Content;
+ proxySettings?: ProxySettings;
}
// config validation ensures either service is set or host/port are set
@@ -44,7 +45,7 @@ export interface Content {
// send an email
export async function sendEmail(logger: Logger, options: SendEmailOptions): Promise {
- const { transport, routing, content } = options;
+ const { transport, routing, content, proxySettings } = options;
const { service, host, port, secure, user, password } = transport;
const { from, to, cc, bcc } = routing;
const { subject, message } = content;
@@ -67,11 +68,16 @@ export async function sendEmail(logger: Logger, options: SendEmailOptions): Prom
transportConfig.host = host;
transportConfig.port = port;
transportConfig.secure = !!secure;
- if (!transportConfig.secure) {
+ if (proxySettings && !transportConfig.secure) {
transportConfig.tls = {
- rejectUnauthorized: false,
+ // do not fail on invalid certs if value is false
+ rejectUnauthorized: proxySettings?.rejectUnauthorizedCertificates,
};
}
+ if (proxySettings) {
+ transportConfig.proxy = proxySettings.proxyUrl;
+ transportConfig.headers = proxySettings.proxyHeaders;
+ }
}
const nodemailerTransport = nodemailer.createTransport(transportConfig);
diff --git a/x-pack/plugins/actions/server/builtin_action_types/pagerduty.ts b/x-pack/plugins/actions/server/builtin_action_types/pagerduty.ts
index b76e57419bc56..c0edfc530e738 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/pagerduty.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/pagerduty.ts
@@ -161,6 +161,7 @@ async function executor(
const secrets = execOptions.secrets;
const params = execOptions.params;
const services = execOptions.services;
+ const proxySettings = execOptions.proxySettings;
const apiUrl = getPagerDutyApiUrl(config);
const headers = {
@@ -171,7 +172,7 @@ async function executor(
let response;
try {
- response = await postPagerduty({ apiUrl, data, headers, services });
+ response = await postPagerduty({ apiUrl, data, headers, services, proxySettings }, logger);
} catch (err) {
const message = i18n.translate('xpack.actions.builtin.pagerduty.postingErrorMessage', {
defaultMessage: 'error posting pagerduty event',
diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/index.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/index.ts
index e98bc71559d3f..1e9cb15589702 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/resilient/index.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/index.ts
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { Logger } from '../../../../../../src/core/server';
import { createConnector } from '../case/utils';
import { api } from './api';
@@ -11,14 +12,25 @@ import { config } from './config';
import { validate } from './validators';
import { createExternalService } from './service';
import { ResilientSecretConfiguration, ResilientPublicConfiguration } from './schema';
+import { ActionsConfigurationUtilities } from '../../actions_config';
+import { ActionType } from '../../types';
-export const getActionType = createConnector({
- api,
- config,
- validate,
- createExternalService,
- validationSchema: {
- config: ResilientPublicConfiguration,
- secrets: ResilientSecretConfiguration,
- },
-});
+export function getActionType({
+ logger,
+ configurationUtilities,
+}: {
+ logger: Logger;
+ configurationUtilities: ActionsConfigurationUtilities;
+}): ActionType {
+ return createConnector({
+ api,
+ config,
+ validate,
+ createExternalService,
+ validationSchema: {
+ config: ResilientPublicConfiguration,
+ secrets: ResilientSecretConfiguration,
+ },
+ logger,
+ })({ configurationUtilities });
+}
diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/service.test.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/service.test.ts
index 573885698014e..a9271671f68b9 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/resilient/service.test.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/service.test.ts
@@ -9,6 +9,9 @@ import axios from 'axios';
import { createExternalService, getValueTextContent, formatUpdateRequest } from './service';
import * as utils from '../lib/axios_utils';
import { ExternalService } from '../case/types';
+import { Logger } from '../../../../../../src/core/server';
+import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
+const logger = loggingSystemMock.create().get() as jest.Mocked;
jest.mock('axios');
jest.mock('../lib/axios_utils', () => {
@@ -72,10 +75,13 @@ describe('IBM Resilient service', () => {
let service: ExternalService;
beforeAll(() => {
- service = createExternalService({
- config: { apiUrl: 'https://resilient.elastic.co', orgId: '201' },
- secrets: { apiKeyId: 'keyId', apiKeySecret: 'secret' },
- });
+ service = createExternalService(
+ {
+ config: { apiUrl: 'https://resilient.elastic.co', orgId: '201' },
+ secrets: { apiKeyId: 'keyId', apiKeySecret: 'secret' },
+ },
+ logger
+ );
});
afterAll(() => {
@@ -138,37 +144,49 @@ describe('IBM Resilient service', () => {
describe('createExternalService', () => {
test('throws without url', () => {
expect(() =>
- createExternalService({
- config: { apiUrl: null, orgId: '201' },
- secrets: { apiKeyId: 'token', apiKeySecret: 'secret' },
- })
+ createExternalService(
+ {
+ config: { apiUrl: null, orgId: '201' },
+ secrets: { apiKeyId: 'token', apiKeySecret: 'secret' },
+ },
+ logger
+ )
).toThrow();
});
test('throws without orgId', () => {
expect(() =>
- createExternalService({
- config: { apiUrl: 'test.com', orgId: null },
- secrets: { apiKeyId: 'token', apiKeySecret: 'secret' },
- })
+ createExternalService(
+ {
+ config: { apiUrl: 'test.com', orgId: null },
+ secrets: { apiKeyId: 'token', apiKeySecret: 'secret' },
+ },
+ logger
+ )
).toThrow();
});
test('throws without username', () => {
expect(() =>
- createExternalService({
- config: { apiUrl: 'test.com', orgId: '201' },
- secrets: { apiKeyId: '', apiKeySecret: 'secret' },
- })
+ createExternalService(
+ {
+ config: { apiUrl: 'test.com', orgId: '201' },
+ secrets: { apiKeyId: '', apiKeySecret: 'secret' },
+ },
+ logger
+ )
).toThrow();
});
test('throws without password', () => {
expect(() =>
- createExternalService({
- config: { apiUrl: 'test.com', orgId: '201' },
- secrets: { apiKeyId: '', apiKeySecret: undefined },
- })
+ createExternalService(
+ {
+ config: { apiUrl: 'test.com', orgId: '201' },
+ secrets: { apiKeyId: '', apiKeySecret: undefined },
+ },
+ logger
+ )
).toThrow();
});
});
@@ -197,6 +215,7 @@ describe('IBM Resilient service', () => {
await service.getIncident('1');
expect(requestMock).toHaveBeenCalledWith({
axios,
+ logger,
url: 'https://resilient.elastic.co/rest/orgs/201/incidents/1',
params: {
text_content_output_format: 'objects_convert',
@@ -256,6 +275,7 @@ describe('IBM Resilient service', () => {
expect(requestMock).toHaveBeenCalledWith({
axios,
url: 'https://resilient.elastic.co/rest/orgs/201/incidents',
+ logger,
method: 'post',
data: {
name: 'title',
@@ -311,6 +331,7 @@ describe('IBM Resilient service', () => {
// The second call to the API is the update call.
expect(requestMock.mock.calls[1][0]).toEqual({
axios,
+ logger,
method: 'patch',
url: 'https://resilient.elastic.co/rest/orgs/201/incidents/1',
data: {
@@ -392,7 +413,9 @@ describe('IBM Resilient service', () => {
expect(requestMock).toHaveBeenCalledWith({
axios,
+ logger,
method: 'post',
+ proxySettings: undefined,
url: 'https://resilient.elastic.co/rest/orgs/201/incidents/1/comments',
data: {
text: {
diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/service.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/service.ts
index 8d0526ca3b571..b2150081f2c89 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/resilient/service.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/service.ts
@@ -6,6 +6,7 @@
import axios from 'axios';
+import { Logger } from '../../../../../../src/core/server';
import { ExternalServiceCredentials, ExternalService, ExternalServiceParams } from '../case/types';
import {
ResilientPublicConfigurationType,
@@ -19,6 +20,7 @@ import {
import * as i18n from './translations';
import { getErrorMessage, request } from '../lib/axios_utils';
+import { ProxySettings } from '../../types';
const BASE_URL = `rest`;
const INCIDENT_URL = `incidents`;
@@ -57,10 +59,11 @@ export const formatUpdateRequest = ({
};
};
-export const createExternalService = ({
- config,
- secrets,
-}: ExternalServiceCredentials): ExternalService => {
+export const createExternalService = (
+ { config, secrets }: ExternalServiceCredentials,
+ logger: Logger,
+ proxySettings?: ProxySettings
+): ExternalService => {
const { apiUrl: url, orgId } = config as ResilientPublicConfigurationType;
const { apiKeyId, apiKeySecret } = secrets as ResilientSecretConfigurationType;
@@ -88,9 +91,11 @@ export const createExternalService = ({
const res = await request({
axios: axiosInstance,
url: `${incidentUrl}/${id}`,
+ logger,
params: {
text_content_output_format: 'objects_convert',
},
+ proxySettings,
});
return { ...res.data, description: res.data.description?.content ?? '' };
@@ -107,6 +112,7 @@ export const createExternalService = ({
axios: axiosInstance,
url: `${incidentUrl}`,
method: 'post',
+ logger,
data: {
...incident,
description: {
@@ -115,6 +121,7 @@ export const createExternalService = ({
},
discovered_date: Date.now(),
},
+ proxySettings,
});
return {
@@ -139,7 +146,9 @@ export const createExternalService = ({
axios: axiosInstance,
method: 'patch',
url: `${incidentUrl}/${incidentId}`,
+ logger,
data,
+ proxySettings,
});
if (!res.data.success) {
@@ -170,7 +179,9 @@ export const createExternalService = ({
axios: axiosInstance,
method: 'post',
url: getCommentsURL(incidentId),
+ logger,
data: { text: { format: 'text', content: comment.comment } },
+ proxySettings,
});
return {
diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts
index 109008b8fc9fb..3addbe7c54dac 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts
@@ -76,10 +76,14 @@ async function executor(
const { subAction, subActionParams } = params;
let data: PushToServiceResponse | null = null;
- const externalService = createExternalService({
- config,
- secrets,
- });
+ const externalService = createExternalService(
+ {
+ config,
+ secrets,
+ },
+ logger,
+ execOptions.proxySettings
+ );
if (!api[subAction]) {
const errorMessage = `[Action][ExternalService] Unsupported subAction type ${subAction}.`;
diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.test.ts
index 07d60ec9f7a05..2adcdf561ce17 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.test.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.test.ts
@@ -9,6 +9,9 @@ import axios from 'axios';
import { createExternalService } from './service';
import * as utils from '../lib/axios_utils';
import { ExternalService } from './types';
+import { Logger } from '../../../../../../src/core/server';
+import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
+const logger = loggingSystemMock.create().get() as jest.Mocked;
jest.mock('axios');
jest.mock('../lib/axios_utils', () => {
@@ -28,10 +31,13 @@ describe('ServiceNow service', () => {
let service: ExternalService;
beforeAll(() => {
- service = createExternalService({
- config: { apiUrl: 'https://dev102283.service-now.com' },
- secrets: { username: 'admin', password: 'admin' },
- });
+ service = createExternalService(
+ {
+ config: { apiUrl: 'https://dev102283.service-now.com' },
+ secrets: { username: 'admin', password: 'admin' },
+ },
+ logger
+ );
});
beforeEach(() => {
@@ -41,28 +47,37 @@ describe('ServiceNow service', () => {
describe('createExternalService', () => {
test('throws without url', () => {
expect(() =>
- createExternalService({
- config: { apiUrl: null },
- secrets: { username: 'admin', password: 'admin' },
- })
+ createExternalService(
+ {
+ config: { apiUrl: null },
+ secrets: { username: 'admin', password: 'admin' },
+ },
+ logger
+ )
).toThrow();
});
test('throws without username', () => {
expect(() =>
- createExternalService({
- config: { apiUrl: 'test.com' },
- secrets: { username: '', password: 'admin' },
- })
+ createExternalService(
+ {
+ config: { apiUrl: 'test.com' },
+ secrets: { username: '', password: 'admin' },
+ },
+ logger
+ )
).toThrow();
});
test('throws without password', () => {
expect(() =>
- createExternalService({
- config: { apiUrl: 'test.com' },
- secrets: { username: '', password: undefined },
- })
+ createExternalService(
+ {
+ config: { apiUrl: 'test.com' },
+ secrets: { username: '', password: undefined },
+ },
+ logger
+ )
).toThrow();
});
});
@@ -84,6 +99,7 @@ describe('ServiceNow service', () => {
await service.getIncident('1');
expect(requestMock).toHaveBeenCalledWith({
axios,
+ logger,
url: 'https://dev102283.service-now.com/api/now/v2/table/incident/1',
});
});
@@ -127,6 +143,7 @@ describe('ServiceNow service', () => {
expect(requestMock).toHaveBeenCalledWith({
axios,
+ logger,
url: 'https://dev102283.service-now.com/api/now/v2/table/incident',
method: 'post',
data: { short_description: 'title', description: 'desc' },
@@ -179,6 +196,7 @@ describe('ServiceNow service', () => {
expect(patchMock).toHaveBeenCalledWith({
axios,
+ logger,
url: 'https://dev102283.service-now.com/api/now/v2/table/incident/1',
data: { short_description: 'title', description: 'desc' },
});
diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.ts
index 2b5204af2eb7d..cf1c26e6462a2 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.ts
@@ -9,8 +9,10 @@ import axios from 'axios';
import { ExternalServiceCredentials, ExternalService, ExternalServiceParams } from './types';
import * as i18n from './translations';
+import { Logger } from '../../../../../../src/core/server';
import { ServiceNowPublicConfigurationType, ServiceNowSecretConfigurationType } from './types';
import { request, getErrorMessage, addTimeZoneToDate, patch } from '../lib/axios_utils';
+import { ProxySettings } from '../../types';
const API_VERSION = 'v2';
const INCIDENT_URL = `api/now/${API_VERSION}/table/incident`;
@@ -18,10 +20,11 @@ const INCIDENT_URL = `api/now/${API_VERSION}/table/incident`;
// Based on: https://docs.servicenow.com/bundle/orlando-platform-user-interface/page/use/navigation/reference/r_NavigatingByURLExamples.html
const VIEW_INCIDENT_URL = `nav_to.do?uri=incident.do?sys_id=`;
-export const createExternalService = ({
- config,
- secrets,
-}: ExternalServiceCredentials): ExternalService => {
+export const createExternalService = (
+ { config, secrets }: ExternalServiceCredentials,
+ logger: Logger,
+ proxySettings?: ProxySettings
+): ExternalService => {
const { apiUrl: url } = config as ServiceNowPublicConfigurationType;
const { username, password } = secrets as ServiceNowSecretConfigurationType;
@@ -43,6 +46,8 @@ export const createExternalService = ({
const res = await request({
axios: axiosInstance,
url: `${incidentUrl}/${id}`,
+ logger,
+ proxySettings,
});
return { ...res.data.result };
@@ -58,6 +63,8 @@ export const createExternalService = ({
const res = await request({
axios: axiosInstance,
url: incidentUrl,
+ logger,
+ proxySettings,
params,
});
@@ -71,9 +78,13 @@ export const createExternalService = ({
const createIncident = async ({ incident }: ExternalServiceParams) => {
try {
+ logger.warn(`incident error : ${JSON.stringify(proxySettings)}`);
+ logger.warn(`incident error : ${url}`);
const res = await request({
axios: axiosInstance,
url: `${incidentUrl}`,
+ logger,
+ proxySettings,
method: 'post',
data: { ...(incident as Record) },
});
@@ -96,7 +107,9 @@ export const createExternalService = ({
const res = await patch({
axios: axiosInstance,
url: `${incidentUrl}/${incidentId}`,
+ logger,
data: { ...(incident as Record) },
+ proxySettings,
});
return {
diff --git a/x-pack/plugins/actions/server/builtin_action_types/slack.test.ts b/x-pack/plugins/actions/server/builtin_action_types/slack.test.ts
index 6d4176067c3ba..812657138152c 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/slack.test.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/slack.test.ts
@@ -4,25 +4,40 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { Logger } from '../../../../../src/core/server';
import { Services, ActionTypeExecutorResult } from '../types';
import { validateParams, validateSecrets } from '../lib';
import { getActionType, SlackActionType, SlackActionTypeExecutorOptions } from './slack';
import { actionsConfigMock } from '../actions_config.mock';
import { actionsMock } from '../mocks';
+import { createActionTypeRegistry } from './index.test';
+
+jest.mock('@slack/webhook', () => {
+ return {
+ IncomingWebhook: jest.fn().mockImplementation(() => {
+ return { send: (message: string) => {} };
+ }),
+ };
+});
const ACTION_TYPE_ID = '.slack';
const services: Services = actionsMock.createServices();
let actionType: SlackActionType;
+let mockedLogger: jest.Mocked;
beforeAll(() => {
+ const { logger } = createActionTypeRegistry();
actionType = getActionType({
async executor(options) {
return { status: 'ok', actionId: options.actionId };
},
configurationUtilities: actionsConfigMock.create(),
+ logger,
});
+ mockedLogger = logger;
+ expect(actionType).toBeTruthy();
});
describe('action registeration', () => {
@@ -83,6 +98,7 @@ describe('validateActionTypeSecrets()', () => {
test('should validate and pass when the slack webhookUrl is whitelisted', () => {
actionType = getActionType({
+ logger: mockedLogger,
configurationUtilities: {
...actionsConfigMock.create(),
ensureWhitelistedUri: (url) => {
@@ -98,9 +114,10 @@ describe('validateActionTypeSecrets()', () => {
test('config validation returns an error if the specified URL isnt whitelisted', () => {
actionType = getActionType({
+ logger: mockedLogger,
configurationUtilities: {
...actionsConfigMock.create(),
- ensureWhitelistedHostname: (url) => {
+ ensureWhitelistedHostname: () => {
throw new Error(`target hostname is not whitelisted`);
},
},
@@ -136,6 +153,7 @@ describe('execute()', () => {
actionType = getActionType({
executor: mockSlackExecutor,
+ logger: mockedLogger,
configurationUtilities: actionsConfigMock.create(),
});
});
@@ -147,6 +165,10 @@ describe('execute()', () => {
config: {},
secrets: { webhookUrl: 'http://example.com' },
params: { message: 'this invocation should succeed' },
+ proxySettings: {
+ proxyUrl: 'https://someproxyhost',
+ rejectUnauthorizedCertificates: false,
+ },
});
expect(response).toMatchInlineSnapshot(`
Object {
@@ -170,4 +192,25 @@ describe('execute()', () => {
`"slack mockExecutor failure: this invocation should fail"`
);
});
+
+ test('calls the mock executor with success proxy', async () => {
+ const actionTypeProxy = getActionType({
+ logger: mockedLogger,
+ configurationUtilities: actionsConfigMock.create(),
+ });
+ await actionTypeProxy.executor({
+ actionId: 'some-id',
+ services,
+ config: {},
+ secrets: { webhookUrl: 'http://example.com' },
+ params: { message: 'this invocation should succeed' },
+ proxySettings: {
+ proxyUrl: 'https://someproxyhost',
+ rejectUnauthorizedCertificates: false,
+ },
+ });
+ expect(mockedLogger.info).toHaveBeenCalledWith(
+ 'IncomingWebhook was called with proxyUrl https://someproxyhost'
+ );
+ });
});
diff --git a/x-pack/plugins/actions/server/builtin_action_types/slack.ts b/x-pack/plugins/actions/server/builtin_action_types/slack.ts
index 209582585256b..293328c809435 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/slack.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/slack.ts
@@ -6,11 +6,14 @@
import { URL } from 'url';
import { curry } from 'lodash';
+import { HttpsProxyAgent } from 'https-proxy-agent';
+import HttpProxyAgent from 'http-proxy-agent';
import { i18n } from '@kbn/i18n';
import { schema, TypeOf } from '@kbn/config-schema';
import { IncomingWebhook, IncomingWebhookResult } from '@slack/webhook';
import { pipe } from 'fp-ts/lib/pipeable';
import { map, getOrElse } from 'fp-ts/lib/Option';
+import { Logger } from '../../../../../src/core/server';
import { getRetryAfterIntervalFromHeaders } from './lib/http_rersponse_retry_header';
import {
@@ -20,6 +23,7 @@ import {
ExecutorType,
} from '../types';
import { ActionsConfigurationUtilities } from '../actions_config';
+import { getProxyAgent } from './lib/get_proxy_agent';
export type SlackActionType = ActionType<{}, ActionTypeSecretsType, ActionParamsType, unknown>;
export type SlackActionTypeExecutorOptions = ActionTypeExecutorOptions<
@@ -49,9 +53,11 @@ const ParamsSchema = schema.object({
// customizing executor is only used for tests
export function getActionType({
+ logger,
configurationUtilities,
- executor = slackExecutor,
+ executor = curry(slackExecutor)({ logger }),
}: {
+ logger: Logger;
configurationUtilities: ActionsConfigurationUtilities;
executor?: ExecutorType<{}, ActionTypeSecretsType, ActionParamsType, unknown>;
}): SlackActionType {
@@ -99,6 +105,7 @@ function valdiateActionTypeConfig(
// action executor
async function slackExecutor(
+ { logger }: { logger: Logger },
execOptions: SlackActionTypeExecutorOptions
): Promise> {
const actionId = execOptions.actionId;
@@ -109,10 +116,22 @@ async function slackExecutor(
const { webhookUrl } = secrets;
const { message } = params;
+ let proxyAgent: HttpsProxyAgent | HttpProxyAgent | undefined;
+ if (execOptions.proxySettings) {
+ proxyAgent = getProxyAgent(execOptions.proxySettings, logger);
+ logger.info(`IncomingWebhook was called with proxyUrl ${execOptions.proxySettings.proxyUrl}`);
+ }
+
try {
- const webhook = new IncomingWebhook(webhookUrl);
+ // https://slack.dev/node-slack-sdk/webhook
+ // node-slack-sdk use Axios inside :)
+ const webhook = new IncomingWebhook(webhookUrl, {
+ agent: proxyAgent,
+ });
result = await webhook.send(message);
} catch (err) {
+ logger.error(`error on ${actionId} slack event: ${err.message}`);
+
if (err.original == null || err.original.response == null) {
return serviceErrorResult(actionId, err.message);
}
@@ -143,6 +162,8 @@ async function slackExecutor(
},
}
);
+ logger.error(`error on ${actionId} slack action: ${errMessage}`);
+
return errorResult(actionId, errMessage);
}
diff --git a/x-pack/plugins/actions/server/builtin_action_types/webhook.test.ts b/x-pack/plugins/actions/server/builtin_action_types/webhook.test.ts
index 26dd8a1a1402a..ea9f30452918c 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/webhook.test.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/webhook.test.ts
@@ -4,10 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-jest.mock('axios', () => ({
- request: jest.fn(),
-}));
-
import { Services } from '../types';
import { validateConfig, validateSecrets, validateParams } from '../lib';
import { actionsConfigMock } from '../actions_config.mock';
@@ -24,7 +20,22 @@ import {
WebhookMethods,
} from './webhook';
-const axiosRequestMock = axios.request as jest.Mock;
+import * as utils from './lib/axios_utils';
+
+jest.mock('axios');
+jest.mock('./lib/axios_utils', () => {
+ const originalUtils = jest.requireActual('./lib/axios_utils');
+ return {
+ ...originalUtils,
+ request: jest.fn(),
+ patch: jest.fn(),
+ };
+});
+
+axios.create = jest.fn(() => axios);
+const requestMock = utils.request as jest.Mock;
+
+axios.create = jest.fn(() => axios);
const ACTION_TYPE_ID = '.webhook';
@@ -227,7 +238,7 @@ describe('params validation', () => {
describe('execute()', () => {
beforeAll(() => {
- axiosRequestMock.mockReset();
+ requestMock.mockReset();
actionType = getActionType({
logger: mockedLogger,
configurationUtilities: actionsConfigMock.create(),
@@ -235,8 +246,8 @@ describe('execute()', () => {
});
beforeEach(() => {
- axiosRequestMock.mockReset();
- axiosRequestMock.mockResolvedValue({
+ requestMock.mockReset();
+ requestMock.mockResolvedValue({
status: 200,
statusText: '',
data: '',
@@ -261,17 +272,42 @@ describe('execute()', () => {
params: { body: 'some data' },
});
- expect(axiosRequestMock.mock.calls[0][0]).toMatchInlineSnapshot(`
+ expect(requestMock.mock.calls[0][0]).toMatchInlineSnapshot(`
Object {
"auth": Object {
"password": "123",
"username": "abc",
},
+ "axios": undefined,
"data": "some data",
"headers": Object {
"aheader": "a value",
},
+ "logger": Object {
+ "context": Array [],
+ "debug": [MockFunction] {
+ "calls": Array [
+ Array [
+ "response from webhook action \\"some-id\\": [HTTP 200] ",
+ ],
+ ],
+ "results": Array [
+ Object {
+ "type": "return",
+ "value": undefined,
+ },
+ ],
+ },
+ "error": [MockFunction],
+ "fatal": [MockFunction],
+ "get": [MockFunction],
+ "info": [MockFunction],
+ "log": [MockFunction],
+ "trace": [MockFunction],
+ "warn": [MockFunction],
+ },
"method": "post",
+ "proxySettings": undefined,
"url": "https://abc.def/my-webhook",
}
`);
@@ -294,13 +330,38 @@ describe('execute()', () => {
params: { body: 'some data' },
});
- expect(axiosRequestMock.mock.calls[0][0]).toMatchInlineSnapshot(`
+ expect(requestMock.mock.calls[0][0]).toMatchInlineSnapshot(`
Object {
+ "axios": undefined,
"data": "some data",
"headers": Object {
"aheader": "a value",
},
+ "logger": Object {
+ "context": Array [],
+ "debug": [MockFunction] {
+ "calls": Array [
+ Array [
+ "response from webhook action \\"some-id\\": [HTTP 200] ",
+ ],
+ ],
+ "results": Array [
+ Object {
+ "type": "return",
+ "value": undefined,
+ },
+ ],
+ },
+ "error": [MockFunction],
+ "fatal": [MockFunction],
+ "get": [MockFunction],
+ "info": [MockFunction],
+ "log": [MockFunction],
+ "trace": [MockFunction],
+ "warn": [MockFunction],
+ },
"method": "post",
+ "proxySettings": undefined,
"url": "https://abc.def/my-webhook",
}
`);
diff --git a/x-pack/plugins/actions/server/builtin_action_types/webhook.ts b/x-pack/plugins/actions/server/builtin_action_types/webhook.ts
index be75742fa882e..d9a005565498d 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/webhook.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/webhook.ts
@@ -15,6 +15,7 @@ import { isOk, promiseResult, Result } from './lib/result_type';
import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from '../types';
import { ActionsConfigurationUtilities } from '../actions_config';
import { Logger } from '../../../../../src/core/server';
+import { request } from './lib/axios_utils';
// config definition
export enum WebhookMethods {
@@ -136,13 +137,18 @@ export async function executor(
? { auth: { username: secrets.user, password: secrets.password } }
: {};
+ const axiosInstance = axios.create();
+
const result: Result = await promiseResult(
- axios.request({
+ request({
+ axios: axiosInstance,
method,
url,
+ logger,
...basicAuth,
headers,
data,
+ proxySettings: execOptions.proxySettings,
})
);
@@ -159,7 +165,7 @@ export async function executor(
if (error.response) {
const { status, statusText, headers: responseHeaders } = error.response;
const message = `[${status}] ${statusText}`;
- logger.warn(`error on ${actionId} webhook event: ${message}`);
+ logger.error(`error on ${actionId} webhook event: ${message}`);
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
// special handling for 5xx
@@ -178,7 +184,7 @@ export async function executor(
return errorResultInvalid(actionId, message);
}
- logger.warn(`error on ${actionId} webhook action: unexpected error`);
+ logger.error(`error on ${actionId} webhook action: unexpected error`);
return errorResultUnexpectedError(actionId);
}
}
diff --git a/x-pack/plugins/actions/server/config.test.ts b/x-pack/plugins/actions/server/config.test.ts
index e86f2d7832828..795fbbf84145b 100644
--- a/x-pack/plugins/actions/server/config.test.ts
+++ b/x-pack/plugins/actions/server/config.test.ts
@@ -15,6 +15,7 @@ describe('config validation', () => {
"*",
],
"preconfigured": Object {},
+ "rejectUnauthorizedCertificates": true,
"whitelistedHosts": Array [
"*",
],
@@ -33,6 +34,7 @@ describe('config validation', () => {
},
},
},
+ rejectUnauthorizedCertificates: false,
};
expect(configSchema.validate(config)).toMatchInlineSnapshot(`
Object {
@@ -50,6 +52,7 @@ describe('config validation', () => {
"secrets": Object {},
},
},
+ "rejectUnauthorizedCertificates": false,
"whitelistedHosts": Array [
"*",
],
diff --git a/x-pack/plugins/actions/server/config.ts b/x-pack/plugins/actions/server/config.ts
index b2f3fa2680a9c..ba80915ebe243 100644
--- a/x-pack/plugins/actions/server/config.ts
+++ b/x-pack/plugins/actions/server/config.ts
@@ -32,6 +32,9 @@ export const configSchema = schema.object({
defaultValue: {},
validate: validatePreconfigured,
}),
+ proxyUrl: schema.maybe(schema.string()),
+ proxyHeaders: schema.maybe(schema.recordOf(schema.string(), schema.string())),
+ rejectUnauthorizedCertificates: schema.boolean({ defaultValue: true }),
});
export type ActionsConfig = TypeOf;
diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts
index bce06c829b1bc..97c08124f5546 100644
--- a/x-pack/plugins/actions/server/lib/action_executor.ts
+++ b/x-pack/plugins/actions/server/lib/action_executor.ts
@@ -12,6 +12,7 @@ import {
GetServicesFunction,
RawAction,
PreConfiguredAction,
+ ProxySettings,
} from '../types';
import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/server';
import { SpacesServiceSetup } from '../../../spaces/server';
@@ -28,6 +29,7 @@ export interface ActionExecutorContext {
actionTypeRegistry: ActionTypeRegistryContract;
eventLogger: IEventLogger;
preconfiguredActions: PreConfiguredAction[];
+ proxySettings?: ProxySettings;
}
export interface ExecuteOptions {
@@ -78,6 +80,7 @@ export class ActionExecutor {
eventLogger,
preconfiguredActions,
getActionsClientWithRequest,
+ proxySettings,
} = this.actionExecutorContext!;
const services = getServices(request);
@@ -133,6 +136,7 @@ export class ActionExecutor {
params: validatedParams,
config: validatedConfig,
secrets: validatedSecrets,
+ proxySettings,
});
} catch (err) {
rawResult = {
diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts
index ca93e88d01203..341a17889923f 100644
--- a/x-pack/plugins/actions/server/plugin.test.ts
+++ b/x-pack/plugins/actions/server/plugin.test.ts
@@ -34,6 +34,7 @@ describe('Actions Plugin', () => {
enabledActionTypes: ['*'],
whitelistedHosts: ['*'],
preconfigured: {},
+ rejectUnauthorizedCertificates: true,
});
plugin = new ActionsPlugin(context);
coreSetup = coreMock.createSetup();
@@ -194,6 +195,7 @@ describe('Actions Plugin', () => {
secrets: {},
},
},
+ rejectUnauthorizedCertificates: true,
});
plugin = new ActionsPlugin(context);
coreSetup = coreMock.createSetup();
@@ -217,7 +219,7 @@ describe('Actions Plugin', () => {
// coreMock.createSetup doesn't support Plugin generics
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await plugin.setup(coreSetup as any, pluginsSetup);
- const pluginStart = plugin.start(coreStart, pluginsStart);
+ const pluginStart = await plugin.start(coreStart, pluginsStart);
expect(pluginStart.isActionExecutable('preconfiguredServerLog', '.server-log')).toBe(true);
});
@@ -232,7 +234,7 @@ describe('Actions Plugin', () => {
usingEphemeralEncryptionKey: false,
},
});
- const pluginStart = plugin.start(coreStart, pluginsStart);
+ const pluginStart = await plugin.start(coreStart, pluginsStart);
await pluginStart.getActionsClientWithRequest(httpServerMock.createKibanaRequest());
});
@@ -241,7 +243,7 @@ describe('Actions Plugin', () => {
// coreMock.createSetup doesn't support Plugin generics
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await plugin.setup(coreSetup as any, pluginsSetup);
- const pluginStart = plugin.start(coreStart, pluginsStart);
+ const pluginStart = await plugin.start(coreStart, pluginsStart);
expect(pluginsSetup.encryptedSavedObjects.usingEphemeralEncryptionKey).toEqual(true);
await expect(
diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts
index ee50ee81d507c..413e6663105b8 100644
--- a/x-pack/plugins/actions/server/plugin.ts
+++ b/x-pack/plugins/actions/server/plugin.ts
@@ -116,6 +116,7 @@ export class ActionsPlugin implements Plugin, Plugi
private readonly config: Promise;
private readonly logger: Logger;
+ private actionsConfig?: ActionsConfig;
private serverBasePath?: string;
private taskRunnerFactory?: TaskRunnerFactory;
private actionTypeRegistry?: ActionTypeRegistry;
@@ -173,12 +174,12 @@ export class ActionsPlugin implements Plugin, Plugi
// get executions count
const taskRunnerFactory = new TaskRunnerFactory(actionExecutor);
- const actionsConfig = (await this.config) as ActionsConfig;
- const actionsConfigUtils = getActionsConfigurationUtilities(actionsConfig);
+ this.actionsConfig = (await this.config) as ActionsConfig;
+ const actionsConfigUtils = getActionsConfigurationUtilities(this.actionsConfig);
- for (const preconfiguredId of Object.keys(actionsConfig.preconfigured)) {
+ for (const preconfiguredId of Object.keys(this.actionsConfig.preconfigured)) {
this.preconfiguredActions.push({
- ...actionsConfig.preconfigured[preconfiguredId],
+ ...this.actionsConfig.preconfigured[preconfiguredId],
id: preconfiguredId,
isPreconfigured: true,
});
@@ -317,6 +318,14 @@ export class ActionsPlugin implements Plugin, Plugi
encryptedSavedObjectsClient,
actionTypeRegistry: actionTypeRegistry!,
preconfiguredActions,
+ proxySettings:
+ this.actionsConfig && this.actionsConfig.proxyUrl
+ ? {
+ proxyUrl: this.actionsConfig.proxyUrl,
+ proxyHeaders: this.actionsConfig.proxyHeaders,
+ rejectUnauthorizedCertificates: this.actionsConfig.rejectUnauthorizedCertificates,
+ }
+ : undefined,
});
taskRunnerFactory!.initialize({
diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts
index ecec45ade0460..bf7bd709a4a88 100644
--- a/x-pack/plugins/actions/server/types.ts
+++ b/x-pack/plugins/actions/server/types.ts
@@ -58,6 +58,7 @@ export interface ActionTypeExecutorOptions {
config: Config;
secrets: Secrets;
params: Params;
+ proxySettings?: ProxySettings;
}
export interface ActionResult {
@@ -140,3 +141,9 @@ export interface ActionTaskExecutorParams {
spaceId: string;
actionTaskParamsId: string;
}
+
+export interface ProxySettings {
+ proxyUrl: string;
+ proxyHeaders?: Record;
+ rejectUnauthorizedCertificates: boolean;
+}
diff --git a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/action_context.ts b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/action_context.ts
index c3a132bc609d6..294a831c93459 100644
--- a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/action_context.ts
+++ b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/action_context.ts
@@ -6,7 +6,7 @@
import { i18n } from '@kbn/i18n';
import { Params } from './alert_type_params';
-import { AlertExecutorOptions } from '../../../../alerts/server';
+import { AlertExecutorOptions, AlertInstanceContext } from '../../../../alerts/server';
// alert type context provided to actions
@@ -19,7 +19,7 @@ export interface ActionContext extends BaseActionContext {
message: string;
}
-export interface BaseActionContext {
+export interface BaseActionContext extends AlertInstanceContext {
// the aggType used in the alert
// the value of the aggField, if used, otherwise 'all documents'
group: string;
diff --git a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/alert_type.ts b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/alert_type.ts
index c0522c08a7b96..01c2c2feb5b9a 100644
--- a/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/alert_type.ts
+++ b/x-pack/plugins/alerting_builtins/server/alert_types/index_threshold/alert_type.ts
@@ -7,7 +7,7 @@
import { i18n } from '@kbn/i18n';
import { AlertType, AlertExecutorOptions } from '../../types';
import { Params, ParamsSchema } from './alert_type_params';
-import { BaseActionContext, addMessages } from './action_context';
+import { ActionContext, BaseActionContext, addMessages } from './action_context';
import { TimeSeriesQuery } from './lib/time_series_query';
import { Service } from '../../types';
import { BUILT_IN_ALERTS_FEATURE_ID } from '../../../common';
@@ -19,7 +19,7 @@ const ActionGroupId = 'threshold met';
const ComparatorFns = getComparatorFns();
export const ComparatorFnNames = new Set(ComparatorFns.keys());
-export function getAlertType(service: Service): AlertType {
+export function getAlertType(service: Service): AlertType {
const { logger } = service;
const alertTypeName = i18n.translate('xpack.alertingBuiltins.indexThreshold.alertTypeTitle', {
@@ -118,9 +118,8 @@ export function getAlertType(service: Service): AlertType {
producer: BUILT_IN_ALERTS_FEATURE_ID,
};
- async function executor(options: AlertExecutorOptions) {
- const { alertId, name, services } = options;
- const params: Params = options.params as Params;
+ async function executor(options: AlertExecutorOptions) {
+ const { alertId, name, services, params } = options;
const compareFn = ComparatorFns.get(params.thresholdComparator);
if (compareFn == null) {
diff --git a/x-pack/plugins/alerts/README.md b/x-pack/plugins/alerts/README.md
index 10568abbe3c72..aab05cb0a7cfd 100644
--- a/x-pack/plugins/alerts/README.md
+++ b/x-pack/plugins/alerts/README.md
@@ -26,6 +26,7 @@ Table of Contents
- [`GET /api/alerts/_find`: Find alerts](#get-apialertfind-find-alerts)
- [`GET /api/alerts/alert/{id}`: Get alert](#get-apialertid-get-alert)
- [`GET /api/alerts/alert/{id}/state`: Get alert state](#get-apialertidstate-get-alert-state)
+ - [`GET /api/alerts/alert/{id}/status`: Get alert status](#get-apialertidstate-get-alert-status)
- [`GET /api/alerts/list_alert_types`: List alert types](#get-apialerttypes-list-alert-types)
- [`PUT /api/alerts/alert/{id}`: Update alert](#put-apialertid-update-alert)
- [`POST /api/alerts/alert/{id}/_enable`: Enable an alert](#post-apialertidenable-enable-an-alert)
@@ -504,6 +505,23 @@ Params:
|---|---|---|
|id|The id of the alert whose state you're trying to get.|string|
+### `GET /api/alerts/alert/{id}/status`: Get alert status
+
+Similar to the `GET state` call, but collects additional information from
+the event log.
+
+Params:
+
+|Property|Description|Type|
+|---|---|---|
+|id|The id of the alert whose status you're trying to get.|string|
+
+Query:
+
+|Property|Description|Type|
+|---|---|---|
+|dateStart|The date to start looking for alert events in the event log. Either an ISO date string, or a duration string indicating time since now.|string|
+
### `GET /api/alerts/list_alert_types`: List alert types
No parameters.
diff --git a/x-pack/plugins/alerts/common/alert.ts b/x-pack/plugins/alerts/common/alert.ts
index b410b6aa0187e..3ff7ed742e810 100644
--- a/x-pack/plugins/alerts/common/alert.ts
+++ b/x-pack/plugins/alerts/common/alert.ts
@@ -6,6 +6,11 @@
import { SavedObjectAttributes } from 'kibana/server';
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export type AlertTypeState = Record;
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export type AlertTypeParams = Record;
+
export interface IntervalSchedule extends SavedObjectAttributes {
interval: string;
}
@@ -28,9 +33,7 @@ export interface Alert {
consumer: string;
schedule: IntervalSchedule;
actions: AlertAction[];
- // This will have to remain `any` until we can extend Alert Executors with generics
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- params: Record;
+ params: AlertTypeParams;
scheduledTaskId?: string;
createdBy: string | null;
updatedBy: string | null;
diff --git a/x-pack/plugins/alerts/common/alert_instance.ts b/x-pack/plugins/alerts/common/alert_instance.ts
index a6852f06efd34..0253a5c6919d6 100644
--- a/x-pack/plugins/alerts/common/alert_instance.ts
+++ b/x-pack/plugins/alerts/common/alert_instance.ts
@@ -17,6 +17,9 @@ export type AlertInstanceMeta = t.TypeOf;
const stateSchema = t.record(t.string, t.unknown);
export type AlertInstanceState = t.TypeOf;
+const contextSchema = t.record(t.string, t.unknown);
+export type AlertInstanceContext = t.TypeOf;
+
export const rawAlertInstance = t.partial({
state: stateSchema,
meta: metaSchema,
diff --git a/x-pack/plugins/alerts/common/alert_status.ts b/x-pack/plugins/alerts/common/alert_status.ts
new file mode 100644
index 0000000000000..517db6d6cb243
--- /dev/null
+++ b/x-pack/plugins/alerts/common/alert_status.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+type AlertStatusValues = 'OK' | 'Active' | 'Error';
+type AlertInstanceStatusValues = 'OK' | 'Active';
+
+export interface AlertStatus {
+ id: string;
+ name: string;
+ tags: string[];
+ alertTypeId: string;
+ consumer: string;
+ muteAll: boolean;
+ throttle: string | null;
+ enabled: boolean;
+ statusStartDate: string;
+ statusEndDate: string;
+ status: AlertStatusValues;
+ lastRun?: string;
+ errorMessages: Array<{ date: string; message: string }>;
+ instances: Record;
+}
+
+export interface AlertInstanceStatus {
+ status: AlertInstanceStatusValues;
+ muted: boolean;
+ activeStartDate?: string;
+}
diff --git a/x-pack/plugins/alerts/common/index.ts b/x-pack/plugins/alerts/common/index.ts
index b839c07a9db89..0922e164a3aa3 100644
--- a/x-pack/plugins/alerts/common/index.ts
+++ b/x-pack/plugins/alerts/common/index.ts
@@ -9,6 +9,7 @@ export * from './alert_type';
export * from './alert_instance';
export * from './alert_task_instance';
export * from './alert_navigation';
+export * from './alert_status';
export interface ActionGroup {
id: string;
diff --git a/x-pack/plugins/alerts/server/alert_instance/alert_instance.ts b/x-pack/plugins/alerts/server/alert_instance/alert_instance.ts
index 4d106178f86fb..661fb75f81c00 100644
--- a/x-pack/plugins/alerts/server/alert_instance/alert_instance.ts
+++ b/x-pack/plugins/alerts/server/alert_instance/alert_instance.ts
@@ -8,24 +8,26 @@ import {
AlertInstanceState,
RawAlertInstance,
rawAlertInstance,
+ AlertInstanceContext,
} from '../../common';
-import { State, Context } from '../types';
import { parseDuration } from '../lib';
-interface ScheduledExecutionOptions {
- actionGroup: string;
- context: Context;
- state: State;
-}
export type AlertInstances = Record;
-export class AlertInstance {
- private scheduledExecutionOptions?: ScheduledExecutionOptions;
+export class AlertInstance<
+ State extends AlertInstanceState = AlertInstanceState,
+ Context extends AlertInstanceContext = AlertInstanceContext
+> {
+ private scheduledExecutionOptions?: {
+ actionGroup: string;
+ context: Context;
+ state: State;
+ };
private meta: AlertInstanceMeta;
- private state: AlertInstanceState;
+ private state: State;
- constructor({ state = {}, meta = {} }: RawAlertInstance = {}) {
- this.state = state;
+ constructor({ state, meta = {} }: RawAlertInstance = {}) {
+ this.state = (state || {}) as State;
this.meta = meta;
}
@@ -62,11 +64,15 @@ export class AlertInstance {
return this.state;
}
- scheduleActions(actionGroup: string, context: Context = {}) {
+ scheduleActions(actionGroup: string, context?: Context) {
if (this.hasScheduledActions()) {
throw new Error('Alert instance execution has already been scheduled, cannot schedule twice');
}
- this.scheduledExecutionOptions = { actionGroup, context, state: this.state };
+ this.scheduledExecutionOptions = {
+ actionGroup,
+ context: (context || {}) as Context,
+ state: this.state,
+ };
return this;
}
diff --git a/x-pack/plugins/alerts/server/alert_type_registry.ts b/x-pack/plugins/alerts/server/alert_type_registry.ts
index 19d3bf13bd66d..7f34803b05a81 100644
--- a/x-pack/plugins/alerts/server/alert_type_registry.ts
+++ b/x-pack/plugins/alerts/server/alert_type_registry.ts
@@ -10,7 +10,13 @@ import { schema } from '@kbn/config-schema';
import typeDetect from 'type-detect';
import { RunContext, TaskManagerSetupContract } from '../../task_manager/server';
import { TaskRunnerFactory } from './task_runner';
-import { AlertType } from './types';
+import {
+ AlertType,
+ AlertTypeParams,
+ AlertTypeState,
+ AlertInstanceState,
+ AlertInstanceContext,
+} from './types';
interface ConstructorOptions {
taskManager: TaskManagerSetupContract;
@@ -59,7 +65,12 @@ export class AlertTypeRegistry {
return this.alertTypes.has(id);
}
- public register(alertType: AlertType) {
+ public register<
+ Params extends AlertTypeParams = AlertTypeParams,
+ State extends AlertTypeState = AlertTypeState,
+ InstanceState extends AlertInstanceState = AlertInstanceState,
+ InstanceContext extends AlertInstanceContext = AlertInstanceContext
+ >(alertType: AlertType) {
if (this.has(alertType.id)) {
throw new Error(
i18n.translate('xpack.alerts.alertTypeRegistry.register.duplicateAlertTypeError', {
@@ -71,18 +82,23 @@ export class AlertTypeRegistry {
);
}
alertType.actionVariables = normalizedActionVariables(alertType.actionVariables);
- this.alertTypes.set(alertIdSchema.validate(alertType.id), { ...alertType });
+ this.alertTypes.set(alertIdSchema.validate(alertType.id), { ...alertType } as AlertType);
this.taskManager.registerTaskDefinitions({
[`alerting:${alertType.id}`]: {
title: alertType.name,
type: `alerting:${alertType.id}`,
createTaskRunner: (context: RunContext) =>
- this.taskRunnerFactory.create(alertType, context),
+ this.taskRunnerFactory.create({ ...alertType } as AlertType, context),
},
});
}
- public get(id: string): AlertType {
+ public get<
+ Params extends AlertTypeParams = AlertTypeParams,
+ State extends AlertTypeState = AlertTypeState,
+ InstanceState extends AlertInstanceState = AlertInstanceState,
+ InstanceContext extends AlertInstanceContext = AlertInstanceContext
+ >(id: string): AlertType {
if (!this.has(id)) {
throw Boom.badRequest(
i18n.translate('xpack.alerts.alertTypeRegistry.get.missingAlertTypeError', {
@@ -93,7 +109,7 @@ export class AlertTypeRegistry {
})
);
}
- return this.alertTypes.get(id)!;
+ return this.alertTypes.get(id)! as AlertType;
}
public list(): Set {
diff --git a/x-pack/plugins/alerts/server/alerts_client.mock.ts b/x-pack/plugins/alerts/server/alerts_client.mock.ts
index be70e441b6fc5..b61139ae72c99 100644
--- a/x-pack/plugins/alerts/server/alerts_client.mock.ts
+++ b/x-pack/plugins/alerts/server/alerts_client.mock.ts
@@ -25,6 +25,7 @@ const createAlertsClientMock = () => {
muteInstance: jest.fn(),
unmuteInstance: jest.fn(),
listAlertTypes: jest.fn(),
+ getAlertStatus: jest.fn(),
};
return mocked;
};
diff --git a/x-pack/plugins/alerts/server/alerts_client.test.ts b/x-pack/plugins/alerts/server/alerts_client.test.ts
index c25e040ad09ce..d994269366ae6 100644
--- a/x-pack/plugins/alerts/server/alerts_client.test.ts
+++ b/x-pack/plugins/alerts/server/alerts_client.test.ts
@@ -11,16 +11,22 @@ import { taskManagerMock } from '../../task_manager/server/task_manager.mock';
import { alertTypeRegistryMock } from './alert_type_registry.mock';
import { alertsAuthorizationMock } from './authorization/alerts_authorization.mock';
import { TaskStatus } from '../../task_manager/server';
-import { IntervalSchedule } from './types';
+import { IntervalSchedule, RawAlert } from './types';
import { resolvable } from './test_utils';
import { encryptedSavedObjectsMock } from '../../encrypted_saved_objects/server/mocks';
import { actionsClientMock, actionsAuthorizationMock } from '../../actions/server/mocks';
import { AlertsAuthorization } from './authorization/alerts_authorization';
import { ActionsAuthorization } from '../../actions/server';
+import { eventLogClientMock } from '../../event_log/server/mocks';
+import { QueryEventsBySavedObjectResult } from '../../event_log/server';
+import { SavedObject } from 'kibana/server';
+import { EventsFactory } from './lib/alert_status_from_event_log.test';
const taskManager = taskManagerMock.start();
const alertTypeRegistry = alertTypeRegistryMock.create();
const unsecuredSavedObjectsClient = savedObjectsClientMock.create();
+const eventLogClient = eventLogClientMock.create();
+
const encryptedSavedObjects = encryptedSavedObjectsMock.createClient();
const authorization = alertsAuthorizationMock.create();
const actionsAuthorization = actionsAuthorizationMock.create();
@@ -39,6 +45,7 @@ const alertsClientParams: jest.Mocked = {
logger: loggingSystemMock.create().get(),
encryptedSavedObjectsClient: encryptedSavedObjects,
getActionsClient: jest.fn(),
+ getEventLogClient: jest.fn(),
};
beforeEach(() => {
@@ -91,17 +98,33 @@ beforeEach(() => {
async executor() {},
producer: 'alerts',
}));
+ alertsClientParams.getEventLogClient.mockResolvedValue(eventLogClient);
});
-const mockedDate = new Date('2019-02-12T21:01:22.479Z');
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const mockedDateString = '2019-02-12T21:01:22.479Z';
+const mockedDate = new Date(mockedDateString);
+const DateOriginal = Date;
+
+// A version of date that responds to `new Date(null|undefined)` and `Date.now()`
+// by returning a fixed date, otherwise should be same as Date.
+/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
(global as any).Date = class Date {
- constructor() {
- return mockedDate;
+ constructor(...args: unknown[]) {
+ // sometimes the ctor has no args, sometimes has a single `null` arg
+ if (args[0] == null) {
+ // @ts-ignore
+ return mockedDate;
+ } else {
+ // @ts-ignore
+ return new DateOriginal(...args);
+ }
}
static now() {
return mockedDate.getTime();
}
+ static parse(string: string) {
+ return DateOriginal.parse(string);
+ }
};
function getMockData(overwrites: Record = {}): CreateOptions['data'] {
@@ -2295,6 +2318,219 @@ describe('getAlertState()', () => {
});
});
+const AlertStatusFindEventsResult: QueryEventsBySavedObjectResult = {
+ page: 1,
+ per_page: 10000,
+ total: 0,
+ data: [],
+};
+
+const AlertStatusIntervalSeconds = 1;
+
+const BaseAlertStatusSavedObject: SavedObject = {
+ id: '1',
+ type: 'alert',
+ attributes: {
+ enabled: true,
+ name: 'alert-name',
+ tags: ['tag-1', 'tag-2'],
+ alertTypeId: '123',
+ consumer: 'alert-consumer',
+ schedule: { interval: `${AlertStatusIntervalSeconds}s` },
+ actions: [],
+ params: {},
+ createdBy: null,
+ updatedBy: null,
+ createdAt: mockedDateString,
+ apiKey: null,
+ apiKeyOwner: null,
+ throttle: null,
+ muteAll: false,
+ mutedInstanceIds: [],
+ },
+ references: [],
+};
+
+function getAlertStatusSavedObject(attributes: Partial = {}): SavedObject {
+ return {
+ ...BaseAlertStatusSavedObject,
+ attributes: { ...BaseAlertStatusSavedObject.attributes, ...attributes },
+ };
+}
+
+describe('getAlertStatus()', () => {
+ let alertsClient: AlertsClient;
+
+ beforeEach(() => {
+ alertsClient = new AlertsClient(alertsClientParams);
+ });
+
+ test('runs as expected with some event log data', async () => {
+ const alertSO = getAlertStatusSavedObject({ mutedInstanceIds: ['instance-muted-no-activity'] });
+ unsecuredSavedObjectsClient.get.mockResolvedValueOnce(alertSO);
+
+ const eventsFactory = new EventsFactory(mockedDateString);
+ const events = eventsFactory
+ .addExecute()
+ .addNewInstance('instance-currently-active')
+ .addNewInstance('instance-previously-active')
+ .addActiveInstance('instance-currently-active')
+ .addActiveInstance('instance-previously-active')
+ .advanceTime(10000)
+ .addExecute()
+ .addResolvedInstance('instance-previously-active')
+ .addActiveInstance('instance-currently-active')
+ .getEvents();
+ const eventsResult = {
+ ...AlertStatusFindEventsResult,
+ total: events.length,
+ data: events,
+ };
+ eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(eventsResult);
+
+ const dateStart = new Date(Date.now() - 60 * 1000).toISOString();
+
+ const result = await alertsClient.getAlertStatus({ id: '1', dateStart });
+ expect(result).toMatchInlineSnapshot(`
+ Object {
+ "alertTypeId": "123",
+ "consumer": "alert-consumer",
+ "enabled": true,
+ "errorMessages": Array [],
+ "id": "1",
+ "instances": Object {
+ "instance-currently-active": Object {
+ "activeStartDate": "2019-02-12T21:01:22.479Z",
+ "muted": false,
+ "status": "Active",
+ },
+ "instance-muted-no-activity": Object {
+ "activeStartDate": undefined,
+ "muted": true,
+ "status": "OK",
+ },
+ "instance-previously-active": Object {
+ "activeStartDate": undefined,
+ "muted": false,
+ "status": "OK",
+ },
+ },
+ "lastRun": "2019-02-12T21:01:32.479Z",
+ "muteAll": false,
+ "name": "alert-name",
+ "status": "Active",
+ "statusEndDate": "2019-02-12T21:01:22.479Z",
+ "statusStartDate": "2019-02-12T21:00:22.479Z",
+ "tags": Array [
+ "tag-1",
+ "tag-2",
+ ],
+ "throttle": null,
+ }
+ `);
+ });
+
+ // Further tests don't check the result of `getAlertStatus()`, as the result
+ // is just the result from the `alertStatusFromEventLog()`, which itself
+ // has a complete set of tests. These tests just make sure the data gets
+ // sent into `getAlertStatus()` as appropriate.
+
+ test('calls saved objects and event log client with default params', async () => {
+ unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertStatusSavedObject());
+ eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(AlertStatusFindEventsResult);
+
+ await alertsClient.getAlertStatus({ id: '1' });
+
+ expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1);
+ expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1);
+ expect(eventLogClient.findEventsBySavedObject.mock.calls[0]).toMatchInlineSnapshot(`
+ Array [
+ "alert",
+ "1",
+ Object {
+ "end": "2019-02-12T21:01:22.479Z",
+ "page": 1,
+ "per_page": 10000,
+ "sort_order": "desc",
+ "start": "2019-02-12T21:00:22.479Z",
+ },
+ ]
+ `);
+ // calculate the expected start/end date for one test
+ const { start, end } = eventLogClient.findEventsBySavedObject.mock.calls[0][2]!;
+ expect(end).toBe(mockedDateString);
+
+ const startMillis = Date.parse(start!);
+ const endMillis = Date.parse(end!);
+ const expectedDuration = 60 * AlertStatusIntervalSeconds * 1000;
+ expect(endMillis - startMillis).toBeGreaterThan(expectedDuration - 2);
+ expect(endMillis - startMillis).toBeLessThan(expectedDuration + 2);
+ });
+
+ test('calls event log client with start date', async () => {
+ unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertStatusSavedObject());
+ eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(AlertStatusFindEventsResult);
+
+ const dateStart = new Date(Date.now() - 60 * AlertStatusIntervalSeconds * 1000).toISOString();
+ await alertsClient.getAlertStatus({ id: '1', dateStart });
+
+ expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1);
+ expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1);
+ const { start, end } = eventLogClient.findEventsBySavedObject.mock.calls[0][2]!;
+
+ expect({ start, end }).toMatchInlineSnapshot(`
+ Object {
+ "end": "2019-02-12T21:01:22.479Z",
+ "start": "2019-02-12T21:00:22.479Z",
+ }
+ `);
+ });
+
+ test('calls event log client with relative start date', async () => {
+ unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertStatusSavedObject());
+ eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(AlertStatusFindEventsResult);
+
+ const dateStart = '2m';
+ await alertsClient.getAlertStatus({ id: '1', dateStart });
+
+ expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1);
+ expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1);
+ const { start, end } = eventLogClient.findEventsBySavedObject.mock.calls[0][2]!;
+
+ expect({ start, end }).toMatchInlineSnapshot(`
+ Object {
+ "end": "2019-02-12T21:01:22.479Z",
+ "start": "2019-02-12T20:59:22.479Z",
+ }
+ `);
+ });
+
+ test('invalid start date throws an error', async () => {
+ unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertStatusSavedObject());
+ eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(AlertStatusFindEventsResult);
+
+ const dateStart = 'ain"t no way this will get parsed as a date';
+ expect(alertsClient.getAlertStatus({ id: '1', dateStart })).rejects.toMatchInlineSnapshot(
+ `[Error: Invalid date for parameter dateStart: "ain"t no way this will get parsed as a date"]`
+ );
+ });
+
+ test('saved object get throws an error', async () => {
+ unsecuredSavedObjectsClient.get.mockRejectedValueOnce(new Error('OMG!'));
+ eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(AlertStatusFindEventsResult);
+
+ expect(alertsClient.getAlertStatus({ id: '1' })).rejects.toMatchInlineSnapshot(`[Error: OMG!]`);
+ });
+
+ test('findEvents throws an error', async () => {
+ unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertStatusSavedObject());
+ eventLogClient.findEventsBySavedObject.mockRejectedValueOnce(new Error('OMG 2!'));
+
+ // error eaten but logged
+ await alertsClient.getAlertStatus({ id: '1' });
+ });
+});
+
describe('find()', () => {
const listedTypes = new Set([
{
diff --git a/x-pack/plugins/alerts/server/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client.ts
index dd66ccc7a0256..80e021fc5cb6e 100644
--- a/x-pack/plugins/alerts/server/alerts_client.ts
+++ b/x-pack/plugins/alerts/server/alerts_client.ts
@@ -24,6 +24,7 @@ import {
IntervalSchedule,
SanitizedAlert,
AlertTaskState,
+ AlertStatus,
} from './types';
import { validateAlertTypeParams } from './lib';
import {
@@ -41,6 +42,11 @@ import {
WriteOperations,
ReadOperations,
} from './authorization/alerts_authorization';
+import { IEventLogClient } from '../../../plugins/event_log/server';
+import { parseIsoOrRelativeDate } from './lib/iso_or_relative_date';
+import { alertStatusFromEventLog } from './lib/alert_status_from_event_log';
+import { IEvent } from '../../event_log/server';
+import { parseDuration } from '../common/parse_duration';
export interface RegistryAlertTypeWithAuth extends RegistryAlertType {
authorizedConsumers: string[];
@@ -67,6 +73,7 @@ export interface ConstructorOptions {
createAPIKey: (name: string) => Promise;
invalidateAPIKey: (params: InvalidateAPIKeyParams) => Promise;
getActionsClient: () => Promise;
+ getEventLogClient: () => Promise;
}
export interface MuteOptions extends IndexType {
@@ -132,6 +139,11 @@ interface UpdateOptions {
};
}
+interface GetAlertStatusParams {
+ id: string;
+ dateStart?: string;
+}
+
export class AlertsClient {
private readonly logger: Logger;
private readonly getUserName: () => Promise;
@@ -147,6 +159,7 @@ export class AlertsClient {
) => Promise;
private readonly getActionsClient: () => Promise;
private readonly actionsAuthorization: ActionsAuthorization;
+ private readonly getEventLogClient: () => Promise;
encryptedSavedObjectsClient: EncryptedSavedObjectsClient;
constructor({
@@ -163,6 +176,7 @@ export class AlertsClient {
encryptedSavedObjectsClient,
getActionsClient,
actionsAuthorization,
+ getEventLogClient,
}: ConstructorOptions) {
this.logger = logger;
this.getUserName = getUserName;
@@ -177,6 +191,7 @@ export class AlertsClient {
this.encryptedSavedObjectsClient = encryptedSavedObjectsClient;
this.getActionsClient = getActionsClient;
this.actionsAuthorization = actionsAuthorization;
+ this.getEventLogClient = getEventLogClient;
}
public async create({ data, options }: CreateOptions): Promise {
@@ -269,6 +284,49 @@ export class AlertsClient {
}
}
+ public async getAlertStatus({ id, dateStart }: GetAlertStatusParams): Promise {
+ this.logger.debug(`getAlertStatus(): getting alert ${id}`);
+ const alert = await this.get({ id });
+ await this.authorization.ensureAuthorized(
+ alert.alertTypeId,
+ alert.consumer,
+ ReadOperations.GetAlertStatus
+ );
+
+ // default duration of status is 60 * alert interval
+ const dateNow = new Date();
+ const durationMillis = parseDuration(alert.schedule.interval) * 60;
+ const defaultDateStart = new Date(dateNow.valueOf() - durationMillis);
+ const parsedDateStart = parseDate(dateStart, 'dateStart', defaultDateStart);
+
+ const eventLogClient = await this.getEventLogClient();
+
+ this.logger.debug(`getAlertStatus(): search the event log for alert ${id}`);
+ let events: IEvent[];
+ try {
+ const queryResults = await eventLogClient.findEventsBySavedObject('alert', id, {
+ page: 1,
+ per_page: 10000,
+ start: parsedDateStart.toISOString(),
+ end: dateNow.toISOString(),
+ sort_order: 'desc',
+ });
+ events = queryResults.data;
+ } catch (err) {
+ this.logger.debug(
+ `alertsClient.getAlertStatus(): error searching event log for alert ${id}: ${err.message}`
+ );
+ events = [];
+ }
+
+ return alertStatusFromEventLog({
+ alert,
+ events,
+ dateStart: parsedDateStart.toISOString(),
+ dateEnd: dateNow.toISOString(),
+ });
+ }
+
public async find({
options: { fields, ...options } = {},
}: { options?: FindOptions } = {}): Promise {
@@ -283,7 +341,6 @@ export class AlertsClient {
? `${options.filter} and ${authorizationFilter}`
: authorizationFilter;
}
-
const {
page,
per_page: perPage,
@@ -886,3 +943,24 @@ export class AlertsClient {
return truncate(`Alerting: ${alertTypeId}/${alertName}`, { length: 256 });
}
}
+
+function parseDate(dateString: string | undefined, propertyName: string, defaultValue: Date): Date {
+ if (dateString === undefined) {
+ return defaultValue;
+ }
+
+ const parsedDate = parseIsoOrRelativeDate(dateString);
+ if (parsedDate === undefined) {
+ throw Boom.badRequest(
+ i18n.translate('xpack.alerts.alertsClient.getAlertStatus.invalidDate', {
+ defaultMessage: 'Invalid date for parameter {field}: "{dateValue}"',
+ values: {
+ field: propertyName,
+ dateValue: dateString,
+ },
+ })
+ );
+ }
+
+ return parsedDate;
+}
diff --git a/x-pack/plugins/alerts/server/alerts_client_factory.test.ts b/x-pack/plugins/alerts/server/alerts_client_factory.test.ts
index 16b5af499bb90..a5eb371633f1e 100644
--- a/x-pack/plugins/alerts/server/alerts_client_factory.test.ts
+++ b/x-pack/plugins/alerts/server/alerts_client_factory.test.ts
@@ -22,6 +22,7 @@ import { actionsMock, actionsAuthorizationMock } from '../../actions/server/mock
import { featuresPluginMock } from '../../features/server/mocks';
import { AuditLogger } from '../../security/server';
import { ALERTS_FEATURE_ID } from '../common';
+import { eventLogMock } from '../../event_log/server/mocks';
jest.mock('./alerts_client');
jest.mock('./authorization/alerts_authorization');
@@ -42,6 +43,7 @@ const alertsClientFactoryParams: jest.Mocked = {
encryptedSavedObjectsClient: encryptedSavedObjectsMock.createClient(),
actions: actionsMock.createStart(),
features,
+ eventLog: eventLogMock.createStart(),
};
const fakeRequest = ({
headers: {},
@@ -119,6 +121,7 @@ test('creates an alerts client with proper constructor arguments when security i
namespace: 'default',
getUserName: expect.any(Function),
getActionsClient: expect.any(Function),
+ getEventLogClient: expect.any(Function),
createAPIKey: expect.any(Function),
invalidateAPIKey: expect.any(Function),
encryptedSavedObjectsClient: alertsClientFactoryParams.encryptedSavedObjectsClient,
@@ -164,6 +167,7 @@ test('creates an alerts client with proper constructor arguments', async () => {
invalidateAPIKey: expect.any(Function),
encryptedSavedObjectsClient: alertsClientFactoryParams.encryptedSavedObjectsClient,
getActionsClient: expect.any(Function),
+ getEventLogClient: expect.any(Function),
});
});
diff --git a/x-pack/plugins/alerts/server/alerts_client_factory.ts b/x-pack/plugins/alerts/server/alerts_client_factory.ts
index 79b0ccaf1f0bc..83202424c9773 100644
--- a/x-pack/plugins/alerts/server/alerts_client_factory.ts
+++ b/x-pack/plugins/alerts/server/alerts_client_factory.ts
@@ -16,6 +16,7 @@ import { PluginStartContract as FeaturesPluginStart } from '../../features/serve
import { AlertsAuthorization } from './authorization/alerts_authorization';
import { AlertsAuthorizationAuditLogger } from './authorization/audit_logger';
import { Space } from '../../spaces/server';
+import { IEventLogClientService } from '../../../plugins/event_log/server';
export interface AlertsClientFactoryOpts {
logger: Logger;
@@ -28,6 +29,7 @@ export interface AlertsClientFactoryOpts {
encryptedSavedObjectsClient: EncryptedSavedObjectsClient;
actions: ActionsPluginStartContract;
features: FeaturesPluginStart;
+ eventLog: IEventLogClientService;
}
export class AlertsClientFactory {
@@ -42,6 +44,7 @@ export class AlertsClientFactory {
private encryptedSavedObjectsClient!: EncryptedSavedObjectsClient;
private actions!: ActionsPluginStartContract;
private features!: FeaturesPluginStart;
+ private eventLog!: IEventLogClientService;
public initialize(options: AlertsClientFactoryOpts) {
if (this.isInitialized) {
@@ -58,10 +61,11 @@ export class AlertsClientFactory {
this.encryptedSavedObjectsClient = options.encryptedSavedObjectsClient;
this.actions = options.actions;
this.features = options.features;
+ this.eventLog = options.eventLog;
}
public create(request: KibanaRequest, savedObjects: SavedObjectsServiceStart): AlertsClient {
- const { securityPluginSetup, actions, features } = this;
+ const { securityPluginSetup, actions, eventLog, features } = this;
const spaceId = this.getSpaceId(request);
const authorization = new AlertsAuthorization({
authorization: securityPluginSetup?.authz,
@@ -135,6 +139,9 @@ export class AlertsClientFactory {
async getActionsClient() {
return actions.getActionsClientWithRequest(request);
},
+ async getEventLogClient() {
+ return eventLog.getClient(request);
+ },
});
}
}
diff --git a/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts b/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts
index 33a9a0bf0396e..b2a214eae9316 100644
--- a/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts
+++ b/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts
@@ -18,6 +18,7 @@ import { Space } from '../../../spaces/server';
export enum ReadOperations {
Get = 'get',
GetAlertState = 'getAlertState',
+ GetAlertStatus = 'getAlertStatus',
Find = 'find',
}
diff --git a/x-pack/plugins/alerts/server/index.ts b/x-pack/plugins/alerts/server/index.ts
index 515de771e7d6b..4c192a896c0c3 100644
--- a/x-pack/plugins/alerts/server/index.ts
+++ b/x-pack/plugins/alerts/server/index.ts
@@ -17,8 +17,11 @@ export {
AlertExecutorOptions,
AlertActionParams,
AlertServices,
- State,
+ AlertTypeState,
+ AlertTypeParams,
PartialAlert,
+ AlertInstanceState,
+ AlertInstanceContext,
} from './types';
export { PluginSetupContract, PluginStartContract } from './plugin';
export { FindResult } from './alerts_client';
diff --git a/x-pack/plugins/alerts/server/lib/alert_status_from_event_log.test.ts b/x-pack/plugins/alerts/server/lib/alert_status_from_event_log.test.ts
new file mode 100644
index 0000000000000..15570d3032f24
--- /dev/null
+++ b/x-pack/plugins/alerts/server/lib/alert_status_from_event_log.test.ts
@@ -0,0 +1,464 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { SanitizedAlert, AlertStatus } from '../types';
+import { IValidatedEvent } from '../../../event_log/server';
+import { EVENT_LOG_ACTIONS, EVENT_LOG_PROVIDER } from '../plugin';
+import { alertStatusFromEventLog } from './alert_status_from_event_log';
+
+const ONE_HOUR_IN_MILLIS = 60 * 60 * 1000;
+const dateStart = '2020-06-18T00:00:00.000Z';
+const dateEnd = dateString(dateStart, ONE_HOUR_IN_MILLIS);
+
+describe('alertStatusFromEventLog', () => {
+ test('no events and muted ids', async () => {
+ const alert = createAlert({});
+ const events: IValidatedEvent[] = [];
+ const status: AlertStatus = alertStatusFromEventLog({ alert, events, dateStart, dateEnd });
+
+ expect(status).toMatchInlineSnapshot(`
+ Object {
+ "alertTypeId": "123",
+ "consumer": "alert-consumer",
+ "enabled": false,
+ "errorMessages": Array [],
+ "id": "alert-123",
+ "instances": Object {},
+ "lastRun": undefined,
+ "muteAll": false,
+ "name": "alert-name",
+ "status": "OK",
+ "statusEndDate": "2020-06-18T01:00:00.000Z",
+ "statusStartDate": "2020-06-18T00:00:00.000Z",
+ "tags": Array [],
+ "throttle": null,
+ }
+ `);
+ });
+
+ test('different alert properties', async () => {
+ const alert = createAlert({
+ id: 'alert-456',
+ alertTypeId: '456',
+ schedule: { interval: '100s' },
+ enabled: true,
+ name: 'alert-name-2',
+ tags: ['tag-1', 'tag-2'],
+ consumer: 'alert-consumer-2',
+ throttle: '1h',
+ muteAll: true,
+ });
+ const events: IValidatedEvent[] = [];
+ const status: AlertStatus = alertStatusFromEventLog({
+ alert,
+ events,
+ dateStart: dateString(dateEnd, ONE_HOUR_IN_MILLIS),
+ dateEnd: dateString(dateEnd, ONE_HOUR_IN_MILLIS * 2),
+ });
+
+ expect(status).toMatchInlineSnapshot(`
+ Object {
+ "alertTypeId": "456",
+ "consumer": "alert-consumer-2",
+ "enabled": true,
+ "errorMessages": Array [],
+ "id": "alert-456",
+ "instances": Object {},
+ "lastRun": undefined,
+ "muteAll": true,
+ "name": "alert-name-2",
+ "status": "OK",
+ "statusEndDate": "2020-06-18T03:00:00.000Z",
+ "statusStartDate": "2020-06-18T02:00:00.000Z",
+ "tags": Array [
+ "tag-1",
+ "tag-2",
+ ],
+ "throttle": "1h",
+ }
+ `);
+ });
+
+ test('two muted instances', async () => {
+ const alert = createAlert({
+ mutedInstanceIds: ['instance-1', 'instance-2'],
+ });
+ const events: IValidatedEvent[] = [];
+ const alertStatus: AlertStatus = alertStatusFromEventLog({ alert, events, dateStart, dateEnd });
+
+ const { lastRun, status, instances } = alertStatus;
+ expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
+ Object {
+ "instances": Object {
+ "instance-1": Object {
+ "activeStartDate": undefined,
+ "muted": true,
+ "status": "OK",
+ },
+ "instance-2": Object {
+ "activeStartDate": undefined,
+ "muted": true,
+ "status": "OK",
+ },
+ },
+ "lastRun": undefined,
+ "status": "OK",
+ }
+ `);
+ });
+
+ test('active alert but no instances', async () => {
+ const alert = createAlert({});
+ const eventsFactory = new EventsFactory();
+ const events = eventsFactory.addExecute().advanceTime(10000).addExecute().getEvents();
+
+ const alertStatus: AlertStatus = alertStatusFromEventLog({ alert, events, dateStart, dateEnd });
+
+ const { lastRun, status, instances } = alertStatus;
+ expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
+ Object {
+ "instances": Object {},
+ "lastRun": "2020-06-18T00:00:10.000Z",
+ "status": "OK",
+ }
+ `);
+ });
+
+ test('active alert with no instances but has errors', async () => {
+ const alert = createAlert({});
+ const eventsFactory = new EventsFactory();
+ const events = eventsFactory
+ .addExecute('oof!')
+ .advanceTime(10000)
+ .addExecute('rut roh!')
+ .getEvents();
+
+ const alertStatus: AlertStatus = alertStatusFromEventLog({ alert, events, dateStart, dateEnd });
+
+ const { lastRun, status, errorMessages, instances } = alertStatus;
+ expect({ lastRun, status, errorMessages, instances }).toMatchInlineSnapshot(`
+ Object {
+ "errorMessages": Array [
+ Object {
+ "date": "2020-06-18T00:00:00.000Z",
+ "message": "oof!",
+ },
+ Object {
+ "date": "2020-06-18T00:00:10.000Z",
+ "message": "rut roh!",
+ },
+ ],
+ "instances": Object {},
+ "lastRun": "2020-06-18T00:00:10.000Z",
+ "status": "Error",
+ }
+ `);
+ });
+
+ test('alert with currently inactive instance', async () => {
+ const alert = createAlert({});
+ const eventsFactory = new EventsFactory();
+ const events = eventsFactory
+ .addExecute()
+ .addNewInstance('instance-1')
+ .addActiveInstance('instance-1')
+ .advanceTime(10000)
+ .addExecute()
+ .addResolvedInstance('instance-1')
+ .getEvents();
+
+ const alertStatus: AlertStatus = alertStatusFromEventLog({ alert, events, dateStart, dateEnd });
+
+ const { lastRun, status, instances } = alertStatus;
+ expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
+ Object {
+ "instances": Object {
+ "instance-1": Object {
+ "activeStartDate": undefined,
+ "muted": false,
+ "status": "OK",
+ },
+ },
+ "lastRun": "2020-06-18T00:00:10.000Z",
+ "status": "OK",
+ }
+ `);
+ });
+
+ test('alert with currently inactive instance, no new-instance', async () => {
+ const alert = createAlert({});
+ const eventsFactory = new EventsFactory();
+ const events = eventsFactory
+ .addExecute()
+ .addActiveInstance('instance-1')
+ .advanceTime(10000)
+ .addExecute()
+ .addResolvedInstance('instance-1')
+ .getEvents();
+
+ const alertStatus: AlertStatus = alertStatusFromEventLog({ alert, events, dateStart, dateEnd });
+
+ const { lastRun, status, instances } = alertStatus;
+ expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
+ Object {
+ "instances": Object {
+ "instance-1": Object {
+ "activeStartDate": undefined,
+ "muted": false,
+ "status": "OK",
+ },
+ },
+ "lastRun": "2020-06-18T00:00:10.000Z",
+ "status": "OK",
+ }
+ `);
+ });
+
+ test('alert with currently active instance', async () => {
+ const alert = createAlert({});
+ const eventsFactory = new EventsFactory();
+ const events = eventsFactory
+ .addExecute()
+ .addNewInstance('instance-1')
+ .addActiveInstance('instance-1')
+ .advanceTime(10000)
+ .addExecute()
+ .addActiveInstance('instance-1')
+ .getEvents();
+
+ const alertStatus: AlertStatus = alertStatusFromEventLog({ alert, events, dateStart, dateEnd });
+
+ const { lastRun, status, instances } = alertStatus;
+ expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
+ Object {
+ "instances": Object {
+ "instance-1": Object {
+ "activeStartDate": "2020-06-18T00:00:00.000Z",
+ "muted": false,
+ "status": "Active",
+ },
+ },
+ "lastRun": "2020-06-18T00:00:10.000Z",
+ "status": "Active",
+ }
+ `);
+ });
+
+ test('alert with currently active instance, no new-instance', async () => {
+ const alert = createAlert({});
+ const eventsFactory = new EventsFactory();
+ const events = eventsFactory
+ .addExecute()
+ .addActiveInstance('instance-1')
+ .advanceTime(10000)
+ .addExecute()
+ .addActiveInstance('instance-1')
+ .getEvents();
+
+ const alertStatus: AlertStatus = alertStatusFromEventLog({ alert, events, dateStart, dateEnd });
+
+ const { lastRun, status, instances } = alertStatus;
+ expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
+ Object {
+ "instances": Object {
+ "instance-1": Object {
+ "activeStartDate": undefined,
+ "muted": false,
+ "status": "Active",
+ },
+ },
+ "lastRun": "2020-06-18T00:00:10.000Z",
+ "status": "Active",
+ }
+ `);
+ });
+
+ test('alert with active and inactive muted alerts', async () => {
+ const alert = createAlert({ mutedInstanceIds: ['instance-1', 'instance-2'] });
+ const eventsFactory = new EventsFactory();
+ const events = eventsFactory
+ .addExecute()
+ .addNewInstance('instance-1')
+ .addActiveInstance('instance-1')
+ .addNewInstance('instance-2')
+ .addActiveInstance('instance-2')
+ .advanceTime(10000)
+ .addExecute()
+ .addActiveInstance('instance-1')
+ .addResolvedInstance('instance-2')
+ .getEvents();
+
+ const alertStatus: AlertStatus = alertStatusFromEventLog({ alert, events, dateStart, dateEnd });
+
+ const { lastRun, status, instances } = alertStatus;
+ expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
+ Object {
+ "instances": Object {
+ "instance-1": Object {
+ "activeStartDate": "2020-06-18T00:00:00.000Z",
+ "muted": true,
+ "status": "Active",
+ },
+ "instance-2": Object {
+ "activeStartDate": undefined,
+ "muted": true,
+ "status": "OK",
+ },
+ },
+ "lastRun": "2020-06-18T00:00:10.000Z",
+ "status": "Active",
+ }
+ `);
+ });
+
+ test('alert with active and inactive alerts over many executes', async () => {
+ const alert = createAlert({});
+ const eventsFactory = new EventsFactory();
+ const events = eventsFactory
+ .addExecute()
+ .addNewInstance('instance-1')
+ .addActiveInstance('instance-1')
+ .addNewInstance('instance-2')
+ .addActiveInstance('instance-2')
+ .advanceTime(10000)
+ .addExecute()
+ .addActiveInstance('instance-1')
+ .addResolvedInstance('instance-2')
+ .advanceTime(10000)
+ .addExecute()
+ .addActiveInstance('instance-1')
+ .advanceTime(10000)
+ .addExecute()
+ .addActiveInstance('instance-1')
+ .getEvents();
+
+ const alertStatus: AlertStatus = alertStatusFromEventLog({ alert, events, dateStart, dateEnd });
+
+ const { lastRun, status, instances } = alertStatus;
+ expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
+ Object {
+ "instances": Object {
+ "instance-1": Object {
+ "activeStartDate": "2020-06-18T00:00:00.000Z",
+ "muted": false,
+ "status": "Active",
+ },
+ "instance-2": Object {
+ "activeStartDate": undefined,
+ "muted": false,
+ "status": "OK",
+ },
+ },
+ "lastRun": "2020-06-18T00:00:30.000Z",
+ "status": "Active",
+ }
+ `);
+ });
+});
+
+function dateString(isoBaseDate: string, offsetMillis = 0): string {
+ return new Date(Date.parse(isoBaseDate) + offsetMillis).toISOString();
+}
+
+export class EventsFactory {
+ private events: IValidatedEvent[] = [];
+
+ constructor(private date: string = dateStart) {}
+
+ getEvents(): IValidatedEvent[] {
+ // ES normally returns events sorted newest to oldest, so we need to sort
+ // that way also
+ const events = this.events.slice();
+ events.sort((a, b) => -a!['@timestamp']!.localeCompare(b!['@timestamp']!));
+ return events;
+ }
+
+ getTime(): string {
+ return this.date;
+ }
+
+ advanceTime(millis: number): EventsFactory {
+ this.date = dateString(this.date, millis);
+ return this;
+ }
+
+ addExecute(errorMessage?: string): EventsFactory {
+ let event: IValidatedEvent = {
+ '@timestamp': this.date,
+ event: {
+ provider: EVENT_LOG_PROVIDER,
+ action: EVENT_LOG_ACTIONS.execute,
+ },
+ };
+
+ if (errorMessage) {
+ event = { ...event, error: { message: errorMessage } };
+ }
+
+ this.events.push(event);
+ return this;
+ }
+
+ addActiveInstance(instanceId: string): EventsFactory {
+ this.events.push({
+ '@timestamp': this.date,
+ event: {
+ provider: EVENT_LOG_PROVIDER,
+ action: EVENT_LOG_ACTIONS.activeInstance,
+ },
+ kibana: { alerting: { instance_id: instanceId } },
+ });
+ return this;
+ }
+
+ addNewInstance(instanceId: string): EventsFactory {
+ this.events.push({
+ '@timestamp': this.date,
+ event: {
+ provider: EVENT_LOG_PROVIDER,
+ action: EVENT_LOG_ACTIONS.newInstance,
+ },
+ kibana: { alerting: { instance_id: instanceId } },
+ });
+ return this;
+ }
+
+ addResolvedInstance(instanceId: string): EventsFactory {
+ this.events.push({
+ '@timestamp': this.date,
+ event: {
+ provider: EVENT_LOG_PROVIDER,
+ action: EVENT_LOG_ACTIONS.resolvedInstance,
+ },
+ kibana: { alerting: { instance_id: instanceId } },
+ });
+ return this;
+ }
+}
+
+function createAlert(overrides: Partial): SanitizedAlert {
+ return { ...BaseAlert, ...overrides };
+}
+
+const BaseAlert: SanitizedAlert = {
+ id: 'alert-123',
+ alertTypeId: '123',
+ schedule: { interval: '10s' },
+ enabled: false,
+ name: 'alert-name',
+ tags: [],
+ consumer: 'alert-consumer',
+ throttle: null,
+ muteAll: false,
+ mutedInstanceIds: [],
+ params: { bar: true },
+ actions: [],
+ createdBy: null,
+ updatedBy: null,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ apiKeyOwner: null,
+};
diff --git a/x-pack/plugins/alerts/server/lib/alert_status_from_event_log.ts b/x-pack/plugins/alerts/server/lib/alert_status_from_event_log.ts
new file mode 100644
index 0000000000000..606bd44c6990c
--- /dev/null
+++ b/x-pack/plugins/alerts/server/lib/alert_status_from_event_log.ts
@@ -0,0 +1,123 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { SanitizedAlert, AlertStatus, AlertInstanceStatus } from '../types';
+import { IEvent } from '../../../event_log/server';
+import { EVENT_LOG_ACTIONS, EVENT_LOG_PROVIDER } from '../plugin';
+
+export interface AlertStatusFromEventLogParams {
+ alert: SanitizedAlert;
+ events: IEvent[];
+ dateStart: string;
+ dateEnd: string;
+}
+
+export function alertStatusFromEventLog(params: AlertStatusFromEventLogParams): AlertStatus {
+ // initialize the result
+ const { alert, events, dateStart, dateEnd } = params;
+ const alertStatus: AlertStatus = {
+ id: alert.id,
+ name: alert.name,
+ tags: alert.tags,
+ alertTypeId: alert.alertTypeId,
+ consumer: alert.consumer,
+ statusStartDate: dateStart,
+ statusEndDate: dateEnd,
+ status: 'OK',
+ muteAll: alert.muteAll,
+ throttle: alert.throttle,
+ enabled: alert.enabled,
+ lastRun: undefined,
+ errorMessages: [],
+ instances: {},
+ };
+
+ const instances = new Map();
+
+ // loop through the events
+ // should be sorted newest to oldest, we want oldest to newest, so reverse
+ for (const event of events.reverse()) {
+ const timeStamp = event?.['@timestamp'];
+ if (timeStamp === undefined) continue;
+
+ const provider = event?.event?.provider;
+ if (provider !== EVENT_LOG_PROVIDER) continue;
+
+ const action = event?.event?.action;
+ if (action === undefined) continue;
+
+ if (action === EVENT_LOG_ACTIONS.execute) {
+ alertStatus.lastRun = timeStamp;
+
+ const errorMessage = event?.error?.message;
+ if (errorMessage !== undefined) {
+ alertStatus.status = 'Error';
+ alertStatus.errorMessages.push({
+ date: timeStamp,
+ message: errorMessage,
+ });
+ } else {
+ alertStatus.status = 'OK';
+ }
+
+ continue;
+ }
+
+ const instanceId = event?.kibana?.alerting?.instance_id;
+ if (instanceId === undefined) continue;
+
+ const status = getAlertInstanceStatus(instances, instanceId);
+ switch (action) {
+ case EVENT_LOG_ACTIONS.newInstance:
+ status.activeStartDate = timeStamp;
+ // intentionally no break here
+ case EVENT_LOG_ACTIONS.activeInstance:
+ status.status = 'Active';
+ break;
+ case EVENT_LOG_ACTIONS.resolvedInstance:
+ status.status = 'OK';
+ status.activeStartDate = undefined;
+ }
+ }
+
+ // set the muted status of instances
+ for (const instanceId of alert.mutedInstanceIds) {
+ getAlertInstanceStatus(instances, instanceId).muted = true;
+ }
+
+ // convert the instances map to object form
+ const instanceIds = Array.from(instances.keys()).sort();
+ for (const instanceId of instanceIds) {
+ alertStatus.instances[instanceId] = instances.get(instanceId)!;
+ }
+
+ // set the overall alert status to Active if appropriate
+ if (alertStatus.status !== 'Error') {
+ if (Array.from(instances.values()).some((instance) => instance.status === 'Active')) {
+ alertStatus.status = 'Active';
+ }
+ }
+
+ alertStatus.errorMessages.sort((a, b) => a.date.localeCompare(b.date));
+
+ return alertStatus;
+}
+
+// return an instance status object, creating and adding to the map if needed
+function getAlertInstanceStatus(
+ instances: Map,
+ instanceId: string
+): AlertInstanceStatus {
+ if (instances.has(instanceId)) return instances.get(instanceId)!;
+
+ const status: AlertInstanceStatus = {
+ status: 'OK',
+ muted: false,
+ activeStartDate: undefined,
+ };
+ instances.set(instanceId, status);
+ return status;
+}
diff --git a/x-pack/plugins/alerts/server/lib/iso_or_relative_date.test.ts b/x-pack/plugins/alerts/server/lib/iso_or_relative_date.test.ts
new file mode 100644
index 0000000000000..91272c1cca3b5
--- /dev/null
+++ b/x-pack/plugins/alerts/server/lib/iso_or_relative_date.test.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { parseIsoOrRelativeDate } from './iso_or_relative_date';
+
+describe('parseIsoOrRelativeDate', () => {
+ test('handles ISO dates', () => {
+ const date = new Date();
+ const parsedDate = parseIsoOrRelativeDate(date.toISOString());
+ expect(parsedDate?.valueOf()).toBe(date.valueOf());
+ });
+
+ test('handles relative dates', () => {
+ const hoursDiff = 1;
+ const date = new Date(Date.now() - hoursDiff * 60 * 60 * 1000);
+ const parsedDate = parseIsoOrRelativeDate(`${hoursDiff}h`);
+ const diff = Math.abs(parsedDate!.valueOf() - date.valueOf());
+ expect(diff).toBeLessThan(1000);
+ });
+
+ test('returns undefined for invalid date strings', () => {
+ const parsedDate = parseIsoOrRelativeDate('this shall not pass');
+ expect(parsedDate).toBeUndefined();
+ });
+});
diff --git a/x-pack/plugins/alerts/server/lib/iso_or_relative_date.ts b/x-pack/plugins/alerts/server/lib/iso_or_relative_date.ts
new file mode 100644
index 0000000000000..77c4eefa04439
--- /dev/null
+++ b/x-pack/plugins/alerts/server/lib/iso_or_relative_date.ts
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { parseDuration } from '../../common/parse_duration';
+
+/**
+ * Parse an ISO date or NNx duration string as a Date
+ *
+ * @param dateString an ISO date or NNx "duration" string representing now-duration
+ * @returns a Date or undefined if the dateString was not valid
+ */
+export function parseIsoOrRelativeDate(dateString: string): Date | undefined {
+ const epochMillis = Date.parse(dateString);
+ if (!isNaN(epochMillis)) return new Date(epochMillis);
+
+ let millis: number;
+ try {
+ millis = parseDuration(dateString);
+ } catch (err) {
+ return;
+ }
+
+ return new Date(Date.now() - millis);
+}
diff --git a/x-pack/plugins/alerts/server/plugin.ts b/x-pack/plugins/alerts/server/plugin.ts
index 5d69887bd5bf0..d5843bd531d84 100644
--- a/x-pack/plugins/alerts/server/plugin.ts
+++ b/x-pack/plugins/alerts/server/plugin.ts
@@ -38,6 +38,7 @@ import {
findAlertRoute,
getAlertRoute,
getAlertStateRoute,
+ getAlertStatusRoute,
listAlertTypesRoute,
updateAlertRoute,
enableAlertRoute,
@@ -57,16 +58,17 @@ import {
import { Services } from './types';
import { registerAlertsUsageCollector } from './usage';
import { initializeAlertingTelemetry, scheduleAlertingTelemetry } from './usage/task';
-import { IEventLogger, IEventLogService } from '../../event_log/server';
+import { IEventLogger, IEventLogService, IEventLogClientService } from '../../event_log/server';
import { PluginStartContract as FeaturesPluginStart } from '../../features/server';
import { setupSavedObjects } from './saved_objects';
-const EVENT_LOG_PROVIDER = 'alerting';
+export const EVENT_LOG_PROVIDER = 'alerting';
export const EVENT_LOG_ACTIONS = {
execute: 'execute',
executeAction: 'execute-action',
newInstance: 'new-instance',
resolvedInstance: 'resolved-instance',
+ activeInstance: 'active-instance',
};
export interface PluginSetupContract {
@@ -92,6 +94,7 @@ export interface AlertingPluginsStart {
taskManager: TaskManagerStartContract;
encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
features: FeaturesPluginStart;
+ eventLog: IEventLogClientService;
}
export class AlertingPlugin {
@@ -189,6 +192,7 @@ export class AlertingPlugin {
findAlertRoute(router, this.licenseState);
getAlertRoute(router, this.licenseState);
getAlertStateRoute(router, this.licenseState);
+ getAlertStatusRoute(router, this.licenseState);
listAlertTypesRoute(router, this.licenseState);
updateAlertRoute(router, this.licenseState);
enableAlertRoute(router, this.licenseState);
@@ -235,6 +239,7 @@ export class AlertingPlugin {
},
actions: plugins.actions,
features: plugins.features,
+ eventLog: plugins.eventLog,
});
const getAlertsClientWithRequest = (request: KibanaRequest) => {
diff --git a/x-pack/plugins/alerts/server/routes/get_alert_status.test.ts b/x-pack/plugins/alerts/server/routes/get_alert_status.test.ts
new file mode 100644
index 0000000000000..1b4cb1941018b
--- /dev/null
+++ b/x-pack/plugins/alerts/server/routes/get_alert_status.test.ts
@@ -0,0 +1,105 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { getAlertStatusRoute } from './get_alert_status';
+import { httpServiceMock } from 'src/core/server/mocks';
+import { mockLicenseState } from '../lib/license_state.mock';
+import { mockHandlerArguments } from './_mock_handler_arguments';
+import { SavedObjectsErrorHelpers } from 'src/core/server';
+import { alertsClientMock } from '../alerts_client.mock';
+import { AlertStatus } from '../types';
+
+const alertsClient = alertsClientMock.create();
+jest.mock('../lib/license_api_access.ts', () => ({
+ verifyApiAccess: jest.fn(),
+}));
+
+beforeEach(() => {
+ jest.resetAllMocks();
+});
+
+describe('getAlertStatusRoute', () => {
+ const dateString = new Date().toISOString();
+ const mockedAlertStatus: AlertStatus = {
+ id: '',
+ name: '',
+ tags: [],
+ alertTypeId: '',
+ consumer: '',
+ muteAll: false,
+ throttle: null,
+ enabled: false,
+ statusStartDate: dateString,
+ statusEndDate: dateString,
+ status: 'OK',
+ errorMessages: [],
+ instances: {},
+ };
+
+ it('gets alert status', async () => {
+ const licenseState = mockLicenseState();
+ const router = httpServiceMock.createRouter();
+
+ getAlertStatusRoute(router, licenseState);
+
+ const [config, handler] = router.get.mock.calls[0];
+
+ expect(config.path).toMatchInlineSnapshot(`"/api/alerts/alert/{id}/status"`);
+
+ alertsClient.getAlertStatus.mockResolvedValueOnce(mockedAlertStatus);
+
+ const [context, req, res] = mockHandlerArguments(
+ { alertsClient },
+ {
+ params: {
+ id: '1',
+ },
+ query: {},
+ },
+ ['ok']
+ );
+
+ await handler(context, req, res);
+
+ expect(alertsClient.getAlertStatus).toHaveBeenCalledTimes(1);
+ expect(alertsClient.getAlertStatus.mock.calls[0]).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "dateStart": undefined,
+ "id": "1",
+ },
+ ]
+ `);
+
+ expect(res.ok).toHaveBeenCalled();
+ });
+
+ it('returns NOT-FOUND when alert is not found', async () => {
+ const licenseState = mockLicenseState();
+ const router = httpServiceMock.createRouter();
+
+ getAlertStatusRoute(router, licenseState);
+
+ const [, handler] = router.get.mock.calls[0];
+
+ alertsClient.getAlertStatus = jest
+ .fn()
+ .mockResolvedValueOnce(SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1'));
+
+ const [context, req, res] = mockHandlerArguments(
+ { alertsClient },
+ {
+ params: {
+ id: '1',
+ },
+ query: {},
+ },
+ ['notFound']
+ );
+
+ expect(await handler(context, req, res)).toEqual(undefined);
+ });
+});
diff --git a/x-pack/plugins/alerts/server/routes/get_alert_status.ts b/x-pack/plugins/alerts/server/routes/get_alert_status.ts
new file mode 100644
index 0000000000000..eab18c50189f4
--- /dev/null
+++ b/x-pack/plugins/alerts/server/routes/get_alert_status.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema, TypeOf } from '@kbn/config-schema';
+import {
+ IRouter,
+ RequestHandlerContext,
+ KibanaRequest,
+ IKibanaResponse,
+ KibanaResponseFactory,
+} from 'kibana/server';
+import { LicenseState } from '../lib/license_state';
+import { verifyApiAccess } from '../lib/license_api_access';
+import { BASE_ALERT_API_PATH } from '../../common';
+
+const paramSchema = schema.object({
+ id: schema.string(),
+});
+
+const querySchema = schema.object({
+ dateStart: schema.maybe(schema.string()),
+});
+
+export const getAlertStatusRoute = (router: IRouter, licenseState: LicenseState) => {
+ router.get(
+ {
+ path: `${BASE_ALERT_API_PATH}/alert/{id}/status`,
+ validate: {
+ params: paramSchema,
+ query: querySchema,
+ },
+ },
+ router.handleLegacyErrors(async function (
+ context: RequestHandlerContext,
+ req: KibanaRequest, TypeOf, unknown>,
+ res: KibanaResponseFactory
+ ): Promise {
+ verifyApiAccess(licenseState);
+ if (!context.alerting) {
+ return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' });
+ }
+ const alertsClient = context.alerting.getAlertsClient();
+ const { id } = req.params;
+ const { dateStart } = req.query;
+ const status = await alertsClient.getAlertStatus({ id, dateStart });
+ return res.ok({ body: status });
+ })
+ );
+};
diff --git a/x-pack/plugins/alerts/server/routes/index.ts b/x-pack/plugins/alerts/server/routes/index.ts
index f833a29c67bb9..4c6b1eb8e9b58 100644
--- a/x-pack/plugins/alerts/server/routes/index.ts
+++ b/x-pack/plugins/alerts/server/routes/index.ts
@@ -9,6 +9,7 @@ export { deleteAlertRoute } from './delete';
export { findAlertRoute } from './find';
export { getAlertRoute } from './get';
export { getAlertStateRoute } from './get_alert_state';
+export { getAlertStatusRoute } from './get_alert_status';
export { listAlertTypesRoute } from './list_alert_types';
export { updateAlertRoute } from './update';
export { enableAlertRoute } from './enable';
diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts
index c21d81779e5e0..bf074e2c60ee3 100644
--- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts
+++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts
@@ -5,12 +5,18 @@
*/
import { map } from 'lodash';
-import { AlertAction, State, Context, AlertType, AlertParams } from '../types';
import { Logger, KibanaRequest } from '../../../../../src/core/server';
import { transformActionParams } from './transform_action_params';
import { PluginStartContract as ActionsPluginStartContract } from '../../../actions/server';
import { IEventLogger, IEvent, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server';
import { EVENT_LOG_ACTIONS } from '../plugin';
+import {
+ AlertAction,
+ AlertInstanceState,
+ AlertInstanceContext,
+ AlertType,
+ AlertTypeParams,
+} from '../types';
interface CreateExecutionHandlerOptions {
alertId: string;
@@ -24,14 +30,14 @@ interface CreateExecutionHandlerOptions {
logger: Logger;
eventLogger: IEventLogger;
request: KibanaRequest;
- alertParams: AlertParams;
+ alertParams: AlertTypeParams;
}
interface ExecutionHandlerOptions {
actionGroup: string;
alertInstanceId: string;
- context: Context;
- state: State;
+ context: AlertInstanceContext;
+ state: AlertInstanceState;
}
export function createExecutionHandler({
diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts
index 4abe58de5a904..58b1fa4a123e1 100644
--- a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts
+++ b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts
@@ -224,7 +224,7 @@ describe('Task Runner', () => {
`);
const eventLogger = taskRunnerFactoryInitializerParams.eventLogger;
- expect(eventLogger.logEvent).toHaveBeenCalledTimes(3);
+ expect(eventLogger.logEvent).toHaveBeenCalledTimes(4);
expect(eventLogger.logEvent).toHaveBeenCalledWith({
event: {
action: 'execute',
@@ -261,6 +261,25 @@ describe('Task Runner', () => {
},
message: "test:1: 'alert-name' created new instance: '1'",
});
+ expect(eventLogger.logEvent).toHaveBeenCalledWith({
+ event: {
+ action: 'active-instance',
+ },
+ kibana: {
+ alerting: {
+ instance_id: '1',
+ },
+ saved_objects: [
+ {
+ id: '1',
+ namespace: undefined,
+ rel: 'primary',
+ type: 'alert',
+ },
+ ],
+ },
+ message: "test:1: 'alert-name' active instance: '1'",
+ });
expect(eventLogger.logEvent).toHaveBeenCalledWith({
event: {
action: 'execute-action',
@@ -345,7 +364,7 @@ describe('Task Runner', () => {
`);
const eventLogger = taskRunnerFactoryInitializerParams.eventLogger;
- expect(eventLogger.logEvent).toHaveBeenCalledTimes(3);
+ expect(eventLogger.logEvent).toHaveBeenCalledTimes(4);
expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
@@ -388,6 +407,27 @@ describe('Task Runner', () => {
"message": "test:1: 'alert-name' created new instance: '1'",
},
],
+ Array [
+ Object {
+ "event": Object {
+ "action": "active-instance",
+ },
+ "kibana": Object {
+ "alerting": Object {
+ "instance_id": "1",
+ },
+ "saved_objects": Array [
+ Object {
+ "id": "1",
+ "namespace": undefined,
+ "rel": "primary",
+ "type": "alert",
+ },
+ ],
+ },
+ "message": "test:1: 'alert-name' active instance: '1'",
+ },
+ ],
Array [
Object {
"event": Object {
@@ -465,7 +505,7 @@ describe('Task Runner', () => {
`);
const eventLogger = taskRunnerFactoryInitializerParams.eventLogger;
- expect(eventLogger.logEvent).toHaveBeenCalledTimes(2);
+ expect(eventLogger.logEvent).toHaveBeenCalledTimes(3);
expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
@@ -508,6 +548,27 @@ describe('Task Runner', () => {
"message": "test:1: 'alert-name' resolved instance: '2'",
},
],
+ Array [
+ Object {
+ "event": Object {
+ "action": "active-instance",
+ },
+ "kibana": Object {
+ "alerting": Object {
+ "instance_id": "1",
+ },
+ "saved_objects": Array [
+ Object {
+ "id": "1",
+ "namespace": undefined,
+ "rel": "primary",
+ "type": "alert",
+ },
+ ],
+ },
+ "message": "test:1: 'alert-name' active instance: '1'",
+ },
+ ],
]
`);
});
diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.ts
index 04fea58f250a3..4c16d23b485b5 100644
--- a/x-pack/plugins/alerts/server/task_runner/task_runner.ts
+++ b/x-pack/plugins/alerts/server/task_runner/task_runner.ts
@@ -355,41 +355,53 @@ interface GenerateNewAndResolvedInstanceEventsParams {
}
function generateNewAndResolvedInstanceEvents(params: GenerateNewAndResolvedInstanceEventsParams) {
- const { currentAlertInstanceIds, originalAlertInstanceIds } = params;
+ const {
+ eventLogger,
+ alertId,
+ namespace,
+ currentAlertInstanceIds,
+ originalAlertInstanceIds,
+ } = params;
+
const newIds = without(currentAlertInstanceIds, ...originalAlertInstanceIds);
const resolvedIds = without(originalAlertInstanceIds, ...currentAlertInstanceIds);
+ for (const id of resolvedIds) {
+ const message = `${params.alertLabel} resolved instance: '${id}'`;
+ logInstanceEvent(id, EVENT_LOG_ACTIONS.resolvedInstance, message);
+ }
+
for (const id of newIds) {
const message = `${params.alertLabel} created new instance: '${id}'`;
logInstanceEvent(id, EVENT_LOG_ACTIONS.newInstance, message);
}
- for (const id of resolvedIds) {
- const message = `${params.alertLabel} resolved instance: '${id}'`;
- logInstanceEvent(id, EVENT_LOG_ACTIONS.resolvedInstance, message);
+ for (const id of currentAlertInstanceIds) {
+ const message = `${params.alertLabel} active instance: '${id}'`;
+ logInstanceEvent(id, EVENT_LOG_ACTIONS.activeInstance, message);
}
- function logInstanceEvent(id: string, action: string, message: string) {
+ function logInstanceEvent(instanceId: string, action: string, message: string) {
const event: IEvent = {
event: {
action,
},
kibana: {
alerting: {
- instance_id: id,
+ instance_id: instanceId,
},
saved_objects: [
{
rel: SAVED_OBJECT_REL_PRIMARY,
type: 'alert',
- id: params.alertId,
- namespace: params.namespace,
+ id: alertId,
+ namespace,
},
],
},
message,
};
- params.eventLogger.logEvent(event);
+ eventLogger.logEvent(event);
}
}
diff --git a/x-pack/plugins/alerts/server/task_runner/transform_action_params.ts b/x-pack/plugins/alerts/server/task_runner/transform_action_params.ts
index 30f062eee3705..913fc51cb0f6e 100644
--- a/x-pack/plugins/alerts/server/task_runner/transform_action_params.ts
+++ b/x-pack/plugins/alerts/server/task_runner/transform_action_params.ts
@@ -6,7 +6,12 @@
import Mustache from 'mustache';
import { isString, cloneDeepWith } from 'lodash';
-import { AlertActionParams, State, Context, AlertParams } from '../types';
+import {
+ AlertActionParams,
+ AlertInstanceState,
+ AlertInstanceContext,
+ AlertTypeParams,
+} from '../types';
interface TransformActionParamsOptions {
alertId: string;
@@ -15,9 +20,9 @@ interface TransformActionParamsOptions {
tags?: string[];
alertInstanceId: string;
actionParams: AlertActionParams;
- state: State;
- context: Context;
- alertParams: AlertParams;
+ alertParams: AlertTypeParams;
+ state: AlertInstanceState;
+ context: AlertInstanceContext;
}
export function transformActionParams({
diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts
index 71ab35f7f434b..20943ba28885c 100644
--- a/x-pack/plugins/alerts/server/types.ts
+++ b/x-pack/plugins/alerts/server/types.ts
@@ -7,7 +7,6 @@
import { AlertInstance } from './alert_instance';
import { AlertTypeRegistry as OrigAlertTypeRegistry } from './alert_type_registry';
import { PluginSetupContract, PluginStartContract } from './plugin';
-import { Alert, AlertActionParams, ActionGroup } from '../common';
import { AlertsClient } from './alerts_client';
export * from '../common';
import {
@@ -17,13 +16,16 @@ import {
SavedObjectAttributes,
SavedObjectsClientContract,
} from '../../../../src/core/server';
+import {
+ Alert,
+ AlertActionParams,
+ ActionGroup,
+ AlertTypeParams,
+ AlertTypeState,
+ AlertInstanceContext,
+ AlertInstanceState,
+} from '../common';
-// This will have to remain `any` until we can extend Alert Executors with generics
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export type State = Record;
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export type Context = Record;
-export type AlertParams = Record;
export type WithoutQueryAndParams = Pick>;
export type GetServicesFunction = (request: KibanaRequest) => Services;
export type GetBasePathFunction = (spaceId?: string) => string;
@@ -44,18 +46,24 @@ export interface Services {
getLegacyScopedClusterClient(clusterClient: ILegacyClusterClient): ILegacyScopedClusterClient;
}
-export interface AlertServices extends Services {
- alertInstanceFactory: (id: string) => AlertInstance;
+export interface AlertServices<
+ InstanceState extends AlertInstanceState = AlertInstanceState,
+ InstanceContext extends AlertInstanceContext = AlertInstanceContext
+> extends Services {
+ alertInstanceFactory: (id: string) => AlertInstance;
}
-export interface AlertExecutorOptions {
+export interface AlertExecutorOptions<
+ Params extends AlertTypeParams = AlertTypeParams,
+ State extends AlertTypeState = AlertTypeState,
+ InstanceState extends AlertInstanceState = AlertInstanceState,
+ InstanceContext extends AlertInstanceContext = AlertInstanceContext
+> {
alertId: string;
startedAt: Date;
previousStartedAt: Date | null;
- services: AlertServices;
- // This will have to remain `any` until we can extend Alert Executors with generics
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- params: Record;
+ services: AlertServices;
+ params: Params;
state: State;
spaceId: string;
namespace?: string;
@@ -70,15 +78,24 @@ export interface ActionVariable {
description: string;
}
-export interface AlertType {
+export interface AlertType<
+ Params extends AlertTypeParams = AlertTypeParams,
+ State extends AlertTypeState = AlertTypeState,
+ InstanceState extends AlertInstanceState = AlertInstanceState,
+ InstanceContext extends AlertInstanceContext = AlertInstanceContext
+> {
id: string;
name: string;
validate?: {
- params?: { validate: (object: unknown) => AlertExecutorOptions['params'] };
+ params?: { validate: (object: unknown) => Params };
};
actionGroups: ActionGroup[];
defaultActionGroupId: ActionGroup['id'];
- executor: ({ services, params, state }: AlertExecutorOptions) => Promise;
+ executor: ({
+ services,
+ params,
+ state,
+ }: AlertExecutorOptions) => Promise;
producer: string;
actionVariables?: {
context?: ActionVariable[];
diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap
index 0589fce727115..40522edc21b52 100644
--- a/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap
+++ b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap
@@ -104,16 +104,15 @@ exports[`ErrorGroupOverview -> List should render empty state 1`] = `
className="euiPopover__anchor"
>