diff --git a/.eslintrc.js b/.eslintrc.js index 09de32a91bca3..67c52117399cc 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -446,6 +446,7 @@ module.exports = { '!(src|x-pack)/plugins/**/(public|server)/mocks/index.{js,mjs,ts}', '!(src|x-pack)/plugins/**/(public|server)/(index|mocks).{js,mjs,ts,tsx}', '!(src|x-pack)/plugins/**/__stories__/index.{js,mjs,ts,tsx}', + '!(src|x-pack)/plugins/**/__fixtures__/index.{js,mjs,ts,tsx}', ], allowSameFolder: true, errorMessage: 'Plugins may only import from top-level public and server modules.', diff --git a/.i18nrc.json b/.i18nrc.json index 732644b43e1f7..0ee1e55ed62c6 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -18,6 +18,7 @@ "expressions": "src/plugins/expressions", "expressionError": "src/plugins/expression_error", "expressionRevealImage": "src/plugins/expression_reveal_image", + "expressionShape": "src/plugins/expression_shape", "inputControl": "src/plugins/input_control_vis", "inspector": "src/plugins/inspector", "inspectorViews": "src/legacy/core_plugins/inspector_views", @@ -28,6 +29,7 @@ "management": ["src/legacy/core_plugins/management", "src/plugins/management"], "maps_legacy": "src/plugins/maps_legacy", "monaco": "packages/kbn-monaco/src", + "esQuery": "packages/kbn-es-query/src", "presentationUtil": "src/plugins/presentation_util", "indexPatternFieldEditor": "src/plugins/index_pattern_field_editor", "indexPatternManagement": "src/plugins/index_pattern_management", diff --git a/api_docs/alerting.json b/api_docs/alerting.json index ec784dc8fc991..7c860a5c19e34 100644 --- a/api_docs/alerting.json +++ b/api_docs/alerting.json @@ -669,10 +669,10 @@ "children": [ { "parentPluginId": "alerting", - "id": "def-server.AlertingApiRequestHandlerContext.getAlertsClient", + "id": "def-server.AlertingApiRequestHandlerContext.getRulesClient", "type": "Function", "tags": [], - "label": "getAlertsClient", + "label": "getRulesClient", "description": [], "signature": [ "() => ", @@ -680,8 +680,8 @@ "pluginId": "alerting", "scope": "server", "docId": "kibAlertingPluginApi", - "section": "def-server.AlertsClient", - "text": "AlertsClient" + "section": "def-server.RulesClient", + "text": "RulesClient" } ], "source": { @@ -1170,7 +1170,7 @@ "" ], "source": { - "path": "x-pack/plugins/alerting/server/alerts_client/alerts_client.ts", + "path": "x-pack/plugins/alerting/server/rules_client/rules_client.ts", "lineNumber": 140 }, "deprecated": false, @@ -1183,7 +1183,7 @@ "label": "page", "description": [], "source": { - "path": "x-pack/plugins/alerting/server/alerts_client/alerts_client.ts", + "path": "x-pack/plugins/alerting/server/rules_client/rules_client.ts", "lineNumber": 141 }, "deprecated": false @@ -1196,7 +1196,7 @@ "label": "perPage", "description": [], "source": { - "path": "x-pack/plugins/alerting/server/alerts_client/alerts_client.ts", + "path": "x-pack/plugins/alerting/server/rules_client/rules_client.ts", "lineNumber": 142 }, "deprecated": false @@ -1209,7 +1209,7 @@ "label": "total", "description": [], "source": { - "path": "x-pack/plugins/alerting/server/alerts_client/alerts_client.ts", + "path": "x-pack/plugins/alerting/server/rules_client/rules_client.ts", "lineNumber": 143 }, "deprecated": false @@ -1233,7 +1233,7 @@ ", \"enabled\" | \"id\" | \"name\" | \"params\" | \"actions\" | \"tags\" | \"alertTypeId\" | \"consumer\" | \"schedule\" | \"scheduledTaskId\" | \"createdBy\" | \"updatedBy\" | \"createdAt\" | \"updatedAt\" | \"apiKeyOwner\" | \"throttle\" | \"notifyWhen\" | \"muteAll\" | \"mutedInstanceIds\" | \"executionStatus\">[]" ], "source": { - "path": "x-pack/plugins/alerting/server/alerts_client/alerts_client.ts", + "path": "x-pack/plugins/alerting/server/rules_client/rules_client.ts", "lineNumber": 144 }, "deprecated": false @@ -1343,10 +1343,10 @@ }, { "parentPluginId": "alerting", - "id": "def-server.PluginStartContract.getAlertsClientWithRequest", + "id": "def-server.PluginStartContract.getRulesClientWithRequest", "type": "Function", "tags": [], - "label": "getAlertsClientWithRequest", + "label": "getRulesClientWithRequest", "description": [], "signature": [ "(request: ", @@ -1362,8 +1362,8 @@ "pluginId": "alerting", "scope": "server", "docId": "kibAlertingPluginApi", - "section": "def-server.AlertsClient", - "text": "AlertsClient" + "section": "def-server.RulesClient", + "text": "RulesClient" }, ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"aggregate\" | \"enable\" | \"disable\" | \"muteAll\" | \"getAlertState\" | \"getAlertInstanceSummary\" | \"updateApiKey\" | \"unmuteAll\" | \"muteInstance\" | \"unmuteInstance\" | \"listAlertTypes\">" ], @@ -1375,7 +1375,7 @@ "children": [ { "parentPluginId": "alerting", - "id": "def-server.PluginStartContract.getAlertsClientWithRequest.$1", + "id": "def-server.PluginStartContract.getRulesClientWithRequest.$1", "type": "Object", "tags": [], "label": "request", @@ -1571,10 +1571,10 @@ }, { "parentPluginId": "alerting", - "id": "def-server.AlertsClient", + "id": "def-server.RulesClient", "type": "Type", "tags": [], - "label": "AlertsClient", + "label": "RulesClient", "description": [], "signature": [ "{ get: = never>({ id, }: { id: string; }) => Promise | [audit_logger.ts#L8](https://github.com/elastic/kibana/tree/master/x-pack/plugins/alerting/server/authorization/audit_logger.ts#L8) | - | | | [audit_logger.ts#L21](https://github.com/elastic/kibana/tree/master/x-pack/plugins/alerting/server/authorization/audit_logger.ts#L21) | - | | | [audit_logger.ts#L23](https://github.com/elastic/kibana/tree/master/x-pack/plugins/alerting/server/authorization/audit_logger.ts#L23) | - | -| | [alerts_client_factory.test.ts#L23](https://github.com/elastic/kibana/tree/master/x-pack/plugins/alerting/server/alerts_client_factory.test.ts#L23) | - | -| | [alerts_client_factory.test.ts#L98](https://github.com/elastic/kibana/tree/master/x-pack/plugins/alerting/server/alerts_client_factory.test.ts#L98) | - | +| | [rules_client_factory.test.ts#L23](https://github.com/elastic/kibana/tree/master/x-pack/plugins/alerting/server/rules_client_factory.test.ts#L23) | - | +| | [rules_client_factory.test.ts#L98](https://github.com/elastic/kibana/tree/master/x-pack/plugins/alerting/server/rules_client_factory.test.ts#L98) | - | diff --git a/api_docs/security.json b/api_docs/security.json index ac0982be3f19e..a02e4cebc5fbf 100644 --- a/api_docs/security.json +++ b/api_docs/security.json @@ -1739,14 +1739,14 @@ { "plugin": "alerting", "link": { - "path": "x-pack/plugins/alerting/server/alerts_client_factory.test.ts", + "path": "x-pack/plugins/alerting/server/rules_client_factory.test.ts", "lineNumber": 23 } }, { "plugin": "alerting", "link": { - "path": "x-pack/plugins/alerting/server/alerts_client_factory.test.ts", + "path": "x-pack/plugins/alerting/server/rules_client_factory.test.ts", "lineNumber": 98 } } diff --git a/docs/dev-tools/painlesslab/index.asciidoc b/docs/dev-tools/painlesslab/index.asciidoc index 7b4e9101a9901..4077ffe87ca1a 100644 --- a/docs/dev-tools/painlesslab/index.asciidoc +++ b/docs/dev-tools/painlesslab/index.asciidoc @@ -7,7 +7,7 @@ beta::[] The *Painless Lab* is an interactive code editor that lets you test and debug {ref}/modules-scripting-painless.html[Painless scripts] in real-time. You can use the Painless scripting -language to create <>, +language to create <>, process {ref}/docs-reindex.html[reindexed data], define complex <>, and work with data in other contexts. diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index c1535e8a2146f..8e08e5f4db1f9 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -99,6 +99,10 @@ want to incorporate their own functions, types, and renderers into the service for use in their own application. +|{kib-repo}blob/{branch}/src/plugins/expression_shape/README.md[expressionShape] +|Expression Shape plugin adds a shape function to the expression plugin and an associated renderer. The renderer will display the given shape with selected decorations. + + |{kib-repo}blob/{branch}/src/plugins/home/README.md[home] |Moves the legacy ui/registry/feature_catalogue module for registering "features" that should be shown in the home page's feature catalogue to a service within a "home" plugin. The feature catalogue refered to here should not be confused with the "feature" plugin for registering features used to derive UI capabilities for feature controls. diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getsearchsourcetimefilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getsearchsourcetimefilter.md index 1f8bc1300a0a8..9ebc685f2a77d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getsearchsourcetimefilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getsearchsourcetimefilter.md @@ -7,7 +7,7 @@ Signature: ```typescript -getSearchSourceTimeFilter(forceNow?: Date): RangeFilter[] | { +getSearchSourceTimeFilter(forceNow?: Date): import("@kbn/es-query").RangeFilter[] | { meta: { index: string | undefined; params: {}; @@ -43,7 +43,7 @@ getSearchSourceTimeFilter(forceNow?: Date): RangeFilter[] | { Returns: -`RangeFilter[] | { +`import("@kbn/es-query").RangeFilter[] | { meta: { index: string | undefined; params: {}; diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.customfilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.customfilter.md index 0a3b4e54cfe55..6763a8d2ba0bf 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.customfilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.customfilter.md @@ -4,10 +4,13 @@ ## CustomFilter type +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> + Signature: ```typescript -export declare type CustomFilter = Filter & { - query: any; -}; +declare type CustomFilter = oldCustomFilter; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md index d06ce1b2ef2bc..eb06d99426197 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md @@ -10,23 +10,23 @@ esFilters: { FilterLabel: (props: import("./ui/filter_bar/filter_editor/lib/filter_label").FilterLabelProps) => JSX.Element; FilterItem: (props: import("./ui/filter_bar/filter_item").FilterItemProps) => JSX.Element; - FILTERS: typeof FILTERS; + FILTERS: typeof import("@kbn/es-query").FILTERS; FilterStateStore: typeof FilterStateStore; - buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("../common").Filter; - buildPhrasesFilter: (field: import("../common").IndexPatternFieldBase, params: any[], indexPattern: import("../common").IndexPatternBase) => import("../common").PhrasesFilter; - buildExistsFilter: (field: import("../common").IndexPatternFieldBase, indexPattern: import("../common").IndexPatternBase) => import("../common").ExistsFilter; - buildPhraseFilter: (field: import("../common").IndexPatternFieldBase, value: any, indexPattern: import("../common").IndexPatternBase) => import("../common").PhraseFilter; - buildQueryFilter: (query: any, index: string, alias: string) => import("../common").QueryStringFilter; - buildRangeFilter: (field: import("../common").IndexPatternFieldBase, params: import("../common").RangeFilterParams, indexPattern: import("../common").IndexPatternBase, formattedValue?: string | undefined) => import("../common").RangeFilter; - isPhraseFilter: (filter: any) => filter is import("../common").PhraseFilter; - isExistsFilter: (filter: any) => filter is import("../common").ExistsFilter; - isPhrasesFilter: (filter: any) => filter is import("../common").PhrasesFilter; - isRangeFilter: (filter: any) => filter is import("../common").RangeFilter; - isMatchAllFilter: (filter: any) => filter is import("../common").MatchAllFilter; - isMissingFilter: (filter: any) => filter is import("../common").MissingFilter; - isQueryStringFilter: (filter: any) => filter is import("../common").QueryStringFilter; - isFilterPinned: (filter: import("../common").Filter) => boolean | undefined; - toggleFilterNegated: (filter: import("../common").Filter) => { + buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("@kbn/es-query").Filter; + buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: any[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; + buildExistsFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").ExistsFilter; + buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: any, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter; + buildQueryFilter: (query: any, index: string, alias: string) => import("@kbn/es-query").QueryStringFilter; + buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter; + isPhraseFilter: (filter: any) => filter is import("@kbn/es-query").PhraseFilter; + isExistsFilter: (filter: any) => filter is import("@kbn/es-query").ExistsFilter; + isPhrasesFilter: (filter: any) => filter is import("@kbn/es-query").PhrasesFilter; + isRangeFilter: (filter: any) => filter is import("@kbn/es-query").RangeFilter; + isMatchAllFilter: (filter: any) => filter is import("@kbn/es-query").MatchAllFilter; + isMissingFilter: (filter: any) => filter is import("@kbn/es-query").MissingFilter; + isQueryStringFilter: (filter: any) => filter is import("@kbn/es-query").QueryStringFilter; + isFilterPinned: (filter: import("@kbn/es-query").Filter) => boolean | undefined; + toggleFilterNegated: (filter: import("@kbn/es-query").Filter) => { meta: { negate: boolean; alias: string | null; @@ -39,20 +39,20 @@ esFilters: { params?: any; value?: string | undefined; }; - $state?: import("../common").FilterState | undefined; + $state?: import("@kbn/es-query/target_types/filters/types").FilterState | undefined; query?: any; }; - disableFilter: (filter: import("../common").Filter) => import("../common").Filter; - getPhraseFilterField: (filter: import("../common").PhraseFilter) => string; - getPhraseFilterValue: (filter: import("../common").PhraseFilter) => string | number | boolean; + disableFilter: (filter: import("@kbn/es-query").Filter) => import("@kbn/es-query").Filter; + getPhraseFilterField: (filter: import("@kbn/es-query").PhraseFilter) => string; + getPhraseFilterValue: (filter: import("@kbn/es-query").PhraseFilter) => string | number | boolean; getDisplayValueFromFilter: typeof getDisplayValueFromFilter; - compareFilters: (first: import("../common").Filter | import("../common").Filter[], second: import("../common").Filter | import("../common").Filter[], comparatorOptions?: import("../common").FilterCompareOptions) => boolean; + compareFilters: (first: import("@kbn/es-query").Filter | import("@kbn/es-query").Filter[], second: import("@kbn/es-query").Filter | import("@kbn/es-query").Filter[], comparatorOptions?: import("../common").FilterCompareOptions) => boolean; COMPARE_ALL_OPTIONS: import("../common").FilterCompareOptions; generateFilters: typeof generateFilters; - onlyDisabledFiltersChanged: (newFilters?: import("../common").Filter[] | undefined, oldFilters?: import("../common").Filter[] | undefined) => boolean; + onlyDisabledFiltersChanged: (newFilters?: import("@kbn/es-query").Filter[] | undefined, oldFilters?: import("@kbn/es-query").Filter[] | undefined) => boolean; changeTimeFilter: typeof changeTimeFilter; convertRangeFilterToTimeRangeString: typeof convertRangeFilterToTimeRangeString; - mapAndFlattenFilters: (filters: import("../common").Filter[]) => import("../common").Filter[]; + mapAndFlattenFilters: (filters: import("@kbn/es-query").Filter[]) => import("@kbn/es-query").Filter[]; extractTimeFilter: typeof extractTimeFilter; extractTimeRange: typeof extractTimeRange; } diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md index 332114e637586..6ed9898ddd718 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md @@ -4,12 +4,17 @@ ## esKuery variable +> Warning: This API is now obsolete. +> +> Please import helpers from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> + Signature: ```typescript esKuery: { - nodeTypes: import("../common/es_query/kuery/node_types").NodeTypes; - fromKueryExpression: (expression: any, parseOptions?: Partial) => import("../common").KueryNode; - toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IndexPatternBase | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; + nodeTypes: import("@kbn/es-query/target_types/kuery/node_types").NodeTypes; + fromKueryExpression: (expression: any, parseOptions?: Partial | undefined) => import("@kbn/es-query").KueryNode; + toElasticsearchQuery: (node: import("@kbn/es-query").KueryNode, indexPattern?: import("@kbn/es-query").IndexPatternBase | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; } ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esquery.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esquery.md index 0bc9c0c12fc3a..fa2ee4faa7466 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esquery.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esquery.md @@ -4,19 +4,24 @@ ## esQuery variable +> Warning: This API is now obsolete. +> +> Please import helpers from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> + Signature: ```typescript esQuery: { - buildEsQuery: typeof buildEsQuery; + buildEsQuery: typeof import("@kbn/es-query").buildEsQuery; getEsQueryConfig: typeof getEsQueryConfig; - buildQueryFromFilters: (filters: import("../common").Filter[] | undefined, indexPattern: import("../common").IndexPatternBase | undefined, ignoreFilterIfFieldNotInIndex?: boolean) => { + buildQueryFromFilters: (filters: import("@kbn/es-query").Filter[] | undefined, indexPattern: import("@kbn/es-query").IndexPatternBase | undefined, ignoreFilterIfFieldNotInIndex?: boolean | undefined) => { must: never[]; - filter: import("../common").Filter[]; + filter: import("@kbn/es-query").Filter[]; should: never[]; - must_not: import("../common").Filter[]; + must_not: import("@kbn/es-query").Filter[]; }; - luceneStringToDsl: typeof luceneStringToDsl; - decorateQuery: typeof decorateQuery; + luceneStringToDsl: typeof import("@kbn/es-query").luceneStringToDsl; + decorateQuery: typeof import("@kbn/es-query").decorateQuery; } ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.allowleadingwildcards.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.allowleadingwildcards.md deleted file mode 100644 index 71eb23ac6299b..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.allowleadingwildcards.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) > [allowLeadingWildcards](./kibana-plugin-plugins-data-public.esqueryconfig.allowleadingwildcards.md) - -## EsQueryConfig.allowLeadingWildcards property - -Signature: - -```typescript -allowLeadingWildcards: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.dateformattz.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.dateformattz.md deleted file mode 100644 index e9c4c26878a97..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.dateformattz.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) > [dateFormatTZ](./kibana-plugin-plugins-data-public.esqueryconfig.dateformattz.md) - -## EsQueryConfig.dateFormatTZ property - -Signature: - -```typescript -dateFormatTZ?: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.ignorefilteriffieldnotinindex.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.ignorefilteriffieldnotinindex.md deleted file mode 100644 index 9f765c51d0a69..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.ignorefilteriffieldnotinindex.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) > [ignoreFilterIfFieldNotInIndex](./kibana-plugin-plugins-data-public.esqueryconfig.ignorefilteriffieldnotinindex.md) - -## EsQueryConfig.ignoreFilterIfFieldNotInIndex property - -Signature: - -```typescript -ignoreFilterIfFieldNotInIndex: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.md index 5252f8058b488..4480329c2df19 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.md @@ -2,20 +2,15 @@ [Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) -## EsQueryConfig interface +## EsQueryConfig type + +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> Signature: ```typescript -export interface EsQueryConfig +declare type EsQueryConfig = oldEsQueryConfig; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [allowLeadingWildcards](./kibana-plugin-plugins-data-public.esqueryconfig.allowleadingwildcards.md) | boolean | | -| [dateFormatTZ](./kibana-plugin-plugins-data-public.esqueryconfig.dateformattz.md) | string | | -| [ignoreFilterIfFieldNotInIndex](./kibana-plugin-plugins-data-public.esqueryconfig.ignorefilteriffieldnotinindex.md) | boolean | | -| [queryStringOptions](./kibana-plugin-plugins-data-public.esqueryconfig.querystringoptions.md) | Record<string, any> | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.querystringoptions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.querystringoptions.md deleted file mode 100644 index feaa8f1821e30..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esqueryconfig.querystringoptions.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) > [queryStringOptions](./kibana-plugin-plugins-data-public.esqueryconfig.querystringoptions.md) - -## EsQueryConfig.queryStringOptions property - -Signature: - -```typescript -queryStringOptions: Record; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.existsfilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.existsfilter.md index f1279934db84c..ab756295eac8c 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.existsfilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.existsfilter.md @@ -4,11 +4,13 @@ ## ExistsFilter type +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> + Signature: ```typescript -export declare type ExistsFilter = Filter & { - meta: ExistsFilterMeta; - exists?: FilterExistsProperty; -}; +declare type ExistsFilter = oldExistsFilter; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.md index 9212b757e07df..bf8d4ced016a3 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filter.md @@ -4,12 +4,13 @@ ## Filter type +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> + Signature: ```typescript -export declare type Filter = { - $state?: FilterState; - meta: FilterMeta; - query?: any; -}; +declare type Filter = oldFilter; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.extract.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.extract.md new file mode 100644 index 0000000000000..60ea060cf6323 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.extract.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FilterManager](./kibana-plugin-plugins-data-public.filtermanager.md) > [extract](./kibana-plugin-plugins-data-public.filtermanager.extract.md) + +## FilterManager.extract property + +Signature: + +```typescript +extract: any; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.getallmigrations.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.getallmigrations.md new file mode 100644 index 0000000000000..0d46d806f0563 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.getallmigrations.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FilterManager](./kibana-plugin-plugins-data-public.filtermanager.md) > [getAllMigrations](./kibana-plugin-plugins-data-public.filtermanager.getallmigrations.md) + +## FilterManager.getAllMigrations property + +Signature: + +```typescript +getAllMigrations: () => {}; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.inject.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.inject.md new file mode 100644 index 0000000000000..0e3b84cd3cf80 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.inject.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FilterManager](./kibana-plugin-plugins-data-public.filtermanager.md) > [inject](./kibana-plugin-plugins-data-public.filtermanager.inject.md) + +## FilterManager.inject property + +Signature: + +```typescript +inject: any; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.migratetolatest.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.migratetolatest.md new file mode 100644 index 0000000000000..2235c55947865 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.migratetolatest.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FilterManager](./kibana-plugin-plugins-data-public.filtermanager.md) > [migrateToLatest](./kibana-plugin-plugins-data-public.filtermanager.migratetolatest.md) + +## FilterManager.migrateToLatest property + +Signature: + +```typescript +migrateToLatest: any; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.telemetry.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.telemetry.md new file mode 100644 index 0000000000000..bab6452c34903 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.filtermanager.telemetry.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FilterManager](./kibana-plugin-plugins-data-public.filtermanager.md) > [telemetry](./kibana-plugin-plugins-data-public.filtermanager.telemetry.md) + +## FilterManager.telemetry property + +Signature: + +```typescript +telemetry: (filters: import("../../../../kibana_utils/common/persistable_state").SerializableState, collector: unknown) => {}; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md index 3969a97fa7789..7fd1914d1a4a5 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md @@ -10,7 +10,7 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: { forceNow?: Date; fieldName?: string; -}): import("../..").RangeFilter | undefined; +}): import("@kbn/es-query").RangeFilter | undefined; ``` ## Parameters @@ -23,5 +23,5 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRan Returns: -`import("../..").RangeFilter | undefined` +`import("@kbn/es-query").RangeFilter | undefined` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.md index 7e6ea86d7f3e8..d5d8a0b62d3c5 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.md @@ -2,18 +2,15 @@ [Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IFieldSubType](./kibana-plugin-plugins-data-public.ifieldsubtype.md) -## IFieldSubType interface +## IFieldSubType type + +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> Signature: ```typescript -export interface IFieldSubType +declare type IFieldSubType = oldIFieldSubType; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [multi](./kibana-plugin-plugins-data-public.ifieldsubtype.multi.md) | {
parent: string;
} | | -| [nested](./kibana-plugin-plugins-data-public.ifieldsubtype.nested.md) | {
path: string;
} | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.multi.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.multi.md deleted file mode 100644 index 6cfc6f037d013..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.multi.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IFieldSubType](./kibana-plugin-plugins-data-public.ifieldsubtype.md) > [multi](./kibana-plugin-plugins-data-public.ifieldsubtype.multi.md) - -## IFieldSubType.multi property - -Signature: - -```typescript -multi?: { - parent: string; - }; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.nested.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.nested.md deleted file mode 100644 index f9308b90a1b96..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ifieldsubtype.nested.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IFieldSubType](./kibana-plugin-plugins-data-public.ifieldsubtype.md) > [nested](./kibana-plugin-plugins-data-public.ifieldsubtype.nested.md) - -## IFieldSubType.nested property - -Signature: - -```typescript -nested?: { - path: string; - }; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md index 16546ceca958d..dc206ceabefe2 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md @@ -37,7 +37,7 @@ export declare class IndexPatternField implements IFieldType | [searchable](./kibana-plugin-plugins-data-public.indexpatternfield.searchable.md) | | boolean | | | [sortable](./kibana-plugin-plugins-data-public.indexpatternfield.sortable.md) | | boolean | | | [spec](./kibana-plugin-plugins-data-public.indexpatternfield.spec.md) | | FieldSpec | | -| [subType](./kibana-plugin-plugins-data-public.indexpatternfield.subtype.md) | | import("../..").IFieldSubType | undefined | | +| [subType](./kibana-plugin-plugins-data-public.indexpatternfield.subtype.md) | | import("@kbn/es-query").IFieldSubType | undefined | | | [type](./kibana-plugin-plugins-data-public.indexpatternfield.type.md) | | string | | | [visualizable](./kibana-plugin-plugins-data-public.indexpatternfield.visualizable.md) | | boolean | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.subtype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.subtype.md index 6cd5247291602..f5e25e3191f72 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.subtype.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.subtype.md @@ -7,5 +7,5 @@ Signature: ```typescript -get subType(): import("../..").IFieldSubType | undefined; +get subType(): import("@kbn/es-query").IFieldSubType | undefined; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tojson.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tojson.md index b77f3d1f374fb..9afcef6afed3a 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tojson.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tojson.md @@ -19,7 +19,7 @@ toJSON(): { searchable: boolean; aggregatable: boolean; readFromDocValues: boolean; - subType: import("../..").IFieldSubType | undefined; + subType: import("@kbn/es-query").IFieldSubType | undefined; customLabel: string | undefined; }; ``` @@ -37,7 +37,7 @@ toJSON(): { searchable: boolean; aggregatable: boolean; readFromDocValues: boolean; - subType: import("../..").IFieldSubType | undefined; + subType: import("@kbn/es-query").IFieldSubType | undefined; customLabel: string | undefined; }` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilter.md index f1916e89c2c98..2848e20edde1b 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilter.md @@ -4,8 +4,13 @@ ## isFilter variable +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> + Signature: ```typescript -isFilter: (x: unknown) => x is Filter +isFilter: (x: unknown) => x is oldFilter ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilters.md index 558da72cc26bb..881d50b8a49e1 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilters.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isfilters.md @@ -4,8 +4,13 @@ ## isFilters variable +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> + Signature: ```typescript -isFilters: (x: unknown) => x is Filter[] +isFilters: (x: unknown) => x is oldFilter[] ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.kuerynode.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.kuerynode.md index 276f25da8cb9f..9cea144ff9d46 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.kuerynode.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.kuerynode.md @@ -2,17 +2,15 @@ [Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [KueryNode](./kibana-plugin-plugins-data-public.kuerynode.md) -## KueryNode interface +## KueryNode type + +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> Signature: ```typescript -export interface KueryNode +declare type KueryNode = oldKueryNode; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [type](./kibana-plugin-plugins-data-public.kuerynode.type.md) | keyof NodeTypes | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.kuerynode.type.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.kuerynode.type.md deleted file mode 100644 index 2ff96b6421c2e..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.kuerynode.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [KueryNode](./kibana-plugin-plugins-data-public.kuerynode.md) > [type](./kibana-plugin-plugins-data-public.kuerynode.type.md) - -## KueryNode.type property - -Signature: - -```typescript -type: keyof NodeTypes; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.matchallfilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.matchallfilter.md index 740b83bb5c563..39ae82865808c 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.matchallfilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.matchallfilter.md @@ -4,11 +4,13 @@ ## MatchAllFilter type +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> + Signature: ```typescript -export declare type MatchAllFilter = Filter & { - meta: MatchAllFilterMeta; - match_all: any; -}; +declare type MatchAllFilter = oldMatchAllFilter; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 7c2911875ee05..e60e26bcb503e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -62,11 +62,9 @@ | [DataPublicPluginStart](./kibana-plugin-plugins-data-public.datapublicpluginstart.md) | Data plugin public Start contract | | [DataPublicPluginStartActions](./kibana-plugin-plugins-data-public.datapublicpluginstartactions.md) | utilities to generate filters from action context | | [DataPublicPluginStartUi](./kibana-plugin-plugins-data-public.datapublicpluginstartui.md) | Data plugin prewired UI components | -| [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) | | | [FieldFormatConfig](./kibana-plugin-plugins-data-public.fieldformatconfig.md) | | | [IDataPluginServices](./kibana-plugin-plugins-data-public.idatapluginservices.md) | | | [IEsSearchRequest](./kibana-plugin-plugins-data-public.iessearchrequest.md) | | -| [IFieldSubType](./kibana-plugin-plugins-data-public.ifieldsubtype.md) | | | [IFieldType](./kibana-plugin-plugins-data-public.ifieldtype.md) | | | [IIndexPattern](./kibana-plugin-plugins-data-public.iindexpattern.md) | | | [IIndexPatternFieldList](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.md) | | @@ -79,7 +77,6 @@ | [ISearchSetup](./kibana-plugin-plugins-data-public.isearchsetup.md) | The setup contract exposed by the Search plugin exposes the search strategy extension point. | | [ISearchStart](./kibana-plugin-plugins-data-public.isearchstart.md) | search service | | [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) | high level search service | -| [KueryNode](./kibana-plugin-plugins-data-public.kuerynode.md) | | | [OptionedValueProp](./kibana-plugin-plugins-data-public.optionedvalueprop.md) | | | [QueryState](./kibana-plugin-plugins-data-public.querystate.md) | All query state service state | | [QueryStateChange](./kibana-plugin-plugins-data-public.querystatechange.md) | | @@ -87,7 +84,6 @@ | [QuerySuggestionBasic](./kibana-plugin-plugins-data-public.querysuggestionbasic.md) | \* | | [QuerySuggestionField](./kibana-plugin-plugins-data-public.querysuggestionfield.md) | \* | | [QuerySuggestionGetFnArgs](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md) | \* | -| [RangeFilterParams](./kibana-plugin-plugins-data-public.rangefilterparams.md) | | | [Reason](./kibana-plugin-plugins-data-public.reason.md) | | | [RefreshInterval](./kibana-plugin-plugins-data-public.refreshinterval.md) | | | [SavedQuery](./kibana-plugin-plugins-data-public.savedquery.md) | | @@ -151,6 +147,7 @@ | [CustomFilter](./kibana-plugin-plugins-data-public.customfilter.md) | | | [EsaggsExpressionFunctionDefinition](./kibana-plugin-plugins-data-public.esaggsexpressionfunctiondefinition.md) | | | [EsdslExpressionFunctionDefinition](./kibana-plugin-plugins-data-public.esdslexpressionfunctiondefinition.md) | | +| [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) | | | [EsQuerySortValue](./kibana-plugin-plugins-data-public.esquerysortvalue.md) | | | [EsRawResponseExpressionTypeDefinition](./kibana-plugin-plugins-data-public.esrawresponseexpressiontypedefinition.md) | | | [ExecutionContextSearch](./kibana-plugin-plugins-data-public.executioncontextsearch.md) | | @@ -170,6 +167,7 @@ | [IFieldFormat](./kibana-plugin-plugins-data-public.ifieldformat.md) | | | [IFieldFormatsRegistry](./kibana-plugin-plugins-data-public.ifieldformatsregistry.md) | | | [IFieldParamType](./kibana-plugin-plugins-data-public.ifieldparamtype.md) | | +| [IFieldSubType](./kibana-plugin-plugins-data-public.ifieldsubtype.md) | | | [IMetricAggType](./kibana-plugin-plugins-data-public.imetricaggtype.md) | | | [IndexPatternAggRestrictions](./kibana-plugin-plugins-data-public.indexpatternaggrestrictions.md) | | | [IndexPatternLoadExpressionFunctionDefinition](./kibana-plugin-plugins-data-public.indexpatternloadexpressionfunctiondefinition.md) | | @@ -181,16 +179,17 @@ | [ISessionsClient](./kibana-plugin-plugins-data-public.isessionsclient.md) | | | [ISessionService](./kibana-plugin-plugins-data-public.isessionservice.md) | | | [KibanaContext](./kibana-plugin-plugins-data-public.kibanacontext.md) | | +| [KueryNode](./kibana-plugin-plugins-data-public.kuerynode.md) | | | [MatchAllFilter](./kibana-plugin-plugins-data-public.matchallfilter.md) | | | [ParsedInterval](./kibana-plugin-plugins-data-public.parsedinterval.md) | | | [PhraseFilter](./kibana-plugin-plugins-data-public.phrasefilter.md) | | | [PhrasesFilter](./kibana-plugin-plugins-data-public.phrasesfilter.md) | | -| [Query](./kibana-plugin-plugins-data-public.query.md) | | | [QueryStart](./kibana-plugin-plugins-data-public.querystart.md) | | | [QuerySuggestion](./kibana-plugin-plugins-data-public.querysuggestion.md) | \* | | [QuerySuggestionGetFn](./kibana-plugin-plugins-data-public.querysuggestiongetfn.md) | | | [RangeFilter](./kibana-plugin-plugins-data-public.rangefilter.md) | | | [RangeFilterMeta](./kibana-plugin-plugins-data-public.rangefiltermeta.md) | | +| [RangeFilterParams](./kibana-plugin-plugins-data-public.rangefilterparams.md) | | | [SavedQueryTimeFilter](./kibana-plugin-plugins-data-public.savedquerytimefilter.md) | | | [SearchBarProps](./kibana-plugin-plugins-data-public.searchbarprops.md) | | | [StatefulSearchBarProps](./kibana-plugin-plugins-data-public.statefulsearchbarprops.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasefilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasefilter.md index 8d0447d58634c..ca38ac25dcf50 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasefilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasefilter.md @@ -4,17 +4,13 @@ ## PhraseFilter type +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> + Signature: ```typescript -export declare type PhraseFilter = Filter & { - meta: PhraseFilterMeta; - script?: { - script: { - source?: any; - lang?: estypes.ScriptLanguage; - params: any; - }; - }; -}; +declare type PhraseFilter = oldPhraseFilter; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasesfilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasesfilter.md index ab205cb62fd14..0c293cb909276 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasesfilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.phrasesfilter.md @@ -4,10 +4,13 @@ ## PhrasesFilter type +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> + Signature: ```typescript -export declare type PhrasesFilter = Filter & { - meta: PhrasesFilterMeta; -}; +declare type PhrasesFilter = oldPhrasesFilter; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.md deleted file mode 100644 index e15b04236a0b5..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.query.md +++ /dev/null @@ -1,16 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Query](./kibana-plugin-plugins-data-public.query.md) - -## Query type - -Signature: - -```typescript -export declare type Query = { - query: string | { - [key: string]: any; - }; - language: string; -}; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilter.md index 1cb627ec3a8f9..3d9af100a707a 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilter.md @@ -4,18 +4,13 @@ ## RangeFilter type +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> + Signature: ```typescript -export declare type RangeFilter = Filter & EsRangeFilter & { - meta: RangeFilterMeta; - script?: { - script: { - params: any; - lang: estypes.ScriptLanguage; - source: any; - }; - }; - match_all?: any; -}; +declare type RangeFilter = oldRangeFilter; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefiltermeta.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefiltermeta.md index 609e98cb6faa8..4060a71e62cd0 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefiltermeta.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefiltermeta.md @@ -4,12 +4,13 @@ ## RangeFilterMeta type +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> + Signature: ```typescript -export declare type RangeFilterMeta = FilterMeta & { - params: RangeFilterParams; - field?: any; - formattedValue?: string; -}; +declare type RangeFilterMeta = oldRangeFilterMeta; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.format.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.format.md deleted file mode 100644 index 15926481923ab..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.format.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RangeFilterParams](./kibana-plugin-plugins-data-public.rangefilterparams.md) > [format](./kibana-plugin-plugins-data-public.rangefilterparams.format.md) - -## RangeFilterParams.format property - -Signature: - -```typescript -format?: string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.from.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.from.md deleted file mode 100644 index 99b8d75e9c316..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.from.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RangeFilterParams](./kibana-plugin-plugins-data-public.rangefilterparams.md) > [from](./kibana-plugin-plugins-data-public.rangefilterparams.from.md) - -## RangeFilterParams.from property - -Signature: - -```typescript -from?: number | string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.gt.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.gt.md deleted file mode 100644 index 32bfc6eeb68cb..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.gt.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RangeFilterParams](./kibana-plugin-plugins-data-public.rangefilterparams.md) > [gt](./kibana-plugin-plugins-data-public.rangefilterparams.gt.md) - -## RangeFilterParams.gt property - -Signature: - -```typescript -gt?: number | string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.gte.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.gte.md deleted file mode 100644 index 81345e4a81610..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.gte.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RangeFilterParams](./kibana-plugin-plugins-data-public.rangefilterparams.md) > [gte](./kibana-plugin-plugins-data-public.rangefilterparams.gte.md) - -## RangeFilterParams.gte property - -Signature: - -```typescript -gte?: number | string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.lt.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.lt.md deleted file mode 100644 index 6250fecfe59d6..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.lt.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RangeFilterParams](./kibana-plugin-plugins-data-public.rangefilterparams.md) > [lt](./kibana-plugin-plugins-data-public.rangefilterparams.lt.md) - -## RangeFilterParams.lt property - -Signature: - -```typescript -lt?: number | string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.lte.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.lte.md deleted file mode 100644 index c4f3cbf00b304..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.lte.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RangeFilterParams](./kibana-plugin-plugins-data-public.rangefilterparams.md) > [lte](./kibana-plugin-plugins-data-public.rangefilterparams.lte.md) - -## RangeFilterParams.lte property - -Signature: - -```typescript -lte?: number | string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.md index 977559f5e6cb2..cdf237ea5a1ec 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.md @@ -2,23 +2,15 @@ [Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RangeFilterParams](./kibana-plugin-plugins-data-public.rangefilterparams.md) -## RangeFilterParams interface +## RangeFilterParams type + +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> Signature: ```typescript -export interface RangeFilterParams +declare type RangeFilterParams = oldRangeFilterParams; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [format](./kibana-plugin-plugins-data-public.rangefilterparams.format.md) | string | | -| [from](./kibana-plugin-plugins-data-public.rangefilterparams.from.md) | number | string | | -| [gt](./kibana-plugin-plugins-data-public.rangefilterparams.gt.md) | number | string | | -| [gte](./kibana-plugin-plugins-data-public.rangefilterparams.gte.md) | number | string | | -| [lt](./kibana-plugin-plugins-data-public.rangefilterparams.lt.md) | number | string | | -| [lte](./kibana-plugin-plugins-data-public.rangefilterparams.lte.md) | number | string | | -| [to](./kibana-plugin-plugins-data-public.rangefilterparams.to.md) | number | string | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.to.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.to.md deleted file mode 100644 index c9d0069fb75f5..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.rangefilterparams.to.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RangeFilterParams](./kibana-plugin-plugins-data-public.rangefilterparams.md) > [to](./kibana-plugin-plugins-data-public.rangefilterparams.to.md) - -## RangeFilterParams.to property - -Signature: - -```typescript -to?: number | string; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.md index 981d956a9e89b..22dc6fa9f627b 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.md @@ -25,7 +25,7 @@ export interface SearchSourceFields | [highlightAll](./kibana-plugin-plugins-data-public.searchsourcefields.highlightall.md) | boolean | | | [index](./kibana-plugin-plugins-data-public.searchsourcefields.index.md) | IndexPattern | | | [parent](./kibana-plugin-plugins-data-public.searchsourcefields.parent.md) | SearchSourceFields | | -| [query](./kibana-plugin-plugins-data-public.searchsourcefields.query.md) | Query | [Query](./kibana-plugin-plugins-data-public.query.md) | +| [query](./kibana-plugin-plugins-data-public.searchsourcefields.query.md) | Query | | | [searchAfter](./kibana-plugin-plugins-data-public.searchsourcefields.searchafter.md) | EsQuerySearchAfter | | | [size](./kibana-plugin-plugins-data-public.searchsourcefields.size.md) | number | | | [sort](./kibana-plugin-plugins-data-public.searchsourcefields.sort.md) | EsQuerySortValue | EsQuerySortValue[] | [EsQuerySortValue](./kibana-plugin-plugins-data-public.esquerysortvalue.md) | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.query.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.query.md index 661ce94a06afb..78bf800c58c20 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.query.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.query.md @@ -4,7 +4,6 @@ ## SearchSourceFields.query property -[Query](./kibana-plugin-plugins-data-public.query.md) Signature: diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md index 594afcf9ee0dd..9006b088993a1 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esfilters.md @@ -8,14 +8,14 @@ ```typescript esFilters: { - buildQueryFilter: (query: any, index: string, alias: string) => import("../common").QueryStringFilter; - buildCustomFilter: typeof buildCustomFilter; - buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("../common").Filter; - buildExistsFilter: (field: import("../common").IndexPatternFieldBase, indexPattern: import("../common").IndexPatternBase) => import("../common").ExistsFilter; - buildFilter: typeof buildFilter; - buildPhraseFilter: (field: import("../common").IndexPatternFieldBase, value: any, indexPattern: import("../common").IndexPatternBase) => import("../common").PhraseFilter; - buildPhrasesFilter: (field: import("../common").IndexPatternFieldBase, params: any[], indexPattern: import("../common").IndexPatternBase) => import("../common").PhrasesFilter; - buildRangeFilter: (field: import("../common").IndexPatternFieldBase, params: import("../common").RangeFilterParams, indexPattern: import("../common").IndexPatternBase, formattedValue?: string | undefined) => import("../common").RangeFilter; - isFilterDisabled: (filter: import("../common").Filter) => boolean; + buildQueryFilter: (query: any, index: string, alias: string) => import("@kbn/es-query").QueryStringFilter; + buildCustomFilter: typeof import("@kbn/es-query").buildCustomFilter; + buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("@kbn/es-query").Filter; + buildExistsFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").ExistsFilter; + buildFilter: typeof import("@kbn/es-query").buildFilter; + buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: any, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter; + buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: any[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; + buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter; + isFilterDisabled: (filter: import("@kbn/es-query").Filter) => boolean; } ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.eskuery.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.eskuery.md index fce25a899de8e..4989b2b5ad584 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.eskuery.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.eskuery.md @@ -8,8 +8,8 @@ ```typescript esKuery: { - nodeTypes: import("../common/es_query/kuery/node_types").NodeTypes; - fromKueryExpression: (expression: any, parseOptions?: Partial) => import("../common").KueryNode; - toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IndexPatternBase | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; + nodeTypes: import("@kbn/es-query/target_types/kuery/node_types").NodeTypes; + fromKueryExpression: (expression: any, parseOptions?: Partial | undefined) => import("@kbn/es-query").KueryNode; + toElasticsearchQuery: (node: import("@kbn/es-query").KueryNode, indexPattern?: import("@kbn/es-query").IndexPatternBase | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; } ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esquery.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esquery.md index 68507f3fb9b81..8dfea00081d89 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esquery.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esquery.md @@ -8,13 +8,13 @@ ```typescript esQuery: { - buildQueryFromFilters: (filters: import("../common").Filter[] | undefined, indexPattern: import("../common").IndexPatternBase | undefined, ignoreFilterIfFieldNotInIndex?: boolean) => { + buildQueryFromFilters: (filters: import("@kbn/es-query").Filter[] | undefined, indexPattern: import("@kbn/es-query").IndexPatternBase | undefined, ignoreFilterIfFieldNotInIndex?: boolean | undefined) => { must: never[]; - filter: import("../common").Filter[]; + filter: import("@kbn/es-query").Filter[]; should: never[]; - must_not: import("../common").Filter[]; + must_not: import("@kbn/es-query").Filter[]; }; getEsQueryConfig: typeof getEsQueryConfig; - buildEsQuery: typeof buildEsQuery; + buildEsQuery: typeof import("@kbn/es-query").buildEsQuery; } ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.allowleadingwildcards.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.allowleadingwildcards.md deleted file mode 100644 index ce8303d720747..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.allowleadingwildcards.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) > [allowLeadingWildcards](./kibana-plugin-plugins-data-server.esqueryconfig.allowleadingwildcards.md) - -## EsQueryConfig.allowLeadingWildcards property - -Signature: - -```typescript -allowLeadingWildcards: boolean; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.dateformattz.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.dateformattz.md deleted file mode 100644 index d3e86f19709f8..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.dateformattz.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) > [dateFormatTZ](./kibana-plugin-plugins-data-server.esqueryconfig.dateformattz.md) - -## EsQueryConfig.dateFormatTZ property - -Signature: - -```typescript -dateFormatTZ?: string; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.ignorefilteriffieldnotinindex.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.ignorefilteriffieldnotinindex.md deleted file mode 100644 index 93b3e8915c482..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.ignorefilteriffieldnotinindex.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) > [ignoreFilterIfFieldNotInIndex](./kibana-plugin-plugins-data-server.esqueryconfig.ignorefilteriffieldnotinindex.md) - -## EsQueryConfig.ignoreFilterIfFieldNotInIndex property - -Signature: - -```typescript -ignoreFilterIfFieldNotInIndex: boolean; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.md index 9ae604e07cabd..5c736f40cdbf4 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.md @@ -2,20 +2,15 @@ [Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) -## EsQueryConfig interface +## EsQueryConfig type + +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> Signature: ```typescript -export interface EsQueryConfig +declare type EsQueryConfig = oldEsQueryConfig; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [allowLeadingWildcards](./kibana-plugin-plugins-data-server.esqueryconfig.allowleadingwildcards.md) | boolean | | -| [dateFormatTZ](./kibana-plugin-plugins-data-server.esqueryconfig.dateformattz.md) | string | | -| [ignoreFilterIfFieldNotInIndex](./kibana-plugin-plugins-data-server.esqueryconfig.ignorefilteriffieldnotinindex.md) | boolean | | -| [queryStringOptions](./kibana-plugin-plugins-data-server.esqueryconfig.querystringoptions.md) | Record<string, any> | | - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.querystringoptions.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.querystringoptions.md deleted file mode 100644 index 437d36112d015..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.esqueryconfig.querystringoptions.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) > [queryStringOptions](./kibana-plugin-plugins-data-server.esqueryconfig.querystringoptions.md) - -## EsQueryConfig.queryStringOptions property - -Signature: - -```typescript -queryStringOptions: Record; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.md index 519bbaf8f9416..f46ff36277d93 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.filter.md @@ -4,12 +4,13 @@ ## Filter type +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> + Signature: ```typescript -export declare type Filter = { - $state?: FilterState; - meta: FilterMeta; - query?: any; -}; +declare type Filter = oldFilter; ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.gettime.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.gettime.md index 54e7cf92f500c..7f2267aff7049 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.gettime.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.gettime.md @@ -10,7 +10,7 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: { forceNow?: Date; fieldName?: string; -}): import("../..").RangeFilter | undefined; +}): import("@kbn/es-query").RangeFilter | undefined; ``` ## Parameters @@ -23,5 +23,5 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRan Returns: -`import("../..").RangeFilter | undefined` +`import("@kbn/es-query").RangeFilter | undefined` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.md index 70140e51a7316..e8e872577b46b 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.md @@ -2,18 +2,15 @@ [Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IFieldSubType](./kibana-plugin-plugins-data-server.ifieldsubtype.md) -## IFieldSubType interface +## IFieldSubType type + +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> Signature: ```typescript -export interface IFieldSubType +declare type IFieldSubType = oldIFieldSubType; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [multi](./kibana-plugin-plugins-data-server.ifieldsubtype.multi.md) | {
parent: string;
} | | -| [nested](./kibana-plugin-plugins-data-server.ifieldsubtype.nested.md) | {
path: string;
} | | - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.multi.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.multi.md deleted file mode 100644 index 31a3bc53d6343..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.multi.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IFieldSubType](./kibana-plugin-plugins-data-server.ifieldsubtype.md) > [multi](./kibana-plugin-plugins-data-server.ifieldsubtype.multi.md) - -## IFieldSubType.multi property - -Signature: - -```typescript -multi?: { - parent: string; - }; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.nested.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.nested.md deleted file mode 100644 index b53a4406aedc2..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ifieldsubtype.nested.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IFieldSubType](./kibana-plugin-plugins-data-server.ifieldsubtype.md) > [nested](./kibana-plugin-plugins-data-server.ifieldsubtype.nested.md) - -## IFieldSubType.nested property - -Signature: - -```typescript -nested?: { - path: string; - }; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.kuerynode.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.kuerynode.md index 3a258a5b98616..a5c14ee8627b1 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.kuerynode.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.kuerynode.md @@ -2,17 +2,15 @@ [Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [KueryNode](./kibana-plugin-plugins-data-server.kuerynode.md) -## KueryNode interface +## KueryNode type + +> Warning: This API is now obsolete. +> +> Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. +> Signature: ```typescript -export interface KueryNode +declare type KueryNode = oldKueryNode; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [type](./kibana-plugin-plugins-data-server.kuerynode.type.md) | keyof NodeTypes | | - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.kuerynode.type.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.kuerynode.type.md deleted file mode 100644 index 192a2c05191c7..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.kuerynode.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [KueryNode](./kibana-plugin-plugins-data-server.kuerynode.md) > [type](./kibana-plugin-plugins-data-server.kuerynode.type.md) - -## KueryNode.type property - -Signature: - -```typescript -type: keyof NodeTypes; -``` 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 ab14abdd74e87..8e23f47976bd9 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 @@ -48,11 +48,9 @@ | [AggParamOption](./kibana-plugin-plugins-data-server.aggparamoption.md) | | | [AsyncSearchResponse](./kibana-plugin-plugins-data-server.asyncsearchresponse.md) | | | [AsyncSearchStatusResponse](./kibana-plugin-plugins-data-server.asyncsearchstatusresponse.md) | | -| [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) | | | [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) | | | [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) | | | [IEsSearchRequest](./kibana-plugin-plugins-data-server.iessearchrequest.md) | | -| [IFieldSubType](./kibana-plugin-plugins-data-server.ifieldsubtype.md) | | | [IFieldType](./kibana-plugin-plugins-data-server.ifieldtype.md) | | | [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) | Interface for an index pattern saved object | | [IScopedSearchClient](./kibana-plugin-plugins-data-server.iscopedsearchclient.md) | | @@ -61,7 +59,6 @@ | [ISearchSetup](./kibana-plugin-plugins-data-server.isearchsetup.md) | | | [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) | | @@ -97,6 +94,7 @@ | [AggGroupName](./kibana-plugin-plugins-data-server.agggroupname.md) | | | [AggParam](./kibana-plugin-plugins-data-server.aggparam.md) | | | [EsaggsExpressionFunctionDefinition](./kibana-plugin-plugins-data-server.esaggsexpressionfunctiondefinition.md) | | +| [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) | | | [ExecutionContextSearch](./kibana-plugin-plugins-data-server.executioncontextsearch.md) | | | [ExpressionFunctionKibana](./kibana-plugin-plugins-data-server.expressionfunctionkibana.md) | | | [ExpressionFunctionKibanaContext](./kibana-plugin-plugins-data-server.expressionfunctionkibanacontext.md) | | @@ -108,11 +106,12 @@ | [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) | | | [IFieldFormatsRegistry](./kibana-plugin-plugins-data-server.ifieldformatsregistry.md) | | | [IFieldParamType](./kibana-plugin-plugins-data-server.ifieldparamtype.md) | | +| [IFieldSubType](./kibana-plugin-plugins-data-server.ifieldsubtype.md) | | | [IMetricAggType](./kibana-plugin-plugins-data-server.imetricaggtype.md) | | | [IndexPatternLoadExpressionFunctionDefinition](./kibana-plugin-plugins-data-server.indexpatternloadexpressionfunctiondefinition.md) | | | [KibanaContext](./kibana-plugin-plugins-data-server.kibanacontext.md) | | +| [KueryNode](./kibana-plugin-plugins-data-server.kuerynode.md) | | | [ParsedInterval](./kibana-plugin-plugins-data-server.parsedinterval.md) | | -| [Query](./kibana-plugin-plugins-data-server.query.md) | | | [SearchRequestHandlerContext](./kibana-plugin-plugins-data-server.searchrequesthandlercontext.md) | | | [TimeRange](./kibana-plugin-plugins-data-server.timerange.md) | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.md deleted file mode 100644 index 6a7bdfe51f1c0..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.query.md +++ /dev/null @@ -1,16 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [Query](./kibana-plugin-plugins-data-server.query.md) - -## Query type - -Signature: - -```typescript -export declare type Query = { - query: string | { - [key: string]: any; - }; - language: string; -}; -``` diff --git a/docs/index-extra-title-page.html b/docs/index-extra-title-page.html index 7bcbc9f075124..2621848ebea8a 100644 --- a/docs/index-extra-title-page.html +++ b/docs/index-extra-title-page.html @@ -1,37 +1,151 @@
-

From creating beautiful visualizations to managing the Elastic Stack, learn how Kibana helps you get the most of your data.

-

Watch our videos

- - - - - - - - - -

New to Kibana?

Popular topics

- - -
-

All topics

+

+ From creating beautiful visualizations to managing the Elastic Stack, learn how Kibana helps you + get the most of your data. +

+

+ How-to videos +

+ + + + + + + + + + + + + + + + + + +

New to Kibana?

Popular topics

+ + + +

Analyze your data

Manage all things Stack

+ + + +
+ +

All topics

diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc index b49b669a00780..bcc00951de3c1 100644 --- a/docs/redirects.asciidoc +++ b/docs/redirects.asciidoc @@ -318,18 +318,26 @@ This content has moved. Refer to <> and <>. +[role="exclude",id="graph-getting-started"] +== Create a graph + +This content has moved. Refer to <>. + +[role="exclude",id="graph-limitations"] +== Graph limitations + +This content has moved. Refer to <>. + [role="exclude",id="profiler-getting-started"] == Getting start with Search Profiler This content has moved. Refer to <>. - [role="exclude",id="profiler-complicated"] == Profiling a more complicated querying This content has moved. Refer to <>. - [role="exclude",id="profiler-render"] == Rendering pre-captured profiler JSON diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 15abd0fa4ad96..a0611b79aae4c 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -782,13 +782,11 @@ out through *Advanced Settings*. *Default: `true`* | Set this value to true to allow Vega to use any URL to access external data sources and images. When false, Vega can only get data from {es}. *Default: `false`* -a| -`xpack.discoverEnhanced.actions.` +|[[settings-explore-data-in-context]] `xpack.discoverEnhanced.actions.` `exploreDataInContextMenu.enabled` | Enables the *Explore underlying data* option that allows you to open *Discover* from a dashboard panel and view the panel data. *Default: `false`* -a| -`xpack.discoverEnhanced.actions.` +|[[settings-explore-data-in-chart]] `xpack.discoverEnhanced.actions.` `exploreDataInChart.enabled` | Enables you to view the underlying documents in a data series from a dashboard panel. *Default: `false`* diff --git a/docs/user/dashboard/dashboard.asciidoc b/docs/user/dashboard/dashboard.asciidoc index 516f4c66d47bb..c251ce7307968 100644 --- a/docs/user/dashboard/dashboard.asciidoc +++ b/docs/user/dashboard/dashboard.asciidoc @@ -265,33 +265,19 @@ Copy panels from one dashboard to another dashboard. [[explore-the-underlying-documents]] == Explore the underlying documents -To gain insight to the data, open the underlying panel or data series documents in *Discover*. The panel documents that you open in *Discover* have the same time range and filters as the source panel. +You can add additional interactions that allow you to open *Discover* from dashboard panels. To use the interactions, the panel must use only one index pattern. -[float] -[[explore-underlying-panel-documents]] -=== Explore the underlying panel documents - -When your visualization panel contains a single index pattern, you can open the panel documents in *Discover*. - -. Open the panel menu. - -. Click *Explore underlying data*. +Panel interaction:: Opens the data in *Discover* with the current dashboard filters, but does not take the filters +saved with the panel. + -[role="screenshot"] -image::images/explore_data_context_menu.png[Explore underlying data from panel context menu] +To enable panel interactions, refere to <>. -[float] -[[explore-underlying-data-series-documents]] -=== Explore the underlying data series documents - -To gain insight to a data series, open the documents in *Discover*. - -. Click the data series in the panel that you want to view. - -. Select *Explore underlying data*. +Series interaction:: +Opens the series data in *Discover* from inside the panel. + -[role="screenshot"] -image::images/explore_data_in_chart.png[Explore underlying data from chart] +To enable series interactions, refer to <>. + +NOTE: In {kib} 7.13 and earlier, the panel interaction was enabled by default. [float] [[download-csv]] diff --git a/docs/user/dashboard/images/explore_data_context_menu.png b/docs/user/dashboard/images/explore_data_context_menu.png deleted file mode 100644 index 5742991030c89..0000000000000 Binary files a/docs/user/dashboard/images/explore_data_context_menu.png and /dev/null differ diff --git a/docs/user/dashboard/images/explore_data_in_chart.png b/docs/user/dashboard/images/explore_data_in_chart.png deleted file mode 100644 index 05d4f5fac9b2f..0000000000000 Binary files a/docs/user/dashboard/images/explore_data_in_chart.png and /dev/null differ diff --git a/docs/user/graph/configuring-graph.asciidoc b/docs/user/graph/configuring-graph.asciidoc index 4eb8939b004ba..968e08db33d49 100644 --- a/docs/user/graph/configuring-graph.asciidoc +++ b/docs/user/graph/configuring-graph.asciidoc @@ -55,7 +55,7 @@ is displayed. For more information on granting access to Kibana, see <>. [role="screenshot"] -image::user/graph/images/graph-read-only-badge.png[Example of Graph's read only access indicator in Kibana's header] +image::user/graph/images/graph-read-only-badge.png[Example of Graph's read only access indicator in Kibana's header, width=50%] [discrete] [[disable-drill-down]] diff --git a/docs/user/graph/images/graph-control-bar.png b/docs/user/graph/images/graph-control-bar.png new file mode 100644 index 0000000000000..6dcf0d693d4c2 Binary files /dev/null and b/docs/user/graph/images/graph-control-bar.png differ diff --git a/docs/user/graph/images/graph-link-summary.png b/docs/user/graph/images/graph-link-summary.png deleted file mode 100644 index a3dfdc0f79d96..0000000000000 Binary files a/docs/user/graph/images/graph-link-summary.png and /dev/null differ diff --git a/docs/user/graph/images/graph-menu.png b/docs/user/graph/images/graph-menu.png new file mode 100644 index 0000000000000..5a46bd595baf8 Binary files /dev/null and b/docs/user/graph/images/graph-menu.png differ diff --git a/docs/user/graph/images/graph-url-connections.png b/docs/user/graph/images/graph-url-connections.png index 34b57d489b048..18b1b0354ee51 100644 Binary files a/docs/user/graph/images/graph-url-connections.png and b/docs/user/graph/images/graph-url-connections.png differ diff --git a/docs/user/graph/index.asciidoc b/docs/user/graph/index.asciidoc index 40c75c868e237..5e7b689b8d8f1 100644 --- a/docs/user/graph/index.asciidoc +++ b/docs/user/graph/index.asciidoc @@ -5,7 +5,7 @@ [partintro] -- The {graph-features} enable you to discover how items in an -Elasticsearch index are related. You can explore the connections between +{es} index are related. You can explore the connections between indexed terms and see which connections are the most meaningful. This can be useful in a variety of applications, from fraud detection to recommendation engines. @@ -15,15 +15,15 @@ that hackers are targeting so you can harden your website. Or, you might provide graph-based personalized recommendations to your e-commerce customers. The {graph-features} provide a simple, yet powerful {ref}/graph-explore-api.html[graph exploration API], -and an interactive graph visualization tool for Kibana. Both work out of the -box with existing Elasticsearch indices--you don't need to store any +and an interactive graph visualization app for {kib}. Both work out of the +box with existing {es} indices—you don't need to store any additional data to use these features. [discrete] [[how-graph-works]] == How Graph works The graph API provides an alternative way to extract and summarize information -about the documents and terms in your Elasticsearch index. A _graph_ is really +about the documents and terms in your {es} index. A _graph_ is really just a network of related items. In our case, this means a network of related terms in the index. @@ -37,14 +37,14 @@ image::user/graph/images/graph-vertices-connections.jpg["Graph components"] NOTE: If you're into https://en.wikipedia.org/wiki/Graph_theory[graph theory], you might know vertices and connections as _nodes_ and _edges_. They're the same thing, we just want to use terminology that makes sense to people who -aren't graph geeks and avoid any confusion with the nodes in an Elasticsearch +aren't graph geeks and avoid any confusion with the nodes in an {es} cluster. The graph vertices are simply the terms that you've already indexed. The -connections are derived on the fly using Elasticsearch aggregations. To +connections are derived on the fly using {es} aggregations. To identify the most _meaningful_ connections, the graph API leverages -Elasticsearch relevance scoring. The same data structures and relevance ranking -tools built into Elasticsearch to support text searches enable the graph API to +{es} relevance scoring. The same data structures and relevance ranking +tools built into {es} to support text searches enable the graph API to separate useful signals from the noise that is typical of most connected data. This foundation lets you easily answer questions like: @@ -55,21 +55,143 @@ be interested in? * Which people on Stack Overflow have expertise in both Hadoop-related technologies and Python-related tech? -But what about performance, you ask? The Elasticsearch aggregation framework +But what about performance? The {es} aggregation framework enables the graph API to quickly summarize millions of documents as a single super-connection. Instead of retrieving every banking transaction between accounts A and B, it derives a single connection that represents that relationship. And, of course, this summarization process works across -multi-node clusters and scales with your Elasticsearch deployment. +multi-node clusters and scales with your {es} deployment. Advanced options let you control how your data is sampled and summarized. You can also set timeouts to prevent graph queries from adversely affecting the cluster. --- -include::getting-started.asciidoc[] +[float] +[[graph-connection]] +== Create a graph + +Use *Graph* to reveal the relationships in your data. + +. Open the main menu, and then click *Graph*. ++ +If you're new to {kib}, and don't yet have any data, follow the link to add sample data. +This example uses the {kib} sample web logs data set. + +. Select the data source that you want to explore. ++ +{kib} graphs the relationships between the top fields. ++ +[role="screenshot"] +image::user/graph/images/graph-url-connections.png["URL connections"] + +. Add more fields, or click an existing field to edit, disable or deselect it. ++ +[role="screenshot"] +image::user/graph/images/graph-menu.png["menu for editing, disabling, or removing a field from the graph", width=75%] + + +. Enter a query to discover relationships between terms in the selected +fields. ++ +For example, +to generate a graph of the successful requests to a +particular location, search for the `geo.src` +field. The weight of the connection between two vertices indicates how strongly they +are related. + +. To view more information about a relationship, click any connection or vertex. ++ +[role="screenshot"] +image::user/graph/images/graph-control-bar.png["Graph toolbar", width=50%] + +. Use the graph toolbar to display additional connections: ++ +* To display additional vertices that connect to your graph, click the expand icon +image:user/graph/images/graph-expand-button.png[Expand Selection]. +* To display additional +connections between the displayed vertices, click the link icon +image:user/graph/images/graph-link-button.png[Add links to existing terms]. +* To explore a particular area of the +graph, select the vertices you are interested in, and then click expand or link. +* To step back through your changes to the graph, click undo +image:user/graph/images/graph-undo-button.png[Undo] and redo +image:user/graph/images/graph-redo-button.png[Redo]. + +. To view more relationships in your data, submit additional queries. + +. *Save* your graph. + +[float] +[[graph-customize]] +== Customize your graph + +Apply custom colors and icons to vertices, configure the number of vertices that +a search adds to the graph, block terms, and more. + +[float] +[[style-vertex-properties]] +==== Style vertex properties + +Each vertex has a color, icon, and label. To change +the color or icon of all vertices +of a certain field, click it's field, and then +select *Edit settings*. + +To change the color and label of selected vertices, +click the style icon image:user/graph/images/graph-style-button.png[Style] +in the control bar. + + +[float] +[[edit-graph-settings]] +==== Tune the noise level + +By default, *Graph* is configured to tune out noise in your data. +If this isn't a good fit for your data, open *Settings > Advanced settings*, +and then adjust the way *Graph* queries your data. You can tune the graph to show +only the results relevant to you and to improve performance. +For more information, see <>. + +You can configure the number of vertices that a search or +expand operation adds to the graph. +By default, only the five most relevant terms for any given field are added +at a time. This keeps the graph from overflowing. To increase this number, click +a field, select *Edit Settings*, and change *Terms per hop*. + +[float] +[[graph-block-terms]] +==== Block terms from the graph +Documents that match a blocked term are not allowed in the graph. +To block a term, select its vertex and click +the block icon +image:user/graph/images/graph-block-button.png[Block selection] +in the graph toolbar. +For a list of blocked terms, open *Settings > Blocked terms*. + +[float] +[[graph-drill-down]] +==== Drill down into raw documents +With drilldowns, you can display additional information about a +selected vertex in a new browser window. For example, you might +configure a drilldown URL to perform a web search for the selected vertex term. + +Use the drilldown icon image:user/graph/images/graph-info-icon.png[Drilldown selection] +in the graph toolbar to show the drilldown buttons for the selected vertices. +To configure drilldowns, go to *Settings > Drilldowns*. See also +<>. + +[float] +[[graph-run-layout]] +==== Run and pause the layout +Graph uses a "force layout", where vertices behave like magnets, +pushing off of one another. By default, when you add a new vertex to +the graph, all vertices begin moving. In some cases, the movement might +go on for some time. To freeze the current vertex position, +click the pause icon +image:user/graph/images/graph-pause-button.png[Block selection] +in the graph toolbar. + +-- include::configuring-graph.asciidoc[] include::troubleshooting.asciidoc[] - -include::limitations.asciidoc[] diff --git a/docs/user/graph/limitations.asciidoc b/docs/user/graph/limitations.asciidoc deleted file mode 100644 index e96910bd27b4c..0000000000000 --- a/docs/user/graph/limitations.asciidoc +++ /dev/null @@ -1,27 +0,0 @@ -[role="xpack"] -[[graph-limitations]] -== Graph limitations -++++ -Limitations -++++ - -[discrete] -=== Limited support for multiple indices -The graph API can explore multiple indices, types, or aliases in a -single API request, but the assumption is that each "hop" it performs -is querying the same set of indices. Currently, it is not possible to -take a term found in a field from one index and use that value to explore -connections in _a different field_ held in another type or index. - -A good example of where this might be useful is if an IP address is -found in the `remote_host` field of an index called "weblogs20160101", -you might want to follow that up by looking for the same address in -the `ip_address` field of an index called "knownthreats". - -Supporting this behaviour would require extra mappings to indicate that -the weblogs' `remote_host` field contained values that had currency and -meaning in the `ip_address` field of the threats index. - -Since we do not currently support this translation, you would have to -perform multiple calls to take the values from the weblogs index -response and build them into a separate request to the threats index. diff --git a/docs/user/graph/troubleshooting.asciidoc b/docs/user/graph/troubleshooting.asciidoc index eaac105c57358..f73d9142ff7e2 100644 --- a/docs/user/graph/troubleshooting.asciidoc +++ b/docs/user/graph/troubleshooting.asciidoc @@ -1,8 +1,8 @@ [role="xpack"] [[graph-troubleshooting]] -== Graph troubleshooting +== Graph troubleshooting and limitations ++++ -Troubleshoot +Troubleshooting and limitations ++++ [discrete] @@ -22,7 +22,7 @@ but they can miss details from individual documents. If you need to perform a detailed forensic analysis, you can adjust the following settings to ensure a graph exploration produces all of the relevant data: -* Increase the `sample_size` to a larger number of documents to analyse more +* Increase the `sample_size` to a larger number of documents to analyze more data on each shard. * Set the `use_significance` setting to `false` to retrieve terms regardless of any statistical correlation with the sample. @@ -53,7 +53,28 @@ large documents with your seed and guiding queries. so even increasing the frequency threshold by one can massively reduce the number of candidate terms whose background frequencies are checked. -Keep in mind that all of these options reduce the scope of information analysed +Keep in mind that all of these options reduce the scope of information analyzed and can increase the potential to miss what could be interesting details. However, the information that's lost tends to be associated with lower-quality documents with lower-frequency terms, which can be an acceptable trade-off. + +[discrete] +=== Limited support for multiple indices +The graph API can explore multiple indices, types, or aliases in a +single API request, but the assumption is that each "hop" it performs +is querying the same set of indices. Currently, it is not possible to +take a term found in a field from one index and use that value to explore +connections in _a different field_ held in another type or index. + +A good example of where this might be useful is if an IP address is +found in the `remote_host` field of an index called "weblogs20160101", +you might want to follow that up by looking for the same address in +the `ip_address` field of an index called "knownthreats". + +Supporting this behavior would require extra mappings to indicate that +the weblogs' `remote_host` field contained values that had currency and +meaning in the `ip_address` field of the threats index. + +Since we do not currently support this translation, you would have to +perform multiple calls to take the values from the weblogs index +response and build them into a separate request to the threats index. diff --git a/docs/user/images/app-navigation-search.png b/docs/user/images/app-navigation-search.png index 3b89eed44b28f..9a644909ceb88 100644 Binary files a/docs/user/images/app-navigation-search.png and b/docs/user/images/app-navigation-search.png differ diff --git a/docs/user/images/home-page.png b/docs/user/images/home-page.png deleted file mode 100755 index 9ca4b7f43f427..0000000000000 Binary files a/docs/user/images/home-page.png and /dev/null differ diff --git a/docs/user/images/kibana-main-menu.png b/docs/user/images/kibana-main-menu.png old mode 100755 new mode 100644 index 79e0a3dca8658..7ee18d9844c66 Binary files a/docs/user/images/kibana-main-menu.png and b/docs/user/images/kibana-main-menu.png differ diff --git a/docs/user/images/login-screen.png b/docs/user/images/login-screen.png deleted file mode 100755 index 7a97c952e1039..0000000000000 Binary files a/docs/user/images/login-screen.png and /dev/null differ diff --git a/docs/user/images/roles-and-privileges.png b/docs/user/images/roles-and-privileges.png deleted file mode 100755 index 28bff6d13c871..0000000000000 Binary files a/docs/user/images/roles-and-privileges.png and /dev/null differ diff --git a/docs/user/images/rules-and-connectors.png b/docs/user/images/rules-and-connectors.png index 5cda25b54536f..1a85eeb6c0bc2 100644 Binary files a/docs/user/images/rules-and-connectors.png and b/docs/user/images/rules-and-connectors.png differ diff --git a/docs/user/images/stack-management.png b/docs/user/images/stack-management.png new file mode 100644 index 0000000000000..a0600b53bd836 Binary files /dev/null and b/docs/user/images/stack-management.png differ diff --git a/docs/user/images/tags-search.png b/docs/user/images/tags-search.png old mode 100755 new mode 100644 index 67458200c50d1..5b0134f25282f Binary files a/docs/user/images/tags-search.png and b/docs/user/images/tags-search.png differ diff --git a/docs/user/introduction.asciidoc b/docs/user/introduction.asciidoc index 783fa2b1c521f..dc7b1ecc50f6e 100644 --- a/docs/user/introduction.asciidoc +++ b/docs/user/introduction.asciidoc @@ -59,7 +59,7 @@ and everything you need to visualize and analyze your data. To access all of {kib} features, use the main menu. Open this menu by clicking the -menu icon. To keep the main menu visible at all times, click *Dock navigation*. +menu icon. For a quick reference of all {kib} features, refer to <> [role="screenshot"] @@ -81,10 +81,6 @@ that it ran in, trace the transaction, and check the overall service availabilit * Designed for security analysts, {security-guide}/es-overview.html[*Elastic Security*] provides an overview of the events and alerts from your environment. Elastic Security helps you defend your organization from threats before damage and loss occur. -+ -[role="screenshot"] -image::siem/images/detections-ui.png[Detections view in Elastic Security] - [float] [[visualize-and-analyze]] @@ -165,7 +161,7 @@ guided processes for administering all things Elastic Stack, including data, indices, clusters, alerts, and security. [role="screenshot"] -image::images/intro-management.png[Index Management view in Stack Management] +image::images/stack-management.png[Index Management view in Stack Management] [float] ==== Manage your data, indices, and clusters @@ -429,7 +425,7 @@ the <>. |<> |Share your data -|<>, <> +|<>, <>, <> 2+|*Administer your Kibana instance* diff --git a/package.json b/package.json index 85343fd8023d0..9034638c689a4 100644 --- a/package.json +++ b/package.json @@ -131,6 +131,7 @@ "@kbn/config": "link:bazel-bin/packages/kbn-config", "@kbn/config-schema": "link:bazel-bin/packages/kbn-config-schema", "@kbn/crypto": "link:bazel-bin/packages/kbn-crypto", + "@kbn/es-query": "link:bazel-bin/packages/kbn-es-query", "@kbn/i18n": "link:bazel-bin/packages/kbn-i18n", "@kbn/interpreter": "link:bazel-bin/packages/kbn-interpreter", "@kbn/io-ts-utils": "link:bazel-bin/packages/kbn-io-ts-utils", @@ -153,9 +154,9 @@ "@kbn/securitysolution-utils": "link:bazel-bin/packages/kbn-securitysolution-utils", "@kbn/server-http-tools": "link:bazel-bin/packages/kbn-server-http-tools", "@kbn/server-route-repository": "link:bazel-bin/packages/kbn-server-route-repository", - "@kbn/typed-react-router-config": "link:bazel-bin/packages/kbn-typed-react-router-config", "@kbn/std": "link:bazel-bin/packages/kbn-std", "@kbn/tinymath": "link:bazel-bin/packages/kbn-tinymath", + "@kbn/typed-react-router-config": "link:bazel-bin/packages/kbn-typed-react-router-config", "@kbn/ui-framework": "link:bazel-bin/packages/kbn-ui-framework", "@kbn/ui-shared-deps": "link:bazel-bin/packages/kbn-ui-shared-deps", "@kbn/utility-types": "link:bazel-bin/packages/kbn-utility-types", @@ -321,7 +322,7 @@ "p-retry": "^4.2.0", "papaparse": "^5.2.0", "pdfmake": "^0.1.65", - "peggy": "^1.0.0", + "peggy": "^1.2.0", "pegjs": "0.10.0", "pluralize": "3.1.0", "pngjs": "^3.4.0", @@ -689,7 +690,7 @@ "copy-webpack-plugin": "^6.0.2", "cpy": "^8.1.1", "css-loader": "^3.4.2", - "css-minimizer-webpack-plugin": "^1.3.0", + "cssnano": "^4.1.11", "cypress": "^6.8.0", "cypress-cucumber-preprocessor": "^2.5.2", "cypress-multi-reporters": "^1.4.0", @@ -741,7 +742,11 @@ "grunt-peg": "^2.0.1", "gulp": "4.0.2", "gulp-babel": "^8.0.0", + "gulp-brotli": "^3.0.0", + "gulp-postcss": "^8.0.0", "gulp-sourcemaps": "2.6.5", + "gulp-terser": "^2.0.1", + "gulp-gzip": "^1.4.2", "gulp-zip": "^5.0.2", "has-ansi": "^3.0.0", "hdr-histogram-js": "^1.2.0", @@ -829,6 +834,7 @@ "tempy": "^0.3.0", "terminal-link": "^2.1.1", "terser-webpack-plugin": "^2.1.2", + "terser": "^5.7.1", "ts-loader": "^7.0.5", "ts-morph": "^9.1.0", "tsd": "^0.13.1", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 938afdc205a44..0719357b6df35 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -21,6 +21,7 @@ filegroup( "//packages/kbn-docs-utils:build", "//packages/kbn-es:build", "//packages/kbn-es-archiver:build", + "//packages/kbn-es-query:build", "//packages/kbn-eslint-import-resolver-kibana:build", "//packages/kbn-eslint-plugin-eslint:build", "//packages/kbn-expect:build", diff --git a/packages/kbn-es-query/BUILD.bazel b/packages/kbn-es-query/BUILD.bazel new file mode 100644 index 0000000000000..9639a1057cac3 --- /dev/null +++ b/packages/kbn-es-query/BUILD.bazel @@ -0,0 +1,133 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@npm//peggy:index.bzl", "peggy") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-es-query" +PKG_REQUIRE_NAME = "@kbn/es-query" + +SOURCE_FILES = glob( + [ + "src/**/*", + ], + exclude = [ + "**/*.test.*", + "**/__fixtures__/**", + "**/__mocks__/**", + "**/__snapshots__/**", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md", +] + +SRC_DEPS = [ + "//packages/kbn-common-utils", + "//packages/kbn-config-schema", + "//packages/kbn-i18n", + "@npm//@elastic/elasticsearch", + "@npm//load-json-file", + "@npm//lodash", + "@npm//moment-timezone", + "@npm//tslib", +] + +TYPES_DEPS = [ + "@npm//@types/jest", + "@npm//@types/lodash", + "@npm//@types/moment-timezone", + "@npm//@types/node", +] + +DEPS = SRC_DEPS + TYPES_DEPS + +peggy( + name = "grammar", + data = [ + ":grammar/grammar.peggy" + ], + output_dir = True, + args = [ + "--allowed-start-rules", + "start,Literal", + "-o", + "$(@D)/index.js", + "./%s/grammar/grammar.peggy" % package_name() + ], +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_config( + name = "tsconfig_browser", + src = "tsconfig.browser.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.browser.json", + ], +) + +ts_project( + name = "tsc", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + declaration = True, + declaration_dir = "target_types", + declaration_map = True, + incremental = True, + out_dir = "target_node", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig", +) + +ts_project( + name = "tsc_browser", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + declaration = False, + incremental = True, + out_dir = "target_web", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig_browser", +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES + [":grammar"], + deps = DEPS + [":tsc", ":tsc_browser"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-es-query/README.md b/packages/kbn-es-query/README.md new file mode 100644 index 0000000000000..644fc4d559eb6 --- /dev/null +++ b/packages/kbn-es-query/README.md @@ -0,0 +1,3 @@ +# @kbn/es-query + +Shared common (client and server sie) utilities shared across packages and plugins. \ No newline at end of file diff --git a/src/plugins/data/common/es_query/kuery/ast/kuery.peg b/packages/kbn-es-query/grammar/grammar.peggy similarity index 95% rename from src/plugins/data/common/es_query/kuery/ast/kuery.peg rename to packages/kbn-es-query/grammar/grammar.peggy index dbea96eaac5b2..8f5ef17340aa9 100644 --- a/src/plugins/data/common/es_query/kuery/ast/kuery.peg +++ b/packages/kbn-es-query/grammar/grammar.peggy @@ -1,6 +1,9 @@ -/** - * To generate the parsing module (kuery.js), run `grunt peg` - * To watch changes and generate on file change, run `grunt watch:peg` +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ // Initialization block diff --git a/packages/kbn-es-query/jest.config.js b/packages/kbn-es-query/jest.config.js new file mode 100644 index 0000000000000..306e12f34f698 --- /dev/null +++ b/packages/kbn-es-query/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-es-query'], +}; diff --git a/packages/kbn-es-query/package.json b/packages/kbn-es-query/package.json new file mode 100644 index 0000000000000..335ef61b8b360 --- /dev/null +++ b/packages/kbn-es-query/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/es-query", + "browser": "./target_web/index.js", + "main": "./target_node/index.js", + "types": "./target_types/index.d.ts", + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0", + "private": true +} \ No newline at end of file diff --git a/src/plugins/data/common/es_query/__fixtures__/index_pattern_response.ts b/packages/kbn-es-query/src/__fixtures__/index_pattern_response.ts similarity index 100% rename from src/plugins/data/common/es_query/__fixtures__/index_pattern_response.ts rename to packages/kbn-es-query/src/__fixtures__/index_pattern_response.ts diff --git a/src/plugins/data/common/es_query/es_query/build_es_query.test.ts b/packages/kbn-es-query/src/es_query/build_es_query.test.ts similarity index 95% rename from src/plugins/data/common/es_query/es_query/build_es_query.test.ts rename to packages/kbn-es-query/src/es_query/build_es_query.test.ts index fa9a2c85aaef5..b31269c4f8160 100644 --- a/src/plugins/data/common/es_query/es_query/build_es_query.test.ts +++ b/packages/kbn-es-query/src/es_query/build_es_query.test.ts @@ -10,15 +10,16 @@ import { buildEsQuery } from './build_es_query'; import { fromKueryExpression, toElasticsearchQuery } from '../kuery'; import { luceneStringToDsl } from './lucene_string_to_dsl'; import { decorateQuery } from './decorate_query'; -import { IIndexPattern } from '../../index_patterns'; -import { MatchAllFilter } from '../filters'; -import { fields } from '../../index_patterns/mocks'; -import { Query } from '../../query/types'; +import { MatchAllFilter, Query } from '../filters'; +import { fields } from '../filters/stubs'; +import { IndexPatternBase } from './types'; + +jest.mock('../kuery/grammar'); describe('build query', () => { - const indexPattern: IIndexPattern = ({ + const indexPattern: IndexPatternBase = { fields, - } as unknown) as IIndexPattern; + }; describe('buildEsQuery', () => { it('should return the parameters of an Elasticsearch bool query', () => { diff --git a/src/plugins/data/common/es_query/es_query/build_es_query.ts b/packages/kbn-es-query/src/es_query/build_es_query.ts similarity index 97% rename from src/plugins/data/common/es_query/es_query/build_es_query.ts rename to packages/kbn-es-query/src/es_query/build_es_query.ts index d7b3c630d1a6e..e8a494ec1b8e4 100644 --- a/src/plugins/data/common/es_query/es_query/build_es_query.ts +++ b/packages/kbn-es-query/src/es_query/build_es_query.ts @@ -10,8 +10,7 @@ import { groupBy, has, isEqual } from 'lodash'; import { buildQueryFromKuery } from './from_kuery'; import { buildQueryFromFilters } from './from_filters'; import { buildQueryFromLucene } from './from_lucene'; -import { Filter } from '../filters'; -import { Query } from '../../query/types'; +import { Filter, Query } from '../filters'; import { IndexPatternBase } from './types'; export interface EsQueryConfig { diff --git a/src/plugins/data/common/es_query/es_query/decorate_query.test.ts b/packages/kbn-es-query/src/es_query/decorate_query.test.ts similarity index 100% rename from src/plugins/data/common/es_query/es_query/decorate_query.test.ts rename to packages/kbn-es-query/src/es_query/decorate_query.test.ts diff --git a/src/plugins/data/common/es_query/es_query/decorate_query.ts b/packages/kbn-es-query/src/es_query/decorate_query.ts similarity index 100% rename from src/plugins/data/common/es_query/es_query/decorate_query.ts rename to packages/kbn-es-query/src/es_query/decorate_query.ts diff --git a/src/plugins/data/common/es_query/es_query/es_query_dsl.ts b/packages/kbn-es-query/src/es_query/es_query_dsl.ts similarity index 100% rename from src/plugins/data/common/es_query/es_query/es_query_dsl.ts rename to packages/kbn-es-query/src/es_query/es_query_dsl.ts diff --git a/src/plugins/data/common/es_query/es_query/filter_matches_index.test.ts b/packages/kbn-es-query/src/es_query/filter_matches_index.test.ts similarity index 92% rename from src/plugins/data/common/es_query/es_query/filter_matches_index.test.ts rename to packages/kbn-es-query/src/es_query/filter_matches_index.test.ts index ad4d7ff8d78e2..bf4e1291ca438 100644 --- a/src/plugins/data/common/es_query/es_query/filter_matches_index.test.ts +++ b/packages/kbn-es-query/src/es_query/filter_matches_index.test.ts @@ -8,12 +8,12 @@ import { Filter } from '../filters'; import { filterMatchesIndex } from './filter_matches_index'; -import { IIndexPattern } from '../../index_patterns'; +import { IndexPatternBase } from './types'; describe('filterMatchesIndex', () => { it('should return true if the filter has no meta', () => { const filter = {} as Filter; - const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] } as IIndexPattern; + const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] } as IndexPatternBase; expect(filterMatchesIndex(filter, indexPattern)).toBe(true); }); @@ -26,35 +26,35 @@ describe('filterMatchesIndex', () => { it('should return true if the filter key matches a field name', () => { const filter = { meta: { index: 'foo', key: 'bar' } } as Filter; - const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] } as IIndexPattern; + const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] } as IndexPatternBase; expect(filterMatchesIndex(filter, indexPattern)).toBe(true); }); it('should return true if custom filter for the same index is passed', () => { const filter = { meta: { index: 'foo', key: 'bar', type: 'custom' } } as Filter; - const indexPattern = { id: 'foo', fields: [{ name: 'bara' }] } as IIndexPattern; + const indexPattern = { id: 'foo', fields: [{ name: 'bara' }] } as IndexPatternBase; expect(filterMatchesIndex(filter, indexPattern)).toBe(true); }); it('should return false if custom filter for a different index is passed', () => { const filter = { meta: { index: 'foo', key: 'bar', type: 'custom' } } as Filter; - const indexPattern = { id: 'food', fields: [{ name: 'bara' }] } as IIndexPattern; + const indexPattern = { id: 'food', fields: [{ name: 'bara' }] } as IndexPatternBase; expect(filterMatchesIndex(filter, indexPattern)).toBe(false); }); it('should return false if the filter key does not match a field name', () => { const filter = { meta: { index: 'foo', key: 'baz' } } as Filter; - const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] } as IIndexPattern; + const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] } as IndexPatternBase; expect(filterMatchesIndex(filter, indexPattern)).toBe(false); }); it('should return true if the filter has meta without a key', () => { const filter = { meta: { index: 'foo' } } as Filter; - const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] } as IIndexPattern; + const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] } as IndexPatternBase; expect(filterMatchesIndex(filter, indexPattern)).toBe(true); }); diff --git a/src/plugins/data/common/es_query/es_query/filter_matches_index.ts b/packages/kbn-es-query/src/es_query/filter_matches_index.ts similarity index 100% rename from src/plugins/data/common/es_query/es_query/filter_matches_index.ts rename to packages/kbn-es-query/src/es_query/filter_matches_index.ts diff --git a/src/plugins/data/common/es_query/es_query/from_filters.test.ts b/packages/kbn-es-query/src/es_query/from_filters.test.ts similarity index 96% rename from src/plugins/data/common/es_query/es_query/from_filters.test.ts rename to packages/kbn-es-query/src/es_query/from_filters.test.ts index 4a60db48c41b6..e3a56b5a9d63d 100644 --- a/src/plugins/data/common/es_query/es_query/from_filters.test.ts +++ b/packages/kbn-es-query/src/es_query/from_filters.test.ts @@ -7,14 +7,14 @@ */ import { buildQueryFromFilters } from './from_filters'; -import { IIndexPattern } from '../../index_patterns'; import { ExistsFilter, Filter, MatchAllFilter } from '../filters'; -import { fields } from '../../index_patterns/mocks'; +import { fields } from '../filters/stubs'; +import { IndexPatternBase } from './types'; describe('build query', () => { - const indexPattern: IIndexPattern = ({ + const indexPattern: IndexPatternBase = { fields, - } as unknown) as IIndexPattern; + }; describe('buildQueryFromFilters', () => { test('should return the parameters of an Elasticsearch bool query', () => { diff --git a/src/plugins/data/common/es_query/es_query/from_filters.ts b/packages/kbn-es-query/src/es_query/from_filters.ts similarity index 100% rename from src/plugins/data/common/es_query/es_query/from_filters.ts rename to packages/kbn-es-query/src/es_query/from_filters.ts diff --git a/src/plugins/data/common/es_query/es_query/from_kuery.test.ts b/packages/kbn-es-query/src/es_query/from_kuery.test.ts similarity index 91% rename from src/plugins/data/common/es_query/es_query/from_kuery.test.ts rename to packages/kbn-es-query/src/es_query/from_kuery.test.ts index 920102566f8b3..2458013854393 100644 --- a/src/plugins/data/common/es_query/es_query/from_kuery.test.ts +++ b/packages/kbn-es-query/src/es_query/from_kuery.test.ts @@ -8,14 +8,16 @@ import { buildQueryFromKuery } from './from_kuery'; import { fromKueryExpression, toElasticsearchQuery } from '../kuery'; -import { IIndexPattern } from '../../index_patterns'; -import { fields } from '../../index_patterns/mocks'; -import { Query } from '../../query/types'; +import { fields } from '../filters/stubs'; +import { IndexPatternBase } from './types'; +import { Query } from '..'; + +jest.mock('../kuery/grammar'); describe('build query', () => { - const indexPattern: IIndexPattern = ({ + const indexPattern: IndexPatternBase = { fields, - } as unknown) as IIndexPattern; + }; describe('buildQueryFromKuery', () => { test('should return the parameters of an Elasticsearch bool query', () => { diff --git a/src/plugins/data/common/es_query/es_query/from_kuery.ts b/packages/kbn-es-query/src/es_query/from_kuery.ts similarity index 96% rename from src/plugins/data/common/es_query/es_query/from_kuery.ts rename to packages/kbn-es-query/src/es_query/from_kuery.ts index 3eccfd8776113..efe8b26a81412 100644 --- a/src/plugins/data/common/es_query/es_query/from_kuery.ts +++ b/packages/kbn-es-query/src/es_query/from_kuery.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ +import { Query } from '../filters'; import { fromKueryExpression, toElasticsearchQuery, nodeTypes, KueryNode } from '../kuery'; import { IndexPatternBase } from './types'; -import { Query } from '../../query/types'; export function buildQueryFromKuery( indexPattern: IndexPatternBase | undefined, diff --git a/src/plugins/data/common/es_query/es_query/from_lucene.test.ts b/packages/kbn-es-query/src/es_query/from_lucene.test.ts similarity index 98% rename from src/plugins/data/common/es_query/es_query/from_lucene.test.ts rename to packages/kbn-es-query/src/es_query/from_lucene.test.ts index 2438a4b40e256..e4ca435ae8862 100644 --- a/src/plugins/data/common/es_query/es_query/from_lucene.test.ts +++ b/packages/kbn-es-query/src/es_query/from_lucene.test.ts @@ -9,7 +9,7 @@ import { buildQueryFromLucene } from './from_lucene'; import { decorateQuery } from './decorate_query'; import { luceneStringToDsl } from './lucene_string_to_dsl'; -import { Query } from '../../query/types'; +import { Query } from '..'; describe('build query', () => { describe('buildQueryFromLucene', () => { diff --git a/src/plugins/data/common/es_query/es_query/from_lucene.ts b/packages/kbn-es-query/src/es_query/from_lucene.ts similarity index 95% rename from src/plugins/data/common/es_query/es_query/from_lucene.ts rename to packages/kbn-es-query/src/es_query/from_lucene.ts index 6485281cc0fb3..cba789513c983 100644 --- a/src/plugins/data/common/es_query/es_query/from_lucene.ts +++ b/packages/kbn-es-query/src/es_query/from_lucene.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ +import { Query } from '..'; import { decorateQuery } from './decorate_query'; import { luceneStringToDsl } from './lucene_string_to_dsl'; -import { Query } from '../../query/types'; export function buildQueryFromLucene( queries: Query[], diff --git a/src/plugins/data/common/es_query/es_query/handle_nested_filter.test.ts b/packages/kbn-es-query/src/es_query/handle_nested_filter.test.ts similarity index 98% rename from src/plugins/data/common/es_query/es_query/handle_nested_filter.test.ts rename to packages/kbn-es-query/src/es_query/handle_nested_filter.test.ts index 24852ebf33bda..9c7b6070c7ec0 100644 --- a/src/plugins/data/common/es_query/es_query/handle_nested_filter.test.ts +++ b/packages/kbn-es-query/src/es_query/handle_nested_filter.test.ts @@ -7,7 +7,7 @@ */ import { handleNestedFilter } from './handle_nested_filter'; -import { fields } from '../../index_patterns/mocks'; +import { fields } from '../filters/stubs'; import { buildPhraseFilter, buildQueryFilter } from '../filters'; import { IndexPatternBase } from './types'; diff --git a/src/plugins/data/common/es_query/es_query/handle_nested_filter.ts b/packages/kbn-es-query/src/es_query/handle_nested_filter.ts similarity index 100% rename from src/plugins/data/common/es_query/es_query/handle_nested_filter.ts rename to packages/kbn-es-query/src/es_query/handle_nested_filter.ts diff --git a/src/plugins/data/common/es_query/es_query/index.ts b/packages/kbn-es-query/src/es_query/index.ts similarity index 92% rename from src/plugins/data/common/es_query/es_query/index.ts rename to packages/kbn-es-query/src/es_query/index.ts index ecc7c8ba5a9f5..beba50f50dd81 100644 --- a/src/plugins/data/common/es_query/es_query/index.ts +++ b/packages/kbn-es-query/src/es_query/index.ts @@ -10,5 +10,4 @@ export { buildEsQuery, EsQueryConfig } from './build_es_query'; export { buildQueryFromFilters } from './from_filters'; export { luceneStringToDsl } from './lucene_string_to_dsl'; export { decorateQuery } from './decorate_query'; -export { getEsQueryConfig } from './get_es_query_config'; export { IndexPatternBase, IndexPatternFieldBase, IFieldSubType } from './types'; diff --git a/src/plugins/data/common/es_query/es_query/lucene_string_to_dsl.test.ts b/packages/kbn-es-query/src/es_query/lucene_string_to_dsl.test.ts similarity index 100% rename from src/plugins/data/common/es_query/es_query/lucene_string_to_dsl.test.ts rename to packages/kbn-es-query/src/es_query/lucene_string_to_dsl.test.ts diff --git a/src/plugins/data/common/es_query/es_query/lucene_string_to_dsl.ts b/packages/kbn-es-query/src/es_query/lucene_string_to_dsl.ts similarity index 100% rename from src/plugins/data/common/es_query/es_query/lucene_string_to_dsl.ts rename to packages/kbn-es-query/src/es_query/lucene_string_to_dsl.ts diff --git a/src/plugins/data/common/es_query/es_query/migrate_filter.test.ts b/packages/kbn-es-query/src/es_query/migrate_filter.test.ts similarity index 100% rename from src/plugins/data/common/es_query/es_query/migrate_filter.test.ts rename to packages/kbn-es-query/src/es_query/migrate_filter.test.ts diff --git a/src/plugins/data/common/es_query/es_query/migrate_filter.ts b/packages/kbn-es-query/src/es_query/migrate_filter.ts similarity index 100% rename from src/plugins/data/common/es_query/es_query/migrate_filter.ts rename to packages/kbn-es-query/src/es_query/migrate_filter.ts diff --git a/src/plugins/data/common/es_query/es_query/types.ts b/packages/kbn-es-query/src/es_query/types.ts similarity index 98% rename from src/plugins/data/common/es_query/es_query/types.ts rename to packages/kbn-es-query/src/es_query/types.ts index 9282072cd444d..ca6a542779053 100644 --- a/src/plugins/data/common/es_query/es_query/types.ts +++ b/packages/kbn-es-query/src/es_query/types.ts @@ -34,4 +34,5 @@ export interface IndexPatternFieldBase { export interface IndexPatternBase { fields: IndexPatternFieldBase[]; id?: string; + title?: string; } diff --git a/src/plugins/data/common/es_query/filters/build_filter.test.ts b/packages/kbn-es-query/src/filters/build_filter.test.ts similarity index 94% rename from src/plugins/data/common/es_query/filters/build_filter.test.ts rename to packages/kbn-es-query/src/filters/build_filter.test.ts index 33221e3838d9e..d7cf7938b3737 100644 --- a/src/plugins/data/common/es_query/filters/build_filter.test.ts +++ b/packages/kbn-es-query/src/filters/build_filter.test.ts @@ -7,9 +7,15 @@ */ import { buildFilter, FilterStateStore, FILTERS } from '.'; -import { stubIndexPattern, stubFields } from '../../../common/stubs'; +import { IndexPatternBase } from '..'; +import { fields as stubFields } from './stubs'; describe('buildFilter', () => { + const stubIndexPattern: IndexPatternBase = { + id: 'logstash-*', + fields: stubFields, + }; + it('should build phrase filters', () => { const params = 'foo'; const alias = 'bar'; diff --git a/src/plugins/data/common/es_query/filters/build_filters.ts b/packages/kbn-es-query/src/filters/build_filters.ts similarity index 94% rename from src/plugins/data/common/es_query/filters/build_filters.ts rename to packages/kbn-es-query/src/filters/build_filters.ts index 1d8d67b6e937f..ae27e64f3a41d 100644 --- a/src/plugins/data/common/es_query/filters/build_filters.ts +++ b/packages/kbn-es-query/src/filters/build_filters.ts @@ -6,18 +6,16 @@ * Side Public License, v 1. */ -import { IndexPatternFieldBase, IndexPatternBase } from '../..'; - import { Filter, FILTERS, - FilterStateStore, - FilterMeta, buildPhraseFilter, buildPhrasesFilter, buildRangeFilter, buildExistsFilter, } from '.'; +import { IndexPatternFieldBase, IndexPatternBase } from '..'; +import { FilterMeta, FilterStateStore } from './types'; export function buildFilter( indexPattern: IndexPatternBase, diff --git a/src/plugins/data/common/es_query/filters/custom_filter.ts b/packages/kbn-es-query/src/filters/custom_filter.ts similarity index 91% rename from src/plugins/data/common/es_query/filters/custom_filter.ts rename to packages/kbn-es-query/src/filters/custom_filter.ts index bab226157ddd1..aa9e798a5b1ac 100644 --- a/src/plugins/data/common/es_query/filters/custom_filter.ts +++ b/packages/kbn-es-query/src/filters/custom_filter.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Filter } from './meta_filter'; +import { Filter } from './types'; export type CustomFilter = Filter & { query: any; diff --git a/src/plugins/data/common/es_query/filters/exists_filter.test.ts b/packages/kbn-es-query/src/filters/exists_filter.test.ts similarity index 81% rename from src/plugins/data/common/es_query/filters/exists_filter.test.ts rename to packages/kbn-es-query/src/filters/exists_filter.test.ts index 848fcead5f5d9..83976d45f8e04 100644 --- a/src/plugins/data/common/es_query/filters/exists_filter.test.ts +++ b/packages/kbn-es-query/src/filters/exists_filter.test.ts @@ -6,14 +6,14 @@ * Side Public License, v 1. */ +import { IndexPatternBase } from '..'; import { buildExistsFilter, getExistsFilterField } from './exists_filter'; -import { IIndexPattern } from '../../index_patterns'; -import { fields } from '../../index_patterns/fields/fields.mocks'; +import { fields } from './stubs/fields.mocks'; describe('exists filter', function () { - const indexPattern: IIndexPattern = ({ + const indexPattern: IndexPatternBase = { fields, - } as unknown) as IIndexPattern; + }; describe('getExistsFilterField', function () { it('should return the name of the field an exists query is targeting', () => { diff --git a/src/plugins/data/common/es_query/filters/exists_filter.ts b/packages/kbn-es-query/src/filters/exists_filter.ts similarity index 95% rename from src/plugins/data/common/es_query/filters/exists_filter.ts rename to packages/kbn-es-query/src/filters/exists_filter.ts index 7a09adb7d9ed6..7785a62261fde 100644 --- a/src/plugins/data/common/es_query/filters/exists_filter.ts +++ b/packages/kbn-es-query/src/filters/exists_filter.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { Filter, FilterMeta } from './meta_filter'; import { IndexPatternFieldBase, IndexPatternBase } from '..'; +import { Filter, FilterMeta } from './types'; export type ExistsFilterMeta = FilterMeta; diff --git a/src/plugins/data/common/es_query/filters/geo_bounding_box_filter.test.ts b/packages/kbn-es-query/src/filters/geo_bounding_box_filter.test.ts similarity index 100% rename from src/plugins/data/common/es_query/filters/geo_bounding_box_filter.test.ts rename to packages/kbn-es-query/src/filters/geo_bounding_box_filter.test.ts diff --git a/src/plugins/data/common/es_query/filters/geo_bounding_box_filter.ts b/packages/kbn-es-query/src/filters/geo_bounding_box_filter.ts similarity index 93% rename from src/plugins/data/common/es_query/filters/geo_bounding_box_filter.ts rename to packages/kbn-es-query/src/filters/geo_bounding_box_filter.ts index 987055405886c..b309515109d48 100644 --- a/src/plugins/data/common/es_query/filters/geo_bounding_box_filter.ts +++ b/packages/kbn-es-query/src/filters/geo_bounding_box_filter.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Filter, FilterMeta, LatLon } from './meta_filter'; +import { Filter, FilterMeta, LatLon } from './types'; export type GeoBoundingBoxFilterMeta = FilterMeta & { params: { diff --git a/src/plugins/data/common/es_query/filters/geo_polygon_filter.test.ts b/packages/kbn-es-query/src/filters/geo_polygon_filter.test.ts similarity index 100% rename from src/plugins/data/common/es_query/filters/geo_polygon_filter.test.ts rename to packages/kbn-es-query/src/filters/geo_polygon_filter.test.ts diff --git a/src/plugins/data/common/es_query/filters/geo_polygon_filter.ts b/packages/kbn-es-query/src/filters/geo_polygon_filter.ts similarity index 93% rename from src/plugins/data/common/es_query/filters/geo_polygon_filter.ts rename to packages/kbn-es-query/src/filters/geo_polygon_filter.ts index 5b284f1b6e3a1..42e417f2c88a4 100644 --- a/src/plugins/data/common/es_query/filters/geo_polygon_filter.ts +++ b/packages/kbn-es-query/src/filters/geo_polygon_filter.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Filter, FilterMeta, LatLon } from './meta_filter'; +import { Filter, FilterMeta, LatLon } from './types'; export type GeoPolygonFilterMeta = FilterMeta & { params: { diff --git a/src/plugins/data/common/es_query/filters/get_filter_field.test.ts b/packages/kbn-es-query/src/filters/get_filter_field.test.ts similarity index 87% rename from src/plugins/data/common/es_query/filters/get_filter_field.test.ts rename to packages/kbn-es-query/src/filters/get_filter_field.test.ts index b9ae8f3abaa0c..0425aa6cc8c72 100644 --- a/src/plugins/data/common/es_query/filters/get_filter_field.test.ts +++ b/packages/kbn-es-query/src/filters/get_filter_field.test.ts @@ -9,14 +9,14 @@ import { buildPhraseFilter } from './phrase_filter'; import { buildQueryFilter } from './query_string_filter'; import { getFilterField } from './get_filter_field'; -import { IIndexPattern } from '../../index_patterns'; -import { fields } from '../../index_patterns/fields/fields.mocks'; +import { IndexPatternBase } from '..'; +import { fields } from './stubs/fields.mocks'; describe('getFilterField', function () { - const indexPattern: IIndexPattern = ({ + const indexPattern: IndexPatternBase = { id: 'logstash-*', fields, - } as unknown) as IIndexPattern; + }; it('should return the field name from known filter types that target a specific field', () => { const field = indexPattern.fields.find((patternField) => patternField.name === 'extension'); diff --git a/src/plugins/data/common/es_query/filters/get_filter_field.ts b/packages/kbn-es-query/src/filters/get_filter_field.ts similarity index 97% rename from src/plugins/data/common/es_query/filters/get_filter_field.ts rename to packages/kbn-es-query/src/filters/get_filter_field.ts index 0782e46bac784..4b540beb03754 100644 --- a/src/plugins/data/common/es_query/filters/get_filter_field.ts +++ b/packages/kbn-es-query/src/filters/get_filter_field.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Filter } from './meta_filter'; +import { Filter } from './types'; import { getExistsFilterField, isExistsFilter } from './exists_filter'; import { getGeoBoundingBoxFilterField, isGeoBoundingBoxFilter } from './geo_bounding_box_filter'; import { getGeoPolygonFilterField, isGeoPolygonFilter } from './geo_polygon_filter'; diff --git a/src/plugins/data/common/es_query/filters/get_filter_params.test.ts b/packages/kbn-es-query/src/filters/get_filter_params.test.ts similarity index 100% rename from src/plugins/data/common/es_query/filters/get_filter_params.test.ts rename to packages/kbn-es-query/src/filters/get_filter_params.test.ts diff --git a/src/plugins/data/common/es_query/filters/get_filter_params.ts b/packages/kbn-es-query/src/filters/get_filter_params.ts similarity index 100% rename from src/plugins/data/common/es_query/filters/get_filter_params.ts rename to packages/kbn-es-query/src/filters/get_filter_params.ts diff --git a/src/plugins/data/common/es_query/filters/index.ts b/packages/kbn-es-query/src/filters/index.ts similarity index 89% rename from src/plugins/data/common/es_query/filters/index.ts rename to packages/kbn-es-query/src/filters/index.ts index fe7cdadabaee3..90c1675e8a3cf 100644 --- a/src/plugins/data/common/es_query/filters/index.ts +++ b/packages/kbn-es-query/src/filters/index.ts @@ -7,7 +7,7 @@ */ import { omit, get } from 'lodash'; -import { Filter } from './meta_filter'; +import { Filter } from './types'; export * from './build_filters'; export * from './custom_filter'; @@ -24,7 +24,7 @@ export * from './phrases_filter'; export * from './query_string_filter'; export * from './range_filter'; -export * from './types'; +export { Query, Filter, FILTERS, LatLon, FilterStateStore, FieldFilter, FilterMeta } from './types'; /** * Clean out any invalid attributes from the filters diff --git a/src/plugins/data/common/es_query/filters/match_all_filter.ts b/packages/kbn-es-query/src/filters/match_all_filter.ts similarity index 92% rename from src/plugins/data/common/es_query/filters/match_all_filter.ts rename to packages/kbn-es-query/src/filters/match_all_filter.ts index 36eb5ee1fce18..a3fdd740986d4 100644 --- a/src/plugins/data/common/es_query/filters/match_all_filter.ts +++ b/packages/kbn-es-query/src/filters/match_all_filter.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Filter, FilterMeta } from './meta_filter'; +import { Filter, FilterMeta } from './types'; export interface MatchAllFilterMeta extends FilterMeta { field: any; diff --git a/src/plugins/data/common/es_query/filters/meta_filter.ts b/packages/kbn-es-query/src/filters/meta_filter.ts similarity index 69% rename from src/plugins/data/common/es_query/filters/meta_filter.ts rename to packages/kbn-es-query/src/filters/meta_filter.ts index 87455cf1cb763..25fd26c410826 100644 --- a/src/plugins/data/common/es_query/filters/meta_filter.ts +++ b/packages/kbn-es-query/src/filters/meta_filter.ts @@ -6,49 +6,7 @@ * Side Public License, v 1. */ -export enum FilterStateStore { - APP_STATE = 'appState', - GLOBAL_STATE = 'globalState', -} - -// eslint-disable-next-line -export type FilterState = { - store: FilterStateStore; -}; - -type FilterFormatterFunction = (value: any) => string; -export interface FilterValueFormatter { - convert: FilterFormatterFunction; - getConverterFor: (type: string) => FilterFormatterFunction; -} - -// eslint-disable-next-line -export type FilterMeta = { - alias: string | null; - disabled: boolean; - negate: boolean; - // controlledBy is there to identify who owns the filter - controlledBy?: string; - // index and type are optional only because when you create a new filter, there are no defaults - index?: string; - isMultiIndex?: boolean; - type?: string; - key?: string; - params?: any; - value?: string; -}; - -// eslint-disable-next-line -export type Filter = { - $state?: FilterState; - meta: FilterMeta; - query?: any; -}; - -export interface LatLon { - lat: number; - lon: number; -} +import { Filter, FilterMeta, FilterState, FilterStateStore } from './types'; export const buildEmptyFilter = (isPinned: boolean, index?: string): Filter => { const meta: FilterMeta = { diff --git a/src/plugins/data/common/es_query/filters/missing_filter.test.ts b/packages/kbn-es-query/src/filters/missing_filter.test.ts similarity index 100% rename from src/plugins/data/common/es_query/filters/missing_filter.test.ts rename to packages/kbn-es-query/src/filters/missing_filter.test.ts diff --git a/src/plugins/data/common/es_query/filters/missing_filter.ts b/packages/kbn-es-query/src/filters/missing_filter.ts similarity index 93% rename from src/plugins/data/common/es_query/filters/missing_filter.ts rename to packages/kbn-es-query/src/filters/missing_filter.ts index d0d337283ca60..0e10cb18d3c21 100644 --- a/src/plugins/data/common/es_query/filters/missing_filter.ts +++ b/packages/kbn-es-query/src/filters/missing_filter.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Filter, FilterMeta } from './meta_filter'; +import { Filter, FilterMeta } from './types'; export type MissingFilterMeta = FilterMeta; diff --git a/src/plugins/data/common/es_query/filters/phrase_filter.test.ts b/packages/kbn-es-query/src/filters/phrase_filter.test.ts similarity index 85% rename from src/plugins/data/common/es_query/filters/phrase_filter.test.ts rename to packages/kbn-es-query/src/filters/phrase_filter.test.ts index 513f0e29b5b24..7e6ccadc800d4 100644 --- a/src/plugins/data/common/es_query/filters/phrase_filter.test.ts +++ b/packages/kbn-es-query/src/filters/phrase_filter.test.ts @@ -11,16 +11,17 @@ import { buildPhraseFilter, getPhraseFilterField, } from './phrase_filter'; -import { fields, getField } from '../../index_patterns/mocks'; -import { IIndexPattern } from '../../index_patterns'; +import { fields, getField } from '../filters/stubs'; +import { IndexPatternBase } from '../es_query'; describe('Phrase filter builder', () => { - let indexPattern: IIndexPattern; + let indexPattern: IndexPatternBase; beforeEach(() => { indexPattern = { id: 'id', - } as IIndexPattern; + fields, + }; }); it('should be a function', () => { @@ -30,7 +31,7 @@ describe('Phrase filter builder', () => { it('should return a match query filter when passed a standard string field', () => { const field = getField('extension'); - expect(buildPhraseFilter(field, 'jpg', indexPattern)).toEqual({ + expect(buildPhraseFilter(field!, 'jpg', indexPattern)).toEqual({ meta: { index: 'id', }, @@ -45,7 +46,7 @@ describe('Phrase filter builder', () => { it('should return a match query filter when passed a standard numeric field', () => { const field = getField('bytes'); - expect(buildPhraseFilter(field, '5', indexPattern)).toEqual({ + expect(buildPhraseFilter(field!, '5', indexPattern)).toEqual({ meta: { index: 'id', }, @@ -60,7 +61,7 @@ describe('Phrase filter builder', () => { it('should return a match query filter when passed a standard bool field', () => { const field = getField('ssl'); - expect(buildPhraseFilter(field, 'true', indexPattern)).toEqual({ + expect(buildPhraseFilter(field!, 'true', indexPattern)).toEqual({ meta: { index: 'id', }, @@ -75,7 +76,7 @@ describe('Phrase filter builder', () => { it('should return a script filter when passed a scripted field', () => { const field = getField('script number'); - expect(buildPhraseFilter(field, 5, indexPattern)).toEqual({ + expect(buildPhraseFilter(field!, 5, indexPattern)).toEqual({ meta: { index: 'id', field: 'script number', @@ -95,7 +96,7 @@ describe('Phrase filter builder', () => { it('should return a script filter when passed a scripted field with numeric conversion', () => { const field = getField('script number'); - expect(buildPhraseFilter(field, '5', indexPattern)).toEqual({ + expect(buildPhraseFilter(field!, '5', indexPattern)).toEqual({ meta: { index: 'id', field: 'script number', @@ -140,9 +141,9 @@ describe('buildInlineScriptForPhraseFilter', () => { }); describe('getPhraseFilterField', function () { - const indexPattern: IIndexPattern = ({ + const indexPattern: IndexPatternBase = { fields, - } as unknown) as IIndexPattern; + }; it('should return the name of the field a phrase query is targeting', () => { const field = indexPattern.fields.find((patternField) => patternField.name === 'extension'); diff --git a/src/plugins/data/common/es_query/filters/phrase_filter.ts b/packages/kbn-es-query/src/filters/phrase_filter.ts similarity index 98% rename from src/plugins/data/common/es_query/filters/phrase_filter.ts rename to packages/kbn-es-query/src/filters/phrase_filter.ts index 68ad16cb31d42..fd5ded010bf07 100644 --- a/src/plugins/data/common/es_query/filters/phrase_filter.ts +++ b/packages/kbn-es-query/src/filters/phrase_filter.ts @@ -7,7 +7,7 @@ */ import type { estypes } from '@elastic/elasticsearch'; import { get, isPlainObject } from 'lodash'; -import { Filter, FilterMeta } from './meta_filter'; +import { Filter, FilterMeta } from './types'; import { IndexPatternFieldBase, IndexPatternBase } from '..'; export type PhraseFilterMeta = FilterMeta & { diff --git a/src/plugins/data/common/es_query/filters/phrases_filter.test.ts b/packages/kbn-es-query/src/filters/phrases_filter.test.ts similarity index 82% rename from src/plugins/data/common/es_query/filters/phrases_filter.test.ts rename to packages/kbn-es-query/src/filters/phrases_filter.test.ts index 68ef69ba76eeb..5e742187cdf05 100644 --- a/src/plugins/data/common/es_query/filters/phrases_filter.test.ts +++ b/packages/kbn-es-query/src/filters/phrases_filter.test.ts @@ -7,13 +7,13 @@ */ import { buildPhrasesFilter, getPhrasesFilterField } from './phrases_filter'; -import { IIndexPattern } from '../../index_patterns'; -import { fields } from '../../index_patterns/fields/fields.mocks'; +import { IndexPatternBase } from '..'; +import { fields } from './stubs/fields.mocks'; describe('phrases filter', function () { - const indexPattern: IIndexPattern = ({ + const indexPattern: IndexPatternBase = { fields, - } as unknown) as IIndexPattern; + }; describe('getPhrasesFilterField', function () { it('should return the name of the field a phrases query is targeting', () => { diff --git a/src/plugins/data/common/es_query/filters/phrases_filter.ts b/packages/kbn-es-query/src/filters/phrases_filter.ts similarity index 97% rename from src/plugins/data/common/es_query/filters/phrases_filter.ts rename to packages/kbn-es-query/src/filters/phrases_filter.ts index 7f7831e1c7978..451a0a46789fb 100644 --- a/src/plugins/data/common/es_query/filters/phrases_filter.ts +++ b/packages/kbn-es-query/src/filters/phrases_filter.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Filter, FilterMeta } from './meta_filter'; +import { Filter, FilterMeta } from './types'; import { getPhraseScript } from './phrase_filter'; import { FILTERS } from './index'; import { IndexPatternFieldBase, IndexPatternBase } from '../es_query'; diff --git a/src/plugins/data/common/es_query/filters/query_string_filter.test.ts b/packages/kbn-es-query/src/filters/query_string_filter.test.ts similarity index 100% rename from src/plugins/data/common/es_query/filters/query_string_filter.test.ts rename to packages/kbn-es-query/src/filters/query_string_filter.test.ts diff --git a/src/plugins/data/common/es_query/filters/query_string_filter.ts b/packages/kbn-es-query/src/filters/query_string_filter.ts similarity index 94% rename from src/plugins/data/common/es_query/filters/query_string_filter.ts rename to packages/kbn-es-query/src/filters/query_string_filter.ts index bf960f0ac04b3..b4774d5e65a67 100644 --- a/src/plugins/data/common/es_query/filters/query_string_filter.ts +++ b/packages/kbn-es-query/src/filters/query_string_filter.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Filter, FilterMeta } from './meta_filter'; +import { Filter, FilterMeta } from './types'; export type QueryStringFilterMeta = FilterMeta; diff --git a/src/plugins/data/common/es_query/filters/range_filter.test.ts b/packages/kbn-es-query/src/filters/range_filter.test.ts similarity index 88% rename from src/plugins/data/common/es_query/filters/range_filter.test.ts rename to packages/kbn-es-query/src/filters/range_filter.test.ts index 30e52b21d52b7..634471b1d4d9e 100644 --- a/src/plugins/data/common/es_query/filters/range_filter.test.ts +++ b/packages/kbn-es-query/src/filters/range_filter.test.ts @@ -8,7 +8,7 @@ import { each } from 'lodash'; import { buildRangeFilter, getRangeFilterField, RangeFilter } from './range_filter'; -import { fields, getField } from '../../index_patterns/mocks'; +import { fields, getField } from '../filters/stubs'; import { IndexPatternBase, IndexPatternFieldBase } from '../es_query'; describe('Range filter builder', () => { @@ -27,7 +27,7 @@ describe('Range filter builder', () => { it('should return a range filter when passed a standard field', () => { const field = getField('bytes'); - expect(buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern)).toEqual({ + expect(buildRangeFilter(field!, { gte: 1, lte: 3 }, indexPattern)).toEqual({ meta: { index: 'id', params: {}, @@ -44,7 +44,7 @@ describe('Range filter builder', () => { it('should return a script filter when passed a scripted field', () => { const field = getField('script number'); - expect(buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern)).toEqual({ + expect(buildRangeFilter(field!, { gte: 1, lte: 3 }, indexPattern)).toEqual({ meta: { field: 'script number', index: 'id', @@ -67,7 +67,7 @@ describe('Range filter builder', () => { it('should convert strings to numbers if the field is scripted and type number', () => { const field = getField('script number'); - expect(buildRangeFilter(field, { gte: '1', lte: '3' }, indexPattern)).toEqual({ + expect(buildRangeFilter(field!, { gte: '1', lte: '3' }, indexPattern)).toEqual({ meta: { field: 'script number', index: 'id', @@ -95,7 +95,7 @@ describe('Range filter builder', () => { `gte(() -> { ${field!.script} }, params.gte) && ` + `lte(() -> { ${field!.script} }, params.lte)`; - const rangeFilter = buildRangeFilter(field, { gte: 1, lte: 3 }, indexPattern); + const rangeFilter = buildRangeFilter(field!, { gte: 1, lte: 3 }, indexPattern); expect(rangeFilter.script!.script.source).toBe(expected); }); @@ -104,11 +104,11 @@ describe('Range filter builder', () => { const field = getField('script number'); expect(() => { - buildRangeFilter(field, { gte: 1, gt: 3 }, indexPattern); + buildRangeFilter(field!, { gte: 1, gt: 3 }, indexPattern); }).toThrowError(); expect(() => { - buildRangeFilter(field, { lte: 1, lt: 3 }, indexPattern); + buildRangeFilter(field!, { lte: 1, lt: 3 }, indexPattern); }).toThrowError(); }); @@ -120,7 +120,7 @@ describe('Range filter builder', () => { [key]: 5, }; - const filter = buildRangeFilter(field, params, indexPattern); + const filter = buildRangeFilter(field!, params, indexPattern); const script = filter.script!.script; expect(script.source).toBe('(' + field!.script + ')' + operator + key); @@ -134,7 +134,7 @@ describe('Range filter builder', () => { let filter: RangeFilter; beforeEach(() => { - field = getField('script number'); + field = getField('script number')!; filter = buildRangeFilter(field, { gte: 0, lt: Infinity }, indexPattern); }); @@ -164,7 +164,7 @@ describe('Range filter builder', () => { let filter: RangeFilter; beforeEach(() => { - field = getField('script number'); + field = getField('script number')!; filter = buildRangeFilter(field, { gte: -Infinity, lt: Infinity }, indexPattern); }); diff --git a/src/plugins/data/common/es_query/filters/range_filter.ts b/packages/kbn-es-query/src/filters/range_filter.ts similarity index 99% rename from src/plugins/data/common/es_query/filters/range_filter.ts rename to packages/kbn-es-query/src/filters/range_filter.ts index e44e23f64936e..2df313eb7dd76 100644 --- a/src/plugins/data/common/es_query/filters/range_filter.ts +++ b/packages/kbn-es-query/src/filters/range_filter.ts @@ -7,7 +7,7 @@ */ import type { estypes } from '@elastic/elasticsearch'; import { map, reduce, mapValues, get, keys, pickBy } from 'lodash'; -import { Filter, FilterMeta } from './meta_filter'; +import { Filter, FilterMeta } from './types'; import { IndexPatternBase, IndexPatternFieldBase } from '..'; const OPERANDS_IN_RANGE = 2; diff --git a/src/plugins/data/common/es_query/filters/stubs/exists_filter.ts b/packages/kbn-es-query/src/filters/stubs/exists_filter.ts similarity index 100% rename from src/plugins/data/common/es_query/filters/stubs/exists_filter.ts rename to packages/kbn-es-query/src/filters/stubs/exists_filter.ts diff --git a/packages/kbn-es-query/src/filters/stubs/fields.mocks.ts b/packages/kbn-es-query/src/filters/stubs/fields.mocks.ts new file mode 100644 index 0000000000000..2507293b87477 --- /dev/null +++ b/packages/kbn-es-query/src/filters/stubs/fields.mocks.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { IndexPatternFieldBase } from '../..'; + +/** + * Base index pattern fields for testing + */ +export const fields: IndexPatternFieldBase[] = [ + { + name: 'bytes', + type: 'number', + scripted: false, + }, + { + name: 'ssl', + type: 'boolean', + scripted: false, + }, + { + name: '@timestamp', + type: 'date', + scripted: false, + }, + { + name: 'extension', + type: 'string', + scripted: false, + }, + { + name: 'machine.os', + type: 'string', + scripted: false, + }, + { + name: 'machine.os.raw', + type: 'string', + scripted: false, + }, + { + name: 'script number', + type: 'number', + scripted: true, + script: '1234', + lang: 'expression', + }, + { + name: 'script date', + type: 'date', + scripted: true, + script: '1234', + lang: 'painless', + }, + { + name: 'script string', + type: 'string', + scripted: true, + script: '1234', + lang: 'painless', + }, + { + name: 'nestedField.child', + type: 'string', + scripted: false, + subType: { nested: { path: 'nestedField' } }, + }, + { + name: 'nestedField.nestedChild.doublyNestedChild', + type: 'string', + scripted: false, + subType: { nested: { path: 'nestedField.nestedChild' } }, + }, +]; + +export const getField = (name: string) => fields.find((field) => field.name === name); diff --git a/packages/kbn-es-query/src/filters/stubs/index.ts b/packages/kbn-es-query/src/filters/stubs/index.ts new file mode 100644 index 0000000000000..e4d99b5082257 --- /dev/null +++ b/packages/kbn-es-query/src/filters/stubs/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './exists_filter'; +export * from './phrase_filter'; +export * from './phrases_filter'; +export * from './range_filter'; +export * from './fields.mocks'; diff --git a/src/plugins/data/common/es_query/filters/stubs/phrase_filter.ts b/packages/kbn-es-query/src/filters/stubs/phrase_filter.ts similarity index 100% rename from src/plugins/data/common/es_query/filters/stubs/phrase_filter.ts rename to packages/kbn-es-query/src/filters/stubs/phrase_filter.ts diff --git a/src/plugins/data/common/es_query/filters/stubs/phrases_filter.ts b/packages/kbn-es-query/src/filters/stubs/phrases_filter.ts similarity index 100% rename from src/plugins/data/common/es_query/filters/stubs/phrases_filter.ts rename to packages/kbn-es-query/src/filters/stubs/phrases_filter.ts diff --git a/src/plugins/data/common/es_query/filters/stubs/range_filter.ts b/packages/kbn-es-query/src/filters/stubs/range_filter.ts similarity index 100% rename from src/plugins/data/common/es_query/filters/stubs/range_filter.ts rename to packages/kbn-es-query/src/filters/stubs/range_filter.ts diff --git a/src/plugins/data/common/es_query/filters/types.ts b/packages/kbn-es-query/src/filters/types.ts similarity index 59% rename from src/plugins/data/common/es_query/filters/types.ts rename to packages/kbn-es-query/src/filters/types.ts index a007189d81a03..13e4a941b9166 100644 --- a/src/plugins/data/common/es_query/filters/types.ts +++ b/packages/kbn-es-query/src/filters/types.ts @@ -40,3 +40,47 @@ export enum FILTERS { GEO_POLYGON = 'geo_polygon', SPATIAL_FILTER = 'spatial_filter', } + +export enum FilterStateStore { + APP_STATE = 'appState', + GLOBAL_STATE = 'globalState', +} + +// eslint-disable-next-line +export type FilterState = { + store: FilterStateStore; +}; + +// eslint-disable-next-line +export type FilterMeta = { + alias: string | null; + disabled: boolean; + negate: boolean; + // controlledBy is there to identify who owns the filter + controlledBy?: string; + // index and type are optional only because when you create a new filter, there are no defaults + index?: string; + isMultiIndex?: boolean; + type?: string; + key?: string; + params?: any; + value?: string; +}; + +// eslint-disable-next-line +export type Filter = { + $state?: FilterState; + meta: FilterMeta; + query?: any; // TODO: can we use the Query type her? +}; + +// eslint-disable-next-line +export type Query = { + query: string | { [key: string]: any }; + language: string; +}; + +export interface LatLon { + lat: number; + lon: number; +} diff --git a/packages/kbn-es-query/src/index.ts b/packages/kbn-es-query/src/index.ts new file mode 100644 index 0000000000000..bbba52871d4c8 --- /dev/null +++ b/packages/kbn-es-query/src/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './es_query'; +export * from './filters'; +export * from './kuery'; diff --git a/src/plugins/data/common/es_query/kuery/ast/ast.test.ts b/packages/kbn-es-query/src/kuery/ast/ast.test.ts similarity index 98% rename from src/plugins/data/common/es_query/kuery/ast/ast.test.ts rename to packages/kbn-es-query/src/kuery/ast/ast.test.ts index f8d7dc02d38fc..459ace026796c 100644 --- a/src/plugins/data/common/es_query/kuery/ast/ast.test.ts +++ b/packages/kbn-es-query/src/kuery/ast/ast.test.ts @@ -8,17 +8,19 @@ import { fromKueryExpression, fromLiteralExpression, toElasticsearchQuery } from './ast'; import { nodeTypes } from '../node_types'; -import { fields } from '../../../index_patterns/mocks'; -import { IIndexPattern } from '../../../index_patterns'; +import { IndexPatternBase } from '../..'; import { KueryNode } from '../types'; +import { fields } from '../../filters/stubs'; + +jest.mock('../grammar'); describe('kuery AST API', () => { - let indexPattern: IIndexPattern; + let indexPattern: IndexPatternBase; beforeEach(() => { - indexPattern = ({ + indexPattern = { fields, - } as unknown) as IIndexPattern; + }; }); describe('fromKueryExpression', () => { diff --git a/src/plugins/data/common/es_query/kuery/ast/ast.ts b/packages/kbn-es-query/src/kuery/ast/ast.ts similarity index 96% rename from src/plugins/data/common/es_query/kuery/ast/ast.ts rename to packages/kbn-es-query/src/kuery/ast/ast.ts index 3e7b25897cab7..6f43098a752de 100644 --- a/src/plugins/data/common/es_query/kuery/ast/ast.ts +++ b/packages/kbn-es-query/src/kuery/ast/ast.ts @@ -11,8 +11,7 @@ import { nodeTypes } from '../node_types/index'; import { KQLSyntaxError } from '../kuery_syntax_error'; import { KueryNode, DslQuery, KueryParseOptions } from '../types'; -// @ts-ignore -import { parse as parseKuery } from './_generated_/kuery'; +import { parse as parseKuery } from '../grammar'; import { IndexPatternBase } from '../..'; const fromExpression = ( diff --git a/src/plugins/data/common/es_query/kuery/ast/index.ts b/packages/kbn-es-query/src/kuery/ast/index.ts similarity index 100% rename from src/plugins/data/common/es_query/kuery/ast/index.ts rename to packages/kbn-es-query/src/kuery/ast/index.ts diff --git a/src/plugins/data/common/es_query/kuery/functions/and.test.ts b/packages/kbn-es-query/src/kuery/functions/and.test.ts similarity index 88% rename from src/plugins/data/common/es_query/kuery/functions/and.test.ts rename to packages/kbn-es-query/src/kuery/functions/and.test.ts index fae7888d75536..1e6797485c964 100644 --- a/src/plugins/data/common/es_query/kuery/functions/and.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/and.test.ts @@ -7,24 +7,24 @@ */ import { nodeTypes } from '../node_types'; -import { fields } from '../../../index_patterns/mocks'; -import { IIndexPattern } from '../../../index_patterns'; +import { fields } from '../../filters/stubs'; import * as ast from '../ast'; - -// @ts-ignore import * as and from './and'; +import { IndexPatternBase } from '../../es_query'; + +jest.mock('../grammar'); const childNode1 = nodeTypes.function.buildNode('is', 'machine.os', 'osx'); const childNode2 = nodeTypes.function.buildNode('is', 'extension', 'jpg'); describe('kuery functions', () => { describe('and', () => { - let indexPattern: IIndexPattern; + let indexPattern: IndexPatternBase; beforeEach(() => { - indexPattern = ({ + indexPattern = { fields, - } as unknown) as IIndexPattern; + }; }); describe('buildNodeParams', () => { diff --git a/src/plugins/data/common/es_query/kuery/functions/and.ts b/packages/kbn-es-query/src/kuery/functions/and.ts similarity index 93% rename from src/plugins/data/common/es_query/kuery/functions/and.ts rename to packages/kbn-es-query/src/kuery/functions/and.ts index ba7d5d1f6645b..239dd67e73d10 100644 --- a/src/plugins/data/common/es_query/kuery/functions/and.ts +++ b/packages/kbn-es-query/src/kuery/functions/and.ts @@ -7,7 +7,7 @@ */ import * as ast from '../ast'; -import { IndexPatternBase, KueryNode } from '../../..'; +import { IndexPatternBase, KueryNode } from '../..'; export function buildNodeParams(children: KueryNode[]) { return { diff --git a/src/plugins/data/common/es_query/kuery/functions/exists.test.ts b/packages/kbn-es-query/src/kuery/functions/exists.test.ts similarity index 92% rename from src/plugins/data/common/es_query/kuery/functions/exists.test.ts rename to packages/kbn-es-query/src/kuery/functions/exists.test.ts index 4036e7c4089f0..0941e478e816b 100644 --- a/src/plugins/data/common/es_query/kuery/functions/exists.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/exists.test.ts @@ -7,20 +7,22 @@ */ import { nodeTypes } from '../node_types'; -import { fields } from '../../../index_patterns/mocks'; -import { IIndexPattern } from '../../../index_patterns'; +import { fields } from '../../filters/stubs'; +import { IndexPatternBase } from '../..'; + +jest.mock('../grammar'); // @ts-ignore import * as exists from './exists'; describe('kuery functions', () => { describe('exists', () => { - let indexPattern: IIndexPattern; + let indexPattern: IndexPatternBase; beforeEach(() => { - indexPattern = ({ + indexPattern = { fields, - } as unknown) as IIndexPattern; + }; }); describe('buildNodeParams', () => { diff --git a/src/plugins/data/common/es_query/kuery/functions/exists.ts b/packages/kbn-es-query/src/kuery/functions/exists.ts similarity index 93% rename from src/plugins/data/common/es_query/kuery/functions/exists.ts rename to packages/kbn-es-query/src/kuery/functions/exists.ts index 4df566d874d8b..0e05ade5181db 100644 --- a/src/plugins/data/common/es_query/kuery/functions/exists.ts +++ b/packages/kbn-es-query/src/kuery/functions/exists.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ +import { IndexPatternFieldBase, IndexPatternBase, KueryNode } from '../..'; import * as literal from '../node_types/literal'; -import { KueryNode, IndexPatternFieldBase, IndexPatternBase } from '../../..'; export function buildNodeParams(fieldName: string) { return { diff --git a/src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.test.ts b/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts similarity index 94% rename from src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.test.ts rename to packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts index 54c2383be785a..028c5e39bf5da 100644 --- a/src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts @@ -8,12 +8,13 @@ import { get } from 'lodash'; import { nodeTypes } from '../node_types'; -import { fields } from '../../../index_patterns/mocks'; -import { IIndexPattern } from '../../../index_patterns'; +import { fields } from '../../filters/stubs'; +import { IndexPatternBase } from '../..'; -// @ts-ignore import * as geoBoundingBox from './geo_bounding_box'; +jest.mock('../grammar'); + const params = { bottomRight: { lat: 50.73, @@ -27,12 +28,12 @@ const params = { describe('kuery functions', () => { describe('geoBoundingBox', () => { - let indexPattern: IIndexPattern; + let indexPattern: IndexPatternBase; beforeEach(() => { - indexPattern = ({ + indexPattern = { fields, - } as unknown) as IIndexPattern; + }; }); describe('buildNodeParams', () => { diff --git a/src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.ts b/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.ts similarity index 96% rename from src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.ts rename to packages/kbn-es-query/src/kuery/functions/geo_bounding_box.ts index 79bef10b14f71..b1fd8680af604 100644 --- a/src/plugins/data/common/es_query/kuery/functions/geo_bounding_box.ts +++ b/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.ts @@ -9,7 +9,7 @@ import _ from 'lodash'; import { nodeTypes } from '../node_types'; import * as ast from '../ast'; -import { IndexPatternBase, KueryNode, LatLon } from '../../..'; +import { IndexPatternBase, KueryNode, LatLon } from '../..'; export function buildNodeParams(fieldName: string, params: any) { params = _.pick(params, 'topLeft', 'bottomRight'); diff --git a/src/plugins/data/common/es_query/kuery/functions/geo_polygon.test.ts b/packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts similarity index 94% rename from src/plugins/data/common/es_query/kuery/functions/geo_polygon.test.ts rename to packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts index a106754a5f56f..f16ca378866f0 100644 --- a/src/plugins/data/common/es_query/kuery/functions/geo_polygon.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts @@ -7,12 +7,13 @@ */ import { nodeTypes } from '../node_types'; -import { fields } from '../../../index_patterns/mocks'; -import { IIndexPattern } from '../../../index_patterns'; +import { fields } from '../../filters/stubs'; +import { IndexPatternBase } from '../..'; -// @ts-ignore import * as geoPolygon from './geo_polygon'; +jest.mock('../grammar'); + const points = [ { lat: 69.77, @@ -30,12 +31,12 @@ const points = [ describe('kuery functions', () => { describe('geoPolygon', () => { - let indexPattern: IIndexPattern; + let indexPattern: IndexPatternBase; beforeEach(() => { - indexPattern = ({ + indexPattern = { fields, - } as unknown) as IIndexPattern; + }; }); describe('buildNodeParams', () => { diff --git a/src/plugins/data/common/es_query/kuery/functions/geo_polygon.ts b/packages/kbn-es-query/src/kuery/functions/geo_polygon.ts similarity index 96% rename from src/plugins/data/common/es_query/kuery/functions/geo_polygon.ts rename to packages/kbn-es-query/src/kuery/functions/geo_polygon.ts index 2e3280138502a..d02ba84237c7f 100644 --- a/src/plugins/data/common/es_query/kuery/functions/geo_polygon.ts +++ b/packages/kbn-es-query/src/kuery/functions/geo_polygon.ts @@ -8,7 +8,7 @@ import { nodeTypes } from '../node_types'; import * as ast from '../ast'; -import { IndexPatternBase, KueryNode, LatLon } from '../../..'; +import { IndexPatternBase, KueryNode, LatLon } from '../..'; import { LiteralTypeBuildNode } from '../node_types/types'; export function buildNodeParams(fieldName: string, points: LatLon[]) { diff --git a/src/plugins/data/common/es_query/kuery/functions/index.ts b/packages/kbn-es-query/src/kuery/functions/index.ts similarity index 100% rename from src/plugins/data/common/es_query/kuery/functions/index.ts rename to packages/kbn-es-query/src/kuery/functions/index.ts diff --git a/src/plugins/data/common/es_query/kuery/functions/is.test.ts b/packages/kbn-es-query/src/kuery/functions/is.test.ts similarity index 97% rename from src/plugins/data/common/es_query/kuery/functions/is.test.ts rename to packages/kbn-es-query/src/kuery/functions/is.test.ts index 55aac8189c1d8..292159e727a92 100644 --- a/src/plugins/data/common/es_query/kuery/functions/is.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/is.test.ts @@ -7,20 +7,21 @@ */ import { nodeTypes } from '../node_types'; -import { fields } from '../../../index_patterns/mocks'; +import { fields } from '../../filters/stubs'; -// @ts-ignore import * as is from './is'; -import { IIndexPattern } from '../../../index_patterns'; +import { IndexPatternBase } from '../..'; + +jest.mock('../grammar'); describe('kuery functions', () => { describe('is', () => { - let indexPattern: IIndexPattern; + let indexPattern: IndexPatternBase; beforeEach(() => { - indexPattern = ({ + indexPattern = { fields, - } as unknown) as IIndexPattern; + }; }); describe('buildNodeParams', () => { diff --git a/src/plugins/data/common/es_query/kuery/functions/is.ts b/packages/kbn-es-query/src/kuery/functions/is.ts similarity index 99% rename from src/plugins/data/common/es_query/kuery/functions/is.ts rename to packages/kbn-es-query/src/kuery/functions/is.ts index 381913670c26a..c8d33921b084a 100644 --- a/src/plugins/data/common/es_query/kuery/functions/is.ts +++ b/packages/kbn-es-query/src/kuery/functions/is.ts @@ -11,7 +11,7 @@ import { getPhraseScript } from '../../filters'; import { getFields } from './utils/get_fields'; import { getTimeZoneFromSettings } from '../../utils'; import { getFullFieldNameNode } from './utils/get_full_field_name_node'; -import { IndexPatternBase, KueryNode, IndexPatternFieldBase } from '../../..'; +import { IndexPatternBase, KueryNode, IndexPatternFieldBase } from '../..'; import * as ast from '../ast'; diff --git a/src/plugins/data/common/es_query/kuery/functions/nested.test.ts b/packages/kbn-es-query/src/kuery/functions/nested.test.ts similarity index 90% rename from src/plugins/data/common/es_query/kuery/functions/nested.test.ts rename to packages/kbn-es-query/src/kuery/functions/nested.test.ts index 50460500c0877..47e515b9bd47f 100644 --- a/src/plugins/data/common/es_query/kuery/functions/nested.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/nested.test.ts @@ -7,24 +7,25 @@ */ import { nodeTypes } from '../node_types'; -import { fields } from '../../../index_patterns/mocks'; -import { IIndexPattern } from '../../../index_patterns'; +import { fields } from '../../filters/stubs'; +import { IndexPatternBase } from '../..'; import * as ast from '../ast'; -// @ts-ignore import * as nested from './nested'; +jest.mock('../grammar'); + const childNode = nodeTypes.function.buildNode('is', 'child', 'foo'); describe('kuery functions', () => { describe('nested', () => { - let indexPattern: IIndexPattern; + let indexPattern: IndexPatternBase; beforeEach(() => { - indexPattern = ({ + indexPattern = { fields, - } as unknown) as IIndexPattern; + }; }); describe('buildNodeParams', () => { diff --git a/src/plugins/data/common/es_query/kuery/functions/nested.ts b/packages/kbn-es-query/src/kuery/functions/nested.ts similarity index 95% rename from src/plugins/data/common/es_query/kuery/functions/nested.ts rename to packages/kbn-es-query/src/kuery/functions/nested.ts index 46ceeaf3e5de6..248de1c40d62a 100644 --- a/src/plugins/data/common/es_query/kuery/functions/nested.ts +++ b/packages/kbn-es-query/src/kuery/functions/nested.ts @@ -8,7 +8,7 @@ import * as ast from '../ast'; import * as literal from '../node_types/literal'; -import { IndexPatternBase, KueryNode } from '../../..'; +import { IndexPatternBase, KueryNode } from '../..'; export function buildNodeParams(path: any, child: any) { const pathNode = diff --git a/src/plugins/data/common/es_query/kuery/functions/not.test.ts b/packages/kbn-es-query/src/kuery/functions/not.test.ts similarity index 87% rename from src/plugins/data/common/es_query/kuery/functions/not.test.ts rename to packages/kbn-es-query/src/kuery/functions/not.test.ts index ab83df581edd7..a44f3e9c5dda8 100644 --- a/src/plugins/data/common/es_query/kuery/functions/not.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/not.test.ts @@ -7,24 +7,24 @@ */ import { nodeTypes } from '../node_types'; -import { fields } from '../../../index_patterns/mocks'; -import { IIndexPattern } from '../../../index_patterns'; +import { fields } from '../../filters/stubs'; +import { IndexPatternBase } from '../..'; import * as ast from '../ast'; - -// @ts-ignore import * as not from './not'; +jest.mock('../grammar'); + const childNode = nodeTypes.function.buildNode('is', 'extension', 'jpg'); describe('kuery functions', () => { describe('not', () => { - let indexPattern: IIndexPattern; + let indexPattern: IndexPatternBase; beforeEach(() => { - indexPattern = ({ + indexPattern = { fields, - } as unknown) as IIndexPattern; + }; }); describe('buildNodeParams', () => { diff --git a/src/plugins/data/common/es_query/kuery/functions/not.ts b/packages/kbn-es-query/src/kuery/functions/not.ts similarity index 93% rename from src/plugins/data/common/es_query/kuery/functions/not.ts rename to packages/kbn-es-query/src/kuery/functions/not.ts index f837cd261c814..96404ee800010 100644 --- a/src/plugins/data/common/es_query/kuery/functions/not.ts +++ b/packages/kbn-es-query/src/kuery/functions/not.ts @@ -7,7 +7,7 @@ */ import * as ast from '../ast'; -import { IndexPatternBase, KueryNode } from '../../..'; +import { IndexPatternBase, KueryNode } from '../..'; export function buildNodeParams(child: KueryNode) { return { diff --git a/src/plugins/data/common/es_query/kuery/functions/or.test.ts b/packages/kbn-es-query/src/kuery/functions/or.test.ts similarity index 90% rename from src/plugins/data/common/es_query/kuery/functions/or.test.ts rename to packages/kbn-es-query/src/kuery/functions/or.test.ts index 5e151098a5ef9..15faa7e1753d3 100644 --- a/src/plugins/data/common/es_query/kuery/functions/or.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/or.test.ts @@ -7,25 +7,25 @@ */ import { nodeTypes } from '../node_types'; -import { fields } from '../../../index_patterns/mocks'; -import { IIndexPattern } from '../../../index_patterns'; +import { fields } from '../../filters/stubs'; +import { IndexPatternBase } from '../..'; import * as ast from '../ast'; -// @ts-ignore import * as or from './or'; +jest.mock('../grammar'); const childNode1 = nodeTypes.function.buildNode('is', 'machine.os', 'osx'); const childNode2 = nodeTypes.function.buildNode('is', 'extension', 'jpg'); describe('kuery functions', () => { describe('or', () => { - let indexPattern: IIndexPattern; + let indexPattern: IndexPatternBase; beforeEach(() => { - indexPattern = ({ + indexPattern = { fields, - } as unknown) as IIndexPattern; + }; }); describe('buildNodeParams', () => { diff --git a/src/plugins/data/common/es_query/kuery/functions/or.ts b/packages/kbn-es-query/src/kuery/functions/or.ts similarity index 94% rename from src/plugins/data/common/es_query/kuery/functions/or.ts rename to packages/kbn-es-query/src/kuery/functions/or.ts index 7365cc39595e6..b94814e1f7c05 100644 --- a/src/plugins/data/common/es_query/kuery/functions/or.ts +++ b/packages/kbn-es-query/src/kuery/functions/or.ts @@ -7,7 +7,7 @@ */ import * as ast from '../ast'; -import { IndexPatternBase, KueryNode } from '../../..'; +import { IndexPatternBase, KueryNode } from '../..'; export function buildNodeParams(children: KueryNode[]) { return { diff --git a/src/plugins/data/common/es_query/kuery/functions/range.test.ts b/packages/kbn-es-query/src/kuery/functions/range.test.ts similarity index 96% rename from src/plugins/data/common/es_query/kuery/functions/range.test.ts rename to packages/kbn-es-query/src/kuery/functions/range.test.ts index c4bc9c1372b27..fa1805e64887c 100644 --- a/src/plugins/data/common/es_query/kuery/functions/range.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/range.test.ts @@ -8,21 +8,21 @@ import { get } from 'lodash'; import { nodeTypes } from '../node_types'; -import { fields } from '../../../index_patterns/mocks'; -import { IIndexPattern } from '../../../index_patterns'; +import { fields } from '../../filters/stubs'; +import { IndexPatternBase } from '../..'; import { RangeFilterParams } from '../../filters'; -// @ts-ignore import * as range from './range'; +jest.mock('../grammar'); describe('kuery functions', () => { describe('range', () => { - let indexPattern: IIndexPattern; + let indexPattern: IndexPatternBase; beforeEach(() => { - indexPattern = ({ + indexPattern = { fields, - } as unknown) as IIndexPattern; + }; }); describe('buildNodeParams', () => { diff --git a/src/plugins/data/common/es_query/kuery/functions/range.ts b/packages/kbn-es-query/src/kuery/functions/range.ts similarity index 98% rename from src/plugins/data/common/es_query/kuery/functions/range.ts rename to packages/kbn-es-query/src/kuery/functions/range.ts index b134434dc182b..e80a365441c43 100644 --- a/src/plugins/data/common/es_query/kuery/functions/range.ts +++ b/packages/kbn-es-query/src/kuery/functions/range.ts @@ -13,7 +13,7 @@ import { getRangeScript, RangeFilterParams } from '../../filters'; import { getFields } from './utils/get_fields'; import { getTimeZoneFromSettings } from '../../utils'; import { getFullFieldNameNode } from './utils/get_full_field_name_node'; -import { IndexPatternBase, KueryNode } from '../../..'; +import { IndexPatternBase, KueryNode } from '../..'; export function buildNodeParams(fieldName: string, params: RangeFilterParams) { const paramsToMap = _.pick(params, 'gt', 'lt', 'gte', 'lte', 'format'); diff --git a/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.test.ts b/packages/kbn-es-query/src/kuery/functions/utils/get_fields.test.ts similarity index 97% rename from src/plugins/data/common/es_query/kuery/functions/utils/get_fields.test.ts rename to packages/kbn-es-query/src/kuery/functions/utils/get_fields.test.ts index 949f94d043553..4125b0a572566 100644 --- a/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/utils/get_fields.test.ts @@ -7,13 +7,13 @@ */ import { IndexPatternBase } from '../../..'; -import { fields } from '../../../../index_patterns/mocks'; +import { fields } from '../../../filters/stubs'; import { nodeTypes } from '../../index'; - -// @ts-ignore import { getFields } from './get_fields'; +jest.mock('../../grammar'); + describe('getFields', () => { let indexPattern: IndexPatternBase; diff --git a/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.ts b/packages/kbn-es-query/src/kuery/functions/utils/get_fields.ts similarity index 94% rename from src/plugins/data/common/es_query/kuery/functions/utils/get_fields.ts rename to packages/kbn-es-query/src/kuery/functions/utils/get_fields.ts index 7dac1262d5062..db3826d4ef8c4 100644 --- a/src/plugins/data/common/es_query/kuery/functions/utils/get_fields.ts +++ b/packages/kbn-es-query/src/kuery/functions/utils/get_fields.ts @@ -8,7 +8,7 @@ import * as literal from '../../node_types/literal'; import * as wildcard from '../../node_types/wildcard'; -import { KueryNode, IndexPatternBase } from '../../../..'; +import { KueryNode, IndexPatternBase } from '../../..'; import { LiteralTypeBuildNode } from '../../node_types/types'; export function getFields(node: KueryNode, indexPattern?: IndexPatternBase) { diff --git a/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.test.ts b/packages/kbn-es-query/src/kuery/functions/utils/get_full_field_name_node.test.ts similarity index 92% rename from src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.test.ts rename to packages/kbn-es-query/src/kuery/functions/utils/get_full_field_name_node.test.ts index 639f5584ef592..dccfc5d1c463a 100644 --- a/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/utils/get_full_field_name_node.test.ts @@ -7,19 +7,19 @@ */ import { nodeTypes } from '../../node_types'; -import { fields } from '../../../../index_patterns/mocks'; -import { IIndexPattern } from '../../../../index_patterns'; - -// @ts-ignore +import { fields } from '../../../filters/stubs'; +import { IndexPatternBase } from '../../..'; import { getFullFieldNameNode } from './get_full_field_name_node'; +jest.mock('../../grammar'); + describe('getFullFieldNameNode', function () { - let indexPattern: IIndexPattern; + let indexPattern: IndexPatternBase; beforeEach(() => { - indexPattern = ({ + indexPattern = { fields, - } as unknown) as IIndexPattern; + }; }); test('should return unchanged name node if no nested path is passed in', () => { diff --git a/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.ts b/packages/kbn-es-query/src/kuery/functions/utils/get_full_field_name_node.ts similarity index 99% rename from src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.ts rename to packages/kbn-es-query/src/kuery/functions/utils/get_full_field_name_node.ts index 2a31ebeee2fab..6b575fbdea8fb 100644 --- a/src/plugins/data/common/es_query/kuery/functions/utils/get_full_field_name_node.ts +++ b/packages/kbn-es-query/src/kuery/functions/utils/get_full_field_name_node.ts @@ -7,7 +7,7 @@ */ import { getFields } from './get_fields'; -import { IndexPatternBase, IndexPatternFieldBase, KueryNode } from '../../../..'; +import { IndexPatternBase, IndexPatternFieldBase, KueryNode } from '../../..'; export function getFullFieldNameNode( rootNameNode: any, diff --git a/packages/kbn-es-query/src/kuery/grammar/__mocks__/grammar.js b/packages/kbn-es-query/src/kuery/grammar/__mocks__/grammar.js new file mode 100644 index 0000000000000..89c13bb2b05c2 --- /dev/null +++ b/packages/kbn-es-query/src/kuery/grammar/__mocks__/grammar.js @@ -0,0 +1,2219 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// Generated by Peggy 1.2.0. +// +// https://peggyjs.org/ +/* eslint-disable */ + +"use strict"; + +function peg$subclass(child, parent) { + function C() { this.constructor = child; } + C.prototype = parent.prototype; + child.prototype = new C(); +} + +function peg$SyntaxError(message, expected, found, location) { + var self = Error.call(this, message); + if (Object.setPrototypeOf) { + Object.setPrototypeOf(self, peg$SyntaxError.prototype); + } + self.expected = expected; + self.found = found; + self.location = location; + self.name = "SyntaxError"; + return self; +} + +peg$subclass(peg$SyntaxError, Error); + +function peg$padEnd(str, targetLength, padString) { + padString = padString || " "; + if (str.length > targetLength) { return str; } + targetLength -= str.length; + padString += padString.repeat(targetLength); + return str + padString.slice(0, targetLength); +} + +peg$SyntaxError.prototype.format = function(sources) { + var str = "Error: " + this.message; + if (this.location) { + var src = null; + var k; + for (k = 0; k < sources.length; k++) { + if (sources[k].source === this.location.source) { + src = sources[k].text.split(/\r\n|\n|\r/g); + break; + } + } + var s = this.location.start; + var loc = this.location.source + ":" + s.line + ":" + s.column; + if (src) { + var e = this.location.end; + var filler = peg$padEnd("", s.line.toString().length); + var line = src[s.line - 1]; + var last = s.line === e.line ? e.column : line.length + 1; + str += "\n --> " + loc + "\n" + + filler + " |\n" + + s.line + " | " + line + "\n" + + filler + " | " + peg$padEnd("", s.column - 1) + + peg$padEnd("", last - s.column, "^"); + } else { + str += "\n at " + loc; + } + } + return str; +}; + +peg$SyntaxError.buildMessage = function(expected, found) { + var DESCRIBE_EXPECTATION_FNS = { + literal: function(expectation) { + return "\"" + literalEscape(expectation.text) + "\""; + }, + + class: function(expectation) { + var escapedParts = expectation.parts.map(function(part) { + return Array.isArray(part) + ? classEscape(part[0]) + "-" + classEscape(part[1]) + : classEscape(part); + }); + + return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]"; + }, + + any: function() { + return "any character"; + }, + + end: function() { + return "end of input"; + }, + + other: function(expectation) { + return expectation.description; + } + }; + + function hex(ch) { + return ch.charCodeAt(0).toString(16).toUpperCase(); + } + + function literalEscape(s) { + return s + .replace(/\\/g, "\\\\") + .replace(/"/g, "\\\"") + .replace(/\0/g, "\\0") + .replace(/\t/g, "\\t") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); }) + .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); }); + } + + function classEscape(s) { + return s + .replace(/\\/g, "\\\\") + .replace(/\]/g, "\\]") + .replace(/\^/g, "\\^") + .replace(/-/g, "\\-") + .replace(/\0/g, "\\0") + .replace(/\t/g, "\\t") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); }) + .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); }); + } + + function describeExpectation(expectation) { + return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation); + } + + function describeExpected(expected) { + var descriptions = expected.map(describeExpectation); + var i, j; + + descriptions.sort(); + + if (descriptions.length > 0) { + for (i = 1, j = 1; i < descriptions.length; i++) { + if (descriptions[i - 1] !== descriptions[i]) { + descriptions[j] = descriptions[i]; + j++; + } + } + descriptions.length = j; + } + + switch (descriptions.length) { + case 1: + return descriptions[0]; + + case 2: + return descriptions[0] + " or " + descriptions[1]; + + default: + return descriptions.slice(0, -1).join(", ") + + ", or " + + descriptions[descriptions.length - 1]; + } + } + + function describeFound(found) { + return found ? "\"" + literalEscape(found) + "\"" : "end of input"; + } + + return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found."; +}; + +function peg$parse(input, options) { + options = options !== undefined ? options : {}; + + var peg$FAILED = {}; + var peg$source = options.grammarSource; + + var peg$startRuleFunctions = { start: peg$parsestart, Literal: peg$parseLiteral }; + var peg$startRuleFunction = peg$parsestart; + + var peg$c0 = "("; + var peg$c1 = ")"; + var peg$c2 = ":"; + var peg$c3 = "{"; + var peg$c4 = "}"; + var peg$c5 = "or"; + var peg$c6 = "and"; + var peg$c7 = "not"; + var peg$c8 = "\""; + var peg$c9 = "\\"; + var peg$c10 = "*"; + var peg$c11 = "\\t"; + var peg$c12 = "\\r"; + var peg$c13 = "\\n"; + var peg$c14 = "u"; + var peg$c15 = "<="; + var peg$c16 = ">="; + var peg$c17 = "<"; + var peg$c18 = ">"; + var peg$c19 = "@kuery-cursor@"; + + var peg$r0 = /^[\\"]/; + var peg$r1 = /^[^"]/; + var peg$r2 = /^[\\():<>"*{}]/; + var peg$r3 = /^[0-9a-f]/i; + var peg$r4 = /^[ \t\r\n\xA0]/; + + var peg$e0 = peg$literalExpectation("(", false); + var peg$e1 = peg$literalExpectation(")", false); + var peg$e2 = peg$literalExpectation(":", false); + var peg$e3 = peg$literalExpectation("{", false); + var peg$e4 = peg$literalExpectation("}", false); + var peg$e5 = peg$otherExpectation("fieldName"); + var peg$e6 = peg$otherExpectation("value"); + var peg$e7 = peg$otherExpectation("OR"); + var peg$e8 = peg$literalExpectation("or", true); + var peg$e9 = peg$otherExpectation("AND"); + var peg$e10 = peg$literalExpectation("and", true); + var peg$e11 = peg$otherExpectation("NOT"); + var peg$e12 = peg$literalExpectation("not", true); + var peg$e13 = peg$otherExpectation("literal"); + var peg$e14 = peg$literalExpectation("\"", false); + var peg$e15 = peg$literalExpectation("\\", false); + var peg$e16 = peg$classExpectation(["\\", "\""], false, false); + var peg$e17 = peg$classExpectation(["\""], true, false); + var peg$e18 = peg$anyExpectation(); + var peg$e19 = peg$literalExpectation("*", false); + var peg$e20 = peg$literalExpectation("\\t", false); + var peg$e21 = peg$literalExpectation("\\r", false); + var peg$e22 = peg$literalExpectation("\\n", false); + var peg$e23 = peg$classExpectation(["\\", "(", ")", ":", "<", ">", "\"", "*", "{", "}"], false, false); + var peg$e24 = peg$literalExpectation("u", false); + var peg$e25 = peg$classExpectation([["0", "9"], ["a", "f"]], false, true); + var peg$e26 = peg$literalExpectation("<=", false); + var peg$e27 = peg$literalExpectation(">=", false); + var peg$e28 = peg$literalExpectation("<", false); + var peg$e29 = peg$literalExpectation(">", false); + var peg$e30 = peg$otherExpectation("whitespace"); + var peg$e31 = peg$classExpectation([" ", "\t", "\r", "\n", "\xA0"], false, false); + var peg$e32 = peg$literalExpectation("@kuery-cursor@", false); + + var peg$f0 = function(query, trailing) { + if (trailing.type === 'cursor') { + return { + ...trailing, + suggestionTypes: ['conjunction'] + }; + } + if (query !== null) return query; + return nodeTypes.function.buildNode('is', '*', '*'); + }; + var peg$f1 = function(head, query) { return query; }; + var peg$f2 = function(head, tail) { + const nodes = [head, ...tail]; + const cursor = parseCursor && nodes.find(node => node.type === 'cursor'); + if (cursor) return cursor; + return buildFunctionNode('or', nodes); + }; + var peg$f3 = function(head, tail) { + const nodes = [head, ...tail]; + const cursor = parseCursor && nodes.find(node => node.type === 'cursor'); + if (cursor) return cursor; + return buildFunctionNode('and', nodes); + }; + var peg$f4 = function(query) { + if (query.type === 'cursor') return query; + return buildFunctionNode('not', [query]); + }; + var peg$f5 = function(query, trailing) { + if (trailing.type === 'cursor') { + return { + ...trailing, + suggestionTypes: ['conjunction'] + }; + } + return query; + }; + var peg$f6 = function(field, query, trailing) { + if (query.type === 'cursor') { + return { + ...query, + nestedPath: query.nestedPath ? `${field.value}.${query.nestedPath}` : field.value, + } + }; + + if (trailing.type === 'cursor') { + return { + ...trailing, + suggestionTypes: ['conjunction'] + }; + } + return buildFunctionNode('nested', [field, query]); + }; + var peg$f7 = function(field, operator, value) { + if (value.type === 'cursor') { + return { + ...value, + suggestionTypes: ['conjunction'] + }; + } + const range = buildNamedArgNode(operator, value); + return buildFunctionNode('range', [field, range]); + }; + var peg$f8 = function(field, partial) { + if (partial.type === 'cursor') { + return { + ...partial, + fieldName: field.value, + suggestionTypes: ['value', 'conjunction'] + }; + } + return partial(field); + }; + var peg$f9 = function(partial) { + if (partial.type === 'cursor') { + const fieldName = `${partial.prefix}${partial.suffix}`.trim(); + return { + ...partial, + fieldName, + suggestionTypes: ['field', 'operator', 'conjunction'] + }; + } + const field = buildLiteralNode(null); + return partial(field); + }; + var peg$f10 = function(partial, trailing) { + if (trailing.type === 'cursor') { + return { + ...trailing, + suggestionTypes: ['conjunction'] + }; + } + return partial; + }; + var peg$f11 = function(head, partial) { return partial; }; + var peg$f12 = function(head, tail) { + const nodes = [head, ...tail]; + const cursor = parseCursor && nodes.find(node => node.type === 'cursor'); + if (cursor) { + return { + ...cursor, + suggestionTypes: ['value'] + }; + } + return (field) => buildFunctionNode('or', nodes.map(partial => partial(field))); + }; + var peg$f13 = function(head, tail) { + const nodes = [head, ...tail]; + const cursor = parseCursor && nodes.find(node => node.type === 'cursor'); + if (cursor) { + return { + ...cursor, + suggestionTypes: ['value'] + }; + } + return (field) => buildFunctionNode('and', nodes.map(partial => partial(field))); + }; + var peg$f14 = function(partial) { + if (partial.type === 'cursor') { + return { + ...list, + suggestionTypes: ['value'] + }; + } + return (field) => buildFunctionNode('not', [partial(field)]); + }; + var peg$f15 = function(value) { + if (value.type === 'cursor') return value; + const isPhrase = buildLiteralNode(true); + return (field) => buildFunctionNode('is', [field, value, isPhrase]); + }; + var peg$f16 = function(value) { + if (value.type === 'cursor') return value; + + if (!allowLeadingWildcards && value.type === 'wildcard' && nodeTypes.wildcard.hasLeadingWildcard(value)) { + error('Leading wildcards are disabled. See query:allowLeadingWildcards in Advanced Settings.'); + } + + const isPhrase = buildLiteralNode(false); + return (field) => buildFunctionNode('is', [field, value, isPhrase]); + }; + var peg$f17 = function() { return parseCursor; }; + var peg$f18 = function(prefix, cursor, suffix) { + const { start, end } = location(); + return { + type: 'cursor', + start: start.offset, + end: end.offset - cursor.length, + prefix: prefix.join(''), + suffix: suffix.join(''), + text: text().replace(cursor, '') + }; + }; + var peg$f19 = function(chars) { + return buildLiteralNode(chars.join('')); + }; + var peg$f20 = function(char) { return char; }; + var peg$f21 = function(chars) { + const sequence = chars.join('').trim(); + if (sequence === 'null') return buildLiteralNode(null); + if (sequence === 'true') return buildLiteralNode(true); + if (sequence === 'false') return buildLiteralNode(false); + if (chars.includes(wildcardSymbol)) return buildWildcardNode(sequence); + return buildLiteralNode(sequence); + }; + var peg$f22 = function() { return wildcardSymbol; }; + var peg$f23 = function() { return '\t'; }; + var peg$f24 = function() { return '\r'; }; + var peg$f25 = function() { return '\n'; }; + var peg$f26 = function(keyword) { return keyword; }; + var peg$f27 = function(sequence) { return sequence; }; + var peg$f28 = function(digits) { + return String.fromCharCode(parseInt(digits, 16)); + }; + var peg$f29 = function() { return 'lte'; }; + var peg$f30 = function() { return 'gte'; }; + var peg$f31 = function() { return 'lt'; }; + var peg$f32 = function() { return 'gt'; }; + var peg$f33 = function() { return cursorSymbol; }; + + var peg$currPos = 0; + var peg$savedPos = 0; + var peg$posDetailsCache = [{ line: 1, column: 1 }]; + var peg$maxFailPos = 0; + var peg$maxFailExpected = []; + var peg$silentFails = 0; + + var peg$result; + + if ("startRule" in options) { + if (!(options.startRule in peg$startRuleFunctions)) { + throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); + } + + peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; + } + + function text() { + return input.substring(peg$savedPos, peg$currPos); + } + + function offset() { + return peg$savedPos; + } + + function range() { + return { + source: peg$source, + start: peg$savedPos, + end: peg$currPos + }; + } + + function location() { + return peg$computeLocation(peg$savedPos, peg$currPos); + } + + function expected(description, location) { + location = location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildStructuredError( + [peg$otherExpectation(description)], + input.substring(peg$savedPos, peg$currPos), + location + ); + } + + function error(message, location) { + location = location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildSimpleError(message, location); + } + + function peg$literalExpectation(text, ignoreCase) { + return { type: "literal", text: text, ignoreCase: ignoreCase }; + } + + function peg$classExpectation(parts, inverted, ignoreCase) { + return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase }; + } + + function peg$anyExpectation() { + return { type: "any" }; + } + + function peg$endExpectation() { + return { type: "end" }; + } + + function peg$otherExpectation(description) { + return { type: "other", description: description }; + } + + function peg$computePosDetails(pos) { + var details = peg$posDetailsCache[pos]; + var p; + + if (details) { + return details; + } else { + p = pos - 1; + while (!peg$posDetailsCache[p]) { + p--; + } + + details = peg$posDetailsCache[p]; + details = { + line: details.line, + column: details.column + }; + + while (p < pos) { + if (input.charCodeAt(p) === 10) { + details.line++; + details.column = 1; + } else { + details.column++; + } + + p++; + } + + peg$posDetailsCache[pos] = details; + + return details; + } + } + + function peg$computeLocation(startPos, endPos) { + var startPosDetails = peg$computePosDetails(startPos); + var endPosDetails = peg$computePosDetails(endPos); + + return { + source: peg$source, + start: { + offset: startPos, + line: startPosDetails.line, + column: startPosDetails.column + }, + end: { + offset: endPos, + line: endPosDetails.line, + column: endPosDetails.column + } + }; + } + + function peg$fail(expected) { + if (peg$currPos < peg$maxFailPos) { return; } + + if (peg$currPos > peg$maxFailPos) { + peg$maxFailPos = peg$currPos; + peg$maxFailExpected = []; + } + + peg$maxFailExpected.push(expected); + } + + function peg$buildSimpleError(message, location) { + return new peg$SyntaxError(message, null, null, location); + } + + function peg$buildStructuredError(expected, found, location) { + return new peg$SyntaxError( + peg$SyntaxError.buildMessage(expected, found), + expected, + found, + location + ); + } + + function peg$parsestart() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = []; + s2 = peg$parseSpace(); + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parseSpace(); + } + s2 = peg$parseOrQuery(); + if (s2 === peg$FAILED) { + s2 = null; + } + s3 = peg$parseOptionalSpace(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f0(s2, s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseOrQuery() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseAndQuery(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parseOr(); + if (s4 !== peg$FAILED) { + s5 = peg$parseAndQuery(); + if (s5 !== peg$FAILED) { + peg$savedPos = s3; + s3 = peg$f1(s1, s5); + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parseOr(); + if (s4 !== peg$FAILED) { + s5 = peg$parseAndQuery(); + if (s5 !== peg$FAILED) { + peg$savedPos = s3; + s3 = peg$f1(s1, s5); + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f2(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseAndQuery(); + } + + return s0; + } + + function peg$parseAndQuery() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseNotQuery(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parseAnd(); + if (s4 !== peg$FAILED) { + s5 = peg$parseNotQuery(); + if (s5 !== peg$FAILED) { + peg$savedPos = s3; + s3 = peg$f1(s1, s5); + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parseAnd(); + if (s4 !== peg$FAILED) { + s5 = peg$parseNotQuery(); + if (s5 !== peg$FAILED) { + peg$savedPos = s3; + s3 = peg$f1(s1, s5); + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f3(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseNotQuery(); + } + + return s0; + } + + function peg$parseNotQuery() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$parseNot(); + if (s1 !== peg$FAILED) { + s2 = peg$parseSubQuery(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f4(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseSubQuery(); + } + + return s0; + } + + function peg$parseSubQuery() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 40) { + s1 = peg$c0; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e0); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseSpace(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseSpace(); + } + s3 = peg$parseOrQuery(); + if (s3 !== peg$FAILED) { + s4 = peg$parseOptionalSpace(); + if (s4 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 41) { + s5 = peg$c1; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f5(s3, s4); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseNestedQuery(); + } + + return s0; + } + + function peg$parseNestedQuery() { + var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; + + s0 = peg$currPos; + s1 = peg$parseField(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseSpace(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseSpace(); + } + if (input.charCodeAt(peg$currPos) === 58) { + s3 = peg$c2; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e2); } + } + if (s3 !== peg$FAILED) { + s4 = []; + s5 = peg$parseSpace(); + while (s5 !== peg$FAILED) { + s4.push(s5); + s5 = peg$parseSpace(); + } + if (input.charCodeAt(peg$currPos) === 123) { + s5 = peg$c3; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + if (s5 !== peg$FAILED) { + s6 = []; + s7 = peg$parseSpace(); + while (s7 !== peg$FAILED) { + s6.push(s7); + s7 = peg$parseSpace(); + } + s7 = peg$parseOrQuery(); + if (s7 !== peg$FAILED) { + s8 = peg$parseOptionalSpace(); + if (s8 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 125) { + s9 = peg$c4; + peg$currPos++; + } else { + s9 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + if (s9 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f6(s1, s7, s8); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseExpression(); + } + + return s0; + } + + function peg$parseExpression() { + var s0; + + s0 = peg$parseFieldRangeExpression(); + if (s0 === peg$FAILED) { + s0 = peg$parseFieldValueExpression(); + if (s0 === peg$FAILED) { + s0 = peg$parseValueExpression(); + } + } + + return s0; + } + + function peg$parseField() { + var s0, s1; + + peg$silentFails++; + s0 = peg$parseLiteral(); + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e5); } + } + + return s0; + } + + function peg$parseFieldRangeExpression() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseField(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseSpace(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseSpace(); + } + s3 = peg$parseRangeOperator(); + if (s3 !== peg$FAILED) { + s4 = []; + s5 = peg$parseSpace(); + while (s5 !== peg$FAILED) { + s4.push(s5); + s5 = peg$parseSpace(); + } + s5 = peg$parseLiteral(); + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f7(s1, s3, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseFieldValueExpression() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseField(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseSpace(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseSpace(); + } + if (input.charCodeAt(peg$currPos) === 58) { + s3 = peg$c2; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e2); } + } + if (s3 !== peg$FAILED) { + s4 = []; + s5 = peg$parseSpace(); + while (s5 !== peg$FAILED) { + s4.push(s5); + s5 = peg$parseSpace(); + } + s5 = peg$parseListOfValues(); + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f8(s1, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseValueExpression() { + var s0, s1; + + s0 = peg$currPos; + s1 = peg$parseValue(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f9(s1); + } + s0 = s1; + + return s0; + } + + function peg$parseListOfValues() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 40) { + s1 = peg$c0; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e0); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseSpace(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseSpace(); + } + s3 = peg$parseOrListOfValues(); + if (s3 !== peg$FAILED) { + s4 = peg$parseOptionalSpace(); + if (s4 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 41) { + s5 = peg$c1; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f10(s3, s4); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseValue(); + } + + return s0; + } + + function peg$parseOrListOfValues() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseAndListOfValues(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parseOr(); + if (s4 !== peg$FAILED) { + s5 = peg$parseAndListOfValues(); + if (s5 !== peg$FAILED) { + peg$savedPos = s3; + s3 = peg$f11(s1, s5); + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parseOr(); + if (s4 !== peg$FAILED) { + s5 = peg$parseAndListOfValues(); + if (s5 !== peg$FAILED) { + peg$savedPos = s3; + s3 = peg$f11(s1, s5); + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f12(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseAndListOfValues(); + } + + return s0; + } + + function peg$parseAndListOfValues() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = peg$parseNotListOfValues(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$currPos; + s4 = peg$parseAnd(); + if (s4 !== peg$FAILED) { + s5 = peg$parseNotListOfValues(); + if (s5 !== peg$FAILED) { + peg$savedPos = s3; + s3 = peg$f11(s1, s5); + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$currPos; + s4 = peg$parseAnd(); + if (s4 !== peg$FAILED) { + s5 = peg$parseNotListOfValues(); + if (s5 !== peg$FAILED) { + peg$savedPos = s3; + s3 = peg$f11(s1, s5); + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f13(s1, s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseNotListOfValues(); + } + + return s0; + } + + function peg$parseNotListOfValues() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$parseNot(); + if (s1 !== peg$FAILED) { + s2 = peg$parseListOfValues(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f14(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parseListOfValues(); + } + + return s0; + } + + function peg$parseValue() { + var s0, s1; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$parseQuotedString(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f15(s1); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseUnquotedLiteral(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f16(s1); + } + s0 = s1; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e6); } + } + + return s0; + } + + function peg$parseOr() { + var s0, s1, s2, s3, s4; + + peg$silentFails++; + s0 = peg$currPos; + s1 = []; + s2 = peg$parseSpace(); + if (s2 !== peg$FAILED) { + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parseSpace(); + } + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + if (input.substr(peg$currPos, 2).toLowerCase() === peg$c5) { + s2 = input.substr(peg$currPos, 2); + peg$currPos += 2; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e8); } + } + if (s2 !== peg$FAILED) { + s3 = []; + s4 = peg$parseSpace(); + if (s4 !== peg$FAILED) { + while (s4 !== peg$FAILED) { + s3.push(s4); + s4 = peg$parseSpace(); + } + } else { + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + s1 = [s1, s2, s3]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e7); } + } + + return s0; + } + + function peg$parseAnd() { + var s0, s1, s2, s3, s4; + + peg$silentFails++; + s0 = peg$currPos; + s1 = []; + s2 = peg$parseSpace(); + if (s2 !== peg$FAILED) { + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parseSpace(); + } + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + if (input.substr(peg$currPos, 3).toLowerCase() === peg$c6) { + s2 = input.substr(peg$currPos, 3); + peg$currPos += 3; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s2 !== peg$FAILED) { + s3 = []; + s4 = peg$parseSpace(); + if (s4 !== peg$FAILED) { + while (s4 !== peg$FAILED) { + s3.push(s4); + s4 = peg$parseSpace(); + } + } else { + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + s1 = [s1, s2, s3]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e9); } + } + + return s0; + } + + function peg$parseNot() { + var s0, s1, s2, s3; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 3).toLowerCase() === peg$c7) { + s1 = input.substr(peg$currPos, 3); + peg$currPos += 3; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseSpace(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseSpace(); + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s1 = [s1, s2]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + + return s0; + } + + function peg$parseLiteral() { + var s0, s1; + + peg$silentFails++; + s0 = peg$parseQuotedString(); + if (s0 === peg$FAILED) { + s0 = peg$parseUnquotedLiteral(); + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e13); } + } + + return s0; + } + + function peg$parseQuotedString() { + var s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + peg$savedPos = peg$currPos; + s1 = peg$f17(); + if (s1) { + s1 = undefined; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 34) { + s2 = peg$c8; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } + if (s2 !== peg$FAILED) { + s3 = []; + s4 = peg$parseQuotedCharacter(); + while (s4 !== peg$FAILED) { + s3.push(s4); + s4 = peg$parseQuotedCharacter(); + } + s4 = peg$parseCursor(); + if (s4 !== peg$FAILED) { + s5 = []; + s6 = peg$parseQuotedCharacter(); + while (s6 !== peg$FAILED) { + s5.push(s6); + s6 = peg$parseQuotedCharacter(); + } + if (input.charCodeAt(peg$currPos) === 34) { + s6 = peg$c8; + peg$currPos++; + } else { + s6 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } + if (s6 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f18(s3, s4, s5); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 34) { + s1 = peg$c8; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseQuotedCharacter(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseQuotedCharacter(); + } + if (input.charCodeAt(peg$currPos) === 34) { + s3 = peg$c8; + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f19(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + + return s0; + } + + function peg$parseQuotedCharacter() { + var s0, s1, s2; + + s0 = peg$parseEscapedWhitespace(); + if (s0 === peg$FAILED) { + s0 = peg$parseEscapedUnicodeSequence(); + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s1 = peg$c9; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e15); } + } + if (s1 !== peg$FAILED) { + if (peg$r0.test(input.charAt(peg$currPos))) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f20(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + s2 = peg$parseCursor(); + peg$silentFails--; + if (s2 === peg$FAILED) { + s1 = undefined; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + if (peg$r1.test(input.charAt(peg$currPos))) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e17); } + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f20(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + } + } + + return s0; + } + + function peg$parseUnquotedLiteral() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + peg$savedPos = peg$currPos; + s1 = peg$f17(); + if (s1) { + s1 = undefined; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseUnquotedCharacter(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseUnquotedCharacter(); + } + s3 = peg$parseCursor(); + if (s3 !== peg$FAILED) { + s4 = []; + s5 = peg$parseUnquotedCharacter(); + while (s5 !== peg$FAILED) { + s4.push(s5); + s5 = peg$parseUnquotedCharacter(); + } + peg$savedPos = s0; + s0 = peg$f18(s2, s3, s4); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = []; + s2 = peg$parseUnquotedCharacter(); + if (s2 !== peg$FAILED) { + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parseUnquotedCharacter(); + } + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f21(s1); + } + s0 = s1; + } + + return s0; + } + + function peg$parseUnquotedCharacter() { + var s0, s1, s2, s3, s4; + + s0 = peg$parseEscapedWhitespace(); + if (s0 === peg$FAILED) { + s0 = peg$parseEscapedSpecialCharacter(); + if (s0 === peg$FAILED) { + s0 = peg$parseEscapedUnicodeSequence(); + if (s0 === peg$FAILED) { + s0 = peg$parseEscapedKeyword(); + if (s0 === peg$FAILED) { + s0 = peg$parseWildcard(); + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + s2 = peg$parseSpecialCharacter(); + peg$silentFails--; + if (s2 === peg$FAILED) { + s1 = undefined; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + peg$silentFails++; + s3 = peg$parseKeyword(); + peg$silentFails--; + if (s3 === peg$FAILED) { + s2 = undefined; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$currPos; + peg$silentFails++; + s4 = peg$parseCursor(); + peg$silentFails--; + if (s4 === peg$FAILED) { + s3 = undefined; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + if (input.length > peg$currPos) { + s4 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e18); } + } + if (s4 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f20(s4); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + } + } + } + } + + return s0; + } + + function peg$parseWildcard() { + var s0, s1; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 42) { + s1 = peg$c10; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e19); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f22(); + } + s0 = s1; + + return s0; + } + + function peg$parseOptionalSpace() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + peg$savedPos = peg$currPos; + s1 = peg$f17(); + if (s1) { + s1 = undefined; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parseSpace(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parseSpace(); + } + s3 = peg$parseCursor(); + if (s3 !== peg$FAILED) { + s4 = []; + s5 = peg$parseSpace(); + while (s5 !== peg$FAILED) { + s4.push(s5); + s5 = peg$parseSpace(); + } + peg$savedPos = s0; + s0 = peg$f18(s2, s3, s4); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = []; + s1 = peg$parseSpace(); + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = peg$parseSpace(); + } + } + + return s0; + } + + function peg$parseEscapedWhitespace() { + var s0, s1; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c11) { + s1 = peg$c11; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e20); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f23(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c12) { + s1 = peg$c12; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f24(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c13) { + s1 = peg$c13; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e22); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f25(); + } + s0 = s1; + } + } + + return s0; + } + + function peg$parseEscapedSpecialCharacter() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s1 = peg$c9; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e15); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseSpecialCharacter(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f20(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseEscapedKeyword() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s1 = peg$c9; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e15); } + } + if (s1 !== peg$FAILED) { + if (input.substr(peg$currPos, 2).toLowerCase() === peg$c5) { + s2 = input.substr(peg$currPos, 2); + peg$currPos += 2; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e8); } + } + if (s2 === peg$FAILED) { + if (input.substr(peg$currPos, 3).toLowerCase() === peg$c6) { + s2 = input.substr(peg$currPos, 3); + peg$currPos += 3; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + if (s2 === peg$FAILED) { + if (input.substr(peg$currPos, 3).toLowerCase() === peg$c7) { + s2 = input.substr(peg$currPos, 3); + peg$currPos += 3; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + } + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f26(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseKeyword() { + var s0; + + s0 = peg$parseOr(); + if (s0 === peg$FAILED) { + s0 = peg$parseAnd(); + if (s0 === peg$FAILED) { + s0 = peg$parseNot(); + } + } + + return s0; + } + + function peg$parseSpecialCharacter() { + var s0; + + if (peg$r2.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + + return s0; + } + + function peg$parseEscapedUnicodeSequence() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 92) { + s1 = peg$c9; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e15); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parseUnicodeSequence(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f27(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseUnicodeSequence() { + var s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 117) { + s1 = peg$c14; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e24); } + } + if (s1 !== peg$FAILED) { + s2 = peg$currPos; + s3 = peg$currPos; + s4 = peg$parseHexDigit(); + if (s4 !== peg$FAILED) { + s5 = peg$parseHexDigit(); + if (s5 !== peg$FAILED) { + s6 = peg$parseHexDigit(); + if (s6 !== peg$FAILED) { + s7 = peg$parseHexDigit(); + if (s7 !== peg$FAILED) { + s4 = [s4, s5, s6, s7]; + s3 = s4; + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + } else { + peg$currPos = s3; + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + s2 = input.substring(s2, peg$currPos); + } else { + s2 = s3; + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f28(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseHexDigit() { + var s0; + + if (peg$r3.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + + return s0; + } + + function peg$parseRangeOperator() { + var s0, s1; + + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c15) { + s1 = peg$c15; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e26); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f29(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c16) { + s1 = peg$c16; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e27); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f30(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 60) { + s1 = peg$c17; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e28); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f31(); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 62) { + s1 = peg$c18; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f32(); + } + s0 = s1; + } + } + } + + return s0; + } + + function peg$parseSpace() { + var s0, s1; + + peg$silentFails++; + if (peg$r4.test(input.charAt(peg$currPos))) { + s0 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e31); } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e30); } + } + + return s0; + } + + function peg$parseCursor() { + var s0, s1, s2; + + s0 = peg$currPos; + peg$savedPos = peg$currPos; + s1 = peg$f17(); + if (s1) { + s1 = undefined; + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + if (input.substr(peg$currPos, 14) === peg$c19) { + s2 = peg$c19; + peg$currPos += 14; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e32); } + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f33(); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + + const { parseCursor, cursorSymbol, allowLeadingWildcards = true, helpers: { nodeTypes } } = options; + const buildFunctionNode = nodeTypes.function.buildNodeWithArgumentNodes; + const buildLiteralNode = nodeTypes.literal.buildNode; + const buildWildcardNode = nodeTypes.wildcard.buildNode; + const buildNamedArgNode = nodeTypes.namedArg.buildNode; + const { wildcardSymbol } = nodeTypes.wildcard; + + + peg$result = peg$startRuleFunction(); + + if (peg$result !== peg$FAILED && peg$currPos === input.length) { + return peg$result; + } else { + if (peg$result !== peg$FAILED && peg$currPos < input.length) { + peg$fail(peg$endExpectation()); + } + + throw peg$buildStructuredError( + peg$maxFailExpected, + peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null, + peg$maxFailPos < input.length + ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) + : peg$computeLocation(peg$maxFailPos, peg$maxFailPos) + ); + } +} + +module.exports = { + SyntaxError: peg$SyntaxError, + parse: peg$parse +}; diff --git a/packages/kbn-es-query/src/kuery/grammar/__mocks__/index.ts b/packages/kbn-es-query/src/kuery/grammar/__mocks__/index.ts new file mode 100644 index 0000000000000..9103c852c4845 --- /dev/null +++ b/packages/kbn-es-query/src/kuery/grammar/__mocks__/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +// @ts-expect-error +export { parse } from './grammar'; diff --git a/packages/kbn-es-query/src/kuery/grammar/index.ts b/packages/kbn-es-query/src/kuery/grammar/index.ts new file mode 100644 index 0000000000000..811fa723da3b8 --- /dev/null +++ b/packages/kbn-es-query/src/kuery/grammar/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// @ts-expect-error +export { parse } from '../../../grammar'; diff --git a/src/plugins/data/common/es_query/kuery/index.ts b/packages/kbn-es-query/src/kuery/index.ts similarity index 80% rename from src/plugins/data/common/es_query/kuery/index.ts rename to packages/kbn-es-query/src/kuery/index.ts index 72eecc09756b4..7796785f85394 100644 --- a/src/plugins/data/common/es_query/kuery/index.ts +++ b/packages/kbn-es-query/src/kuery/index.ts @@ -8,6 +8,5 @@ export { KQLSyntaxError } from './kuery_syntax_error'; export { nodeTypes, nodeBuilder } from './node_types'; -export * from './ast'; - -export * from './types'; +export { fromKueryExpression, toElasticsearchQuery } from './ast'; +export { DslQuery, KueryNode } from './types'; diff --git a/src/plugins/data/common/es_query/kuery/kuery_syntax_error.test.ts b/packages/kbn-es-query/src/kuery/kuery_syntax_error.test.ts similarity index 89% rename from src/plugins/data/common/es_query/kuery/kuery_syntax_error.test.ts rename to packages/kbn-es-query/src/kuery/kuery_syntax_error.test.ts index 6875bc3e5f74f..e04b1abdc8494 100644 --- a/src/plugins/data/common/es_query/kuery/kuery_syntax_error.test.ts +++ b/packages/kbn-es-query/src/kuery/kuery_syntax_error.test.ts @@ -8,6 +8,8 @@ import { fromKueryExpression } from './ast'; +jest.mock('./grammar'); + describe('kql syntax errors', () => { it('should throw an error for a field query missing a value', () => { expect(() => { @@ -47,7 +49,7 @@ describe('kql syntax errors', () => { ); }); - it('should throw an error for a NOT list missing a sub-query', () => { + it('should throw an error for a "missing a sub-query', () => { expect(() => { fromKueryExpression('response:(200 and not )'); }).toThrow( @@ -66,13 +68,17 @@ describe('kql syntax errors', () => { it('should throw an error for unescaped quotes in a quoted string', () => { expect(() => { fromKueryExpression('foo:"ba "r"'); - }).toThrow('Expected AND, OR, end of input but "r" found.\n' + 'foo:"ba "r"\n' + '---------^'); + }).toThrow( + 'Expected AND, OR, end of input, whitespace but "r" found.\n' + 'foo:"ba "r"\n' + '---------^' + ); }); it('should throw an error for unescaped special characters in literals', () => { expect(() => { fromKueryExpression('foo:ba:r'); - }).toThrow('Expected AND, OR, end of input but ":" found.\n' + 'foo:ba:r\n' + '------^'); + }).toThrow( + 'Expected AND, OR, end of input, whitespace but ":" found.\n' + 'foo:ba:r\n' + '------^' + ); }); it('should throw an error for range queries missing a value', () => { diff --git a/src/plugins/data/common/es_query/kuery/kuery_syntax_error.ts b/packages/kbn-es-query/src/kuery/kuery_syntax_error.ts similarity index 59% rename from src/plugins/data/common/es_query/kuery/kuery_syntax_error.ts rename to packages/kbn-es-query/src/kuery/kuery_syntax_error.ts index a9adbad4781b7..aa4440579eb49 100644 --- a/src/plugins/data/common/es_query/kuery/kuery_syntax_error.ts +++ b/packages/kbn-es-query/src/kuery/kuery_syntax_error.ts @@ -6,28 +6,40 @@ * Side Public License, v 1. */ -import { repeat } from 'lodash'; +import { repeat, uniq } from 'lodash'; import { i18n } from '@kbn/i18n'; -const endOfInputText = i18n.translate('data.common.kql.errors.endOfInputText', { +const endOfInputText = i18n.translate('esQuery.kql.errors.endOfInputText', { defaultMessage: 'end of input', }); const grammarRuleTranslations: Record = { - fieldName: i18n.translate('data.common.kql.errors.fieldNameText', { + fieldName: i18n.translate('esQuery.kql.errors.fieldNameText', { defaultMessage: 'field name', }), - value: i18n.translate('data.common.kql.errors.valueText', { + value: i18n.translate('esQuery.kql.errors.valueText', { defaultMessage: 'value', }), - literal: i18n.translate('data.common.kql.errors.literalText', { + literal: i18n.translate('esQuery.kql.errors.literalText', { defaultMessage: 'literal', }), - whitespace: i18n.translate('data.common.kql.errors.whitespaceText', { + whitespace: i18n.translate('esQuery.kql.errors.whitespaceText', { defaultMessage: 'whitespace', }), }; +const getItemText = (item: KQLSyntaxErrorExpected): string => { + if (item.type === 'other') { + return item.description!; + } else if (item.type === 'literal') { + return `"${item.text!}"`; + } else if (item.type === 'end') { + return 'end of input'; + } else { + return item.text || item.description || ''; + } +}; + interface KQLSyntaxErrorData extends Error { found: string; expected: KQLSyntaxErrorExpected[] | null; @@ -35,7 +47,9 @@ interface KQLSyntaxErrorData extends Error { } interface KQLSyntaxErrorExpected { - description: string; + description?: string; + text?: string; + type: string; } export class KQLSyntaxError extends Error { @@ -45,12 +59,16 @@ export class KQLSyntaxError extends Error { let message = error.message; if (error.expected) { const translatedExpectations = error.expected.map((expected) => { - return grammarRuleTranslations[expected.description] || expected.description; + const key = getItemText(expected); + return grammarRuleTranslations[key] || key; }); - const translatedExpectationText = translatedExpectations.join(', '); + const translatedExpectationText = uniq(translatedExpectations) + .filter((item) => item !== undefined) + .sort() + .join(', '); - message = i18n.translate('data.common.kql.errors.syntaxError', { + message = i18n.translate('esQuery.kql.errors.syntaxError', { defaultMessage: 'Expected {expectedList} but {foundInput} found.', values: { expectedList: translatedExpectationText, diff --git a/src/plugins/data/common/es_query/kuery/node_types/function.test.ts b/packages/kbn-es-query/src/kuery/node_types/function.test.ts similarity index 91% rename from src/plugins/data/common/es_query/kuery/node_types/function.test.ts rename to packages/kbn-es-query/src/kuery/node_types/function.test.ts index 42c06d7fdb603..5df6ba1916046 100644 --- a/src/plugins/data/common/es_query/kuery/node_types/function.test.ts +++ b/packages/kbn-es-query/src/kuery/node_types/function.test.ts @@ -6,22 +6,23 @@ * Side Public License, v 1. */ -import { fields } from '../../../index_patterns/mocks'; - import { nodeTypes } from './index'; -import { IIndexPattern } from '../../../index_patterns'; import { buildNode, buildNodeWithArgumentNodes, toElasticsearchQuery } from './function'; import { toElasticsearchQuery as isFunctionToElasticsearchQuery } from '../functions/is'; +import { IndexPatternBase } from '../../es_query'; +import { fields } from '../../filters/stubs/fields.mocks'; + +jest.mock('../grammar'); describe('kuery node types', () => { describe('function', () => { - let indexPattern: IIndexPattern; + let indexPattern: IndexPatternBase; beforeEach(() => { - indexPattern = ({ + indexPattern = { fields, - } as unknown) as IIndexPattern; + }; }); describe('buildNode', () => { diff --git a/src/plugins/data/common/es_query/kuery/node_types/function.ts b/packages/kbn-es-query/src/kuery/node_types/function.ts similarity index 96% rename from src/plugins/data/common/es_query/kuery/node_types/function.ts rename to packages/kbn-es-query/src/kuery/node_types/function.ts index 642089a101f31..e72f8a6b1e77a 100644 --- a/src/plugins/data/common/es_query/kuery/node_types/function.ts +++ b/packages/kbn-es-query/src/kuery/node_types/function.ts @@ -9,7 +9,7 @@ import _ from 'lodash'; import { functions } from '../functions'; -import { IndexPatternBase, KueryNode } from '../../..'; +import { IndexPatternBase, KueryNode } from '../..'; import { FunctionName, FunctionTypeBuildNode } from './types'; export function buildNode(functionName: FunctionName, ...args: any[]) { diff --git a/src/plugins/data/common/es_query/kuery/node_types/index.ts b/packages/kbn-es-query/src/kuery/node_types/index.ts similarity index 100% rename from src/plugins/data/common/es_query/kuery/node_types/index.ts rename to packages/kbn-es-query/src/kuery/node_types/index.ts diff --git a/src/plugins/data/common/es_query/kuery/node_types/literal.test.ts b/packages/kbn-es-query/src/kuery/node_types/literal.test.ts similarity index 97% rename from src/plugins/data/common/es_query/kuery/node_types/literal.test.ts rename to packages/kbn-es-query/src/kuery/node_types/literal.test.ts index c370292de38bc..7a36be704a609 100644 --- a/src/plugins/data/common/es_query/kuery/node_types/literal.test.ts +++ b/packages/kbn-es-query/src/kuery/node_types/literal.test.ts @@ -9,6 +9,8 @@ // @ts-ignore import { buildNode, toElasticsearchQuery } from './literal'; +jest.mock('../grammar'); + describe('kuery node types', () => { describe('literal', () => { describe('buildNode', () => { diff --git a/src/plugins/data/common/es_query/kuery/node_types/literal.ts b/packages/kbn-es-query/src/kuery/node_types/literal.ts similarity index 100% rename from src/plugins/data/common/es_query/kuery/node_types/literal.ts rename to packages/kbn-es-query/src/kuery/node_types/literal.ts diff --git a/src/plugins/data/common/es_query/kuery/node_types/named_arg.test.ts b/packages/kbn-es-query/src/kuery/node_types/named_arg.test.ts similarity index 98% rename from src/plugins/data/common/es_query/kuery/node_types/named_arg.test.ts rename to packages/kbn-es-query/src/kuery/node_types/named_arg.test.ts index 2c3fb43ee0f59..fa944660288d5 100644 --- a/src/plugins/data/common/es_query/kuery/node_types/named_arg.test.ts +++ b/packages/kbn-es-query/src/kuery/node_types/named_arg.test.ts @@ -7,10 +7,10 @@ */ import { nodeTypes } from './index'; - -// @ts-ignore import { buildNode, toElasticsearchQuery } from './named_arg'; +jest.mock('../grammar'); + describe('kuery node types', () => { describe('named arg', () => { describe('buildNode', () => { diff --git a/src/plugins/data/common/es_query/kuery/node_types/named_arg.ts b/packages/kbn-es-query/src/kuery/node_types/named_arg.ts similarity index 100% rename from src/plugins/data/common/es_query/kuery/node_types/named_arg.ts rename to packages/kbn-es-query/src/kuery/node_types/named_arg.ts diff --git a/src/plugins/data/common/es_query/kuery/node_types/node_builder.test.ts b/packages/kbn-es-query/src/kuery/node_types/node_builder.test.ts similarity index 99% rename from src/plugins/data/common/es_query/kuery/node_types/node_builder.test.ts rename to packages/kbn-es-query/src/kuery/node_types/node_builder.test.ts index d6439f8e7cc87..aefd40c6db3fb 100644 --- a/src/plugins/data/common/es_query/kuery/node_types/node_builder.test.ts +++ b/packages/kbn-es-query/src/kuery/node_types/node_builder.test.ts @@ -9,6 +9,8 @@ import { nodeBuilder } from './node_builder'; import { toElasticsearchQuery } from '../index'; +jest.mock('../grammar'); + describe('nodeBuilder', () => { describe('is method', () => { test('string value', () => { diff --git a/src/plugins/data/common/es_query/kuery/node_types/node_builder.ts b/packages/kbn-es-query/src/kuery/node_types/node_builder.ts similarity index 100% rename from src/plugins/data/common/es_query/kuery/node_types/node_builder.ts rename to packages/kbn-es-query/src/kuery/node_types/node_builder.ts diff --git a/src/plugins/data/common/es_query/kuery/node_types/types.ts b/packages/kbn-es-query/src/kuery/node_types/types.ts similarity index 100% rename from src/plugins/data/common/es_query/kuery/node_types/types.ts rename to packages/kbn-es-query/src/kuery/node_types/types.ts diff --git a/src/plugins/data/common/es_query/kuery/node_types/wildcard.test.ts b/packages/kbn-es-query/src/kuery/node_types/wildcard.test.ts similarity index 99% rename from src/plugins/data/common/es_query/kuery/node_types/wildcard.test.ts rename to packages/kbn-es-query/src/kuery/node_types/wildcard.test.ts index 8c20851cfe3f9..9f444eec82b69 100644 --- a/src/plugins/data/common/es_query/kuery/node_types/wildcard.test.ts +++ b/packages/kbn-es-query/src/kuery/node_types/wildcard.test.ts @@ -16,6 +16,8 @@ import { // @ts-ignore } from './wildcard'; +jest.mock('../grammar'); + describe('kuery node types', () => { describe('wildcard', () => { describe('buildNode', () => { diff --git a/src/plugins/data/common/es_query/kuery/node_types/wildcard.ts b/packages/kbn-es-query/src/kuery/node_types/wildcard.ts similarity index 100% rename from src/plugins/data/common/es_query/kuery/node_types/wildcard.ts rename to packages/kbn-es-query/src/kuery/node_types/wildcard.ts diff --git a/src/plugins/data/common/es_query/kuery/types.ts b/packages/kbn-es-query/src/kuery/types.ts similarity index 100% rename from src/plugins/data/common/es_query/kuery/types.ts rename to packages/kbn-es-query/src/kuery/types.ts diff --git a/src/plugins/data/common/es_query/utils.ts b/packages/kbn-es-query/src/utils.ts similarity index 100% rename from src/plugins/data/common/es_query/utils.ts rename to packages/kbn-es-query/src/utils.ts diff --git a/packages/kbn-es-query/tsconfig.browser.json b/packages/kbn-es-query/tsconfig.browser.json new file mode 100644 index 0000000000000..0a1c21cc8e05b --- /dev/null +++ b/packages/kbn-es-query/tsconfig.browser.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.browser.json", + "compilerOptions": { + "incremental": true, + "outDir": "./target_web", + "declaration": false, + "sourceMap": true, + "sourceRoot": "../../../../packages/kbn-es-query/src", + "types": [ + "jest", + "node" + ], + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "**/__fixtures__/**/*", + "**/__mocks__/**/*" + ] +} diff --git a/packages/kbn-es-query/tsconfig.json b/packages/kbn-es-query/tsconfig.json new file mode 100644 index 0000000000000..b48d90373e2cb --- /dev/null +++ b/packages/kbn-es-query/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "incremental": true, + "declarationDir": "./target_types", + "outDir": "./target_node", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "sourceRoot": "../../../../packages/kbn-es-query/src", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "**/__fixtures__/**/*", + "**/__mocks__/**/grammar.js", + ] +} diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 49d4bdbc2de64..7a2c9095e2011 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -114,3 +114,4 @@ pageLoadAssetSize: cases: 144442 expressionError: 22127 userSetup: 18532 + expressionShape: 30033 diff --git a/packages/kbn-test/src/jest/utils/get_url.ts b/packages/kbn-test/src/jest/utils/get_url.ts index 734e26c5199d7..e08695b334e1b 100644 --- a/packages/kbn-test/src/jest/utils/get_url.ts +++ b/packages/kbn-test/src/jest/utils/get_url.ts @@ -22,11 +22,6 @@ interface UrlParam { username?: string; } -interface App { - pathname?: string; - hash?: string; -} - /** * Converts a config and a pathname to a url * @param {object} config A url config @@ -46,11 +41,11 @@ interface App { * @return {string} */ -function getUrl(config: UrlParam, app: App) { +function getUrl(config: UrlParam, app: UrlParam) { return url.format(_.assign({}, config, app)); } -getUrl.noAuth = function getUrlNoAuth(config: UrlParam, app: App) { +getUrl.noAuth = function getUrlNoAuth(config: UrlParam, app: UrlParam) { config = _.pickBy(config, function (val, param) { return param !== 'auth'; }); diff --git a/packages/kbn-ui-shared-deps/BUILD.bazel b/packages/kbn-ui-shared-deps/BUILD.bazel index f92049292f373..426f8d0b7485a 100644 --- a/packages/kbn-ui-shared-deps/BUILD.bazel +++ b/packages/kbn-ui-shared-deps/BUILD.bazel @@ -44,9 +44,7 @@ SRC_DEPS = [ "@npm//abortcontroller-polyfill", "@npm//angular", "@npm//babel-loader", - "@npm//compression-webpack-plugin", "@npm//core-js", - "@npm//css-minimizer-webpack-plugin", "@npm//css-loader", "@npm//fflate", "@npm//jquery", @@ -67,7 +65,6 @@ SRC_DEPS = [ "@npm//rxjs", "@npm//styled-components", "@npm//symbol-observable", - "@npm//terser-webpack-plugin", "@npm//url-loader", "@npm//val-loader", "@npm//whatwg-fetch" diff --git a/packages/kbn-ui-shared-deps/webpack.config.js b/packages/kbn-ui-shared-deps/webpack.config.js index 9d18c8033ff67..9692f768cb3b4 100644 --- a/packages/kbn-ui-shared-deps/webpack.config.js +++ b/packages/kbn-ui-shared-deps/webpack.config.js @@ -7,20 +7,23 @@ */ const Path = require('path'); - const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); -const TerserPlugin = require('terser-webpack-plugin'); -const CompressionPlugin = require('compression-webpack-plugin'); const { REPO_ROOT } = require('@kbn/utils'); -const { RawSource } = require('webpack-sources'); const UiSharedDeps = require('./src/index'); const MOMENT_SRC = require.resolve('moment/min/moment-with-locales.js'); +const WEBPACK_SRC = require.resolve('webpack'); module.exports = { + node: { + child_process: 'empty', + fs: 'empty', + }, + externals: { + module: 'module', + }, mode: 'production', entry: { 'kbn-ui-shared-deps': './src/entry.js', @@ -30,8 +33,7 @@ module.exports = { 'kbn-ui-shared-deps.v8.light': ['@elastic/eui/dist/eui_theme_amsterdam_light.css'], }, context: __dirname, - // cheap-source-map should be used if needed - devtool: false, + devtool: 'cheap-source-map', output: { path: UiSharedDeps.distDir, filename: '[name].js', @@ -39,10 +41,11 @@ module.exports = { devtoolModuleFilenameTemplate: (info) => `kbn-ui-shared-deps/${Path.relative(REPO_ROOT, info.absoluteResourcePath)}`, library: '__kbnSharedDeps__', + futureEmitAssets: true, }, module: { - noParse: [MOMENT_SRC], + noParse: [MOMENT_SRC, WEBPACK_SRC], rules: [ { include: [require.resolve('./src/entry.js')], @@ -102,35 +105,17 @@ module.exports = { resolve: { alias: { moment: MOMENT_SRC, + // NOTE: Used to include react profiling on bundles + // https://gist.github.com/bvaughn/25e6233aeb1b4f0cdb8d8366e54a3977#webpack-4 + 'react-dom$': 'react-dom/profiling', + 'scheduler/tracing': 'scheduler/tracing-profiling', }, extensions: ['.js', '.ts'], symlinks: false, }, optimization: { - minimizer: [ - new CssMinimizerPlugin({ - parallel: false, - minimizerOptions: { - preset: [ - 'default', - { - discardComments: false, - }, - ], - }, - }), - new TerserPlugin({ - cache: false, - sourceMap: false, - extractComments: false, - parallel: false, - terserOptions: { - compress: true, - mangle: true, - }, - }), - ], + minimize: false, noEmitOnErrors: true, splitChunks: { cacheGroups: { @@ -155,44 +140,5 @@ module.exports = { new MiniCssExtractPlugin({ filename: '[name].css', }), - new CompressionPlugin({ - algorithm: 'brotliCompress', - filename: '[path].br', - test: /\.(js|css)$/, - cache: false, - }), - new CompressionPlugin({ - algorithm: 'gzip', - filename: '[path].gz', - test: /\.(js|css)$/, - cache: false, - }), - new (class MetricsPlugin { - apply(compiler) { - compiler.hooks.emit.tap('MetricsPlugin', (compilation) => { - const metrics = [ - { - group: 'page load bundle size', - id: 'kbnUiSharedDeps-js', - value: compilation.assets['kbn-ui-shared-deps.js'].size(), - }, - { - group: 'page load bundle size', - id: 'kbnUiSharedDeps-css', - value: - compilation.assets['kbn-ui-shared-deps.css'].size() + - compilation.assets['kbn-ui-shared-deps.v7.light.css'].size(), - }, - { - group: 'page load bundle size', - id: 'kbnUiSharedDeps-elastic', - value: compilation.assets['kbn-ui-shared-deps.@elastic.js'].size(), - }, - ]; - - compilation.emitAsset('metrics.json', new RawSource(JSON.stringify(metrics, null, 2))); - }); - } - })(), ], }; diff --git a/src/core/server/core_usage_data/core_usage_data_service.test.ts b/src/core/server/core_usage_data/core_usage_data_service.test.ts index 2395d6d1c1725..7ecfa37492242 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.test.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.test.ts @@ -122,6 +122,7 @@ describe('CoreUsageDataService', () => { hidden: true, namespaceType: 'agnostic', mappings: expect.anything(), + migrations: expect.anything(), }); }); }); diff --git a/src/core/server/core_usage_data/core_usage_stats.ts b/src/core/server/core_usage_data/core_usage_stats.ts index d02c92353d13e..8cdd83bcad2c9 100644 --- a/src/core/server/core_usage_data/core_usage_stats.ts +++ b/src/core/server/core_usage_data/core_usage_stats.ts @@ -8,6 +8,7 @@ import { SavedObjectsType } from '../saved_objects'; import { CORE_USAGE_STATS_TYPE } from './constants'; +import { migrateTo7141 } from './migrations'; /** @internal */ export const coreUsageStatsType: SavedObjectsType = { @@ -18,4 +19,7 @@ export const coreUsageStatsType: SavedObjectsType = { dynamic: false, // we aren't querying or aggregating over this data, so we don't need to specify any fields properties: {}, }, + migrations: { + '7.14.1': migrateTo7141, + }, }; diff --git a/src/core/server/core_usage_data/core_usage_stats_client.test.ts b/src/core/server/core_usage_data/core_usage_stats_client.test.ts index dc4a81adf5f8e..384e3d7b932c1 100644 --- a/src/core/server/core_usage_data/core_usage_stats_client.test.ts +++ b/src/core/server/core_usage_data/core_usage_stats_client.test.ts @@ -790,8 +790,14 @@ describe('CoreUsageStatsClient', () => { createNewCopies: true, overwrite: true, } as IncrementSavedObjectsImportOptions); - expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1); - expect(repositoryMock.incrementCounter).toHaveBeenCalledWith( + await usageStatsClient.incrementSavedObjectsImport({ + request, + createNewCopies: false, + overwrite: true, + } as IncrementSavedObjectsImportOptions); + expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(2); + expect(repositoryMock.incrementCounter).toHaveBeenNthCalledWith( + 1, CORE_USAGE_STATS_TYPE, CORE_USAGE_STATS_ID, [ @@ -799,6 +805,19 @@ describe('CoreUsageStatsClient', () => { `${IMPORT_STATS_PREFIX}.namespace.default.total`, `${IMPORT_STATS_PREFIX}.namespace.default.kibanaRequest.yes`, `${IMPORT_STATS_PREFIX}.createNewCopiesEnabled.yes`, + // excludes 'overwriteEnabled.yes' and 'overwriteEnabled.no' when createNewCopies is true + ], + incrementOptions + ); + expect(repositoryMock.incrementCounter).toHaveBeenNthCalledWith( + 2, + CORE_USAGE_STATS_TYPE, + CORE_USAGE_STATS_ID, + [ + `${IMPORT_STATS_PREFIX}.total`, + `${IMPORT_STATS_PREFIX}.namespace.default.total`, + `${IMPORT_STATS_PREFIX}.namespace.default.kibanaRequest.yes`, + `${IMPORT_STATS_PREFIX}.createNewCopiesEnabled.no`, `${IMPORT_STATS_PREFIX}.overwriteEnabled.yes`, ], incrementOptions diff --git a/src/core/server/core_usage_data/core_usage_stats_client.ts b/src/core/server/core_usage_data/core_usage_stats_client.ts index 3b73b475a30f4..29d6e875c7962 100644 --- a/src/core/server/core_usage_data/core_usage_stats_client.ts +++ b/src/core/server/core_usage_data/core_usage_stats_client.ts @@ -150,7 +150,7 @@ export class CoreUsageStatsClient { const { createNewCopies, overwrite } = options; const counterFieldNames = [ `createNewCopiesEnabled.${createNewCopies ? 'yes' : 'no'}`, - `overwriteEnabled.${overwrite ? 'yes' : 'no'}`, + ...(!createNewCopies ? [`overwriteEnabled.${overwrite ? 'yes' : 'no'}`] : []), // the overwrite option is ignored when createNewCopies is true ]; await this.updateUsageStats(counterFieldNames, IMPORT_STATS_PREFIX, options); } diff --git a/src/core/server/core_usage_data/migrations.test.ts b/src/core/server/core_usage_data/migrations.test.ts new file mode 100644 index 0000000000000..27ea745c3fab1 --- /dev/null +++ b/src/core/server/core_usage_data/migrations.test.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { SavedObjectUnsanitizedDoc } from '../saved_objects'; +import { migrateTo7141 } from './migrations'; +import type { CoreUsageStats } from './types'; + +const type = 'obj-type'; +const id = 'obj-id'; + +describe('#migrateTo7141', () => { + it('Resets targeted counter fields and leaves others unchanged', () => { + const doc = { + type, + id, + attributes: { + foo: 'bar', + 'apiCalls.savedObjectsImport.total': 10, + }, + } as SavedObjectUnsanitizedDoc; + + expect(migrateTo7141(doc)).toEqual({ + type, + id, + attributes: { + foo: 'bar', + 'apiCalls.savedObjectsImport.total': 0, + 'apiCalls.savedObjectsImport.namespace.default.total': 0, + 'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.yes': 0, + 'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.no': 0, + 'apiCalls.savedObjectsImport.namespace.custom.total': 0, + 'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.yes': 0, + 'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.no': 0, + 'apiCalls.savedObjectsImport.createNewCopiesEnabled.yes': 0, + 'apiCalls.savedObjectsImport.createNewCopiesEnabled.no': 0, + 'apiCalls.savedObjectsImport.overwriteEnabled.yes': 0, + 'apiCalls.savedObjectsImport.overwriteEnabled.no': 0, + }, + }); + }); +}); diff --git a/src/core/server/core_usage_data/migrations.ts b/src/core/server/core_usage_data/migrations.ts new file mode 100644 index 0000000000000..8cbb2a267f0c7 --- /dev/null +++ b/src/core/server/core_usage_data/migrations.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { cloneDeep } from 'lodash'; +import type { SavedObjectUnsanitizedDoc } from '../saved_objects'; +import type { CoreUsageStats } from './types'; + +export const migrateTo7141 = (doc: SavedObjectUnsanitizedDoc) => { + try { + return resetFields(doc, [ + // Prior to this, we were counting the `overwrite` option incorrectly; reset all import API counter fields so we get clean data + 'apiCalls.savedObjectsImport.total', + 'apiCalls.savedObjectsImport.namespace.default.total', + 'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.yes', + 'apiCalls.savedObjectsImport.namespace.default.kibanaRequest.no', + 'apiCalls.savedObjectsImport.namespace.custom.total', + 'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.yes', + 'apiCalls.savedObjectsImport.namespace.custom.kibanaRequest.no', + 'apiCalls.savedObjectsImport.createNewCopiesEnabled.yes', + 'apiCalls.savedObjectsImport.createNewCopiesEnabled.no', + 'apiCalls.savedObjectsImport.overwriteEnabled.yes', + 'apiCalls.savedObjectsImport.overwriteEnabled.no', + ]); + } catch (err) { + // fail-safe + } + return doc; +}; + +function resetFields( + doc: SavedObjectUnsanitizedDoc, + fieldsToReset: Array +) { + const newDoc = cloneDeep(doc); + const { attributes = {} } = newDoc; + for (const field of fieldsToReset) { + attributes[field] = 0; + } + return { ...newDoc, attributes }; +} diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js index 27228361aef22..474721ff3610a 100644 --- a/src/core/server/saved_objects/service/lib/repository.test.js +++ b/src/core/server/saved_objects/service/lib/repository.test.js @@ -33,7 +33,6 @@ import { errors as EsErrors } from '@elastic/elasticsearch'; const { nodeTypes } = esKuery; jest.mock('./search_dsl/search_dsl', () => ({ getSearchDsl: jest.fn() })); - // BEWARE: The SavedObjectClient depends on the implementation details of the SavedObjectsRepository // so any breaking changes to this repository are considered breaking changes to the SavedObjectsClient. diff --git a/src/dev/build/build_distributables.ts b/src/dev/build/build_distributables.ts index 159281ed71db0..b243bb10e507b 100644 --- a/src/dev/build/build_distributables.ts +++ b/src/dev/build/build_distributables.ts @@ -64,6 +64,7 @@ export async function buildDistributables(log: ToolingLog, options: BuildOptions await run(Tasks.TranspileBabel); await run(Tasks.CreatePackageJson); await run(Tasks.InstallDependencies); + await run(Tasks.GeneratePackagesOptimizedAssets); await run(Tasks.CleanPackages); await run(Tasks.CreateNoticeFile); await run(Tasks.UpdateLicenseFile); diff --git a/src/dev/build/tasks/generate_packages_optimized_assets.ts b/src/dev/build/tasks/generate_packages_optimized_assets.ts new file mode 100644 index 0000000000000..f638e38d14a7a --- /dev/null +++ b/src/dev/build/tasks/generate_packages_optimized_assets.ts @@ -0,0 +1,200 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { pipeline } from 'stream'; +import { promisify } from 'util'; + +import fs from 'fs'; +import gulpBrotli from 'gulp-brotli'; +// @ts-expect-error +import gulpGzip from 'gulp-gzip'; +// @ts-expect-error +import gulpPostCSS from 'gulp-postcss'; +// @ts-expect-error +import gulpTerser from 'gulp-terser'; +import terser from 'terser'; +import vfs from 'vinyl-fs'; + +import { ToolingLog } from '@kbn/dev-utils'; +import { Task, Build, write, deleteAll } from '../lib'; + +const asyncPipeline = promisify(pipeline); +const asyncStat = promisify(fs.stat); + +const removePreMinifySourceMaps = async (log: ToolingLog, build: Build) => { + log.debug('Remove Pre Minify Sourcemaps'); + + await deleteAll( + [build.resolvePath('node_modules/@kbn/ui-shared-deps/shared_built_assets', '**', '*.map')], + log + ); +}; + +const minifyKbnUiSharedDepsCSS = async (log: ToolingLog, build: Build) => { + const buildRoot = build.resolvePath(); + + log.debug('Minify CSS'); + + await asyncPipeline( + vfs.src(['node_modules/@kbn/ui-shared-deps/shared_built_assets/**/*.css'], { + cwd: buildRoot, + }), + + gulpPostCSS([ + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('cssnano')({ + preset: [ + 'default', + { + discardComments: false, + }, + ], + }), + ]), + + vfs.dest('node_modules/@kbn/ui-shared-deps/shared_built_assets', { cwd: buildRoot }) + ); +}; + +const minifyKbnUiSharedDepsJS = async (log: ToolingLog, build: Build) => { + const buildRoot = build.resolvePath(); + + log.debug('Minify JS'); + + await asyncPipeline( + vfs.src(['node_modules/@kbn/ui-shared-deps/shared_built_assets/**/*.js'], { + cwd: buildRoot, + }), + + gulpTerser( + { + compress: true, + mangle: true, + }, + terser.minify + ), + + vfs.dest('node_modules/@kbn/ui-shared-deps/shared_built_assets', { cwd: buildRoot }) + ); +}; + +const brotliCompressKbnUiSharedDeps = async (log: ToolingLog, build: Build) => { + const buildRoot = build.resolvePath(); + + log.debug('Brotli compress'); + + await asyncPipeline( + vfs.src(['node_modules/@kbn/ui-shared-deps/shared_built_assets/**/*.{js,css}'], { + cwd: buildRoot, + }), + + gulpBrotli(), + + vfs.dest('node_modules/@kbn/ui-shared-deps/shared_built_assets', { cwd: buildRoot }) + ); +}; + +const gzipCompressKbnUiSharedDeps = async (log: ToolingLog, build: Build) => { + const buildRoot = build.resolvePath(); + + log.debug('GZip compress'); + + await asyncPipeline( + vfs.src(['node_modules/@kbn/ui-shared-deps/shared_built_assets/**/*.{js,css}'], { + cwd: buildRoot, + }), + + gulpGzip(), + + vfs.dest('node_modules/@kbn/ui-shared-deps/shared_built_assets', { cwd: buildRoot }) + ); +}; + +const createKbnUiSharedDepsBundleMetrics = async (log: ToolingLog, build: Build) => { + const bundleMetricsFilePath = build.resolvePath( + 'node_modules/@kbn/ui-shared-deps/shared_built_assets', + 'metrics.json' + ); + + const kbnUISharedDepsJSFileSize = ( + await asyncStat( + build.resolvePath( + 'node_modules/@kbn/ui-shared-deps/shared_built_assets', + 'kbn-ui-shared-deps.js' + ) + ) + ).size; + + const kbnUISharedDepsCSSFileSize = + ( + await asyncStat( + build.resolvePath( + 'node_modules/@kbn/ui-shared-deps/shared_built_assets', + 'kbn-ui-shared-deps.css' + ) + ) + ).size + + ( + await asyncStat( + build.resolvePath( + 'node_modules/@kbn/ui-shared-deps/shared_built_assets', + 'kbn-ui-shared-deps.v7.light.css' + ) + ) + ).size; + + const kbnUISharedDepsElasticJSFileSize = ( + await asyncStat( + build.resolvePath( + 'node_modules/@kbn/ui-shared-deps/shared_built_assets', + 'kbn-ui-shared-deps.@elastic.js' + ) + ) + ).size; + + log.debug('Create metrics.json'); + + const metrics = [ + { + group: 'page load bundle size', + id: 'kbnUiSharedDeps-js', + value: kbnUISharedDepsJSFileSize, + }, + { + group: 'page load bundle size', + id: 'kbnUiSharedDeps-css', + value: kbnUISharedDepsCSSFileSize, + }, + { + group: 'page load bundle size', + id: 'kbnUiSharedDeps-elastic', + value: kbnUISharedDepsElasticJSFileSize, + }, + ]; + + await write(bundleMetricsFilePath, JSON.stringify(metrics, null, 2)); +}; + +const generateKbnUiSharedDepsOptimizedAssets = async (log: ToolingLog, build: Build) => { + log.info('Creating optimized assets for @kbn/ui-shared-deps'); + await removePreMinifySourceMaps(log, build); + await minifyKbnUiSharedDepsCSS(log, build); + await minifyKbnUiSharedDepsJS(log, build); + await createKbnUiSharedDepsBundleMetrics(log, build); + await brotliCompressKbnUiSharedDeps(log, build); + await gzipCompressKbnUiSharedDeps(log, build); +}; + +export const GeneratePackagesOptimizedAssets: Task = { + description: 'Generates Optimized Assets for Packages', + + async run(config, log, build) { + // Create optimized assets for @kbn/ui-shared-deps + await generateKbnUiSharedDepsOptimizedAssets(log, build); + }, +}; diff --git a/src/dev/build/tasks/index.ts b/src/dev/build/tasks/index.ts index f22e8aca01a2d..c07da103ca150 100644 --- a/src/dev/build/tasks/index.ts +++ b/src/dev/build/tasks/index.ts @@ -15,6 +15,7 @@ export * from './create_archives_sources_task'; export * from './create_archives_task'; export * from './create_empty_dirs_and_files_task'; export * from './create_readme_task'; +export * from './generate_packages_optimized_assets'; export * from './install_dependencies_task'; export * from './license_file_task'; export * from './nodejs'; diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 15497258d4574..51ed25bfc69f6 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -19,6 +19,7 @@ export const storybookAliases = { embeddable: 'src/plugins/embeddable/.storybook', expression_error: 'src/plugins/expression_error/.storybook', expression_reveal_image: 'src/plugins/expression_reveal_image/.storybook', + expression_shape: 'src/plugins/expression_shape/.storybook', infra: 'x-pack/plugins/infra/.storybook', security_solution: 'x-pack/plugins/security_solution/.storybook', ui_actions_enhanced: 'x-pack/plugins/ui_actions_enhanced/.storybook', diff --git a/src/plugins/dashboard/public/application/lib/sync_dashboard_index_patterns.ts b/src/plugins/dashboard/public/application/lib/sync_dashboard_index_patterns.ts index b2873febee0d8..7360725e39cc1 100644 --- a/src/plugins/dashboard/public/application/lib/sync_dashboard_index_patterns.ts +++ b/src/plugins/dashboard/public/application/lib/sync_dashboard_index_patterns.ts @@ -8,8 +8,16 @@ import { uniqBy } from 'lodash'; import deepEqual from 'fast-deep-equal'; -import { merge, Observable, pipe } from 'rxjs'; -import { distinctUntilChanged, switchMap, startWith, filter, mapTo, map } from 'rxjs/operators'; +import { merge, Observable, pipe, EMPTY } from 'rxjs'; +import { + distinctUntilChanged, + catchError, + switchMap, + startWith, + filter, + mapTo, + map, +} from 'rxjs/operators'; import { DashboardContainer } from '..'; import { isErrorEmbeddable } from '../../services/embeddable'; @@ -73,7 +81,16 @@ export const syncDashboardIndexPatterns = ({ map(() => dashboardContainer!.getChildIds()), distinctUntilChanged(deepEqual), switchMap((newChildIds: string[]) => - merge(...newChildIds.map((childId) => dashboardContainer!.getChild(childId).getOutput$())) + merge( + ...newChildIds.map((childId) => + dashboardContainer! + .getChild(childId) + .getOutput$() + // Embeddables often throw errors into their output streams. + // This should not affect dashboard loading + .pipe(catchError(() => EMPTY)) + ) + ) ) ) ) diff --git a/src/plugins/data/common/es_query/es_query/get_es_query_config.test.ts b/src/plugins/data/common/es_query/get_es_query_config.test.ts similarity index 97% rename from src/plugins/data/common/es_query/es_query/get_es_query_config.test.ts rename to src/plugins/data/common/es_query/get_es_query_config.test.ts index 6963960d7ce03..5513f2649265f 100644 --- a/src/plugins/data/common/es_query/es_query/get_es_query_config.test.ts +++ b/src/plugins/data/common/es_query/get_es_query_config.test.ts @@ -9,7 +9,7 @@ import { get } from 'lodash'; import { getEsQueryConfig } from './get_es_query_config'; import { IUiSettingsClient } from 'kibana/public'; -import { UI_SETTINGS } from '../../'; +import { UI_SETTINGS } from '..'; const config = ({ get(item: string) { diff --git a/src/plugins/data/common/es_query/es_query/get_es_query_config.ts b/src/plugins/data/common/es_query/get_es_query_config.ts similarity index 82% rename from src/plugins/data/common/es_query/es_query/get_es_query_config.ts rename to src/plugins/data/common/es_query/get_es_query_config.ts index a074bb2ddc0e3..e3d39df75255c 100644 --- a/src/plugins/data/common/es_query/es_query/get_es_query_config.ts +++ b/src/plugins/data/common/es_query/get_es_query_config.ts @@ -6,14 +6,14 @@ * Side Public License, v 1. */ -import { EsQueryConfig } from './build_es_query'; -import { GetConfigFn, UI_SETTINGS } from '../../'; +import { EsQueryConfig } from '@kbn/es-query'; +import { GetConfigFn, UI_SETTINGS } from '..'; interface KibanaConfig { get: GetConfigFn; } -export function getEsQueryConfig(config: KibanaConfig) { +export function getEsQueryConfig(config: KibanaConfig): EsQueryConfig { const allowLeadingWildcards = config.get(UI_SETTINGS.QUERY_ALLOW_LEADING_WILDCARDS); const queryStringOptions = config.get(UI_SETTINGS.QUERY_STRING_OPTIONS); const ignoreFilterIfFieldNotInIndex = config.get( @@ -26,5 +26,5 @@ export function getEsQueryConfig(config: KibanaConfig) { queryStringOptions, ignoreFilterIfFieldNotInIndex, dateFormatTZ, - } as EsQueryConfig; + }; } diff --git a/src/plugins/data/common/es_query/index.ts b/src/plugins/data/common/es_query/index.ts index bbba52871d4c8..6d5084900b11d 100644 --- a/src/plugins/data/common/es_query/index.ts +++ b/src/plugins/data/common/es_query/index.ts @@ -6,6 +6,358 @@ * Side Public License, v 1. */ -export * from './es_query'; -export * from './filters'; -export * from './kuery'; +export { getEsQueryConfig } from './get_es_query_config'; + +// NOTE: Trick to deprecate exports https://stackoverflow.com/a/49152018/372086 +import { + isFilterDisabled as oldIsFilterDisabled, + disableFilter as oldDisableFilter, + fromKueryExpression as oldFromKueryExpression, + toElasticsearchQuery as oldToElasticsearchQuery, + nodeTypes as oldNodeTypes, + buildEsQuery as oldBuildEsQuery, + buildQueryFromFilters as oldBuildQueryFromFilters, + luceneStringToDsl as oldLuceneStringToDsl, + decorateQuery as olddecorateQuery, + getPhraseFilterField as oldgetPhraseFilterField, + getPhraseFilterValue as oldgetPhraseFilterValue, + isFilterPinned as oldIsFilterPinned, + nodeBuilder as oldNodeBuilder, + isFilters as oldIsFilters, + isExistsFilter as oldIsExistsFilter, + isMatchAllFilter as oldIsMatchAllFilter, + isGeoBoundingBoxFilter as oldIsGeoBoundingBoxFilter, + isGeoPolygonFilter as oldIsGeoPolygonFilter, + isMissingFilter as oldIsMissingFilter, + isPhraseFilter as oldIsPhraseFilter, + isPhrasesFilter as oldIsPhrasesFilter, + isRangeFilter as oldIsRangeFilter, + isQueryStringFilter as oldIsQueryStringFilter, + buildQueryFilter as oldBuildQueryFilter, + buildPhrasesFilter as oldBuildPhrasesFilter, + buildPhraseFilter as oldBuildPhraseFilter, + buildRangeFilter as oldBuildRangeFilter, + buildCustomFilter as oldBuildCustomFilter, + buildFilter as oldBuildFilter, + buildEmptyFilter as oldBuildEmptyFilter, + buildExistsFilter as oldBuildExistsFilter, + toggleFilterNegated as oldtoggleFilterNegated, + Filter as oldFilter, + RangeFilterMeta as oldRangeFilterMeta, + RangeFilterParams as oldRangeFilterParams, + ExistsFilter as oldExistsFilter, + GeoPolygonFilter as oldGeoPolygonFilter, + PhrasesFilter as oldPhrasesFilter, + PhraseFilter as oldPhraseFilter, + MatchAllFilter as oldMatchAllFilter, + CustomFilter as oldCustomFilter, + MissingFilter as oldMissingFilter, + RangeFilter as oldRangeFilter, + GeoBoundingBoxFilter as oldGeoBoundingBoxFilter, + KueryNode as oldKueryNode, + FilterMeta as oldFilterMeta, + FILTERS as oldFILTERS, + IFieldSubType as oldIFieldSubType, + EsQueryConfig as oldEsQueryConfig, + isFilter as oldIsFilter, + FilterStateStore, +} from '@kbn/es-query'; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const isFilter = oldIsFilter; +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const isFilterDisabled = oldIsFilterDisabled; +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const disableFilter = oldDisableFilter; +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const fromKueryExpression = oldFromKueryExpression; +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const toElasticsearchQuery = oldToElasticsearchQuery; +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const nodeTypes = oldNodeTypes; +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const buildEsQuery = oldBuildEsQuery; +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const buildQueryFromFilters = oldBuildQueryFromFilters; +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const luceneStringToDsl = oldLuceneStringToDsl; +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const decorateQuery = olddecorateQuery; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const getPhraseFilterField = oldgetPhraseFilterField; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const getPhraseFilterValue = oldgetPhraseFilterValue; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const isFilterPinned = oldIsFilterPinned; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const nodeBuilder = oldNodeBuilder; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const isFilters = oldIsFilters; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const isExistsFilter = oldIsExistsFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const isMatchAllFilter = oldIsMatchAllFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const isGeoBoundingBoxFilter = oldIsGeoBoundingBoxFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const isGeoPolygonFilter = oldIsGeoPolygonFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const isMissingFilter = oldIsMissingFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const isPhraseFilter = oldIsPhraseFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const isPhrasesFilter = oldIsPhrasesFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const isRangeFilter = oldIsRangeFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const isQueryStringFilter = oldIsQueryStringFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const buildQueryFilter = oldBuildQueryFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const buildPhrasesFilter = oldBuildPhrasesFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const buildPhraseFilter = oldBuildPhraseFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const buildRangeFilter = oldBuildRangeFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const buildCustomFilter = oldBuildCustomFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const buildFilter = oldBuildFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const buildEmptyFilter = oldBuildEmptyFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const buildExistsFilter = oldBuildExistsFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const toggleFilterNegated = oldtoggleFilterNegated; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +const FILTERS = oldFILTERS; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type Filter = oldFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type RangeFilterMeta = oldRangeFilterMeta; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type RangeFilterParams = oldRangeFilterParams; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type ExistsFilter = oldExistsFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type GeoPolygonFilter = oldGeoPolygonFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type PhrasesFilter = oldPhrasesFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type PhraseFilter = oldPhraseFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type MatchAllFilter = oldMatchAllFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type CustomFilter = oldCustomFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type MissingFilter = oldMissingFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type RangeFilter = oldRangeFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type GeoBoundingBoxFilter = oldGeoBoundingBoxFilter; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type KueryNode = oldKueryNode; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type FilterMeta = oldFilterMeta; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type IFieldSubType = oldIFieldSubType; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ +type EsQueryConfig = oldEsQueryConfig; + +/** + * @deprecated Please import from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ + +export { + disableFilter, + fromKueryExpression, + toElasticsearchQuery, + nodeTypes, + buildEsQuery, + buildQueryFromFilters, + luceneStringToDsl, + decorateQuery, + getPhraseFilterField, + getPhraseFilterValue, + isFilterPinned, + nodeBuilder, + isFilters, + isExistsFilter, + isMatchAllFilter, + isGeoBoundingBoxFilter, + isGeoPolygonFilter, + isMissingFilter, + isPhraseFilter, + isPhrasesFilter, + isRangeFilter, + isQueryStringFilter, + buildQueryFilter, + buildPhrasesFilter, + buildPhraseFilter, + buildRangeFilter, + buildCustomFilter, + buildFilter, + buildEmptyFilter, + buildExistsFilter, + toggleFilterNegated, + FILTERS, + isFilter, + isFilterDisabled, + FilterStateStore, + Filter, + RangeFilterMeta, + RangeFilterParams, + ExistsFilter, + GeoPolygonFilter, + PhrasesFilter, + PhraseFilter, + MatchAllFilter, + CustomFilter, + MissingFilter, + RangeFilter, + GeoBoundingBoxFilter, + KueryNode, + FilterMeta, + IFieldSubType, + EsQueryConfig, +}; diff --git a/src/plugins/data/common/es_query/kuery/ast/_generated_/kuery.js b/src/plugins/data/common/es_query/kuery/ast/_generated_/kuery.js deleted file mode 100644 index 7ee744ad5f4c8..0000000000000 --- a/src/plugins/data/common/es_query/kuery/ast/_generated_/kuery.js +++ /dev/null @@ -1,2635 +0,0 @@ -module.exports = (function() { - "use strict"; - - /* - * Generated by PEG.js 0.9.0. - * - * http://pegjs.org/ - */ - - function peg$subclass(child, parent) { - function ctor() { this.constructor = child; } - ctor.prototype = parent.prototype; - child.prototype = new ctor(); - } - - function peg$SyntaxError(message, expected, found, location) { - this.message = message; - this.expected = expected; - this.found = found; - this.location = location; - this.name = "SyntaxError"; - - if (typeof Error.captureStackTrace === "function") { - Error.captureStackTrace(this, peg$SyntaxError); - } - } - - peg$subclass(peg$SyntaxError, Error); - - function peg$parse(input) { - var options = arguments.length > 1 ? arguments[1] : {}, - parser = this, - - peg$FAILED = {}, - - peg$startRuleFunctions = { start: peg$parsestart, Literal: peg$parseLiteral }, - peg$startRuleFunction = peg$parsestart, - - peg$c0 = function(query, trailing) { - if (trailing.type === 'cursor') { - return { - ...trailing, - suggestionTypes: ['conjunction'] - }; - } - if (query !== null) return query; - return nodeTypes.function.buildNode('is', '*', '*'); - }, - peg$c1 = function(head, query) { return query; }, - peg$c2 = function(head, tail) { - const nodes = [head, ...tail]; - const cursor = parseCursor && nodes.find(node => node.type === 'cursor'); - if (cursor) return cursor; - return buildFunctionNode('or', nodes); - }, - peg$c3 = function(head, tail) { - const nodes = [head, ...tail]; - const cursor = parseCursor && nodes.find(node => node.type === 'cursor'); - if (cursor) return cursor; - return buildFunctionNode('and', nodes); - }, - peg$c4 = function(query) { - if (query.type === 'cursor') return query; - return buildFunctionNode('not', [query]); - }, - peg$c5 = "(", - peg$c6 = { type: "literal", value: "(", description: "\"(\"" }, - peg$c7 = ")", - peg$c8 = { type: "literal", value: ")", description: "\")\"" }, - peg$c9 = function(query, trailing) { - if (trailing.type === 'cursor') { - return { - ...trailing, - suggestionTypes: ['conjunction'] - }; - } - return query; - }, - peg$c10 = ":", - peg$c11 = { type: "literal", value: ":", description: "\":\"" }, - peg$c12 = "{", - peg$c13 = { type: "literal", value: "{", description: "\"{\"" }, - peg$c14 = "}", - peg$c15 = { type: "literal", value: "}", description: "\"}\"" }, - peg$c16 = function(field, query, trailing) { - if (query.type === 'cursor') { - return { - ...query, - nestedPath: query.nestedPath ? `${field.value}.${query.nestedPath}` : field.value, - } - }; - - if (trailing.type === 'cursor') { - return { - ...trailing, - suggestionTypes: ['conjunction'] - }; - } - return buildFunctionNode('nested', [field, query]); - }, - peg$c17 = { type: "other", description: "fieldName" }, - peg$c18 = function(field, operator, value) { - if (value.type === 'cursor') { - return { - ...value, - suggestionTypes: ['conjunction'] - }; - } - const range = buildNamedArgNode(operator, value); - return buildFunctionNode('range', [field, range]); - }, - peg$c19 = function(field, partial) { - if (partial.type === 'cursor') { - return { - ...partial, - fieldName: field.value, - suggestionTypes: ['value', 'conjunction'] - }; - } - return partial(field); - }, - peg$c20 = function(partial) { - if (partial.type === 'cursor') { - const fieldName = `${partial.prefix}${partial.suffix}`.trim(); - return { - ...partial, - fieldName, - suggestionTypes: ['field', 'operator', 'conjunction'] - }; - } - const field = buildLiteralNode(null); - return partial(field); - }, - peg$c21 = function(partial, trailing) { - if (trailing.type === 'cursor') { - return { - ...trailing, - suggestionTypes: ['conjunction'] - }; - } - return partial; - }, - peg$c22 = function(head, partial) { return partial; }, - peg$c23 = function(head, tail) { - const nodes = [head, ...tail]; - const cursor = parseCursor && nodes.find(node => node.type === 'cursor'); - if (cursor) { - return { - ...cursor, - suggestionTypes: ['value'] - }; - } - return (field) => buildFunctionNode('or', nodes.map(partial => partial(field))); - }, - peg$c24 = function(head, tail) { - const nodes = [head, ...tail]; - const cursor = parseCursor && nodes.find(node => node.type === 'cursor'); - if (cursor) { - return { - ...cursor, - suggestionTypes: ['value'] - }; - } - return (field) => buildFunctionNode('and', nodes.map(partial => partial(field))); - }, - peg$c25 = function(partial) { - if (partial.type === 'cursor') { - return { - ...list, - suggestionTypes: ['value'] - }; - } - return (field) => buildFunctionNode('not', [partial(field)]); - }, - peg$c26 = { type: "other", description: "value" }, - peg$c27 = function(value) { - if (value.type === 'cursor') return value; - const isPhrase = buildLiteralNode(true); - return (field) => buildFunctionNode('is', [field, value, isPhrase]); - }, - peg$c28 = function(value) { - if (value.type === 'cursor') return value; - - if (!allowLeadingWildcards && value.type === 'wildcard' && nodeTypes.wildcard.hasLeadingWildcard(value)) { - error('Leading wildcards are disabled. See query:allowLeadingWildcards in Advanced Settings.'); - } - - const isPhrase = buildLiteralNode(false); - return (field) => buildFunctionNode('is', [field, value, isPhrase]); - }, - peg$c29 = { type: "other", description: "OR" }, - peg$c30 = "or", - peg$c31 = { type: "literal", value: "or", description: "\"or\"" }, - peg$c32 = { type: "other", description: "AND" }, - peg$c33 = "and", - peg$c34 = { type: "literal", value: "and", description: "\"and\"" }, - peg$c35 = { type: "other", description: "NOT" }, - peg$c36 = "not", - peg$c37 = { type: "literal", value: "not", description: "\"not\"" }, - peg$c38 = { type: "other", description: "literal" }, - peg$c39 = function() { return parseCursor; }, - peg$c40 = "\"", - peg$c41 = { type: "literal", value: "\"", description: "\"\\\"\"" }, - peg$c42 = function(prefix, cursor, suffix) { - const { start, end } = location(); - return { - type: 'cursor', - start: start.offset, - end: end.offset - cursor.length, - prefix: prefix.join(''), - suffix: suffix.join(''), - text: text().replace(cursor, '') - }; - }, - peg$c43 = function(chars) { - return buildLiteralNode(chars.join('')); - }, - peg$c44 = "\\", - peg$c45 = { type: "literal", value: "\\", description: "\"\\\\\"" }, - peg$c46 = /^[\\"]/, - peg$c47 = { type: "class", value: "[\\\\\"]", description: "[\\\\\"]" }, - peg$c48 = function(char) { return char; }, - peg$c49 = /^[^"]/, - peg$c50 = { type: "class", value: "[^\"]", description: "[^\"]" }, - peg$c51 = function(chars) { - const sequence = chars.join('').trim(); - if (sequence === 'null') return buildLiteralNode(null); - if (sequence === 'true') return buildLiteralNode(true); - if (sequence === 'false') return buildLiteralNode(false); - if (chars.includes(wildcardSymbol)) return buildWildcardNode(sequence); - return buildLiteralNode(sequence); - }, - peg$c52 = { type: "any", description: "any character" }, - peg$c53 = "*", - peg$c54 = { type: "literal", value: "*", description: "\"*\"" }, - peg$c55 = function() { return wildcardSymbol; }, - peg$c56 = "\\t", - peg$c57 = { type: "literal", value: "\\t", description: "\"\\\\t\"" }, - peg$c58 = function() { return '\t'; }, - peg$c59 = "\\r", - peg$c60 = { type: "literal", value: "\\r", description: "\"\\\\r\"" }, - peg$c61 = function() { return '\r'; }, - peg$c62 = "\\n", - peg$c63 = { type: "literal", value: "\\n", description: "\"\\\\n\"" }, - peg$c64 = function() { return '\n'; }, - peg$c65 = function(keyword) { return keyword; }, - peg$c66 = /^[\\():<>"*{}]/, - peg$c67 = { type: "class", value: "[\\\\():<>\"*{}]", description: "[\\\\():<>\"*{}]" }, - peg$c68 = function(sequence) { return sequence; }, - peg$c69 = "u", - peg$c70 = { type: "literal", value: "u", description: "\"u\"" }, - peg$c71 = function(digits) { - return String.fromCharCode(parseInt(digits, 16)); - }, - peg$c72 = /^[0-9a-f]/i, - peg$c73 = { type: "class", value: "[0-9a-f]i", description: "[0-9a-f]i" }, - peg$c74 = "<=", - peg$c75 = { type: "literal", value: "<=", description: "\"<=\"" }, - peg$c76 = function() { return 'lte'; }, - peg$c77 = ">=", - peg$c78 = { type: "literal", value: ">=", description: "\">=\"" }, - peg$c79 = function() { return 'gte'; }, - peg$c80 = "<", - peg$c81 = { type: "literal", value: "<", description: "\"<\"" }, - peg$c82 = function() { return 'lt'; }, - peg$c83 = ">", - peg$c84 = { type: "literal", value: ">", description: "\">\"" }, - peg$c85 = function() { return 'gt'; }, - peg$c86 = { type: "other", description: "whitespace" }, - peg$c87 = /^[ \t\r\n\xA0]/, - peg$c88 = { type: "class", value: "[\\ \\t\\r\\n\\u00A0]", description: "[\\ \\t\\r\\n\\u00A0]" }, - peg$c89 = "@kuery-cursor@", - peg$c90 = { type: "literal", value: "@kuery-cursor@", description: "\"@kuery-cursor@\"" }, - peg$c91 = function() { return cursorSymbol; }, - - peg$currPos = 0, - peg$savedPos = 0, - peg$posDetailsCache = [{ line: 1, column: 1, seenCR: false }], - peg$maxFailPos = 0, - peg$maxFailExpected = [], - peg$silentFails = 0, - - peg$resultsCache = {}, - - peg$result; - - if ("startRule" in options) { - if (!(options.startRule in peg$startRuleFunctions)) { - throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); - } - - peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; - } - - function text() { - return input.substring(peg$savedPos, peg$currPos); - } - - function location() { - return peg$computeLocation(peg$savedPos, peg$currPos); - } - - function expected(description) { - throw peg$buildException( - null, - [{ type: "other", description: description }], - input.substring(peg$savedPos, peg$currPos), - peg$computeLocation(peg$savedPos, peg$currPos) - ); - } - - function error(message) { - throw peg$buildException( - message, - null, - input.substring(peg$savedPos, peg$currPos), - peg$computeLocation(peg$savedPos, peg$currPos) - ); - } - - function peg$computePosDetails(pos) { - var details = peg$posDetailsCache[pos], - p, ch; - - if (details) { - return details; - } else { - p = pos - 1; - while (!peg$posDetailsCache[p]) { - p--; - } - - details = peg$posDetailsCache[p]; - details = { - line: details.line, - column: details.column, - seenCR: details.seenCR - }; - - while (p < pos) { - ch = input.charAt(p); - if (ch === "\n") { - if (!details.seenCR) { details.line++; } - details.column = 1; - details.seenCR = false; - } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") { - details.line++; - details.column = 1; - details.seenCR = true; - } else { - details.column++; - details.seenCR = false; - } - - p++; - } - - peg$posDetailsCache[pos] = details; - return details; - } - } - - function peg$computeLocation(startPos, endPos) { - var startPosDetails = peg$computePosDetails(startPos), - endPosDetails = peg$computePosDetails(endPos); - - return { - start: { - offset: startPos, - line: startPosDetails.line, - column: startPosDetails.column - }, - end: { - offset: endPos, - line: endPosDetails.line, - column: endPosDetails.column - } - }; - } - - function peg$fail(expected) { - if (peg$currPos < peg$maxFailPos) { return; } - - if (peg$currPos > peg$maxFailPos) { - peg$maxFailPos = peg$currPos; - peg$maxFailExpected = []; - } - - peg$maxFailExpected.push(expected); - } - - function peg$buildException(message, expected, found, location) { - function cleanupExpected(expected) { - var i = 1; - - expected.sort(function(a, b) { - if (a.description < b.description) { - return -1; - } else if (a.description > b.description) { - return 1; - } else { - return 0; - } - }); - - while (i < expected.length) { - if (expected[i - 1] === expected[i]) { - expected.splice(i, 1); - } else { - i++; - } - } - } - - function buildMessage(expected, found) { - function stringEscape(s) { - function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); } - - return s - .replace(/\\/g, '\\\\') - .replace(/"/g, '\\"') - .replace(/\x08/g, '\\b') - .replace(/\t/g, '\\t') - .replace(/\n/g, '\\n') - .replace(/\f/g, '\\f') - .replace(/\r/g, '\\r') - .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); }) - .replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); }) - .replace(/[\u0100-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); }) - .replace(/[\u1000-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); }); - } - - var expectedDescs = new Array(expected.length), - expectedDesc, foundDesc, i; - - for (i = 0; i < expected.length; i++) { - expectedDescs[i] = expected[i].description; - } - - expectedDesc = expected.length > 1 - ? expectedDescs.slice(0, -1).join(", ") - + " or " - + expectedDescs[expected.length - 1] - : expectedDescs[0]; - - foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input"; - - return "Expected " + expectedDesc + " but " + foundDesc + " found."; - } - - if (expected !== null) { - cleanupExpected(expected); - } - - return new peg$SyntaxError( - message !== null ? message : buildMessage(expected, found), - expected, - found, - location - ); - } - - function peg$parsestart() { - var s0, s1, s2, s3; - - var key = peg$currPos * 37 + 0, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = []; - s2 = peg$parseSpace(); - while (s2 !== peg$FAILED) { - s1.push(s2); - s2 = peg$parseSpace(); - } - if (s1 !== peg$FAILED) { - s2 = peg$parseOrQuery(); - if (s2 === peg$FAILED) { - s2 = null; - } - if (s2 !== peg$FAILED) { - s3 = peg$parseOptionalSpace(); - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c0(s2, s3); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseOrQuery() { - var s0, s1, s2, s3, s4, s5; - - var key = peg$currPos * 37 + 1, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseAndQuery(); - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$currPos; - s4 = peg$parseOr(); - if (s4 !== peg$FAILED) { - s5 = peg$parseAndQuery(); - if (s5 !== peg$FAILED) { - peg$savedPos = s3; - s4 = peg$c1(s1, s5); - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - if (s3 !== peg$FAILED) { - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$currPos; - s4 = peg$parseOr(); - if (s4 !== peg$FAILED) { - s5 = peg$parseAndQuery(); - if (s5 !== peg$FAILED) { - peg$savedPos = s3; - s4 = peg$c1(s1, s5); - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } - } else { - s2 = peg$FAILED; - } - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c2(s1, s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$parseAndQuery(); - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseAndQuery() { - var s0, s1, s2, s3, s4, s5; - - var key = peg$currPos * 37 + 2, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseNotQuery(); - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$currPos; - s4 = peg$parseAnd(); - if (s4 !== peg$FAILED) { - s5 = peg$parseNotQuery(); - if (s5 !== peg$FAILED) { - peg$savedPos = s3; - s4 = peg$c1(s1, s5); - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - if (s3 !== peg$FAILED) { - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$currPos; - s4 = peg$parseAnd(); - if (s4 !== peg$FAILED) { - s5 = peg$parseNotQuery(); - if (s5 !== peg$FAILED) { - peg$savedPos = s3; - s4 = peg$c1(s1, s5); - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } - } else { - s2 = peg$FAILED; - } - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c3(s1, s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$parseNotQuery(); - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseNotQuery() { - var s0, s1, s2; - - var key = peg$currPos * 37 + 3, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseNot(); - if (s1 !== peg$FAILED) { - s2 = peg$parseSubQuery(); - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c4(s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$parseSubQuery(); - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseSubQuery() { - var s0, s1, s2, s3, s4, s5; - - var key = peg$currPos * 37 + 4, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 40) { - s1 = peg$c5; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c6); } - } - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parseSpace(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parseSpace(); - } - if (s2 !== peg$FAILED) { - s3 = peg$parseOrQuery(); - if (s3 !== peg$FAILED) { - s4 = peg$parseOptionalSpace(); - if (s4 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 41) { - s5 = peg$c7; - peg$currPos++; - } else { - s5 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c8); } - } - if (s5 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c9(s3, s4); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$parseNestedQuery(); - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseNestedQuery() { - var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; - - var key = peg$currPos * 37 + 5, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseField(); - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parseSpace(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parseSpace(); - } - if (s2 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 58) { - s3 = peg$c10; - peg$currPos++; - } else { - s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c11); } - } - if (s3 !== peg$FAILED) { - s4 = []; - s5 = peg$parseSpace(); - while (s5 !== peg$FAILED) { - s4.push(s5); - s5 = peg$parseSpace(); - } - if (s4 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 123) { - s5 = peg$c12; - peg$currPos++; - } else { - s5 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c13); } - } - if (s5 !== peg$FAILED) { - s6 = []; - s7 = peg$parseSpace(); - while (s7 !== peg$FAILED) { - s6.push(s7); - s7 = peg$parseSpace(); - } - if (s6 !== peg$FAILED) { - s7 = peg$parseOrQuery(); - if (s7 !== peg$FAILED) { - s8 = peg$parseOptionalSpace(); - if (s8 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 125) { - s9 = peg$c14; - peg$currPos++; - } else { - s9 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c15); } - } - if (s9 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c16(s1, s7, s8); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$parseExpression(); - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseExpression() { - var s0; - - var key = peg$currPos * 37 + 6, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$parseFieldRangeExpression(); - if (s0 === peg$FAILED) { - s0 = peg$parseFieldValueExpression(); - if (s0 === peg$FAILED) { - s0 = peg$parseValueExpression(); - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseField() { - var s0, s1; - - var key = peg$currPos * 37 + 7, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - peg$silentFails++; - s0 = peg$parseLiteral(); - peg$silentFails--; - if (s0 === peg$FAILED) { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c17); } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseFieldRangeExpression() { - var s0, s1, s2, s3, s4, s5; - - var key = peg$currPos * 37 + 8, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseField(); - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parseSpace(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parseSpace(); - } - if (s2 !== peg$FAILED) { - s3 = peg$parseRangeOperator(); - if (s3 !== peg$FAILED) { - s4 = []; - s5 = peg$parseSpace(); - while (s5 !== peg$FAILED) { - s4.push(s5); - s5 = peg$parseSpace(); - } - if (s4 !== peg$FAILED) { - s5 = peg$parseLiteral(); - if (s5 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c18(s1, s3, s5); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseFieldValueExpression() { - var s0, s1, s2, s3, s4, s5; - - var key = peg$currPos * 37 + 9, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseField(); - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parseSpace(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parseSpace(); - } - if (s2 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 58) { - s3 = peg$c10; - peg$currPos++; - } else { - s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c11); } - } - if (s3 !== peg$FAILED) { - s4 = []; - s5 = peg$parseSpace(); - while (s5 !== peg$FAILED) { - s4.push(s5); - s5 = peg$parseSpace(); - } - if (s4 !== peg$FAILED) { - s5 = peg$parseListOfValues(); - if (s5 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c19(s1, s5); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseValueExpression() { - var s0, s1; - - var key = peg$currPos * 37 + 10, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseValue(); - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c20(s1); - } - s0 = s1; - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseListOfValues() { - var s0, s1, s2, s3, s4, s5; - - var key = peg$currPos * 37 + 11, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 40) { - s1 = peg$c5; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c6); } - } - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parseSpace(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parseSpace(); - } - if (s2 !== peg$FAILED) { - s3 = peg$parseOrListOfValues(); - if (s3 !== peg$FAILED) { - s4 = peg$parseOptionalSpace(); - if (s4 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 41) { - s5 = peg$c7; - peg$currPos++; - } else { - s5 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c8); } - } - if (s5 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c21(s3, s4); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$parseValue(); - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseOrListOfValues() { - var s0, s1, s2, s3, s4, s5; - - var key = peg$currPos * 37 + 12, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseAndListOfValues(); - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$currPos; - s4 = peg$parseOr(); - if (s4 !== peg$FAILED) { - s5 = peg$parseAndListOfValues(); - if (s5 !== peg$FAILED) { - peg$savedPos = s3; - s4 = peg$c22(s1, s5); - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - if (s3 !== peg$FAILED) { - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$currPos; - s4 = peg$parseOr(); - if (s4 !== peg$FAILED) { - s5 = peg$parseAndListOfValues(); - if (s5 !== peg$FAILED) { - peg$savedPos = s3; - s4 = peg$c22(s1, s5); - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } - } else { - s2 = peg$FAILED; - } - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c23(s1, s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$parseAndListOfValues(); - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseAndListOfValues() { - var s0, s1, s2, s3, s4, s5; - - var key = peg$currPos * 37 + 13, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseNotListOfValues(); - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$currPos; - s4 = peg$parseAnd(); - if (s4 !== peg$FAILED) { - s5 = peg$parseNotListOfValues(); - if (s5 !== peg$FAILED) { - peg$savedPos = s3; - s4 = peg$c22(s1, s5); - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - if (s3 !== peg$FAILED) { - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$currPos; - s4 = peg$parseAnd(); - if (s4 !== peg$FAILED) { - s5 = peg$parseNotListOfValues(); - if (s5 !== peg$FAILED) { - peg$savedPos = s3; - s4 = peg$c22(s1, s5); - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } - } else { - s2 = peg$FAILED; - } - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c24(s1, s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$parseNotListOfValues(); - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseNotListOfValues() { - var s0, s1, s2; - - var key = peg$currPos * 37 + 14, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - s1 = peg$parseNot(); - if (s1 !== peg$FAILED) { - s2 = peg$parseListOfValues(); - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c25(s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$parseListOfValues(); - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseValue() { - var s0, s1; - - var key = peg$currPos * 37 + 15, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - peg$silentFails++; - s0 = peg$currPos; - s1 = peg$parseQuotedString(); - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c27(s1); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - s1 = peg$parseUnquotedLiteral(); - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c28(s1); - } - s0 = s1; - } - peg$silentFails--; - if (s0 === peg$FAILED) { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c26); } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseOr() { - var s0, s1, s2, s3, s4; - - var key = peg$currPos * 37 + 16, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - peg$silentFails++; - s0 = peg$currPos; - s1 = []; - s2 = peg$parseSpace(); - if (s2 !== peg$FAILED) { - while (s2 !== peg$FAILED) { - s1.push(s2); - s2 = peg$parseSpace(); - } - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - if (input.substr(peg$currPos, 2).toLowerCase() === peg$c30) { - s2 = input.substr(peg$currPos, 2); - peg$currPos += 2; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c31); } - } - if (s2 !== peg$FAILED) { - s3 = []; - s4 = peg$parseSpace(); - if (s4 !== peg$FAILED) { - while (s4 !== peg$FAILED) { - s3.push(s4); - s4 = peg$parseSpace(); - } - } else { - s3 = peg$FAILED; - } - if (s3 !== peg$FAILED) { - s1 = [s1, s2, s3]; - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - peg$silentFails--; - if (s0 === peg$FAILED) { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c29); } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseAnd() { - var s0, s1, s2, s3, s4; - - var key = peg$currPos * 37 + 17, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - peg$silentFails++; - s0 = peg$currPos; - s1 = []; - s2 = peg$parseSpace(); - if (s2 !== peg$FAILED) { - while (s2 !== peg$FAILED) { - s1.push(s2); - s2 = peg$parseSpace(); - } - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - if (input.substr(peg$currPos, 3).toLowerCase() === peg$c33) { - s2 = input.substr(peg$currPos, 3); - peg$currPos += 3; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c34); } - } - if (s2 !== peg$FAILED) { - s3 = []; - s4 = peg$parseSpace(); - if (s4 !== peg$FAILED) { - while (s4 !== peg$FAILED) { - s3.push(s4); - s4 = peg$parseSpace(); - } - } else { - s3 = peg$FAILED; - } - if (s3 !== peg$FAILED) { - s1 = [s1, s2, s3]; - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - peg$silentFails--; - if (s0 === peg$FAILED) { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c32); } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseNot() { - var s0, s1, s2, s3; - - var key = peg$currPos * 37 + 18, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - peg$silentFails++; - s0 = peg$currPos; - if (input.substr(peg$currPos, 3).toLowerCase() === peg$c36) { - s1 = input.substr(peg$currPos, 3); - peg$currPos += 3; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c37); } - } - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parseSpace(); - if (s3 !== peg$FAILED) { - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parseSpace(); - } - } else { - s2 = peg$FAILED; - } - if (s2 !== peg$FAILED) { - s1 = [s1, s2]; - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - peg$silentFails--; - if (s0 === peg$FAILED) { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c35); } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseLiteral() { - var s0, s1; - - var key = peg$currPos * 37 + 19, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - peg$silentFails++; - s0 = peg$parseQuotedString(); - if (s0 === peg$FAILED) { - s0 = peg$parseUnquotedLiteral(); - } - peg$silentFails--; - if (s0 === peg$FAILED) { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c38); } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseQuotedString() { - var s0, s1, s2, s3, s4, s5, s6; - - var key = peg$currPos * 37 + 20, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - peg$savedPos = peg$currPos; - s1 = peg$c39(); - if (s1) { - s1 = void 0; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 34) { - s2 = peg$c40; - peg$currPos++; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c41); } - } - if (s2 !== peg$FAILED) { - s3 = []; - s4 = peg$parseQuotedCharacter(); - while (s4 !== peg$FAILED) { - s3.push(s4); - s4 = peg$parseQuotedCharacter(); - } - if (s3 !== peg$FAILED) { - s4 = peg$parseCursor(); - if (s4 !== peg$FAILED) { - s5 = []; - s6 = peg$parseQuotedCharacter(); - while (s6 !== peg$FAILED) { - s5.push(s6); - s6 = peg$parseQuotedCharacter(); - } - if (s5 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 34) { - s6 = peg$c40; - peg$currPos++; - } else { - s6 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c41); } - } - if (s6 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c42(s3, s4, s5); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 34) { - s1 = peg$c40; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c41); } - } - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parseQuotedCharacter(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parseQuotedCharacter(); - } - if (s2 !== peg$FAILED) { - if (input.charCodeAt(peg$currPos) === 34) { - s3 = peg$c40; - peg$currPos++; - } else { - s3 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c41); } - } - if (s3 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c43(s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseQuotedCharacter() { - var s0, s1, s2; - - var key = peg$currPos * 37 + 21, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$parseEscapedWhitespace(); - if (s0 === peg$FAILED) { - s0 = peg$parseEscapedUnicodeSequence(); - if (s0 === peg$FAILED) { - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 92) { - s1 = peg$c44; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c45); } - } - if (s1 !== peg$FAILED) { - if (peg$c46.test(input.charAt(peg$currPos))) { - s2 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c47); } - } - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c48(s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$currPos; - s1 = peg$currPos; - peg$silentFails++; - s2 = peg$parseCursor(); - peg$silentFails--; - if (s2 === peg$FAILED) { - s1 = void 0; - } else { - peg$currPos = s1; - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - if (peg$c49.test(input.charAt(peg$currPos))) { - s2 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c50); } - } - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c48(s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseUnquotedLiteral() { - var s0, s1, s2, s3, s4, s5; - - var key = peg$currPos * 37 + 22, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - peg$savedPos = peg$currPos; - s1 = peg$c39(); - if (s1) { - s1 = void 0; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parseUnquotedCharacter(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parseUnquotedCharacter(); - } - if (s2 !== peg$FAILED) { - s3 = peg$parseCursor(); - if (s3 !== peg$FAILED) { - s4 = []; - s5 = peg$parseUnquotedCharacter(); - while (s5 !== peg$FAILED) { - s4.push(s5); - s5 = peg$parseUnquotedCharacter(); - } - if (s4 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c42(s2, s3, s4); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = peg$currPos; - s1 = []; - s2 = peg$parseUnquotedCharacter(); - if (s2 !== peg$FAILED) { - while (s2 !== peg$FAILED) { - s1.push(s2); - s2 = peg$parseUnquotedCharacter(); - } - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c51(s1); - } - s0 = s1; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseUnquotedCharacter() { - var s0, s1, s2, s3, s4; - - var key = peg$currPos * 37 + 23, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$parseEscapedWhitespace(); - if (s0 === peg$FAILED) { - s0 = peg$parseEscapedSpecialCharacter(); - if (s0 === peg$FAILED) { - s0 = peg$parseEscapedUnicodeSequence(); - if (s0 === peg$FAILED) { - s0 = peg$parseEscapedKeyword(); - if (s0 === peg$FAILED) { - s0 = peg$parseWildcard(); - if (s0 === peg$FAILED) { - s0 = peg$currPos; - s1 = peg$currPos; - peg$silentFails++; - s2 = peg$parseSpecialCharacter(); - peg$silentFails--; - if (s2 === peg$FAILED) { - s1 = void 0; - } else { - peg$currPos = s1; - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - s2 = peg$currPos; - peg$silentFails++; - s3 = peg$parseKeyword(); - peg$silentFails--; - if (s3 === peg$FAILED) { - s2 = void 0; - } else { - peg$currPos = s2; - s2 = peg$FAILED; - } - if (s2 !== peg$FAILED) { - s3 = peg$currPos; - peg$silentFails++; - s4 = peg$parseCursor(); - peg$silentFails--; - if (s4 === peg$FAILED) { - s3 = void 0; - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - if (s3 !== peg$FAILED) { - if (input.length > peg$currPos) { - s4 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s4 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c52); } - } - if (s4 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c48(s4); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } - } - } - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseWildcard() { - var s0, s1; - - var key = peg$currPos * 37 + 24, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 42) { - s1 = peg$c53; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c54); } - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c55(); - } - s0 = s1; - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseOptionalSpace() { - var s0, s1, s2, s3, s4, s5; - - var key = peg$currPos * 37 + 25, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - peg$savedPos = peg$currPos; - s1 = peg$c39(); - if (s1) { - s1 = void 0; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parseSpace(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parseSpace(); - } - if (s2 !== peg$FAILED) { - s3 = peg$parseCursor(); - if (s3 !== peg$FAILED) { - s4 = []; - s5 = peg$parseSpace(); - while (s5 !== peg$FAILED) { - s4.push(s5); - s5 = peg$parseSpace(); - } - if (s4 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c42(s2, s3, s4); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - if (s0 === peg$FAILED) { - s0 = []; - s1 = peg$parseSpace(); - while (s1 !== peg$FAILED) { - s0.push(s1); - s1 = peg$parseSpace(); - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseEscapedWhitespace() { - var s0, s1; - - var key = peg$currPos * 37 + 26, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c56) { - s1 = peg$c56; - peg$currPos += 2; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c57); } - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c58(); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c59) { - s1 = peg$c59; - peg$currPos += 2; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c60); } - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c61(); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c62) { - s1 = peg$c62; - peg$currPos += 2; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c63); } - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c64(); - } - s0 = s1; - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseEscapedSpecialCharacter() { - var s0, s1, s2; - - var key = peg$currPos * 37 + 27, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 92) { - s1 = peg$c44; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c45); } - } - if (s1 !== peg$FAILED) { - s2 = peg$parseSpecialCharacter(); - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c48(s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseEscapedKeyword() { - var s0, s1, s2; - - var key = peg$currPos * 37 + 28, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 92) { - s1 = peg$c44; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c45); } - } - if (s1 !== peg$FAILED) { - if (input.substr(peg$currPos, 2).toLowerCase() === peg$c30) { - s2 = input.substr(peg$currPos, 2); - peg$currPos += 2; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c31); } - } - if (s2 === peg$FAILED) { - if (input.substr(peg$currPos, 3).toLowerCase() === peg$c33) { - s2 = input.substr(peg$currPos, 3); - peg$currPos += 3; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c34); } - } - if (s2 === peg$FAILED) { - if (input.substr(peg$currPos, 3).toLowerCase() === peg$c36) { - s2 = input.substr(peg$currPos, 3); - peg$currPos += 3; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c37); } - } - } - } - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c65(s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseKeyword() { - var s0; - - var key = peg$currPos * 37 + 29, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$parseOr(); - if (s0 === peg$FAILED) { - s0 = peg$parseAnd(); - if (s0 === peg$FAILED) { - s0 = peg$parseNot(); - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseSpecialCharacter() { - var s0; - - var key = peg$currPos * 37 + 30, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - if (peg$c66.test(input.charAt(peg$currPos))) { - s0 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c67); } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseEscapedUnicodeSequence() { - var s0, s1, s2; - - var key = peg$currPos * 37 + 31, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 92) { - s1 = peg$c44; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c45); } - } - if (s1 !== peg$FAILED) { - s2 = peg$parseUnicodeSequence(); - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c68(s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseUnicodeSequence() { - var s0, s1, s2, s3, s4, s5, s6, s7; - - var key = peg$currPos * 37 + 32, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 117) { - s1 = peg$c69; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c70); } - } - if (s1 !== peg$FAILED) { - s2 = peg$currPos; - s3 = peg$currPos; - s4 = peg$parseHexDigit(); - if (s4 !== peg$FAILED) { - s5 = peg$parseHexDigit(); - if (s5 !== peg$FAILED) { - s6 = peg$parseHexDigit(); - if (s6 !== peg$FAILED) { - s7 = peg$parseHexDigit(); - if (s7 !== peg$FAILED) { - s4 = [s4, s5, s6, s7]; - s3 = s4; - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - } else { - peg$currPos = s3; - s3 = peg$FAILED; - } - if (s3 !== peg$FAILED) { - s2 = input.substring(s2, peg$currPos); - } else { - s2 = s3; - } - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c71(s2); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseHexDigit() { - var s0; - - var key = peg$currPos * 37 + 33, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - if (peg$c72.test(input.charAt(peg$currPos))) { - s0 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c73); } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseRangeOperator() { - var s0, s1; - - var key = peg$currPos * 37 + 34, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c74) { - s1 = peg$c74; - peg$currPos += 2; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c75); } - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c76(); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - if (input.substr(peg$currPos, 2) === peg$c77) { - s1 = peg$c77; - peg$currPos += 2; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c78); } - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c79(); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 60) { - s1 = peg$c80; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c81); } - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c82(); - } - s0 = s1; - if (s0 === peg$FAILED) { - s0 = peg$currPos; - if (input.charCodeAt(peg$currPos) === 62) { - s1 = peg$c83; - peg$currPos++; - } else { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c84); } - } - if (s1 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c85(); - } - s0 = s1; - } - } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseSpace() { - var s0, s1; - - var key = peg$currPos * 37 + 35, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - peg$silentFails++; - if (peg$c87.test(input.charAt(peg$currPos))) { - s0 = input.charAt(peg$currPos); - peg$currPos++; - } else { - s0 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c88); } - } - peg$silentFails--; - if (s0 === peg$FAILED) { - s1 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c86); } - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - function peg$parseCursor() { - var s0, s1, s2; - - var key = peg$currPos * 37 + 36, - cached = peg$resultsCache[key]; - - if (cached) { - peg$currPos = cached.nextPos; - - return cached.result; - } - - s0 = peg$currPos; - peg$savedPos = peg$currPos; - s1 = peg$c39(); - if (s1) { - s1 = void 0; - } else { - s1 = peg$FAILED; - } - if (s1 !== peg$FAILED) { - if (input.substr(peg$currPos, 14) === peg$c89) { - s2 = peg$c89; - peg$currPos += 14; - } else { - s2 = peg$FAILED; - if (peg$silentFails === 0) { peg$fail(peg$c90); } - } - if (s2 !== peg$FAILED) { - peg$savedPos = s0; - s1 = peg$c91(); - s0 = s1; - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - - peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 }; - - return s0; - } - - - const { parseCursor, cursorSymbol, allowLeadingWildcards = true, helpers: { nodeTypes } } = options; - const buildFunctionNode = nodeTypes.function.buildNodeWithArgumentNodes; - const buildLiteralNode = nodeTypes.literal.buildNode; - const buildWildcardNode = nodeTypes.wildcard.buildNode; - const buildNamedArgNode = nodeTypes.namedArg.buildNode; - const { wildcardSymbol } = nodeTypes.wildcard; - - - peg$result = peg$startRuleFunction(); - - if (peg$result !== peg$FAILED && peg$currPos === input.length) { - return peg$result; - } else { - if (peg$result !== peg$FAILED && peg$currPos < input.length) { - peg$fail({ type: "end", description: "end of input" }); - } - - throw peg$buildException( - null, - peg$maxFailExpected, - peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null, - peg$maxFailPos < input.length - ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) - : peg$computeLocation(peg$maxFailPos, peg$maxFailPos) - ); - } - } - - return { - SyntaxError: peg$SyntaxError, - parse: peg$parse - }; -})(); \ No newline at end of file diff --git a/src/plugins/data/common/es_query/stubs/exists_filter.ts b/src/plugins/data/common/es_query/stubs/exists_filter.ts new file mode 100644 index 0000000000000..b10aa67db517e --- /dev/null +++ b/src/plugins/data/common/es_query/stubs/exists_filter.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ExistsFilter, FilterStateStore } from '..'; + +export const existsFilter: ExistsFilter = { + meta: { + index: 'logstash-*', + negate: false, + disabled: false, + type: 'exists', + key: 'machine.os', + alias: null, + }, + $state: { + store: FilterStateStore.APP_STATE, + }, +}; diff --git a/src/plugins/data/common/es_query/filters/stubs/index.ts b/src/plugins/data/common/es_query/stubs/index.ts similarity index 100% rename from src/plugins/data/common/es_query/filters/stubs/index.ts rename to src/plugins/data/common/es_query/stubs/index.ts diff --git a/src/plugins/data/common/es_query/stubs/phrase_filter.ts b/src/plugins/data/common/es_query/stubs/phrase_filter.ts new file mode 100644 index 0000000000000..23b51afd64e51 --- /dev/null +++ b/src/plugins/data/common/es_query/stubs/phrase_filter.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FilterStateStore, PhraseFilter } from '@kbn/es-query'; + +export const phraseFilter: PhraseFilter = { + meta: { + negate: false, + index: 'logstash-*', + type: 'phrase', + key: 'machine.os', + value: 'ios', + disabled: false, + alias: null, + params: { + query: 'ios', + }, + }, + $state: { + store: FilterStateStore.APP_STATE, + }, +}; diff --git a/src/plugins/data/common/es_query/stubs/phrases_filter.ts b/src/plugins/data/common/es_query/stubs/phrases_filter.ts new file mode 100644 index 0000000000000..56c3af56175da --- /dev/null +++ b/src/plugins/data/common/es_query/stubs/phrases_filter.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FilterStateStore, PhrasesFilter } from '@kbn/es-query'; + +export const phrasesFilter: PhrasesFilter = { + meta: { + index: 'logstash-*', + type: 'phrases', + key: 'machine.os.raw', + value: 'win xp, osx', + params: ['win xp', 'osx'], + negate: false, + disabled: false, + alias: null, + }, + $state: { + store: FilterStateStore.APP_STATE, + }, +}; diff --git a/src/plugins/data/common/es_query/stubs/range_filter.ts b/src/plugins/data/common/es_query/stubs/range_filter.ts new file mode 100644 index 0000000000000..485a569eb9d4b --- /dev/null +++ b/src/plugins/data/common/es_query/stubs/range_filter.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FilterStateStore, RangeFilter } from '@kbn/es-query'; + +export const rangeFilter: RangeFilter = { + meta: { + index: 'logstash-*', + negate: false, + disabled: false, + alias: null, + type: 'range', + key: 'bytes', + value: '0 to 10', + params: { + gte: 0, + lt: 10, + }, + }, + $state: { + store: FilterStateStore.APP_STATE, + }, + range: {}, +}; diff --git a/src/plugins/data/common/index_patterns/fields/types.ts b/src/plugins/data/common/index_patterns/fields/types.ts index 3b2e25d3d80a6..38258dd4f53f4 100644 --- a/src/plugins/data/common/index_patterns/fields/types.ts +++ b/src/plugins/data/common/index_patterns/fields/types.ts @@ -5,7 +5,8 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { IndexPatternFieldBase, FieldSpec, IndexPattern } from '../..'; +import { IndexPatternFieldBase } from '@kbn/es-query'; +import { FieldSpec, IndexPattern } from '../..'; /** * @deprecated diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 58cc6d0478d5e..d05a7ea6e2d93 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ import type { estypes } from '@elastic/elasticsearch'; +import type { IndexPatternFieldBase, IFieldSubType, IndexPatternBase } from '@kbn/es-query'; import { ToastInputFields, ErrorToastOptions } from 'src/core/public/notifications'; // eslint-disable-next-line import type { SavedObject } from 'src/core/server'; -import type { IndexPatternFieldBase, IFieldSubType, IndexPatternBase } from '../es_query'; import { IFieldType } from './fields'; import { RUNTIME_FIELD_TYPES } from './constants'; import { SerializedFieldFormat } from '../../../expressions/common'; diff --git a/src/plugins/data/common/query/filter_manager/compare_filters.ts b/src/plugins/data/common/query/filter_manager/compare_filters.ts index a6190adc1ba65..fc820779b2461 100644 --- a/src/plugins/data/common/query/filter_manager/compare_filters.ts +++ b/src/plugins/data/common/query/filter_manager/compare_filters.ts @@ -7,7 +7,7 @@ */ import { defaults, isEqual, omit, map } from 'lodash'; -import { FilterMeta, Filter } from '../../es_query'; +import { FilterMeta, Filter } from '@kbn/es-query'; export interface FilterCompareOptions { index?: boolean; diff --git a/src/plugins/data/common/query/persistable_state.test.ts b/src/plugins/data/common/query/persistable_state.test.ts index 62ea17e030413..807cc72a071be 100644 --- a/src/plugins/data/common/query/persistable_state.test.ts +++ b/src/plugins/data/common/query/persistable_state.test.ts @@ -7,7 +7,7 @@ */ import { extract, inject } from './persistable_state'; -import { Filter } from '../es_query/filters'; +import { Filter } from '@kbn/es-query'; describe('filter manager persistable state tests', () => { const filters: Filter[] = [ diff --git a/src/plugins/data/common/query/persistable_state.ts b/src/plugins/data/common/query/persistable_state.ts index ef0543bb84a2c..08cda6eb59fbf 100644 --- a/src/plugins/data/common/query/persistable_state.ts +++ b/src/plugins/data/common/query/persistable_state.ts @@ -7,9 +7,9 @@ */ import uuid from 'uuid'; +import { Filter } from '@kbn/es-query'; import { SerializableState } from '../../../kibana_utils/common/persistable_state'; import { SavedObjectReference } from '../../../../core/types'; -import { Filter } from '../es_query/filters'; export const extract = (filters: Filter[]) => { const references: SavedObjectReference[] = []; diff --git a/src/plugins/data/common/query/timefilter/get_time.ts b/src/plugins/data/common/query/timefilter/get_time.ts index 58194fc72dfcf..64842be20fbad 100644 --- a/src/plugins/data/common/query/timefilter/get_time.ts +++ b/src/plugins/data/common/query/timefilter/get_time.ts @@ -7,7 +7,8 @@ */ import dateMath from '@elastic/datemath'; -import { buildRangeFilter, IIndexPattern, TimeRange, TimeRangeBounds } from '../..'; +import { buildRangeFilter } from '@kbn/es-query'; +import { IIndexPattern, TimeRange, TimeRangeBounds } from '../..'; interface CalculateBoundsOptions { forceNow?: Date; diff --git a/src/plugins/data/common/query/types.ts b/src/plugins/data/common/query/types.ts index e4e386afdec77..c1861beb1ed90 100644 --- a/src/plugins/data/common/query/types.ts +++ b/src/plugins/data/common/query/types.ts @@ -8,8 +8,4 @@ export * from './timefilter/types'; -// eslint-disable-next-line -export type Query = { - query: string | { [key: string]: any }; - language: string; -}; +export { Query } from '@kbn/es-query'; diff --git a/src/plugins/data/common/search/aggs/agg_configs.ts b/src/plugins/data/common/search/aggs/agg_configs.ts index c205b46e077f0..c80becd271bba 100644 --- a/src/plugins/data/common/search/aggs/agg_configs.ts +++ b/src/plugins/data/common/search/aggs/agg_configs.ts @@ -10,6 +10,7 @@ import moment from 'moment'; import _, { cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; import { Assign } from '@kbn/utility-types'; +import { isRangeFilter } from '@kbn/es-query'; import type { estypes } from '@elastic/elasticsearch'; import { @@ -23,7 +24,7 @@ import { IAggType } from './agg_type'; import { AggTypesRegistryStart } from './agg_types_registry'; import { AggGroupNames } from './agg_groups'; import { IndexPattern } from '../../index_patterns/index_patterns/index_pattern'; -import { TimeRange, getTime, isRangeFilter, calculateBounds } from '../../../common'; +import { TimeRange, getTime, calculateBounds } from '../../../common'; import { IBucketAggConfig } from './buckets'; import { insertTimeShiftSplit, mergeTimeShifts } from './utils/time_splits'; diff --git a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts index 2a1cd873f6282..39fba23a42210 100644 --- a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts +++ b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts @@ -8,7 +8,7 @@ import { isNumber, keys, values, find, each, cloneDeep, flatten } from 'lodash'; import { estypes } from '@elastic/elasticsearch'; -import { buildExistsFilter, buildPhrasesFilter, buildQueryFromFilters } from '../../../../common'; +import { buildExistsFilter, buildPhrasesFilter, buildQueryFromFilters } from '@kbn/es-query'; import { AggGroupNames } from '../agg_groups'; import { IAggConfigs } from '../agg_configs'; import { IBucketAggConfig } from './bucket_agg_type'; diff --git a/src/plugins/data/common/search/aggs/buckets/create_filter/date_histogram.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/date_histogram.ts index 2c0f35d28e989..1fd2250ec9e8b 100644 --- a/src/plugins/data/common/search/aggs/buckets/create_filter/date_histogram.ts +++ b/src/plugins/data/common/search/aggs/buckets/create_filter/date_histogram.ts @@ -7,8 +7,8 @@ */ import moment from 'moment'; +import { buildRangeFilter } from '@kbn/es-query'; import { IBucketDateHistogramAggConfig } from '../date_histogram'; -import { buildRangeFilter } from '../../../../../common'; export const createFilterDateHistogram = ( agg: IBucketDateHistogramAggConfig, diff --git a/src/plugins/data/common/search/aggs/buckets/create_filter/date_range.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/date_range.ts index 5ba80d35e2cb3..1231637771e32 100644 --- a/src/plugins/data/common/search/aggs/buckets/create_filter/date_range.ts +++ b/src/plugins/data/common/search/aggs/buckets/create_filter/date_range.ts @@ -7,9 +7,9 @@ */ import moment from 'moment'; +import { buildRangeFilter, RangeFilterParams } from '@kbn/es-query'; import { IBucketAggConfig } from '../bucket_agg_type'; import { DateRangeKey } from '../lib/date_range'; -import { buildRangeFilter, RangeFilterParams } from '../../../../../common'; export const createFilterDateRange = (agg: IBucketAggConfig, { from, to }: DateRangeKey) => { const filter: RangeFilterParams = {}; diff --git a/src/plugins/data/common/search/aggs/buckets/create_filter/filters.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/filters.ts index 948dac1d23b5b..a265584cf3765 100644 --- a/src/plugins/data/common/search/aggs/buckets/create_filter/filters.ts +++ b/src/plugins/data/common/search/aggs/buckets/create_filter/filters.ts @@ -7,8 +7,8 @@ */ import { get } from 'lodash'; +import { buildQueryFilter } from '@kbn/es-query'; import { IBucketAggConfig } from '../bucket_agg_type'; -import { buildQueryFilter } from '../../../../../common'; export const createFilterFilters = (aggConfig: IBucketAggConfig, key: string) => { // have the aggConfig write agg dsl params diff --git a/src/plugins/data/common/search/aggs/buckets/create_filter/histogram.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/histogram.ts index d104c8f5c57f5..d5c98dc51562c 100644 --- a/src/plugins/data/common/search/aggs/buckets/create_filter/histogram.ts +++ b/src/plugins/data/common/search/aggs/buckets/create_filter/histogram.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { buildRangeFilter, RangeFilterParams } from '../../../../../common'; +import { buildRangeFilter, RangeFilterParams } from '@kbn/es-query'; import { AggTypesDependencies } from '../../agg_types'; import { IBucketAggConfig } from '../bucket_agg_type'; diff --git a/src/plugins/data/common/search/aggs/buckets/create_filter/ip_range.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/ip_range.ts index 374c2fdbcf705..bd16b0210a856 100644 --- a/src/plugins/data/common/search/aggs/buckets/create_filter/ip_range.ts +++ b/src/plugins/data/common/search/aggs/buckets/create_filter/ip_range.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ +import { buildRangeFilter, RangeFilterParams } from '@kbn/es-query'; import { CidrMask } from '../lib/cidr_mask'; import { IBucketAggConfig } from '../bucket_agg_type'; import { IpRangeKey } from '../lib/ip_range'; -import { buildRangeFilter, RangeFilterParams } from '../../../../../common'; export const createFilterIpRange = (aggConfig: IBucketAggConfig, key: IpRangeKey) => { let range: RangeFilterParams; diff --git a/src/plugins/data/common/search/aggs/buckets/create_filter/range.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/range.ts index 4c2f929aa0675..258f10f2fd15a 100644 --- a/src/plugins/data/common/search/aggs/buckets/create_filter/range.ts +++ b/src/plugins/data/common/search/aggs/buckets/create_filter/range.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { buildRangeFilter } from '../../../../../common'; +import { buildRangeFilter } from '@kbn/es-query'; import { AggTypesDependencies } from '../../agg_types'; import { IBucketAggConfig } from '../bucket_agg_type'; diff --git a/src/plugins/data/common/search/aggs/buckets/create_filter/terms.ts b/src/plugins/data/common/search/aggs/buckets/create_filter/terms.ts index 935b52dc55708..25aa3f334e4b8 100644 --- a/src/plugins/data/common/search/aggs/buckets/create_filter/terms.ts +++ b/src/plugins/data/common/search/aggs/buckets/create_filter/terms.ts @@ -6,13 +6,8 @@ * Side Public License, v 1. */ +import { buildPhrasesFilter, buildExistsFilter, buildPhraseFilter, Filter } from '@kbn/es-query'; import { IBucketAggConfig } from '../bucket_agg_type'; -import { - buildPhrasesFilter, - buildExistsFilter, - buildPhraseFilter, - Filter, -} from '../../../../../common'; export const createFilterTerms = (aggConfig: IBucketAggConfig, key: string, params: any) => { const field = aggConfig.params.field; diff --git a/src/plugins/data/common/search/aggs/buckets/filter.ts b/src/plugins/data/common/search/aggs/buckets/filter.ts index 900848bb9517f..2f04e71d0af87 100644 --- a/src/plugins/data/common/search/aggs/buckets/filter.ts +++ b/src/plugins/data/common/search/aggs/buckets/filter.ts @@ -8,13 +8,13 @@ import { cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; +import { buildEsQuery, Query } from '@kbn/es-query'; import { BucketAggType } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { GeoBoundingBox } from './lib/geo_point'; import { aggFilterFnName } from './filter_fn'; import { BaseAggParams } from '../types'; -import { Query } from '../../../types'; -import { buildEsQuery, getEsQueryConfig } from '../../../es_query'; +import { getEsQueryConfig } from '../../../es_query'; const filterTitle = i18n.translate('data.search.aggs.buckets.filterTitle', { defaultMessage: 'Filter', diff --git a/src/plugins/data/common/search/aggs/buckets/filters.ts b/src/plugins/data/common/search/aggs/buckets/filters.ts index 107b86de04058..c2bb7a6d7c81f 100644 --- a/src/plugins/data/common/search/aggs/buckets/filters.ts +++ b/src/plugins/data/common/search/aggs/buckets/filters.ts @@ -8,13 +8,14 @@ import { i18n } from '@kbn/i18n'; import { size, transform, cloneDeep } from 'lodash'; +import { buildEsQuery, Query } from '@kbn/es-query'; import { createFilterFilters } from './create_filter/filters'; import { toAngularJSON } from '../utils'; import { BucketAggType } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { aggFiltersFnName } from './filters_fn'; -import { getEsQueryConfig, buildEsQuery, Query, UI_SETTINGS } from '../../../../common'; +import { getEsQueryConfig, UI_SETTINGS } from '../../../../common'; import { BaseAggParams } from '../types'; const filtersTitle = i18n.translate('data.search.aggs.buckets.filtersTitle', { diff --git a/src/plugins/data/common/search/expressions/esaggs/create_filter.test.ts b/src/plugins/data/common/search/expressions/esaggs/create_filter.test.ts index 591bcc4947b08..b78980cb5136e 100644 --- a/src/plugins/data/common/search/expressions/esaggs/create_filter.test.ts +++ b/src/plugins/data/common/search/expressions/esaggs/create_filter.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { isRangeFilter } from '../../../es_query/filters'; +import { isRangeFilter } from '@kbn/es-query'; import { BytesFormat, FieldFormatsGetConfigFn } from '../../../field_formats'; import { AggConfigs, IAggConfig } from '../../aggs'; import { mockAggTypesRegistry } from '../../aggs/test_helpers'; diff --git a/src/plugins/data/common/search/expressions/esdsl.ts b/src/plugins/data/common/search/expressions/esdsl.ts index dee1b19eb3360..a5834001143e4 100644 --- a/src/plugins/data/common/search/expressions/esdsl.ts +++ b/src/plugins/data/common/search/expressions/esdsl.ts @@ -7,12 +7,13 @@ */ import { i18n } from '@kbn/i18n'; +import { buildEsQuery } from '@kbn/es-query'; import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { EsRawResponse } from './es_raw_response'; import { RequestStatistics, RequestAdapter } from '../../../../inspector/common'; import { ISearchGeneric, KibanaContext } from '..'; -import { buildEsQuery, getEsQueryConfig } from '../../es_query/es_query'; +import { getEsQueryConfig } from '../../es_query'; import { UiSettingsCommon } from '../../index_patterns'; const name = 'esdsl'; diff --git a/src/plugins/data/common/search/expressions/exists_filter.ts b/src/plugins/data/common/search/expressions/exists_filter.ts index 0979328860b4c..75d83ca7f2592 100644 --- a/src/plugins/data/common/search/expressions/exists_filter.ts +++ b/src/plugins/data/common/search/expressions/exists_filter.ts @@ -8,8 +8,8 @@ import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; +import { buildFilter, FILTERS } from '@kbn/es-query'; import { KibanaField, KibanaFilter } from './kibana_context_type'; -import { buildFilter, FILTERS } from '../../es_query/filters'; import { IndexPattern } from '../../index_patterns/index_patterns'; interface Arguments { diff --git a/src/plugins/data/common/search/expressions/filters_to_ast.ts b/src/plugins/data/common/search/expressions/filters_to_ast.ts index edcf884b3ed31..3eb3a11b09857 100644 --- a/src/plugins/data/common/search/expressions/filters_to_ast.ts +++ b/src/plugins/data/common/search/expressions/filters_to_ast.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ +import { Filter } from '@kbn/es-query'; import { buildExpression, buildExpressionFunction } from '../../../../expressions/common'; -import { Filter } from '../../es_query/filters'; import { ExpressionFunctionKibanaFilter } from './kibana_filter'; export const filtersToAst = (filters: Filter[] | Filter) => { diff --git a/src/plugins/data/common/search/expressions/kibana_context.ts b/src/plugins/data/common/search/expressions/kibana_context.ts index 22a7150d4a64e..9c1c78604ea83 100644 --- a/src/plugins/data/common/search/expressions/kibana_context.ts +++ b/src/plugins/data/common/search/expressions/kibana_context.ts @@ -10,6 +10,7 @@ import { uniqBy } from 'lodash'; import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition, ExecutionContext } from 'src/plugins/expressions/common'; import { Adapters } from 'src/plugins/inspector/common'; +import { Filter } from '@kbn/es-query'; import { unboxExpressionValue } from '../../../../expressions/common'; import { Query, uniqFilters } from '../../query'; import { ExecutionContextSearch, KibanaContext, KibanaFilter } from './kibana_context_type'; @@ -17,7 +18,6 @@ import { KibanaQueryOutput } from './kibana_context_type'; import { KibanaTimerangeOutput } from './timerange'; import { SavedObjectReference } from '../../../../../core/types'; import { SavedObjectsClientCommon } from '../../index_patterns'; -import { Filter } from '../../es_query/filters'; /** @internal */ export interface KibanaContextStartDependencies { diff --git a/src/plugins/data/common/search/expressions/phrase_filter.ts b/src/plugins/data/common/search/expressions/phrase_filter.ts index 0b19e8a1e416d..837d4be4d52ea 100644 --- a/src/plugins/data/common/search/expressions/phrase_filter.ts +++ b/src/plugins/data/common/search/expressions/phrase_filter.ts @@ -8,8 +8,8 @@ import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; +import { buildFilter, FILTERS } from '@kbn/es-query'; import { KibanaField, KibanaFilter } from './kibana_context_type'; -import { buildFilter, FILTERS } from '../../es_query/filters'; import { IndexPattern } from '../../index_patterns/index_patterns'; interface Arguments { diff --git a/src/plugins/data/common/search/expressions/range_filter.ts b/src/plugins/data/common/search/expressions/range_filter.ts index ed71f5362fe85..ed65475a9d285 100644 --- a/src/plugins/data/common/search/expressions/range_filter.ts +++ b/src/plugins/data/common/search/expressions/range_filter.ts @@ -8,8 +8,8 @@ import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; +import { buildFilter, FILTERS } from '@kbn/es-query'; import { KibanaField, KibanaFilter } from './kibana_context_type'; -import { buildFilter, FILTERS } from '../../es_query/filters'; import { IndexPattern } from '../../index_patterns/index_patterns'; import { KibanaRange } from './range'; diff --git a/src/plugins/data/common/search/search_source/create_search_source.test.ts b/src/plugins/data/common/search/search_source/create_search_source.test.ts index 6a6ac1dfa93e7..c084b029a5bd2 100644 --- a/src/plugins/data/common/search/search_source/create_search_source.test.ts +++ b/src/plugins/data/common/search/search_source/create_search_source.test.ts @@ -10,7 +10,7 @@ import { createSearchSource as createSearchSourceFactory } from './create_search import { SearchSourceDependencies } from './search_source'; import { IIndexPattern } from '../../index_patterns'; import { IndexPatternsContract } from '../../index_patterns/index_patterns'; -import { Filter } from '../../es_query/filters'; +import { Filter } from '../../es_query'; describe('createSearchSource', () => { const indexPatternMock: IIndexPattern = {} as IIndexPattern; diff --git a/src/plugins/data/common/search/search_source/extract_references.ts b/src/plugins/data/common/search/search_source/extract_references.ts index b63b8ed1cfee2..f099443ef7605 100644 --- a/src/plugins/data/common/search/search_source/extract_references.ts +++ b/src/plugins/data/common/search/search_source/extract_references.ts @@ -7,7 +7,7 @@ */ import { SavedObjectReference } from 'src/core/types'; -import { Filter } from '../../es_query/filters'; +import { Filter } from '@kbn/es-query'; import { SearchSourceFields } from './types'; import { INDEX_PATTERN_SAVED_OBJECT_TYPE } from '../../constants'; diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index e60e6fa00b270..13f157da731a6 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -72,6 +72,7 @@ import { } from 'rxjs/operators'; import { defer, EMPTY, from, Observable } from 'rxjs'; import { estypes } from '@elastic/elasticsearch'; +import { buildEsQuery, Filter } from '@kbn/es-query'; import { normalizeSortRequest } from './normalize_sort_request'; import { fieldWildcardFilter } from '../../../../kibana_utils/common'; import { IIndexPattern, IndexPattern, IndexPatternField } from '../../index_patterns'; @@ -93,8 +94,6 @@ import { getRequestInspectorStats, getResponseInspectorStats } from './inspect'; import { getEsQueryConfig, - buildEsQuery, - Filter, UI_SETTINGS, isErrorResponse, isPartialResponse, diff --git a/src/plugins/data/common/search/session/types.ts b/src/plugins/data/common/search/session/types.ts index a7ba8ab9576b6..8e3c298aa9316 100644 --- a/src/plugins/data/common/search/session/types.ts +++ b/src/plugins/data/common/search/session/types.ts @@ -71,6 +71,10 @@ export interface SearchSessionSavedObjectAttributes { realmType?: string; realmName?: string; username?: string; + /** + * Version information to display warnings when trying to restore a session from a different version + */ + version: string; } export interface SearchSessionRequestInfo { diff --git a/src/plugins/data/common/search/tabify/types.ts b/src/plugins/data/common/search/tabify/types.ts index c170b13774932..758a2dfb181f2 100644 --- a/src/plugins/data/common/search/tabify/types.ts +++ b/src/plugins/data/common/search/tabify/types.ts @@ -7,7 +7,7 @@ */ import { Moment } from 'moment'; -import { RangeFilterParams } from '../../../common'; +import { RangeFilterParams } from '@kbn/es-query'; import { IAggConfig } from '../aggs'; /** @internal **/ diff --git a/src/plugins/data/common/stubs.ts b/src/plugins/data/common/stubs.ts index 25f9dda7d33b4..d64d788d60ead 100644 --- a/src/plugins/data/common/stubs.ts +++ b/src/plugins/data/common/stubs.ts @@ -8,4 +8,4 @@ export { stubIndexPattern, stubIndexPatternWithFields } from './index_patterns/index_pattern.stub'; export { stubFields } from './index_patterns/field.stub'; -export * from './es_query/filters/stubs'; +export * from './es_query/stubs'; diff --git a/src/plugins/data/common/types.ts b/src/plugins/data/common/types.ts index 8072928a1b670..7f6ae4a346bd0 100644 --- a/src/plugins/data/common/types.ts +++ b/src/plugins/data/common/types.ts @@ -21,3 +21,9 @@ export * from './index_patterns/types'; * not possible. */ export type GetConfigFn = (key: string, defaultOverride?: T) => T; + +type FilterFormatterFunction = (value: any) => string; +export interface FilterValueFormatter { + convert: FilterFormatterFunction; + getConverterFor: (type: string) => FilterFormatterFunction; +} diff --git a/src/plugins/data/public/actions/value_click_action.ts b/src/plugins/data/public/actions/value_click_action.ts index 9644a96a68752..eb8e991cb8891 100644 --- a/src/plugins/data/public/actions/value_click_action.ts +++ b/src/plugins/data/public/actions/value_click_action.ts @@ -6,11 +6,11 @@ * Side Public License, v 1. */ +import type { Filter } from '@kbn/es-query'; import { Datatable } from 'src/plugins/expressions/public'; import { Action, createAction, UiActionsStart } from '../../../../plugins/ui_actions/public'; import { APPLY_FILTER_TRIGGER } from '../triggers'; import { createFiltersFromValueClickAction } from './filters/create_filters_from_value_click'; -import type { Filter } from '../../common/es_query/filters'; export type ValueClickActionContext = ValueClickContext; export const ACTION_VALUE_CLICK = 'ACTION_VALUE_CLICK'; diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/types.ts b/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/types.ts index 48e87a73f3671..12a0dae97ebaa 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/types.ts +++ b/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/types.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ +import { KueryNode } from '@kbn/es-query'; import { CoreSetup } from 'kibana/public'; import { DataPublicPluginStart, - KueryNode, QuerySuggestionBasic, QuerySuggestionGetFnArgs, } from '../../../../../../../src/plugins/data/public'; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 51f3d9fd660e5..e6a5a02123c2b 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -6,6 +6,10 @@ * Side Public License, v 1. */ +/* + * esQuery and esKuery: + */ + import { PluginInitializerContext } from '../../../core/public'; import { ConfigSchema } from '../config'; @@ -14,15 +18,6 @@ import { ConfigSchema } from '../config'; */ import { - buildEmptyFilter, - buildExistsFilter, - buildPhraseFilter, - buildPhrasesFilter, - buildQueryFilter, - buildRangeFilter, - disableFilter, - FILTERS, - FilterStateStore, getPhraseFilterField, getPhraseFilterValue, isExistsFilter, @@ -34,6 +29,22 @@ import { isQueryStringFilter, isRangeFilter, toggleFilterNegated, + buildEmptyFilter, + buildExistsFilter, + buildPhraseFilter, + buildPhrasesFilter, + buildQueryFilter, + buildRangeFilter, + disableFilter, + fromKueryExpression, + toElasticsearchQuery, + nodeTypes, + buildEsQuery, + buildQueryFromFilters, + luceneStringToDsl, + decorateQuery, + FILTERS, + FilterStateStore, compareFilters, COMPARE_ALL_OPTIONS, } from '../common'; @@ -94,7 +105,8 @@ export const esFilters = { extractTimeRange, }; -export type { +export { + KueryNode, RangeFilter, RangeFilterMeta, RangeFilterParams, @@ -103,29 +115,26 @@ export type { PhraseFilter, CustomFilter, MatchAllFilter, + IFieldSubType, + EsQueryConfig, + isFilter, + isFilters, } from '../common'; -/* - * esQuery and esKuery: - */ - -import { - fromKueryExpression, - toElasticsearchQuery, - nodeTypes, - buildEsQuery, - getEsQueryConfig, - buildQueryFromFilters, - luceneStringToDsl, - decorateQuery, -} from '../common'; +import { getEsQueryConfig } from '../common'; +/** + * @deprecated Please import helpers from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ export const esKuery = { nodeTypes, fromKueryExpression, toElasticsearchQuery, }; +/** + * @deprecated Please import helpers from the package kbn/es-query directly. This import will be deprecated in v8.0.0. + */ export const esQuery = { buildEsQuery, getEsQueryConfig, @@ -134,8 +143,6 @@ export const esQuery = { decorateQuery, }; -export { EsQueryConfig, KueryNode } from '../common'; - /* * Field Formatters: */ @@ -260,7 +267,6 @@ export { export { IIndexPattern, IFieldType, - IFieldSubType, ES_FIELD_TYPES, KBN_FIELD_TYPES, IndexPatternAttributes, @@ -489,7 +495,7 @@ export { getKbnTypeNames, } from '../common'; -export { isTimeRange, isQuery, isFilter, isFilters } from '../common'; +export { isTimeRange, isQuery } from '../common'; export { ACTION_GLOBAL_APPLY_FILTER, ApplyGlobalFilterActionContext } from './actions'; export { APPLY_FILTER_TRIGGER } from './triggers'; diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 0d075e6c3872f..33113076c7247 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -17,6 +17,7 @@ import { CoreSetup } from 'src/core/public'; import { CoreSetup as CoreSetup_2 } from 'kibana/public'; import { CoreStart } from 'kibana/public'; import { CoreStart as CoreStart_2 } from 'src/core/public'; +import { CustomFilter as CustomFilter_2 } from '@kbn/es-query'; import { Datatable as Datatable_2 } from 'src/plugins/expressions'; import { Datatable as Datatable_3 } from 'src/plugins/expressions/common'; import { DatatableColumn as DatatableColumn_2 } from 'src/plugins/expressions'; @@ -25,6 +26,7 @@ import { DetailedPeerCertificate } from 'tls'; import { Ensure } from '@kbn/utility-types'; import { EnvironmentMode } from '@kbn/config'; import { ErrorToastOptions } from 'src/core/public/notifications'; +import { EsQueryConfig as EsQueryConfig_2 } from '@kbn/es-query'; import { estypes } from '@elastic/elasticsearch'; import { EuiBreadcrumb } from '@elastic/eui'; import { EuiButtonEmptyProps } from '@elastic/eui'; @@ -35,10 +37,13 @@ import { EuiGlobalToastListToast } from '@elastic/eui'; import { EuiIconProps } from '@elastic/eui'; import { EventEmitter } from 'events'; import { ExecutionContext } from 'src/plugins/expressions/common'; +import { ExistsFilter as ExistsFilter_2 } from '@kbn/es-query'; import { ExpressionAstExpression } from 'src/plugins/expressions/common'; import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { ExpressionsSetup } from 'src/plugins/expressions/public'; import { ExpressionValueBoxed } from 'src/plugins/expressions/common'; +import { Filter as Filter_2 } from '@kbn/es-query'; +import { FilterStateStore } from '@kbn/es-query'; import { FormatFactory as FormatFactory_2 } from 'src/plugins/data/common/field_formats/utils'; import { History } from 'history'; import { Href } from 'history'; @@ -46,19 +51,23 @@ import { HttpSetup } from 'kibana/public'; import { IAggConfigs as IAggConfigs_2 } from 'src/plugins/data/public'; import { IconType } from '@elastic/eui'; import { IEsSearchResponse as IEsSearchResponse_2 } from 'src/plugins/data/public'; +import { IFieldSubType as IFieldSubType_2 } from '@kbn/es-query'; import { IncomingHttpHeaders } from 'http'; +import { IndexPatternBase } from '@kbn/es-query'; +import { IndexPatternFieldBase } from '@kbn/es-query'; import { InjectedIntl } from '@kbn/i18n/react'; import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; 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 { JsonValue } from '@kbn/common-utils'; import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { KibanaExecutionContext } from 'src/core/public'; +import { KueryNode as KueryNode_2 } from '@kbn/es-query'; import { Location } from 'history'; import { LocationDescriptorObject } from 'history'; import { Logger } from '@kbn/logging'; import { LogMeta } from '@kbn/logging'; +import { MatchAllFilter as MatchAllFilter_2 } from '@kbn/es-query'; import { MaybePromise } from '@kbn/utility-types'; import { Moment } from 'moment'; import moment from 'moment'; @@ -68,6 +77,8 @@ import { Observable } from 'rxjs'; import { PackageInfo } from '@kbn/config'; import { Path } from 'history'; import { PeerCertificate } from 'tls'; +import { PhraseFilter as PhraseFilter_2 } from '@kbn/es-query'; +import { PhrasesFilter as PhrasesFilter_2 } from '@kbn/es-query'; import { Plugin } from 'src/core/public'; import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/public'; import { PluginInitializerContext as PluginInitializerContext_3 } from 'kibana/public'; @@ -75,7 +86,10 @@ import { PopoverAnchorPosition } from '@elastic/eui'; import { PublicContract } from '@kbn/utility-types'; import { PublicMethodsOf } from '@kbn/utility-types'; import { PublicUiSettingsParams } from 'src/core/server/types'; -import { RangeFilter as RangeFilter_2 } from 'src/plugins/data/public'; +import { Query } from '@kbn/es-query'; +import { RangeFilter as RangeFilter_2 } from '@kbn/es-query'; +import { RangeFilterMeta as RangeFilterMeta_2 } from '@kbn/es-query'; +import { RangeFilterParams as RangeFilterParams_2 } from '@kbn/es-query'; import React from 'react'; import * as React_2 from 'react'; import { RecursiveReadonly } from '@kbn/utility-types'; @@ -262,7 +276,7 @@ export class AggConfigs { getResponseAggById(id: string): AggConfig | undefined; getResponseAggs(): AggConfig[]; // (undocumented) - getSearchSourceTimeFilter(forceNow?: Date): RangeFilter_2[] | { + getSearchSourceTimeFilter(forceNow?: Date): import("@kbn/es-query").RangeFilter[] | { meta: { index: string | undefined; params: {}; @@ -617,10 +631,8 @@ export const createSavedQueryService: (savedObjectsClient: SavedObjectsClientCon // Warning: (ae-missing-release-tag) "CustomFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type CustomFilter = Filter & { - query: any; -}; +// @public @deprecated (undocumented) +export type CustomFilter = CustomFilter_2; // Warning: (ae-forgotten-export) The symbol "DataSetupDependencies" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DataStartDependencies" needs to be exported by the entry point index.d.ts @@ -804,23 +816,23 @@ export type EsdslExpressionFunctionDefinition = ExpressionFunctionDefinition JSX.Element; FilterItem: (props: import("./ui/filter_bar/filter_item").FilterItemProps) => JSX.Element; - FILTERS: typeof FILTERS; + FILTERS: typeof import("@kbn/es-query").FILTERS; FilterStateStore: typeof FilterStateStore; - buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("../common").Filter; - buildPhrasesFilter: (field: import("../common").IndexPatternFieldBase, params: any[], indexPattern: import("../common").IndexPatternBase) => import("../common").PhrasesFilter; - buildExistsFilter: (field: import("../common").IndexPatternFieldBase, indexPattern: import("../common").IndexPatternBase) => import("../common").ExistsFilter; - buildPhraseFilter: (field: import("../common").IndexPatternFieldBase, value: any, indexPattern: import("../common").IndexPatternBase) => import("../common").PhraseFilter; - buildQueryFilter: (query: any, index: string, alias: string) => import("../common").QueryStringFilter; - buildRangeFilter: (field: import("../common").IndexPatternFieldBase, params: import("../common").RangeFilterParams, indexPattern: import("../common").IndexPatternBase, formattedValue?: string | undefined) => import("../common").RangeFilter; - isPhraseFilter: (filter: any) => filter is import("../common").PhraseFilter; - isExistsFilter: (filter: any) => filter is import("../common").ExistsFilter; - isPhrasesFilter: (filter: any) => filter is import("../common").PhrasesFilter; - isRangeFilter: (filter: any) => filter is import("../common").RangeFilter; - isMatchAllFilter: (filter: any) => filter is import("../common").MatchAllFilter; - isMissingFilter: (filter: any) => filter is import("../common").MissingFilter; - isQueryStringFilter: (filter: any) => filter is import("../common").QueryStringFilter; - isFilterPinned: (filter: import("../common").Filter) => boolean | undefined; - toggleFilterNegated: (filter: import("../common").Filter) => { + buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("@kbn/es-query").Filter; + buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: any[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; + buildExistsFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").ExistsFilter; + buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: any, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter; + buildQueryFilter: (query: any, index: string, alias: string) => import("@kbn/es-query").QueryStringFilter; + buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter; + isPhraseFilter: (filter: any) => filter is import("@kbn/es-query").PhraseFilter; + isExistsFilter: (filter: any) => filter is import("@kbn/es-query").ExistsFilter; + isPhrasesFilter: (filter: any) => filter is import("@kbn/es-query").PhrasesFilter; + isRangeFilter: (filter: any) => filter is import("@kbn/es-query").RangeFilter; + isMatchAllFilter: (filter: any) => filter is import("@kbn/es-query").MatchAllFilter; + isMissingFilter: (filter: any) => filter is import("@kbn/es-query").MissingFilter; + isQueryStringFilter: (filter: any) => filter is import("@kbn/es-query").QueryStringFilter; + isFilterPinned: (filter: import("@kbn/es-query").Filter) => boolean | undefined; + toggleFilterNegated: (filter: import("@kbn/es-query").Filter) => { meta: { negate: boolean; alias: string | null; @@ -833,62 +845,53 @@ export const esFilters: { params?: any; value?: string | undefined; }; - $state?: import("../common").FilterState | undefined; + $state?: import("@kbn/es-query/target_types/filters/types").FilterState | undefined; query?: any; }; - disableFilter: (filter: import("../common").Filter) => import("../common").Filter; - getPhraseFilterField: (filter: import("../common").PhraseFilter) => string; - getPhraseFilterValue: (filter: import("../common").PhraseFilter) => string | number | boolean; + disableFilter: (filter: import("@kbn/es-query").Filter) => import("@kbn/es-query").Filter; + getPhraseFilterField: (filter: import("@kbn/es-query").PhraseFilter) => string; + getPhraseFilterValue: (filter: import("@kbn/es-query").PhraseFilter) => string | number | boolean; getDisplayValueFromFilter: typeof getDisplayValueFromFilter; - compareFilters: (first: import("../common").Filter | import("../common").Filter[], second: import("../common").Filter | import("../common").Filter[], comparatorOptions?: import("../common").FilterCompareOptions) => boolean; + compareFilters: (first: import("@kbn/es-query").Filter | import("@kbn/es-query").Filter[], second: import("@kbn/es-query").Filter | import("@kbn/es-query").Filter[], comparatorOptions?: import("../common").FilterCompareOptions) => boolean; COMPARE_ALL_OPTIONS: import("../common").FilterCompareOptions; generateFilters: typeof generateFilters; - onlyDisabledFiltersChanged: (newFilters?: import("../common").Filter[] | undefined, oldFilters?: import("../common").Filter[] | undefined) => boolean; + onlyDisabledFiltersChanged: (newFilters?: import("@kbn/es-query").Filter[] | undefined, oldFilters?: import("@kbn/es-query").Filter[] | undefined) => boolean; changeTimeFilter: typeof changeTimeFilter; convertRangeFilterToTimeRangeString: typeof convertRangeFilterToTimeRangeString; - mapAndFlattenFilters: (filters: import("../common").Filter[]) => import("../common").Filter[]; + mapAndFlattenFilters: (filters: import("@kbn/es-query").Filter[]) => import("@kbn/es-query").Filter[]; extractTimeFilter: typeof extractTimeFilter; extractTimeRange: typeof extractTimeRange; }; // Warning: (ae-missing-release-tag) "esKuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public @deprecated (undocumented) export const esKuery: { - nodeTypes: import("../common/es_query/kuery/node_types").NodeTypes; - fromKueryExpression: (expression: any, parseOptions?: Partial) => import("../common").KueryNode; - toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IndexPatternBase | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; + nodeTypes: import("@kbn/es-query/target_types/kuery/node_types").NodeTypes; + fromKueryExpression: (expression: any, parseOptions?: Partial | undefined) => import("@kbn/es-query").KueryNode; + toElasticsearchQuery: (node: import("@kbn/es-query").KueryNode, indexPattern?: import("@kbn/es-query").IndexPatternBase | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; }; // Warning: (ae-missing-release-tag) "esQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public @deprecated (undocumented) export const esQuery: { - buildEsQuery: typeof buildEsQuery; + buildEsQuery: typeof import("@kbn/es-query").buildEsQuery; getEsQueryConfig: typeof getEsQueryConfig; - buildQueryFromFilters: (filters: import("../common").Filter[] | undefined, indexPattern: import("../common").IndexPatternBase | undefined, ignoreFilterIfFieldNotInIndex?: boolean) => { + buildQueryFromFilters: (filters: import("@kbn/es-query").Filter[] | undefined, indexPattern: import("@kbn/es-query").IndexPatternBase | undefined, ignoreFilterIfFieldNotInIndex?: boolean | undefined) => { must: never[]; - filter: import("../common").Filter[]; + filter: import("@kbn/es-query").Filter[]; should: never[]; - must_not: import("../common").Filter[]; + must_not: import("@kbn/es-query").Filter[]; }; - luceneStringToDsl: typeof luceneStringToDsl; - decorateQuery: typeof decorateQuery; + luceneStringToDsl: typeof import("@kbn/es-query").luceneStringToDsl; + decorateQuery: typeof import("@kbn/es-query").decorateQuery; }; // Warning: (ae-missing-release-tag) "EsQueryConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export interface EsQueryConfig { - // (undocumented) - allowLeadingWildcards: boolean; - // (undocumented) - dateFormatTZ?: string; - // (undocumented) - ignoreFilterIfFieldNotInIndex: boolean; - // (undocumented) - queryStringOptions: Record; -} +// @public @deprecated (undocumented) +export type EsQueryConfig = EsQueryConfig_2; // Warning: (ae-forgotten-export) The symbol "SortDirectionNumeric" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "SortDirectionFormat" needs to be exported by the entry point index.d.ts @@ -916,11 +919,8 @@ export type ExecutionContextSearch = { // Warning: (ae-missing-release-tag) "ExistsFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type ExistsFilter = Filter & { - meta: ExistsFilterMeta; - exists?: FilterExistsProperty; -}; +// @public @deprecated (undocumented) +export type ExistsFilter = ExistsFilter_2; // Warning: (ae-missing-release-tag) "exporters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1079,12 +1079,8 @@ export const fieldList: (specs?: FieldSpec[], shortDotsEnable?: boolean) => IInd // Warning: (ae-missing-release-tag) "Filter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type Filter = { - $state?: FilterState; - meta: FilterMeta; - query?: any; -}; +// @public @deprecated (undocumented) +export type Filter = Filter_2; // Warning: (ae-forgotten-export) The symbol "PersistableStateService" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "FilterManager" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1093,19 +1089,19 @@ export type Filter = { export class FilterManager implements PersistableStateService { constructor(uiSettings: IUiSettingsClient); // (undocumented) - addFilters(filters: Filter[] | Filter, pinFilterStatus?: boolean): void; + addFilters(filters: Filter_2[] | Filter_2, pinFilterStatus?: boolean): void; // (undocumented) extract: any; // (undocumented) getAllMigrations: () => {}; // (undocumented) - getAppFilters(): Filter[]; + getAppFilters(): Filter_2[]; // (undocumented) getFetches$(): import("rxjs").Observable; // (undocumented) - getFilters(): Filter[]; + getFilters(): Filter_2[]; // (undocumented) - getGlobalFilters(): Filter[]; + getGlobalFilters(): Filter_2[]; // Warning: (ae-forgotten-export) The symbol "PartitionedFilters" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -1119,13 +1115,13 @@ export class FilterManager implements PersistableStateService { // (undocumented) removeAll(): void; // (undocumented) - removeFilter(filter: Filter): void; - setAppFilters(newAppFilters: Filter[]): void; + removeFilter(filter: Filter_2): void; + setAppFilters(newAppFilters: Filter_2[]): void; // (undocumented) - setFilters(newFilters: Filter[], pinFilterStatus?: boolean): void; + setFilters(newFilters: Filter_2[], pinFilterStatus?: boolean): void; // (undocumented) - static setFiltersStore(filters: Filter[], store: FilterStateStore, shouldOverrideStore?: boolean): void; - setGlobalFilters(newGlobalFilters: Filter[]): void; + static setFiltersStore(filters: Filter_2[], store: FilterStateStore, shouldOverrideStore?: boolean): void; + setGlobalFilters(newGlobalFilters: Filter_2[]): void; // (undocumented) telemetry: (filters: import("../../../../kibana_utils/common/persistable_state").SerializableState, collector: unknown) => {}; } @@ -1164,7 +1160,7 @@ export function getSearchParamsFromRequest(searchRequest: SearchRequest, depende export function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: { forceNow?: Date; fieldName?: string; -}): import("../..").RangeFilter | undefined; +}): import("@kbn/es-query").RangeFilter | undefined; // Warning: (ae-missing-release-tag) "IAggConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1242,19 +1238,9 @@ 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) -export interface IFieldSubType { - // (undocumented) - multi?: { - parent: string; - }; - // (undocumented) - nested?: { - path: string; - }; -} +// @public @deprecated (undocumented) +export type IFieldSubType = IFieldSubType_2; -// Warning: (ae-forgotten-export) The symbol "IndexPatternFieldBase" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "IFieldType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @deprecated (undocumented) @@ -1287,7 +1273,6 @@ export interface IFieldType extends IndexPatternFieldBase { visualizable?: boolean; } -// Warning: (ae-forgotten-export) The symbol "IndexPatternBase" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "IIndexPattern" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @deprecated (undocumented) @@ -1565,7 +1550,7 @@ export class IndexPatternField implements IFieldType { // (undocumented) readonly spec: FieldSpec; // (undocumented) - get subType(): import("../..").IFieldSubType | undefined; + get subType(): import("@kbn/es-query").IFieldSubType | undefined; // (undocumented) toJSON(): { count: number; @@ -1579,7 +1564,7 @@ export class IndexPatternField implements IFieldType { searchable: boolean; aggregatable: boolean; readFromDocValues: boolean; - subType: import("../..").IFieldSubType | undefined; + subType: import("@kbn/es-query").IFieldSubType | undefined; customLabel: string | undefined; }; // (undocumented) @@ -1826,13 +1811,13 @@ export type ISessionService = PublicContract; // Warning: (ae-missing-release-tag) "isFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export const isFilter: (x: unknown) => x is Filter; +// @public @deprecated (undocumented) +export const isFilter: (x: unknown) => x is Filter_2; // Warning: (ae-missing-release-tag) "isFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export const isFilters: (x: unknown) => x is Filter[]; +// @public @deprecated (undocumented) +export const isFilters: (x: unknown) => x is Filter_2[]; // Warning: (ae-missing-release-tag) "isPartialResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1898,23 +1883,13 @@ export type KibanaContext = ExpressionValueSearchContext; // Warning: (ae-missing-release-tag) "KueryNode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export interface KueryNode { - // (undocumented) - [key: string]: any; - // Warning: (ae-forgotten-export) The symbol "NodeTypes" needs to be exported by the entry point index.d.ts - // - // (undocumented) - type: keyof NodeTypes; -} +// @public @deprecated (undocumented) +export type KueryNode = KueryNode_2; // Warning: (ae-missing-release-tag) "MatchAllFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type MatchAllFilter = Filter & { - meta: MatchAllFilterMeta; - match_all: any; -}; +// @public @deprecated (undocumented) +export type MatchAllFilter = MatchAllFilter_2; // Warning: (ae-missing-release-tag) "METRIC_TYPES" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2023,24 +1998,13 @@ export const parseSearchSourceJSON: (searchSourceJSON: string) => SearchSourceFi // Warning: (ae-missing-release-tag) "PhraseFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type PhraseFilter = Filter & { - meta: PhraseFilterMeta; - script?: { - script: { - source?: any; - lang?: estypes.ScriptLanguage; - params: any; - }; - }; -}; +// @public @deprecated (undocumented) +export type PhraseFilter = PhraseFilter_2; // Warning: (ae-missing-release-tag) "PhrasesFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type PhrasesFilter = Filter & { - meta: PhrasesFilterMeta; -}; +// @public @deprecated (undocumented) +export type PhrasesFilter = PhrasesFilter_2; // Warning: (ae-forgotten-export) The symbol "PluginInitializerContext" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "plugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2048,15 +2012,7 @@ export type PhrasesFilter = Filter & { // @public (undocumented) export function plugin(initializerContext: PluginInitializerContext): DataPlugin; -// Warning: (ae-missing-release-tag) "Query" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type Query = { - query: string | { - [key: string]: any; - }; - language: string; -}; +export { Query } // Warning: (ae-forgotten-export) The symbol "QueryService" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "QueryStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2226,50 +2182,20 @@ export enum QuerySuggestionTypes { Value = "value" } -// Warning: (ae-forgotten-export) The symbol "EsRangeFilter" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "RangeFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type RangeFilter = Filter & EsRangeFilter & { - meta: RangeFilterMeta; - script?: { - script: { - params: any; - lang: estypes.ScriptLanguage; - source: any; - }; - }; - match_all?: any; -}; +// @public @deprecated (undocumented) +export type RangeFilter = RangeFilter_2; // Warning: (ae-missing-release-tag) "RangeFilterMeta" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type RangeFilterMeta = FilterMeta & { - params: RangeFilterParams; - field?: any; - formattedValue?: string; -}; +// @public @deprecated (undocumented) +export type RangeFilterMeta = RangeFilterMeta_2; // Warning: (ae-missing-release-tag) "RangeFilterParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export interface RangeFilterParams { - // (undocumented) - format?: string; - // (undocumented) - from?: number | string; - // (undocumented) - gt?: number | string; - // (undocumented) - gte?: number | string; - // (undocumented) - lt?: number | string; - // (undocumented) - lte?: number | string; - // (undocumented) - to?: number | string; -} +// @public @deprecated (undocumented) +export type RangeFilterParams = RangeFilterParams_2; // Warning: (ae-missing-release-tag) "Reason" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2565,6 +2491,8 @@ export interface SearchSourceFields { index?: IndexPattern; // (undocumented) parent?: SearchSourceFields; + // Warning: (ae-unresolved-link) The @link reference could not be resolved: Reexported declarations are not supported + // // (undocumented) query?: Query; // Warning: (ae-forgotten-export) The symbol "EsQuerySearchAfter" needs to be exported by the entry point index.d.ts @@ -2754,65 +2682,53 @@ export interface WaitUntilNextSessionCompletesOptions { // Warnings were encountered during analysis: // -// src/plugins/data/common/es_query/filters/exists_filter.ts:19:3 - (ae-forgotten-export) The symbol "ExistsFilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/exists_filter.ts:20:3 - (ae-forgotten-export) The symbol "FilterExistsProperty" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/match_all_filter.ts:17:3 - (ae-forgotten-export) The symbol "MatchAllFilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/meta_filter.ts:44:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/phrase_filter.ts:22:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/phrases_filter.ts:20:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:65:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:138:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:169:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts // src/plugins/data/common/search/aggs/types.ts:129:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts // src/plugins/data/public/field_formats/field_formats_service.ts:56:3 - (ae-forgotten-export) The symbol "FormatFactory" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:56:23 - (ae-forgotten-export) The symbol "FILTERS" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:56:23 - (ae-forgotten-export) The symbol "getDisplayValueFromFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:56:23 - (ae-forgotten-export) The symbol "generateFilters" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:56:23 - (ae-forgotten-export) The symbol "changeTimeFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:56:23 - (ae-forgotten-export) The symbol "convertRangeFilterToTimeRangeString" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:56:23 - (ae-forgotten-export) The symbol "extractTimeFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:56:23 - (ae-forgotten-export) The symbol "extractTimeRange" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:129:21 - (ae-forgotten-export) The symbol "buildEsQuery" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:129:21 - (ae-forgotten-export) The symbol "getEsQueryConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:129:21 - (ae-forgotten-export) The symbol "luceneStringToDsl" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:129:21 - (ae-forgotten-export) The symbol "decorateQuery" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "FieldFormatsRegistry" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "BoolFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "BytesFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "ColorFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "DurationFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "IpFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "NumberFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "PercentFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "RelativeDateFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "SourceFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "StaticLookupFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:170:26 - (ae-forgotten-export) The symbol "HistogramFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:213:23 - (ae-forgotten-export) The symbol "datatableToCSV" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:240:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:240:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:240:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:240:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:240:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:412:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:412:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:412:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:414:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:415:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:424:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:425:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:426:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:427:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:431:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:432:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:435:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:436:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:439:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:34:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:67:23 - (ae-forgotten-export) The symbol "getDisplayValueFromFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:67:23 - (ae-forgotten-export) The symbol "generateFilters" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:67:23 - (ae-forgotten-export) The symbol "changeTimeFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:67:23 - (ae-forgotten-export) The symbol "convertRangeFilterToTimeRangeString" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:67:23 - (ae-forgotten-export) The symbol "extractTimeFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:67:23 - (ae-forgotten-export) The symbol "extractTimeRange" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:138:21 - (ae-forgotten-export) The symbol "getEsQueryConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "FieldFormatsRegistry" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "BoolFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "BytesFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "ColorFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "DurationFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "IpFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "NumberFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "PercentFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "RelativeDateFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "SourceFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "StaticLookupFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:177:26 - (ae-forgotten-export) The symbol "HistogramFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:220:23 - (ae-forgotten-export) The symbol "datatableToCSV" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:247:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:247:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:247:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:247:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:247:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:418:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:418:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:418:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:420:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:421:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:430:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:431:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:432:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:433:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:437:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:438:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:441:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:442:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:445:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/search/session/session_service.ts:62:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/data/public/query/filter_manager/filter_manager.ts b/src/plugins/data/public/query/filter_manager/filter_manager.ts index d47e4fb33f2a5..d514e0eb18705 100644 --- a/src/plugins/data/public/query/filter_manager/filter_manager.ts +++ b/src/plugins/data/public/query/filter_manager/filter_manager.ts @@ -11,6 +11,7 @@ import { Subject } from 'rxjs'; import { IUiSettingsClient } from 'src/core/public'; +import { isFilterPinned, Filter } from '@kbn/es-query'; import { sortFilters } from './lib/sort_filters'; import { mapAndFlattenFilters } from './lib/map_and_flatten_filters'; import { onlyDisabledFiltersChanged } from './lib/only_disabled'; @@ -18,9 +19,7 @@ import { PartitionedFilters } from './types'; import { FilterStateStore, - Filter, uniqFilters, - isFilterPinned, compareFilters, COMPARE_ALL_OPTIONS, UI_SETTINGS, diff --git a/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts b/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts index 0a4998a159523..566b26b0698dd 100644 --- a/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts +++ b/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts @@ -8,8 +8,6 @@ import _ from 'lodash'; import { - IFieldType, - IIndexPattern, Filter, isExistsFilter, isPhraseFilter, @@ -19,7 +17,9 @@ import { buildFilter, FilterStateStore, FILTERS, -} from '../../../../common'; +} from '@kbn/es-query'; + +import { IFieldType, IIndexPattern } from '../../../../common'; import { FilterManager } from '../filter_manager'; function getExistingFilter( diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts index f4abd65385da2..c30965e777c46 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts @@ -7,7 +7,7 @@ */ import { find, keys, get } from 'lodash'; -import { Filter, FILTERS } from '../../../../../common'; +import { Filter, FILTERS } from '@kbn/es-query'; export const mapDefault = (filter: Filter) => { const metaProperty = /(^\$|meta)/; diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts index b40701726d52d..19ebc66ae2d46 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts @@ -7,7 +7,7 @@ */ import { get } from 'lodash'; -import { Filter, isExistsFilter, FILTERS } from '../../../../../common'; +import { Filter, isExistsFilter, FILTERS } from '@kbn/es-query'; export const mapExists = (filter: Filter) => { if (isExistsFilter(filter)) { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts index d1e2a649c400f..dfa8e862e1b4c 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts @@ -6,13 +6,9 @@ * Side Public License, v 1. */ -import { - FilterValueFormatter, - GeoBoundingBoxFilter, - FILTERS, - isGeoBoundingBoxFilter, - Filter, -} from '../../../../../common'; +import { GeoBoundingBoxFilter, FILTERS, isGeoBoundingBoxFilter, Filter } from '@kbn/es-query'; + +import { FilterValueFormatter } from '../../../../../common'; const getFormattedValueFn = (params: any) => { return (formatter?: FilterValueFormatter) => { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts index 7bcedd9a7f951..e1c21aae64da6 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts @@ -6,13 +6,9 @@ * Side Public License, v 1. */ -import { - FilterValueFormatter, - GeoPolygonFilter, - FILTERS, - Filter, - isGeoPolygonFilter, -} from '../../../../../common'; +import { GeoPolygonFilter, FILTERS, Filter, isGeoPolygonFilter } from '@kbn/es-query'; + +import { FilterValueFormatter } from '../../../../../common'; const POINTS_SEPARATOR = ', '; diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts index a3c9086676f66..32231492b2f22 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Filter, isMatchAllFilter, FILTERS } from '../../../../../common'; +import { Filter, isMatchAllFilter, FILTERS } from '@kbn/es-query'; export const mapMatchAll = (filter: Filter) => { if (isMatchAllFilter(filter)) { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts index b14fe4fb9da0f..41af3760a652e 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Filter, isMissingFilter, FILTERS } from '../../../../../common'; +import { Filter, isMissingFilter, FILTERS } from '@kbn/es-query'; export const mapMissing = (filter: Filter) => { if (isMissingFilter(filter)) { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts index d8eb14ec5a67c..64576d4978c99 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts @@ -9,14 +9,15 @@ import { get } from 'lodash'; import { PhraseFilter, - FilterValueFormatter, getPhraseFilterValue, getPhraseFilterField, FILTERS, isScriptedPhraseFilter, Filter, isPhraseFilter, -} from '../../../../../common'; +} from '@kbn/es-query'; + +import { FilterValueFormatter } from '../../../../../common'; const getScriptedPhraseValue = (filter: PhraseFilter) => get(filter, ['script', 'script', 'params', 'value']); diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts index 5601dd66e5206..9ffdd3070e43a 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts @@ -6,7 +6,9 @@ * Side Public License, v 1. */ -import { Filter, FilterValueFormatter, isPhrasesFilter } from '../../../../../common'; +import { Filter, isPhrasesFilter } from '@kbn/es-query'; + +import { FilterValueFormatter } from '../../../../../common'; const getFormattedValueFn = (params: any) => { return (formatter?: FilterValueFormatter) => { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts index 9c11f74b60516..89355b6d1f423 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { FILTERS, Filter, isQueryStringFilter } from '../../../../../common'; +import { FILTERS, Filter, isQueryStringFilter } from '@kbn/es-query'; export const mapQueryString = (filter: Filter) => { if (isQueryStringFilter(filter)) { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts index b58128bd5dbdb..ee86c1aa3c309 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts @@ -7,14 +7,9 @@ */ import { get, hasIn } from 'lodash'; -import { - FilterValueFormatter, - RangeFilter, - isScriptedRangeFilter, - isRangeFilter, - Filter, - FILTERS, -} from '../../../../../common'; +import { RangeFilter, isScriptedRangeFilter, isRangeFilter, Filter, FILTERS } from '@kbn/es-query'; + +import { FilterValueFormatter } from '../../../../../common'; const getFormattedValueFn = (left: any, right: any) => { return (formatter?: FilterValueFormatter) => { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_spatial_filter.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_spatial_filter.ts index 229257c1a7d81..d31b5bb9e608d 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_spatial_filter.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_spatial_filter.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Filter, FILTERS } from '../../../../../common'; +import { Filter, FILTERS } from '@kbn/es-query'; // Use mapSpatialFilter mapper to avoid bloated meta with value and params for spatial filters. export const mapSpatialFilter = (filter: Filter) => { diff --git a/src/plugins/data/public/query/query_service.ts b/src/plugins/data/public/query/query_service.ts index 3e86c6aa01fd9..5104a934fdec8 100644 --- a/src/plugins/data/public/query/query_service.ts +++ b/src/plugins/data/public/query/query_service.ts @@ -9,13 +9,14 @@ import { share } from 'rxjs/operators'; import { IUiSettingsClient, SavedObjectsClientContract } from 'src/core/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import { buildEsQuery } from '@kbn/es-query'; import { FilterManager } from './filter_manager'; import { createAddToQueryLog } from './lib'; import { TimefilterService, TimefilterSetup } from './timefilter'; import { createSavedQueryService } from './saved_query/saved_query_service'; import { createQueryStateObservable } from './state_sync/create_global_query_observable'; import { QueryStringManager, QueryStringContract } from './query_string'; -import { buildEsQuery, getEsQueryConfig, TimeRange } from '../../common'; +import { getEsQueryConfig, TimeRange } from '../../common'; import { getUiSettings } from '../services'; import { NowProviderInternalContract } from '../now_provider'; import { IndexPattern } from '..'; diff --git a/src/plugins/data/public/query/state_sync/create_global_query_observable.ts b/src/plugins/data/public/query/state_sync/create_global_query_observable.ts index 7e5b4ebed8785..3c94d6eb3c056 100644 --- a/src/plugins/data/public/query/state_sync/create_global_query_observable.ts +++ b/src/plugins/data/public/query/state_sync/create_global_query_observable.ts @@ -8,11 +8,12 @@ import { Observable, Subscription } from 'rxjs'; import { map, tap } from 'rxjs/operators'; +import { isFilterPinned } from '@kbn/es-query'; import { TimefilterSetup } from '../timefilter'; import { FilterManager } from '../filter_manager'; import { QueryState, QueryStateChange } from './index'; import { createStateContainer } from '../../../../kibana_utils/public'; -import { isFilterPinned, compareFilters, COMPARE_ALL_OPTIONS } from '../../../common'; +import { compareFilters, COMPARE_ALL_OPTIONS } from '../../../common'; import { QueryStringContract } from '../query_string'; export function createQueryStateObservable({ diff --git a/src/plugins/data/public/query/state_sync/sync_state_with_url.ts b/src/plugins/data/public/query/state_sync/sync_state_with_url.ts index abb9953b844da..2f068ccdfab4f 100644 --- a/src/plugins/data/public/query/state_sync/sync_state_with_url.ts +++ b/src/plugins/data/public/query/state_sync/sync_state_with_url.ts @@ -14,7 +14,7 @@ import { import { QuerySetup, QueryStart } from '../query_service'; import { connectToQueryState } from './connect_to_query_state'; import { QueryState } from './types'; -import { FilterStateStore } from '../../../common/es_query/filters'; +import { FilterStateStore } from '../../../common'; const GLOBAL_STATE_STORAGE_KEY = '_g'; diff --git a/src/plugins/data/public/query/timefilter/lib/change_time_filter.ts b/src/plugins/data/public/query/timefilter/lib/change_time_filter.ts index a5601bbc2457a..0f4cdabc69d6b 100644 --- a/src/plugins/data/public/query/timefilter/lib/change_time_filter.ts +++ b/src/plugins/data/public/query/timefilter/lib/change_time_filter.ts @@ -8,8 +8,9 @@ import moment from 'moment'; import { keys } from 'lodash'; +import { RangeFilter } from '@kbn/es-query'; import { TimefilterContract } from '../../timefilter'; -import { RangeFilter, TimeRange } from '../../../../common'; +import { TimeRange } from '../../../../common'; export function convertRangeFilterToTimeRange(filter: RangeFilter) { const key = keys(filter.range)[0]; diff --git a/src/plugins/data/public/query/timefilter/lib/extract_time_filter.ts b/src/plugins/data/public/query/timefilter/lib/extract_time_filter.ts index 9e2b19002ff7f..19e2c656be50d 100644 --- a/src/plugins/data/public/query/timefilter/lib/extract_time_filter.ts +++ b/src/plugins/data/public/query/timefilter/lib/extract_time_filter.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ +import { Filter, isRangeFilter, RangeFilter } from '@kbn/es-query'; import { keys, partition } from 'lodash'; -import { Filter, isRangeFilter, RangeFilter, TimeRange } from '../../../../common'; +import { TimeRange } from '../../../../common'; import { convertRangeFilterToTimeRangeString } from './change_time_filter'; export function extractTimeFilter(timeFieldName: string, filters: Filter[]) { diff --git a/src/plugins/data/public/search/session/search_session_state.test.ts b/src/plugins/data/public/search/session/search_session_state.test.ts index d702d5c71a24b..65b931f23cf2e 100644 --- a/src/plugins/data/public/search/session/search_session_state.test.ts +++ b/src/plugins/data/public/search/session/search_session_state.test.ts @@ -24,6 +24,7 @@ const mockSavedObject: SearchSessionSavedObject = { expires: new Date().toISOString(), status: SearchSessionStatus.COMPLETE, persisted: true, + version: '8.0.0', }, references: [], }; diff --git a/src/plugins/data/public/search/session/session_service.test.ts b/src/plugins/data/public/search/session/session_service.test.ts index c2c4d1540c387..5c1882248f76a 100644 --- a/src/plugins/data/public/search/session/session_service.test.ts +++ b/src/plugins/data/public/search/session/session_service.test.ts @@ -33,6 +33,7 @@ const mockSavedObject: SearchSessionSavedObject = { expires: new Date().toISOString(), status: SearchSessionStatus.COMPLETE, persisted: true, + version: '8.0.0', }, references: [], }; diff --git a/src/plugins/data/public/ui/filter_bar/filter_bar.tsx b/src/plugins/data/public/ui/filter_bar/filter_bar.tsx index cc796ad749f0b..70f25bd510ef0 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_bar.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_bar.tsx @@ -8,15 +8,6 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiPopover } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; -import classNames from 'classnames'; -import React, { useState, useRef } from 'react'; - -import { METRIC_TYPE } from '@kbn/analytics'; -import { FilterEditor } from './filter_editor'; -import { FILTER_EDITOR_WIDTH, FilterItem } from './filter_item'; -import { FilterOptions } from './filter_options'; -import { useKibana } from '../../../../kibana_react/public'; -import { IDataPluginServices, IIndexPattern } from '../..'; import { buildEmptyFilter, Filter, @@ -26,8 +17,18 @@ import { toggleFilterDisabled, toggleFilterNegated, unpinFilter, - UI_SETTINGS, -} from '../../../common'; +} from '@kbn/es-query'; +import classNames from 'classnames'; +import React, { useState, useRef } from 'react'; + +import { METRIC_TYPE } from '@kbn/analytics'; +import { FilterEditor } from './filter_editor'; +import { FILTER_EDITOR_WIDTH, FilterItem } from './filter_item'; +import { FilterOptions } from './filter_options'; +import { useKibana } from '../../../../kibana_react/public'; +import { IDataPluginServices, IIndexPattern } from '../..'; + +import { UI_SETTINGS } from '../../../common'; interface Props { filters: Filter[]; diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx b/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx index 734161ea87232..90ca8e0783a20 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx @@ -23,6 +23,14 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; +import { + Filter, + FieldFilter, + buildFilter, + buildCustomFilter, + cleanFilter, + getFilterParams, +} from '@kbn/es-query'; import { get } from 'lodash'; import React, { Component } from 'react'; import { GenericComboBox, GenericComboBoxProps } from './generic_combo_box'; @@ -39,14 +47,6 @@ import { PhrasesValuesInput } from './phrases_values_input'; import { RangeValueInput } from './range_value_input'; import { getIndexPatternFromFilter } from '../../../query'; import { IIndexPattern, IFieldType } from '../../..'; -import { - Filter, - FieldFilter, - buildFilter, - buildCustomFilter, - cleanFilter, - getFilterParams, -} from '../../../../common'; export interface Props { filter: Filter; diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_editor_utils.ts b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_editor_utils.ts index e15f667128c4f..ea172c2ccac35 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_editor_utils.ts +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_editor_utils.ts @@ -7,15 +7,9 @@ */ import dateMath from '@elastic/datemath'; +import { Filter, FieldFilter } from '@kbn/es-query'; import { FILTER_OPERATORS, Operator } from './filter_operators'; -import { - isFilterable, - IIndexPattern, - IFieldType, - IpAddress, - Filter, - FieldFilter, -} from '../../../../../common'; +import { isFilterable, IIndexPattern, IFieldType, IpAddress } from '../../../../../common'; export function getFieldFromFilter(filter: FieldFilter, indexPattern: IIndexPattern) { return indexPattern.fields.find((field) => field.name === filter.meta.key); diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_operators.ts b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_operators.ts index 218f8f5790e44..bc3f01aeb3c8f 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_operators.ts +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_operators.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import { FILTERS } from '../../../../../common/es_query/filters'; +import { FILTERS } from '@kbn/es-query'; export interface Operator { message: string; diff --git a/src/plugins/data/public/ui/filter_bar/filter_item.tsx b/src/plugins/data/public/ui/filter_bar/filter_item.tsx index 09e0571c2a870..b37fc9108ccd2 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_item.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_item.tsx @@ -8,6 +8,13 @@ import { EuiContextMenu, EuiPopover } from '@elastic/eui'; import { InjectedIntl } from '@kbn/i18n/react'; +import { + Filter, + isFilterPinned, + toggleFilterNegated, + toggleFilterPinned, + toggleFilterDisabled, +} from '@kbn/es-query'; import classNames from 'classnames'; import React, { MouseEvent, useState, useEffect } from 'react'; import { IUiSettingsClient } from 'src/core/public'; @@ -15,13 +22,6 @@ import { FilterEditor } from './filter_editor'; import { FilterView } from './filter_view'; import { IIndexPattern } from '../..'; import { getDisplayValueFromFilter, getIndexPatternFromFilter } from '../../query'; -import { - Filter, - isFilterPinned, - toggleFilterNegated, - toggleFilterPinned, - toggleFilterDisabled, -} from '../../../common'; import { getIndexPatterns } from '../../services'; type PanelOptions = 'pinFilter' | 'editFilter' | 'negateFilter' | 'disableFilter' | 'deleteFilter'; diff --git a/src/plugins/data/public/ui/filter_bar/filter_view/index.tsx b/src/plugins/data/public/ui/filter_bar/filter_view/index.tsx index a15cec4bd286d..d551af87c7279 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_view/index.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_view/index.tsx @@ -9,8 +9,8 @@ import { EuiBadge, useInnerText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { FC } from 'react'; +import { Filter, isFilterPinned } from '@kbn/es-query'; import { FilterLabel } from '../'; -import { Filter, isFilterPinned } from '../../../../common'; import type { FilterLabelStatus } from '../filter_item'; interface Props { diff --git a/src/plugins/data/server/autocomplete/terms_enum.test.ts b/src/plugins/data/server/autocomplete/terms_enum.test.ts index 41eaf3f4032ab..4e1eecc71db7c 100644 --- a/src/plugins/data/server/autocomplete/terms_enum.test.ts +++ b/src/plugins/data/server/autocomplete/terms_enum.test.ts @@ -12,6 +12,7 @@ import { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server'; import { ConfigSchema } from '../../config'; import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; import type { ApiResponse } from '@elastic/elasticsearch'; +import { TermsEnumResponse } from '@elastic/elasticsearch/api/types'; let savedObjectsClientMock: jest.Mocked; let esClientMock: DeeplyMockedKeys; @@ -29,7 +30,9 @@ describe('_terms_enum suggestions', () => { const requestHandlerContext = coreMock.createRequestHandlerContext(); savedObjectsClientMock = requestHandlerContext.savedObjects.client; esClientMock = requestHandlerContext.elasticsearch.client.asCurrentUser; - esClientMock.transport.request.mockResolvedValue((mockResponse as unknown) as ApiResponse); + esClientMock.termsEnum.mockResolvedValue( + (mockResponse as unknown) as ApiResponse + ); }); it('calls the _terms_enum API with the field, query, filters, and config tiers', async () => { @@ -44,7 +47,7 @@ describe('_terms_enum suggestions', () => { { name: 'field_name', type: 'string' } ); - const [[args]] = esClientMock.transport.request.mock.calls; + const [[args]] = esClientMock.termsEnum.mock.calls; expect(args).toMatchInlineSnapshot(` Object { @@ -67,8 +70,7 @@ describe('_terms_enum suggestions', () => { }, "string": "query", }, - "method": "POST", - "path": "/index/_terms_enum", + "index": "index", } `); expect(result).toEqual(mockResponse.body.terms); @@ -85,7 +87,7 @@ describe('_terms_enum suggestions', () => { [] ); - const [[args]] = esClientMock.transport.request.mock.calls; + const [[args]] = esClientMock.termsEnum.mock.calls; expect(args).toMatchInlineSnapshot(` Object { @@ -108,8 +110,7 @@ describe('_terms_enum suggestions', () => { }, "string": "query", }, - "method": "POST", - "path": "/index/_terms_enum", + "index": "index", } `); expect(result).toEqual(mockResponse.body.terms); diff --git a/src/plugins/data/server/autocomplete/terms_enum.ts b/src/plugins/data/server/autocomplete/terms_enum.ts index 40329586a3621..4bfd1545c6fe5 100644 --- a/src/plugins/data/server/autocomplete/terms_enum.ts +++ b/src/plugins/data/server/autocomplete/terms_enum.ts @@ -11,7 +11,6 @@ import { estypes } from '@elastic/elasticsearch'; import { IFieldType } from '../../common'; import { findIndexPatternById, getFieldByName } from '../index_patterns'; import { shimAbortSignal } from '../search'; -import { getKbnServerError } from '../../../kibana_utils/server'; import { ConfigSchema } from '../../config'; export async function termsEnumSuggestions( @@ -31,32 +30,26 @@ export async function termsEnumSuggestions( field = indexPattern && getFieldByName(fieldName, indexPattern); } - try { - const promise = esClient.transport.request({ - method: 'POST', - path: encodeURI(`/${index}/_terms_enum`), - body: { - field: field?.name ?? fieldName, - string: query, - index_filter: { - bool: { - must: [ - ...(filters ?? []), - { - terms: { - _tier: tiers, - }, + const promise = esClient.termsEnum({ + index, + body: { + field: field?.name ?? fieldName, + string: query, + index_filter: { + bool: { + must: [ + ...(filters ?? []), + { + terms: { + _tier: tiers, }, - ], - }, + }, + ], }, }, - }); + }, + }); - const result = await shimAbortSignal(promise, abortSignal); - - return result.body.terms; - } catch (e) { - throw getKbnServerError(e); - } + const result = await shimAbortSignal(promise, abortSignal); + return result.body.terms; } diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 143400a2c09d3..a9e8cda082314 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -6,10 +6,6 @@ * Side Public License, v 1. */ -import { PluginConfigDescriptor, PluginInitializerContext } from '../../../core/server'; -import { ConfigSchema, configSchema } from '../config'; -import { DataServerPlugin, DataPluginSetup, DataPluginStart } from './plugin'; - import { buildQueryFilter, buildCustomFilter, @@ -20,12 +16,20 @@ import { buildPhrasesFilter, buildRangeFilter, isFilterDisabled, + nodeTypes, + fromKueryExpression, + toElasticsearchQuery, + buildEsQuery, + buildQueryFromFilters, } from '../common'; +import { PluginConfigDescriptor, PluginInitializerContext } from '../../../core/server'; +import { ConfigSchema, configSchema } from '../config'; +import { DataServerPlugin, DataPluginSetup, DataPluginStart } from './plugin'; /* + * @deprecated Please import from the package kbn/es-query directly. This will be deprecated in v8.0.0. * Filter helper namespace: */ - export const esFilters = { buildQueryFilter, buildCustomFilter, @@ -52,28 +56,29 @@ export const exporters = { * esQuery and esKuery: */ -import { - nodeTypes, - fromKueryExpression, - toElasticsearchQuery, - buildEsQuery, - buildQueryFromFilters, - getEsQueryConfig, -} from '../common'; +import { getEsQueryConfig } from '../common'; +/* + * Filter helper namespace + * @deprecated Please import from the package kbn/es-query directly. This will be deprecated in v8.0.0. + */ export const esKuery = { nodeTypes, fromKueryExpression, toElasticsearchQuery, }; +/* + * Filter helper namespace + * @deprecated Please import from the package kbn/es-query directly. This will be deprecated in v8.0.0. + */ export const esQuery = { buildQueryFromFilters, getEsQueryConfig, buildEsQuery, }; -export { EsQueryConfig, KueryNode } from '../common'; +export type { EsQueryConfig, KueryNode, IFieldSubType } from '../common'; /* * Field Formats: @@ -146,7 +151,6 @@ export { export { IFieldType, - IFieldSubType, ES_FIELD_TYPES, KBN_FIELD_TYPES, IndexPatternAttributes, diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index adce494cf4782..8616343c1154c 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -23,6 +23,7 @@ import { ElasticsearchClient as ElasticsearchClient_2 } from 'kibana/server'; import { Ensure } from '@kbn/utility-types'; import { EnvironmentMode } from '@kbn/config'; import { ErrorToastOptions } from 'src/core/public/notifications'; +import { EsQueryConfig as EsQueryConfig_2 } from '@kbn/es-query'; import { estypes } from '@elastic/elasticsearch'; import { EventEmitter } from 'events'; import { ExecutionContext } from 'src/plugins/expressions/common'; @@ -30,18 +31,22 @@ import { ExpressionAstExpression } from 'src/plugins/expressions/common'; import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { ExpressionValueBoxed } from 'src/plugins/expressions/common'; +import { Filter as Filter_2 } from '@kbn/es-query'; import { FormatFactory as FormatFactory_2 } from 'src/plugins/data/common/field_formats/utils'; import { IAggConfigs as IAggConfigs_2 } from 'src/plugins/data/public'; import { IEsSearchResponse as IEsSearchResponse_2 } from 'src/plugins/data/public'; +import { IFieldSubType as IFieldSubType_2 } from '@kbn/es-query'; +import { IndexPatternBase } from '@kbn/es-query'; +import { IndexPatternFieldBase } from '@kbn/es-query'; import { IScopedClusterClient } from 'src/core/server'; import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; import { ISearchSource } from 'src/plugins/data/public'; import { IUiSettingsClient } from 'src/core/server'; import { IUiSettingsClient as IUiSettingsClient_3 } from 'kibana/server'; -import { JsonValue } from '@kbn/common-utils'; import { KibanaExecutionContext } from 'src/core/public'; import { KibanaRequest } from 'src/core/server'; import { KibanaRequest as KibanaRequest_2 } from 'kibana/server'; +import { KueryNode as KueryNode_2 } from '@kbn/es-query'; import { Logger } from 'src/core/server'; import { Logger as Logger_2 } from 'kibana/server'; import { LoggerFactory } from '@kbn/logging'; @@ -55,7 +60,7 @@ import { Plugin as Plugin_2 } from 'src/core/server'; import { Plugin as Plugin_3 } from 'kibana/server'; import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/server'; import { PublicMethodsOf } from '@kbn/utility-types'; -import { RangeFilter } from 'src/plugins/data/public'; +import { Query } from '@kbn/es-query'; import { RecursiveReadonly } from '@kbn/utility-types'; import { RequestAdapter } from 'src/plugins/inspector/common'; import { RequestHandlerContext } from 'src/core/server'; @@ -445,53 +450,44 @@ export type EsaggsExpressionFunctionDefinition = ExpressionFunctionDefinition<'e // // @public (undocumented) export const esFilters: { - buildQueryFilter: (query: any, index: string, alias: string) => import("../common").QueryStringFilter; - buildCustomFilter: typeof buildCustomFilter; - buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("../common").Filter; - buildExistsFilter: (field: import("../common").IndexPatternFieldBase, indexPattern: import("../common").IndexPatternBase) => import("../common").ExistsFilter; - buildFilter: typeof buildFilter; - buildPhraseFilter: (field: import("../common").IndexPatternFieldBase, value: any, indexPattern: import("../common").IndexPatternBase) => import("../common").PhraseFilter; - buildPhrasesFilter: (field: import("../common").IndexPatternFieldBase, params: any[], indexPattern: import("../common").IndexPatternBase) => import("../common").PhrasesFilter; - buildRangeFilter: (field: import("../common").IndexPatternFieldBase, params: import("../common").RangeFilterParams, indexPattern: import("../common").IndexPatternBase, formattedValue?: string | undefined) => import("../common").RangeFilter; - isFilterDisabled: (filter: import("../common").Filter) => boolean; + buildQueryFilter: (query: any, index: string, alias: string) => import("@kbn/es-query").QueryStringFilter; + buildCustomFilter: typeof import("@kbn/es-query").buildCustomFilter; + buildEmptyFilter: (isPinned: boolean, index?: string | undefined) => import("@kbn/es-query").Filter; + buildExistsFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").ExistsFilter; + buildFilter: typeof import("@kbn/es-query").buildFilter; + buildPhraseFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, value: any, indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhraseFilter; + buildPhrasesFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: any[], indexPattern: import("@kbn/es-query").IndexPatternBase) => import("@kbn/es-query").PhrasesFilter; + buildRangeFilter: (field: import("@kbn/es-query").IndexPatternFieldBase, params: import("@kbn/es-query").RangeFilterParams, indexPattern: import("@kbn/es-query").IndexPatternBase, formattedValue?: string | undefined) => import("@kbn/es-query").RangeFilter; + isFilterDisabled: (filter: import("@kbn/es-query").Filter) => boolean; }; // Warning: (ae-missing-release-tag) "esKuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export const esKuery: { - nodeTypes: import("../common/es_query/kuery/node_types").NodeTypes; - fromKueryExpression: (expression: any, parseOptions?: Partial) => import("../common").KueryNode; - toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IndexPatternBase | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; + nodeTypes: import("@kbn/es-query/target_types/kuery/node_types").NodeTypes; + fromKueryExpression: (expression: any, parseOptions?: Partial | undefined) => import("@kbn/es-query").KueryNode; + toElasticsearchQuery: (node: import("@kbn/es-query").KueryNode, indexPattern?: import("@kbn/es-query").IndexPatternBase | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; }; // Warning: (ae-missing-release-tag) "esQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export const esQuery: { - buildQueryFromFilters: (filters: import("../common").Filter[] | undefined, indexPattern: import("../common").IndexPatternBase | undefined, ignoreFilterIfFieldNotInIndex?: boolean) => { + buildQueryFromFilters: (filters: import("@kbn/es-query").Filter[] | undefined, indexPattern: import("@kbn/es-query").IndexPatternBase | undefined, ignoreFilterIfFieldNotInIndex?: boolean | undefined) => { must: never[]; - filter: import("../common").Filter[]; + filter: import("@kbn/es-query").Filter[]; should: never[]; - must_not: import("../common").Filter[]; + must_not: import("@kbn/es-query").Filter[]; }; getEsQueryConfig: typeof getEsQueryConfig; - buildEsQuery: typeof buildEsQuery; + buildEsQuery: typeof import("@kbn/es-query").buildEsQuery; }; // Warning: (ae-missing-release-tag) "EsQueryConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export interface EsQueryConfig { - // (undocumented) - allowLeadingWildcards: boolean; - // (undocumented) - dateFormatTZ?: string; - // (undocumented) - ignoreFilterIfFieldNotInIndex: boolean; - // (undocumented) - queryStringOptions: Record; -} +// @public @deprecated (undocumented) +export type EsQueryConfig = EsQueryConfig_2; // Warning: (ae-missing-release-tag) "ExecutionContextSearch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -596,12 +592,8 @@ export type FieldFormatsGetConfigFn = GetConfigFn; // Warning: (ae-missing-release-tag) "Filter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type Filter = { - $state?: FilterState; - meta: FilterMeta; - query?: any; -}; +// @public @deprecated (undocumented) +export type Filter = Filter_2; // Warning: (ae-missing-release-tag) "getCapabilitiesForRollupIndices" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -629,7 +621,7 @@ export function getShardTimeout(config: SharedGlobalConfig_2): Pick): { @@ -682,19 +674,9 @@ 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) -export interface IFieldSubType { - // (undocumented) - multi?: { - parent: string; - }; - // (undocumented) - nested?: { - path: string; - }; -} +// @public @deprecated (undocumented) +export type IFieldSubType = IFieldSubType_2; -// Warning: (ae-forgotten-export) The symbol "IndexPatternFieldBase" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "IFieldType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @deprecated (undocumented) @@ -1131,15 +1113,8 @@ export type KibanaContext = ExpressionValueSearchContext; // Warning: (ae-missing-release-tag) "KueryNode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export interface KueryNode { - // (undocumented) - [key: string]: any; - // Warning: (ae-forgotten-export) The symbol "NodeTypes" needs to be exported by the entry point index.d.ts - // - // (undocumented) - type: keyof NodeTypes; -} +// @public @deprecated (undocumented) +export type KueryNode = KueryNode_2; // Warning: (ae-missing-release-tag) "mergeCapabilitiesWithFields" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1304,15 +1279,7 @@ export interface PluginStart { search: ISearchStart; } -// Warning: (ae-missing-release-tag) "Query" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type Query = { - query: string | { - [key: string]: any; - }; - language: string; -}; +export { Query } // Warning: (ae-missing-release-tag) "RefreshInterval" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1511,47 +1478,42 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // Warnings were encountered during analysis: // -// src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/es_query/filters/meta_filter.ts:44:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:52:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:65:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:138:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts // src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:169:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:29:23 - (ae-forgotten-export) The symbol "buildCustomFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:29:23 - (ae-forgotten-export) The symbol "buildFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:46:23 - (ae-forgotten-export) The symbol "datatableToCSV" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:70:21 - (ae-forgotten-export) The symbol "getEsQueryConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:70:21 - (ae-forgotten-export) The symbol "buildEsQuery" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "FieldFormatsRegistry" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "FieldFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "BoolFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "BytesFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "ColorFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "DurationFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "IpFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "NumberFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "PercentFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "RelativeDateFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "SourceFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "StaticLookupFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts -// 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:101:26 - (ae-forgotten-export) The symbol "HistogramFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:133:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:133:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:250:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:250:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:252:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:253:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:262:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:263:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:264:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:268:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:269:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:273:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:276:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:277:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:50:23 - (ae-forgotten-export) The symbol "datatableToCSV" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:75:21 - (ae-forgotten-export) The symbol "getEsQueryConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "FieldFormatsRegistry" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "FieldFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "BoolFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "BytesFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "ColorFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "DurationFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "IpFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "NumberFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "PercentFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "RelativeDateFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "SourceFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "StaticLookupFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:106:26 - (ae-forgotten-export) The symbol "HistogramFormat" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:138:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:138:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:254:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:254:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:256:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:257:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:266:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:267:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:268:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:272:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:273:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:277:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:280:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:281:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts // src/plugins/data/server/plugin.ts:81:74 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts // src/plugins/data/server/search/types.ts:115:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/discover/public/application/angular/doc_table/_doc_table.scss b/src/plugins/discover/public/application/angular/doc_table/_doc_table.scss index c4bba1a8bbf2b..ead426fa9c4eb 100644 --- a/src/plugins/discover/public/application/angular/doc_table/_doc_table.scss +++ b/src/plugins/discover/public/application/angular/doc_table/_doc_table.scss @@ -33,10 +33,6 @@ doc-table { th { white-space: nowrap; padding-right: $euiSizeS; - - .fa { - font-size: 1.1em; - } } } @@ -134,25 +130,3 @@ doc-table { } } } - -table { - th { - i.fa-sort { - color: $euiColorLightShade; - } - - button.fa-sort-asc, - button.fa-sort-down, - i.fa-sort-asc, - i.fa-sort-down { - color: $euiColorPrimary; - } - - button.fa-sort-desc, - button.fa-sort-up, - i.fa-sort-desc, - i.fa-sort-up { - color: $euiColorPrimary; - } - } -} diff --git a/src/plugins/discover/public/application/angular/doc_table/components/_table_header.scss b/src/plugins/discover/public/application/angular/doc_table/components/_table_header.scss index 9ea4e21632ace..3450084e19269 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/_table_header.scss +++ b/src/plugins/discover/public/application/angular/doc_table/components/_table_header.scss @@ -2,7 +2,7 @@ white-space: nowrap; } .kbnDocTableHeader button { - margin-left: $euiSizeXS; + margin-left: $euiSizeXS * .5; } .kbnDocTableHeader__move, .kbnDocTableHeader__sortChange { @@ -12,3 +12,7 @@ opacity: 1; } } +.kbnDocTableHeader__actions { + display: flex; + align-items: center; +} diff --git a/src/plugins/discover/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_buttons.test.tsx.snap b/src/plugins/discover/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_buttons.test.tsx.snap index 20e503fd5ff91..fd0c1b4b2af8d 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_buttons.test.tsx.snap +++ b/src/plugins/discover/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_buttons.test.tsx.snap @@ -1,30 +1,38 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`it renders ToolBarPagerButtons 1`] = ` -
- - -
+ + `; diff --git a/src/plugins/discover/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_text.test.tsx.snap b/src/plugins/discover/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_text.test.tsx.snap index fe168c013cb1a..96d2994bbe68f 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_text.test.tsx.snap +++ b/src/plugins/discover/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_text.test.tsx.snap @@ -2,7 +2,7 @@ exports[`it renders ToolBarPagerText without crashing 1`] = `
1–2 of 3 diff --git a/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx index eafa1e8fe59b2..d825220163165 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; interface Props { hasPreviousPage: boolean; @@ -18,35 +19,41 @@ interface Props { export function ToolBarPagerButtons(props: Props) { return ( -
- - -
+ + + props.onPagePrevious()} + isDisabled={!props.hasPreviousPage} + data-test-subj="btnPrevPage" + aria-label={i18n.translate( + 'discover.docTable.pager.toolbarPagerButtons.previousButtonAriaLabel', + { + defaultMessage: 'Previous page in table', + } + )} + /> + + + props.onPageNext()} + isDisabled={!props.hasNextPage} + data-test-subj="btnNextPage" + aria-label={i18n.translate( + 'discover.docTable.pager.toolbarPagerButtons.nextButtonAriaLabel', + { + defaultMessage: 'Next page in table', + } + )} + /> + + ); } diff --git a/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_text.scss b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_text.scss new file mode 100644 index 0000000000000..446e852f51e05 --- /dev/null +++ b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_text.scss @@ -0,0 +1,5 @@ +.kbnDocTable__toolBarText { + line-height: $euiLineHeight; + color: #69707D; + white-space: nowrap; +} diff --git a/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_text.tsx b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_text.tsx index 131950cb6391e..5db68952b69ca 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_text.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/components/pager/tool_bar_pager_text.tsx @@ -7,6 +7,7 @@ */ import React from 'react'; +import './tool_bar_pager_text.scss'; import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; interface Props { @@ -18,7 +19,7 @@ interface Props { export function ToolBarPagerText({ startItem, endItem, totalItems }: Props) { return ( -
+
Time @@ -20,9 +21,18 @@ exports[`TableHeader with time column renders correctly 1`] = ` > @@ -30,6 +40,7 @@ exports[`TableHeader with time column renders correctly 1`] = ` data-test-subj="docTableHeaderField" > first @@ -38,18 +49,36 @@ exports[`TableHeader with time column renders correctly 1`] = ` > @@ -57,6 +86,7 @@ exports[`TableHeader with time column renders correctly 1`] = ` data-test-subj="docTableHeaderField" > middle @@ -65,27 +95,54 @@ exports[`TableHeader with time column renders correctly 1`] = ` > @@ -93,6 +150,7 @@ exports[`TableHeader with time column renders correctly 1`] = ` data-test-subj="docTableHeaderField" > last @@ -101,18 +159,36 @@ exports[`TableHeader with time column renders correctly 1`] = ` > @@ -131,6 +207,7 @@ exports[`TableHeader without time column renders correctly 1`] = ` data-test-subj="docTableHeaderField" > first @@ -139,18 +216,36 @@ exports[`TableHeader without time column renders correctly 1`] = ` > @@ -158,6 +253,7 @@ exports[`TableHeader without time column renders correctly 1`] = ` data-test-subj="docTableHeaderField" > middle @@ -166,27 +262,54 @@ exports[`TableHeader without time column renders correctly 1`] = ` > @@ -194,6 +317,7 @@ exports[`TableHeader without time column renders correctly 1`] = ` data-test-subj="docTableHeaderField" > last @@ -202,18 +326,36 @@ exports[`TableHeader without time column renders correctly 1`] = ` > diff --git a/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header_column.tsx b/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header_column.tsx index 6d69d2e5029f2..e4cbac052ca67 100644 --- a/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header_column.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/components/table_header/table_header_column.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiToolTip } from '@elastic/eui'; +import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import { SortOrder } from './helpers'; interface Props { @@ -24,12 +24,29 @@ interface Props { sortOrder: SortOrder[]; } -const sortDirectionToIcon: Record = { - desc: 'fa fa-sort-down', - asc: 'fa fa-sort-up', - '': 'fa fa-sort', +interface IconProps { + iconType: string; + color: 'primary' | 'text'; +} + +interface IconButtonProps { + active: boolean; + ariaLabel: string; + className: string; + iconProps: IconProps; + onClick: () => void | undefined; + testSubject: string; + tooltip: string; +} + +const sortDirectionToIcon: Record = { + desc: { iconType: 'sortDown', color: 'primary' }, + asc: { iconType: 'sortUp', color: 'primary' }, + '': { iconType: 'sortable', color: 'text' }, }; +const ICON_BUTTON_STYLE = { width: 12, height: 12 }; + export function TableHeaderColumn({ colLeftIdx, colRightIdx, @@ -43,28 +60,24 @@ export function TableHeaderColumn({ sortOrder, }: Props) { const [, sortDirection = ''] = sortOrder.find((sortPair) => name === sortPair[0]) || []; - const currentSortWithoutColumn = sortOrder.filter((pair) => pair[0] !== name); - const currentColumnSort = sortOrder.find((pair) => pair[0] === name); - const currentColumnSortDirection = (currentColumnSort && currentColumnSort[1]) || ''; - - const btnSortIcon = sortDirectionToIcon[sortDirection]; - const btnSortClassName = - sortDirection !== '' ? btnSortIcon : `kbnDocTableHeader__sortChange ${btnSortIcon}`; + const curSortWithoutCol = sortOrder.filter((pair) => pair[0] !== name); + const curColSort = sortOrder.find((pair) => pair[0] === name); + const curColSortDir = (curColSort && curColSort[1]) || ''; const handleChangeSortOrder = () => { if (!onChangeSortOrder) return; // Cycle goes Unsorted -> Asc -> Desc -> Unsorted - if (currentColumnSort === undefined) { - onChangeSortOrder([...currentSortWithoutColumn, [name, 'asc']]); - } else if (currentColumnSortDirection === 'asc') { - onChangeSortOrder([...currentSortWithoutColumn, [name, 'desc']]); - } else if (currentColumnSortDirection === 'desc' && currentSortWithoutColumn.length === 0) { + if (curColSort === undefined) { + onChangeSortOrder([...curSortWithoutCol, [name, 'asc']]); + } else if (curColSortDir === 'asc') { + onChangeSortOrder([...curSortWithoutCol, [name, 'desc']]); + } else if (curColSortDir === 'desc' && curSortWithoutCol.length === 0) { // If we're at the end of the cycle and this is the only existing sort, we switch // back to ascending sort instead of removing it. onChangeSortOrder([[name, 'asc']]); } else { - onChangeSortOrder(currentSortWithoutColumn); + onChangeSortOrder(curSortWithoutCol); } }; @@ -91,11 +104,11 @@ export function TableHeaderColumn({ } ); - if (currentColumnSort === undefined) { + if (curColSort === undefined) { return sortAscendingMessage; } else if (sortDirection === 'asc') { return sortDescendingMessage; - } else if (sortDirection === 'desc' && currentSortWithoutColumn.length === 0) { + } else if (sortDirection === 'desc' && curSortWithoutCol.length === 0) { return sortAscendingMessage; } else { return stopSortingMessage; @@ -103,12 +116,13 @@ export function TableHeaderColumn({ }; // action buttons displayed on the right side of the column name - const buttons = [ + const buttons: IconButtonProps[] = [ // Sort Button { active: isSortable && typeof onChangeSortOrder === 'function', ariaLabel: getSortButtonAriaLabel(), - className: btnSortClassName, + className: !sortDirection ? 'kbnDocTableHeader__sortChange' : '', + iconProps: sortDirectionToIcon[sortDirection], onClick: handleChangeSortOrder, testSubject: `docTableHeaderFieldSort_${name}`, tooltip: getSortButtonAriaLabel(), @@ -120,7 +134,8 @@ export function TableHeaderColumn({ defaultMessage: 'Remove {columnName} column', values: { columnName: name }, }), - className: 'fa fa-remove kbnDocTableHeader__move', + className: 'kbnDocTableHeader__move', + iconProps: { iconType: 'cross', color: 'text' }, onClick: () => onRemoveColumn && onRemoveColumn(name), testSubject: `docTableRemoveHeader-${name}`, tooltip: i18n.translate('discover.docTable.tableHeader.removeColumnButtonTooltip', { @@ -134,7 +149,8 @@ export function TableHeaderColumn({ defaultMessage: 'Move {columnName} column to the left', values: { columnName: name }, }), - className: 'fa fa-angle-double-left kbnDocTableHeader__move', + className: 'kbnDocTableHeader__move', + iconProps: { iconType: 'sortLeft', color: 'text' }, onClick: () => onMoveColumn && onMoveColumn(name, colLeftIdx), testSubject: `docTableMoveLeftHeader-${name}`, tooltip: i18n.translate('discover.docTable.tableHeader.moveColumnLeftButtonTooltip', { @@ -148,7 +164,8 @@ export function TableHeaderColumn({ defaultMessage: 'Move {columnName} column to the right', values: { columnName: name }, }), - className: 'fa fa-angle-double-right kbnDocTableHeader__move', + className: 'kbnDocTableHeader__move', + iconProps: { iconType: 'sortRight', color: 'text' }, onClick: () => onMoveColumn && onMoveColumn(name, colRightIdx), testSubject: `docTableMoveRightHeader-${name}`, tooltip: i18n.translate('discover.docTable.tableHeader.moveColumnRightButtonTooltip', { @@ -159,7 +176,7 @@ export function TableHeaderColumn({ return ( - + {displayName} {buttons .filter((button) => button.active) @@ -169,11 +186,14 @@ export function TableHeaderColumn({ content={button.tooltip} key={`button-${idx}`} > -
- - ", - } - } - /> + > + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
diff --git a/x-pack/plugins/canvas/public/components/shape_picker/__stories__/shape_picker.stories.tsx b/x-pack/plugins/canvas/public/components/shape_picker/__stories__/shape_picker.stories.tsx index f6357e3976c23..5fdea5591f538 100644 --- a/x-pack/plugins/canvas/public/components/shape_picker/__stories__/shape_picker.stories.tsx +++ b/x-pack/plugins/canvas/public/components/shape_picker/__stories__/shape_picker.stories.tsx @@ -9,9 +9,8 @@ import { action } from '@storybook/addon-actions'; import { storiesOf } from '@storybook/react'; import React from 'react'; import { ShapePicker } from '../shape_picker'; - -import { shapes } from '../../../../canvas_plugin_src/renderers/shape/shapes'; +import { getAvailableShapes } from '../../../../../../../src/plugins/expression_shape/common'; storiesOf('components/Shapes/ShapePicker', module).add('default', () => ( - + )); diff --git a/x-pack/plugins/canvas/public/components/shape_picker/shape_picker.tsx b/x-pack/plugins/canvas/public/components/shape_picker/shape_picker.tsx index 78cd989543ca9..e06a199f85fee 100644 --- a/x-pack/plugins/canvas/public/components/shape_picker/shape_picker.tsx +++ b/x-pack/plugins/canvas/public/components/shape_picker/shape_picker.tsx @@ -9,29 +9,24 @@ import React, { FC } from 'react'; import PropTypes from 'prop-types'; import { EuiFlexGrid, EuiFlexItem, EuiLink } from '@elastic/eui'; import { ShapePreview } from '../shape_preview'; +import { Shape } from '../../../../../../src/plugins/expression_shape/common'; interface Props { - shapes: { - [key: string]: string; - }; + shapes: Shape[]; onChange?: (key: string) => void; } -export const ShapePicker: FC = ({ shapes, onChange = () => {} }) => { - return ( - - {Object.keys(shapes) - .sort() - .map((shapeKey) => ( - - onChange(shapeKey)}> - - - - ))} - - ); -}; +export const ShapePicker: FC = ({ shapes, onChange = () => {} }) => ( + + {shapes.sort().map((shapeKey) => ( + + onChange(shapeKey)}> + + + + ))} + +); ShapePicker.propTypes = { onChange: PropTypes.func, diff --git a/x-pack/plugins/canvas/public/components/shape_picker_popover/__stories__/__snapshots__/shape_picker_popover.stories.storyshot b/x-pack/plugins/canvas/public/components/shape_picker_popover/__stories__/__snapshots__/shape_picker_popover.stories.storyshot index aca6e9770573c..247616e842956 100644 --- a/x-pack/plugins/canvas/public/components/shape_picker_popover/__stories__/__snapshots__/shape_picker_popover.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/shape_picker_popover/__stories__/__snapshots__/shape_picker_popover.stories.storyshot @@ -53,14 +53,21 @@ exports[`Storyshots components/Shapes/ShapePickerPopover interactive 1`] = ` >
- - ", - } - } - /> + > + + + +
@@ -90,14 +97,21 @@ exports[`Storyshots components/Shapes/ShapePickerPopover shape selected 1`] = ` >
- - ", - } - } - /> + > + + + +
diff --git a/x-pack/plugins/canvas/public/components/shape_picker_popover/__stories__/shape_picker_popover.stories.tsx b/x-pack/plugins/canvas/public/components/shape_picker_popover/__stories__/shape_picker_popover.stories.tsx index f91f509318b47..babc03b8f6763 100644 --- a/x-pack/plugins/canvas/public/components/shape_picker_popover/__stories__/shape_picker_popover.stories.tsx +++ b/x-pack/plugins/canvas/public/components/shape_picker_popover/__stories__/shape_picker_popover.stories.tsx @@ -9,18 +9,20 @@ import { action } from '@storybook/addon-actions'; import { storiesOf } from '@storybook/react'; import React from 'react'; import { ShapePickerPopover } from '../shape_picker_popover'; - -import { shapes } from '../../../../canvas_plugin_src/renderers/shape/shapes'; +import { + getAvailableShapes, + Shape, +} from '../../../../../../../src/plugins/expression_shape/common'; class Interactive extends React.Component<{}, { value: string }> { public state = { - value: 'square', + value: Shape.SQUARE, }; public render() { return ( this.setState({ value })} value={this.state.value} /> @@ -29,9 +31,15 @@ class Interactive extends React.Component<{}, { value: string }> { } storiesOf('components/Shapes/ShapePickerPopover', module) - .add('default', () => ) + .add('default', () => ( + + )) .add('shape selected', () => ( - + )) .add('interactive', () => , { info: { diff --git a/x-pack/plugins/canvas/public/components/shape_picker_popover/shape_picker_popover.tsx b/x-pack/plugins/canvas/public/components/shape_picker_popover/shape_picker_popover.tsx index cb3556c2c0fef..5701c3cb1e799 100644 --- a/x-pack/plugins/canvas/public/components/shape_picker_popover/shape_picker_popover.tsx +++ b/x-pack/plugins/canvas/public/components/shape_picker_popover/shape_picker_popover.tsx @@ -11,13 +11,12 @@ import { EuiLink, EuiPanel } from '@elastic/eui'; import { Popover } from '../popover'; import { ShapePicker } from '../shape_picker'; import { ShapePreview } from '../shape_preview'; +import { Shape } from '../../../../../../src/plugins/expression_shape/common'; interface Props { - shapes: { - [key: string]: string; - }; + shapes: Shape[]; onChange?: (key: string) => void; - value?: string; + value?: Shape; ariaLabel?: string; } @@ -25,7 +24,7 @@ export const ShapePickerPopover: FC = ({ shapes, onChange, value, ariaLab const button = (handleClick: React.MouseEventHandler) => ( - + ); diff --git a/x-pack/plugins/canvas/public/components/shape_preview/__stories__/__snapshots__/shape_preview.stories.storyshot b/x-pack/plugins/canvas/public/components/shape_preview/__stories__/__snapshots__/shape_preview.stories.storyshot index 8c7ff2e92b86d..747938d33f532 100644 --- a/x-pack/plugins/canvas/public/components/shape_preview/__stories__/__snapshots__/shape_preview.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/shape_preview/__stories__/__snapshots__/shape_preview.stories.storyshot @@ -3,25 +3,36 @@ exports[`Storyshots components/Shapes/ShapePreview arrow 1`] = `
- - ", - } - } -/> +> + + + +
`; exports[`Storyshots components/Shapes/ShapePreview square 1`] = `
- - ", - } - } -/> +> + + + +
`; diff --git a/x-pack/plugins/canvas/public/components/shape_preview/__stories__/shape_preview.stories.tsx b/x-pack/plugins/canvas/public/components/shape_preview/__stories__/shape_preview.stories.tsx index 1a135bae1c096..de7ce4b411860 100644 --- a/x-pack/plugins/canvas/public/components/shape_preview/__stories__/shape_preview.stories.tsx +++ b/x-pack/plugins/canvas/public/components/shape_preview/__stories__/shape_preview.stories.tsx @@ -8,9 +8,8 @@ import { storiesOf } from '@storybook/react'; import React from 'react'; import { ShapePreview } from '../shape_preview'; - -import { shapes } from '../../../../canvas_plugin_src/renderers/shape/shapes'; +import { Shape } from '../../../../../../../src/plugins/expression_shape/public'; storiesOf('components/Shapes/ShapePreview', module) - .add('arrow', () => ) - .add('square', () => ); + .add('arrow', () => ) + .add('square', () => ); diff --git a/x-pack/plugins/canvas/public/components/shape_preview/shape_preview.tsx b/x-pack/plugins/canvas/public/components/shape_preview/shape_preview.tsx index 7fbb28b771f39..22d0d8cca84ef 100644 --- a/x-pack/plugins/canvas/public/components/shape_preview/shape_preview.tsx +++ b/x-pack/plugins/canvas/public/components/shape_preview/shape_preview.tsx @@ -5,45 +5,55 @@ * 2.0. */ -import React, { FC } from 'react'; +import React, { FC, RefCallback, useCallback, useState } from 'react'; import PropTypes from 'prop-types'; +import { + LazyShapeDrawer, + Shape, + ShapeDrawerComponentProps, + getDefaultShapeData, + SvgConfig, + ShapeRef, + ViewBoxParams, +} from '../../../../../../src/plugins/expression_shape/public'; +import { withSuspense } from '../../../../../../src/plugins/presentation_util/public'; interface Props { - shape?: string; + shape?: Shape; +} + +const ShapeDrawer = withSuspense(LazyShapeDrawer); + +function getViewBox(defaultWidth: number, defaultViewBox: ViewBoxParams): ViewBoxParams { + const { minX, minY, width, height } = defaultViewBox; + return { + minX: minX - defaultWidth / 2, + minY: minY - defaultWidth / 2, + width: width + defaultWidth, + height: height + defaultWidth, + }; } export const ShapePreview: FC = ({ shape }) => { - if (!shape) { - return
; - } - - const weight = 5; - const parser = new DOMParser(); - const shapeSvg = parser - .parseFromString(shape, 'image/svg+xml') - .getElementsByTagName('svg') - .item(0); - - if (!shapeSvg) { - throw new Error('An unexpected error occurred: the SVG was not parseable'); - } - - shapeSvg.setAttribute('fill', 'none'); - shapeSvg.setAttribute('stroke', 'black'); - - const viewBox = shapeSvg.getAttribute('viewBox') || '0 0 0 0'; - const initialViewBox = viewBox.split(' ').map((v: string) => parseInt(v, 10)); - - let [minX, minY, width, height] = initialViewBox; - minX -= weight / 2; - minY -= weight / 2; - width += weight; - height += weight; - shapeSvg.setAttribute('viewBox', [minX, minY, width, height].join(' ')); + const [shapeData, setShapeData] = useState(getDefaultShapeData()); + + const shapeRef = useCallback>((node) => { + if (node !== null) setShapeData(node.getData()); + }, []); + if (!shape) return
; return ( - // eslint-disable-next-line react/no-danger -
+
+ +
); }; diff --git a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js index df894a65afab1..bb5880b7f40a9 100644 --- a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js +++ b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js @@ -12,7 +12,6 @@ import { metric } from '../canvas_plugin_src/renderers/metric'; import { pie } from '../canvas_plugin_src/renderers/pie'; import { plot } from '../canvas_plugin_src/renderers/plot'; import { progress } from '../canvas_plugin_src/renderers/progress'; -import { shape } from '../canvas_plugin_src/renderers/shape'; import { table } from '../canvas_plugin_src/renderers/table'; import { text } from '../canvas_plugin_src/renderers/text'; import { revealImageRenderer as revealImage } from '../../../../src/plugins/expression_reveal_image/public'; @@ -20,6 +19,7 @@ import { errorRenderer as error, debugRenderer as debug, } from '../../../../src/plugins/expression_error/public'; +import { shapeRenderer as shape } from '../../../../src/plugins/expression_shape/public'; /** * This is a collection of renderers which are bundled with the runtime. If diff --git a/x-pack/plugins/canvas/storybook/storyshots.test.tsx b/x-pack/plugins/canvas/storybook/storyshots.test.tsx index 643bd1dc22041..f99a1ed5d4335 100644 --- a/x-pack/plugins/canvas/storybook/storyshots.test.tsx +++ b/x-pack/plugins/canvas/storybook/storyshots.test.tsx @@ -39,19 +39,6 @@ jest.mock('../public/lib/ui_metric', () => ({ trackCanvasUiMetric: () => {} })); // Mock EUI generated ids to be consistently predictable for snapshots. jest.mock(`@elastic/eui/lib/components/form/form_row/make_id`, () => () => `generated-id`); -// Jest automatically mocks SVGs to be a plain-text string that isn't an SVG. Canvas uses -// them in examples, so let's mock a few for tests. -jest.mock('../canvas_plugin_src/renderers/shape/shapes', () => ({ - shapes: { - arrow: ` - - `, - square: ` - - `, - }, -})); - // Mock react-datepicker dep used by eui to avoid rendering the entire large component jest.mock('@elastic/eui/packages/react-datepicker', () => { return { diff --git a/x-pack/plugins/canvas/tsconfig.json b/x-pack/plugins/canvas/tsconfig.json index bf9544a173f16..8c97a78da7f0f 100644 --- a/x-pack/plugins/canvas/tsconfig.json +++ b/x-pack/plugins/canvas/tsconfig.json @@ -33,6 +33,7 @@ { "path": "../../../src/plugins/expressions/tsconfig.json" }, { "path": "../../../src/plugins/expression_error/tsconfig.json" }, { "path": "../../../src/plugins/expression_reveal_image/tsconfig.json" }, + { "path": "../../../src/plugins/expression_shape/tsconfig.json" }, { "path": "../../../src/plugins/home/tsconfig.json" }, { "path": "../../../src/plugins/inspector/tsconfig.json" }, { "path": "../../../src/plugins/kibana_legacy/tsconfig.json" }, diff --git a/x-pack/plugins/data_enhanced/public/plugin.ts b/x-pack/plugins/data_enhanced/public/plugin.ts index ebc270e96767e..a222f7c734a1e 100644 --- a/x-pack/plugins/data_enhanced/public/plugin.ts +++ b/x-pack/plugins/data_enhanced/public/plugin.ts @@ -51,7 +51,12 @@ export class DataEnhancedPlugin this.config = this.initializerContext.config.get(); if (this.config.search.sessions.enabled) { const sessionsConfig = this.config.search.sessions; - registerSearchSessionsMgmt(core, sessionsConfig, { data, management }); + registerSearchSessionsMgmt( + core, + sessionsConfig, + this.initializerContext.env.packageInfo.version, + { data, management } + ); } this.usageCollector = data.search.usageCollector; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx index 2dfca534c20b5..a2d51d7d21248 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx @@ -22,6 +22,7 @@ export class SearchSessionsMgmtApp { constructor( private coreSetup: CoreSetup, private config: SessionsConfigSchema, + private kibanaVersion: string, private params: ManagementAppMountParams, private pluginsSetup: IManagementSectionsPluginsSetup ) {} @@ -65,6 +66,7 @@ export class SearchSessionsMgmtApp { i18n, uiSettings, share, + kibanaVersion: this.kibanaVersion, }; const { element } = params; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx index 13dfe12ccb5e4..4c945e717464c 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx @@ -82,6 +82,7 @@ describe('Background Search Session Management Main', () => { timezone="UTC" documentation={new AsyncSearchIntroDocumentation(docLinks)} config={mockConfig} + kibanaVersion={'8.0.0'} /> ); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx index c398c92abf3a9..e82ab2bbb7043 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx @@ -23,6 +23,7 @@ interface Props { timezone: string; config: SessionsConfigSchema; plugins: IManagementSectionsPluginsSetup; + kibanaVersion: string; } export function SearchSessionsMgmtMain({ documentation, ...tableProps }: Props) { diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/status.test.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/status.test.tsx index 59da0f0f4d17e..4711ff975fbd7 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/status.test.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/status.test.tsx @@ -34,6 +34,7 @@ describe('Background Search Session management status labels', () => { expires: '2020-12-07T00:19:32Z', initialState: {}, restoreState: {}, + version: '8.0.0', }; }); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx index 6dfe3a5153670..b122155d8c93a 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx @@ -91,6 +91,7 @@ describe('Background Search Session Management Table', () => { api={api} timezone="UTC" config={mockConfig} + kibanaVersion={'8.0.0'} /> ); @@ -123,6 +124,7 @@ describe('Background Search Session Management Table', () => { api={api} timezone="UTC" config={mockConfig} + kibanaVersion={'8.0.0'} /> ); @@ -132,7 +134,7 @@ describe('Background Search Session Management Table', () => { expect(table.find('tbody td').map((node) => node.text())).toMatchInlineSnapshot(` Array [ "App", - "Namevery background search ", + "Namevery background search ", "# Searches0", "StatusExpired", "Created2 Dec, 2020, 00:19:32", @@ -166,6 +168,7 @@ describe('Background Search Session Management Table', () => { api={api} timezone="UTC" config={mockConfig} + kibanaVersion={'8.0.0'} /> ); @@ -199,6 +202,7 @@ describe('Background Search Session Management Table', () => { api={api} timezone="UTC" config={mockConfig} + kibanaVersion={'8.0.0'} /> ); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.tsx index 962ad5a91efb0..803a5f9c10273 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.tsx @@ -28,9 +28,18 @@ interface Props { timezone: string; config: SessionsConfigSchema; plugins: IManagementSectionsPluginsSetup; + kibanaVersion: string; } -export function SearchSessionsMgmtTable({ core, api, timezone, config, plugins, ...props }: Props) { +export function SearchSessionsMgmtTable({ + core, + api, + timezone, + config, + plugins, + kibanaVersion, + ...props +}: Props) { const [tableData, setTableData] = useState([]); const [isLoading, setIsLoading] = useState(false); const [debouncedIsLoading, setDebouncedIsLoading] = useState(false); @@ -111,7 +120,7 @@ export function SearchSessionsMgmtTable({ core, api, timezone, config, plugins, rowProps={() => ({ 'data-test-subj': 'searchSessionsRow', })} - columns={getColumns(core, plugins, api, config, timezone, onActionComplete)} + columns={getColumns(core, plugins, api, config, timezone, onActionComplete, kibanaVersion)} items={tableData} pagination={pagination} search={search} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts index 0ac8fa798cc92..87d3792df2fb7 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts @@ -37,6 +37,7 @@ export interface AppDependencies { http: HttpStart; i18n: I18nStart; config: SessionsConfigSchema; + kibanaVersion: string; } export const APP = { @@ -52,6 +53,7 @@ export type SessionsConfigSchema = ConfigSchema['search']['sessions']; export function registerSearchSessionsMgmt( coreSetup: CoreSetup, config: SessionsConfigSchema, + kibanaVersion: string, services: IManagementSectionsPluginsSetup ) { services.management.sections.section.kibana.registerApp({ @@ -60,7 +62,7 @@ export function registerSearchSessionsMgmt( order: 1.75, mount: async (params) => { const { SearchSessionsMgmtApp: MgmtApp } = await import('./application'); - const mgmtApp = new MgmtApp(coreSetup, config, params, services); + const mgmtApp = new MgmtApp(coreSetup, config, kibanaVersion, params, services); return mgmtApp.mountManagementSection(); }, }); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts index cc79f8002a98c..a3bc3b51f61bd 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts @@ -84,6 +84,7 @@ describe('Search Sessions Management API', () => { "restoreState": Object {}, "restoreUrl": "hello-cool-undefined-url", "status": "complete", + "version": undefined, }, ] `); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts index 0369dc4a839b5..eb38d47c26ffb 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts @@ -91,6 +91,7 @@ const mapToUISession = (urls: UrlGeneratorsStart, config: SessionsConfigSchema) initialState, restoreState, idMapping, + version, } = savedObject.attributes; const status = getUIStatus(savedObject.attributes); @@ -115,6 +116,7 @@ const mapToUISession = (urls: UrlGeneratorsStart, config: SessionsConfigSchema) initialState, restoreState, numSearches: Object.keys(idMapping).length, + version, }; }; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.test.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.test.tsx index fc4e67360ea4a..f46ded96d877c 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.test.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.test.tsx @@ -76,11 +76,20 @@ describe('Search Sessions Management table column factory', () => { expires: '2020-12-07T00:19:32Z', initialState: {}, restoreState: {}, + version: '7.14.0', }; }); test('returns columns', () => { - const columns = getColumns(mockCoreStart, mockPluginsSetup, api, mockConfig, tz, handleAction); + const columns = getColumns( + mockCoreStart, + mockPluginsSetup, + api, + mockConfig, + tz, + handleAction, + '7.14.0' + ); expect(columns).toMatchInlineSnapshot(` Array [ Object { @@ -144,7 +153,8 @@ describe('Search Sessions Management table column factory', () => { api, mockConfig, tz, - handleAction + handleAction, + '7.14.0' ) as Array>; const name = mount(nameColumn.render!(mockSession.name, mockSession) as ReactElement); @@ -162,7 +172,8 @@ describe('Search Sessions Management table column factory', () => { api, mockConfig, tz, - handleAction + handleAction, + '7.14.0' ) as Array>; const numOfSearchesLine = mount( @@ -181,7 +192,8 @@ describe('Search Sessions Management table column factory', () => { api, mockConfig, tz, - handleAction + handleAction, + '7.14.0' ) as Array>; const statusLine = mount(status.render!(mockSession.status, mockSession) as ReactElement); @@ -197,7 +209,8 @@ describe('Search Sessions Management table column factory', () => { api, mockConfig, tz, - handleAction + handleAction, + '7.14.0' ) as Array>; mockSession.status = 'INVALID' as SearchSessionStatus; @@ -220,7 +233,8 @@ describe('Search Sessions Management table column factory', () => { api, mockConfig, tz, - handleAction + handleAction, + '7.14.0' ) as Array>; const date = mount(createdDateCol.render!(mockSession.created, mockSession) as ReactElement); @@ -237,7 +251,8 @@ describe('Search Sessions Management table column factory', () => { api, mockConfig, tz, - handleAction + handleAction, + '7.14.0' ) as Array>; const date = mount(createdDateCol.render!(mockSession.created, mockSession) as ReactElement); @@ -252,7 +267,8 @@ describe('Search Sessions Management table column factory', () => { api, mockConfig, tz, - handleAction + handleAction, + '7.14.0' ) as Array>; mockSession.created = 'INVALID'; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.tsx index 94033a2536a87..9583fc4b843a9 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.tsx @@ -49,7 +49,8 @@ export const getColumns = ( api: SearchSessionsMgmtAPI, config: SessionsConfigSchema, timezone: string, - onActionComplete: OnActionComplete + onActionComplete: OnActionComplete, + kibanaVersion: string ): Array> => { // Use a literal array of table column definitions to detail a UISession object return [ @@ -82,7 +83,7 @@ export const getColumns = ( }), sortable: true, width: '20%', - render: (name: UISession['name'], { restoreUrl, reloadUrl, status }) => { + render: (name: UISession['name'], { restoreUrl, reloadUrl, status, version }) => { const isRestorable = isSessionRestorable(status); const href = isRestorable ? restoreUrl : reloadUrl; const trackAction = isRestorable @@ -102,6 +103,21 @@ export const getColumns = ( /> ); + const versionIncompatibleWarning = + isRestorable && version === kibanaVersion ? null : ( + <> + {' '} + + } + /> + + ); return ( {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} @@ -113,6 +129,7 @@ export const getColumns = ( {name} {notRestorableWarning} + {versionIncompatibleWarning} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts index 6a8ace8dbdc79..f4f928e67e19c 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts @@ -40,4 +40,5 @@ export interface UISession { restoreUrl: string; initialState: Record; restoreState: Record; + version: string; } diff --git a/x-pack/plugins/data_enhanced/server/plugin.ts b/x-pack/plugins/data_enhanced/server/plugin.ts index a1ce3709e51a7..ce98cf06a9dfe 100644 --- a/x-pack/plugins/data_enhanced/server/plugin.ts +++ b/x-pack/plugins/data_enhanced/server/plugin.ts @@ -31,7 +31,12 @@ export class EnhancedDataServerPlugin public setup(core: CoreSetup, deps: SetupDependencies) { core.savedObjects.registerType(searchSessionSavedObjectType); - this.sessionService = new SearchSessionService(this.logger, this.config, deps.security); + this.sessionService = new SearchSessionService( + this.logger, + this.config, + this.initializerContext.env.packageInfo.version, + deps.security + ); deps.data.__enhance({ search: { diff --git a/x-pack/plugins/data_enhanced/server/saved_objects/search_session.ts b/x-pack/plugins/data_enhanced/server/saved_objects/search_session.ts index 9b5af9a6fa9e8..9a359679c0e7a 100644 --- a/x-pack/plugins/data_enhanced/server/saved_objects/search_session.ts +++ b/x-pack/plugins/data_enhanced/server/saved_objects/search_session.ts @@ -66,6 +66,9 @@ export const searchSessionSavedObjectType: SavedObjectsType = { username: { type: 'keyword', }, + version: { + type: 'keyword', + }, }, }, migrations: searchSessionSavedObjectMigrations, diff --git a/x-pack/plugins/data_enhanced/server/saved_objects/search_session_migration.test.ts b/x-pack/plugins/data_enhanced/server/saved_objects/search_session_migration.test.ts index 6682122c66f9c..a04a7a4d3302f 100644 --- a/x-pack/plugins/data_enhanced/server/saved_objects/search_session_migration.test.ts +++ b/x-pack/plugins/data_enhanced/server/saved_objects/search_session_migration.test.ts @@ -8,58 +8,59 @@ import { searchSessionSavedObjectMigrations, SearchSessionSavedObjectAttributesPre$7$13$0, + SearchSessionSavedObjectAttributesPre$7$14$0, } from './search_session_migration'; import { SavedObject } from '../../../../../src/core/types'; import { SEARCH_SESSION_TYPE, SearchSessionStatus } from '../../../../../src/plugins/data/common'; import { SavedObjectMigrationContext } from 'kibana/server'; -const mockCompletedSessionSavedObject: SavedObject = { - id: 'id', - type: SEARCH_SESSION_TYPE, - attributes: { - name: 'my_name', - appId: 'my_app_id', - sessionId: 'sessionId', - urlGeneratorId: 'my_url_generator_id', - initialState: {}, - restoreState: {}, - persisted: true, - idMapping: {}, - realmType: 'realmType', - realmName: 'realmName', - username: 'username', - created: '2021-03-26T00:00:00.000Z', - expires: '2021-03-30T00:00:00.000Z', - touched: '2021-03-29T00:00:00.000Z', - status: SearchSessionStatus.COMPLETE, - }, - references: [], -}; +describe('7.12.0 -> 7.13.0', () => { + const mockCompletedSessionSavedObject: SavedObject = { + id: 'id', + type: SEARCH_SESSION_TYPE, + attributes: { + name: 'my_name', + appId: 'my_app_id', + sessionId: 'sessionId', + urlGeneratorId: 'my_url_generator_id', + initialState: {}, + restoreState: {}, + persisted: true, + idMapping: {}, + realmType: 'realmType', + realmName: 'realmName', + username: 'username', + created: '2021-03-26T00:00:00.000Z', + expires: '2021-03-30T00:00:00.000Z', + touched: '2021-03-29T00:00:00.000Z', + status: SearchSessionStatus.COMPLETE, + }, + references: [], + }; -const mockInProgressSessionSavedObject: SavedObject = { - id: 'id', - type: SEARCH_SESSION_TYPE, - attributes: { - name: 'my_name', - appId: 'my_app_id', - sessionId: 'sessionId', - urlGeneratorId: 'my_url_generator_id', - initialState: {}, - restoreState: {}, - persisted: true, - idMapping: {}, - realmType: 'realmType', - realmName: 'realmName', - username: 'username', - created: '2021-03-26T00:00:00.000Z', - expires: '2021-03-30T00:00:00.000Z', - touched: '2021-03-29T00:00:00.000Z', - status: SearchSessionStatus.IN_PROGRESS, - }, - references: [], -}; + const mockInProgressSessionSavedObject: SavedObject = { + id: 'id', + type: SEARCH_SESSION_TYPE, + attributes: { + name: 'my_name', + appId: 'my_app_id', + sessionId: 'sessionId', + urlGeneratorId: 'my_url_generator_id', + initialState: {}, + restoreState: {}, + persisted: true, + idMapping: {}, + realmType: 'realmType', + realmName: 'realmName', + username: 'username', + created: '2021-03-26T00:00:00.000Z', + expires: '2021-03-30T00:00:00.000Z', + touched: '2021-03-29T00:00:00.000Z', + status: SearchSessionStatus.IN_PROGRESS, + }, + references: [], + }; -describe('7.12.0 -> 7.13.0', () => { const migration = searchSessionSavedObjectMigrations['7.13.0']; test('"completed" is populated from "touched" for completed session', () => { const migratedCompletedSession = migration( @@ -106,3 +107,58 @@ describe('7.12.0 -> 7.13.0', () => { ); }); }); + +describe('7.13.0 -> 7.14.0', () => { + const mockSessionSavedObject: SavedObject = { + id: 'id', + type: SEARCH_SESSION_TYPE, + attributes: { + name: 'my_name', + appId: 'my_app_id', + sessionId: 'sessionId', + urlGeneratorId: 'my_url_generator_id', + initialState: {}, + restoreState: {}, + persisted: true, + idMapping: {}, + realmType: 'realmType', + realmName: 'realmName', + username: 'username', + created: '2021-03-26T00:00:00.000Z', + expires: '2021-03-30T00:00:00.000Z', + touched: '2021-03-29T00:00:00.000Z', + completed: '2021-03-29T00:00:00.000Z', + status: SearchSessionStatus.COMPLETE, + }, + references: [], + }; + + const migration = searchSessionSavedObjectMigrations['7.14.0']; + test('version is populated', () => { + const migratedSession = migration(mockSessionSavedObject, {} as SavedObjectMigrationContext); + + expect(migratedSession.attributes).toHaveProperty('version'); + expect(migratedSession.attributes.version).toBe('7.13.0'); + expect(migratedSession.attributes).toMatchInlineSnapshot(` + Object { + "appId": "my_app_id", + "completed": "2021-03-29T00:00:00.000Z", + "created": "2021-03-26T00:00:00.000Z", + "expires": "2021-03-30T00:00:00.000Z", + "idMapping": Object {}, + "initialState": Object {}, + "name": "my_name", + "persisted": true, + "realmName": "realmName", + "realmType": "realmType", + "restoreState": Object {}, + "sessionId": "sessionId", + "status": "complete", + "touched": "2021-03-29T00:00:00.000Z", + "urlGeneratorId": "my_url_generator_id", + "username": "username", + "version": "7.13.0", + } + `); + }); +}); diff --git a/x-pack/plugins/data_enhanced/server/saved_objects/search_session_migration.ts b/x-pack/plugins/data_enhanced/server/saved_objects/search_session_migration.ts index 0ba8858ef525b..fa1428b3a3aad 100644 --- a/x-pack/plugins/data_enhanced/server/saved_objects/search_session_migration.ts +++ b/x-pack/plugins/data_enhanced/server/saved_objects/search_session_migration.ts @@ -17,14 +17,26 @@ import { * It is a timestamp representing the session was transitioned into "completed" status. */ export type SearchSessionSavedObjectAttributesPre$7$13$0 = Omit< - SearchSessionSavedObjectAttributesLatest, + SearchSessionSavedObjectAttributesPre$7$14$0, 'completed' >; +/** + * In 7.14.0 a `version` field was added. When search session is created it is populated with current kibana version. + * It is used to display warnings when trying to restore a session from a different version + * For saved object created before 7.14.0 we populate "7.13.0" inside the migration. + * It is less then ideal because the saved object could have actually been created in "7.12.x" or "7.13.x", + * but what is important for 7.14.0 is that the version is less then "7.14.0" + */ +export type SearchSessionSavedObjectAttributesPre$7$14$0 = Omit< + SearchSessionSavedObjectAttributesLatest, + 'version' +>; + export const searchSessionSavedObjectMigrations: SavedObjectMigrationMap = { '7.13.0': ( doc: SavedObjectUnsanitizedDoc - ): SavedObjectUnsanitizedDoc => { + ): SavedObjectUnsanitizedDoc => { if (doc.attributes.status === SearchSessionStatus.COMPLETE) { return { ...doc, @@ -37,4 +49,15 @@ export const searchSessionSavedObjectMigrations: SavedObjectMigrationMap = { return doc; }, + '7.14.0': ( + doc: SavedObjectUnsanitizedDoc + ): SavedObjectUnsanitizedDoc => { + return { + ...doc, + attributes: { + ...doc.attributes, + version: '7.13.0', + }, + }; + }, }; diff --git a/x-pack/plugins/data_enhanced/server/search/session/session_service.test.ts b/x-pack/plugins/data_enhanced/server/search/session/session_service.test.ts index dd1eafa5d60f8..4b5e1a1f86a11 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/session_service.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/session_service.test.ts @@ -91,7 +91,7 @@ describe('SearchSessionService', () => { warn: jest.fn(), error: jest.fn(), }; - service = new SearchSessionService(mockLogger, config); + service = new SearchSessionService(mockLogger, config, '8.0.0'); const coreStart = coreMock.createStart(); mockTaskManager = taskManagerMock.createStart(); await flushPromises(); @@ -171,7 +171,7 @@ describe('SearchSessionService', () => { warn: jest.fn(), error: jest.fn(), }; - service = new SearchSessionService(mockLogger, config); + service = new SearchSessionService(mockLogger, config, '8.0.0'); const coreStart = coreMock.createStart(); mockTaskManager = taskManagerMock.createStart(); await flushPromises(); diff --git a/x-pack/plugins/data_enhanced/server/search/session/session_service.ts b/x-pack/plugins/data_enhanced/server/search/session/session_service.ts index af646ac3c5604..19f32860384d1 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/session_service.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/session_service.ts @@ -98,6 +98,7 @@ export class SearchSessionService constructor( private readonly logger: Logger, private readonly config: ConfigSchema, + private readonly version: string, private readonly security?: SecurityPluginSetup ) { this.sessionConfig = this.config.search.sessions; @@ -330,6 +331,7 @@ export class SearchSessionService touched: new Date().toISOString(), idMapping: {}, persisted: false, + version: this.version, realmType, realmName, username, diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index feeba68644334..16b8c419fdff8 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -30,12 +30,7 @@ "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "isInitialized": { - "type": "boolean" - } - } + "$ref": "#/components/schemas/fleet_setup_response" } } } @@ -301,15 +296,7 @@ "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "isInitialized": { - "type": "boolean" - } - }, - "required": [ - "isInitialized" - ] + "$ref": "#/components/schemas/fleet_status_response" } } } @@ -331,15 +318,7 @@ "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "isInitialized": { - "type": "boolean" - } - }, - "required": [ - "isInitialized" - ] + "$ref": "#/components/schemas/fleet_setup_response" } } } @@ -1393,6 +1372,37 @@ } }, "schemas": { + "fleet_setup_response": { + "title": "Fleet Setup response", + "type": "object", + "properties": { + "isInitialized": { + "type": "boolean" + }, + "nonFatalErrors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "name", + "message" + ] + } + } + }, + "required": [ + "isInitialized", + "nonFatalErrors" + ] + }, "search_result": { "title": "SearchResult", "type": "object", @@ -1618,6 +1628,32 @@ "path" ] }, + "fleet_status_response": { + "title": "Fleet status response", + "type": "object", + "properties": { + "isReady": { + "type": "boolean" + }, + "missing_requirements": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "tls_required", + "api_keys", + "fleet_admin_user", + "fleet_server", + "encrypted_saved_object_encryption_key_required" + ] + } + } + }, + "required": [ + "isReady", + "missing_requirements" + ] + }, "agent_type": { "type": "string", "title": "AgentType", diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index 38daf60b33e0d..c5e4a02b13574 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -23,10 +23,7 @@ paths: content: application/json: schema: - type: object - properties: - isInitialized: - type: boolean + $ref: '#/components/schemas/fleet_setup_response' '500': description: Internal Server Error content: @@ -189,12 +186,7 @@ paths: content: application/json: schema: - type: object - properties: - isInitialized: - type: boolean - required: - - isInitialized + $ref: '#/components/schemas/fleet_status_response' operationId: get-agents-setup security: - basicAuth: [] @@ -207,12 +199,7 @@ paths: content: application/json: schema: - type: object - properties: - isInitialized: - type: boolean - required: - - isInitialized + $ref: '#/components/schemas/fleet_setup_response' requestBody: content: application/json: @@ -865,6 +852,27 @@ components: schema: type: string schemas: + fleet_setup_response: + title: Fleet Setup response + type: object + properties: + isInitialized: + type: boolean + nonFatalErrors: + type: array + items: + type: object + properties: + name: + type: string + message: + type: string + required: + - name + - message + required: + - isInitialized + - nonFatalErrors search_result: title: SearchResult type: object @@ -1018,6 +1026,25 @@ components: - format_version - download - path + fleet_status_response: + title: Fleet status response + type: object + properties: + isReady: + type: boolean + missing_requirements: + type: array + items: + type: string + enum: + - tls_required + - api_keys + - fleet_admin_user + - fleet_server + - encrypted_saved_object_encryption_key_required + required: + - isReady + - missing_requirements agent_type: type: string title: AgentType diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/fleet_setup_response.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/fleet_setup_response.yaml new file mode 100644 index 0000000000000..3022c394b1433 --- /dev/null +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/fleet_setup_response.yaml @@ -0,0 +1,20 @@ +title: Fleet Setup response +type: object +properties: + isInitialized: + type: boolean + nonFatalErrors: + type: array + items: + type: object + properties: + name: + type: string + message: + type: string + required: + - name + - message +required: + - isInitialized + - nonFatalErrors diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/fleet_status_response.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/fleet_status_response.yaml new file mode 100644 index 0000000000000..0d0c3db134386 --- /dev/null +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/fleet_status_response.yaml @@ -0,0 +1,18 @@ +title: Fleet status response +type: object +properties: + isReady: + type: boolean + missing_requirements: + type: array + items: + type: string + enum: + - 'tls_required' + - 'api_keys' + - 'fleet_admin_user' + - 'fleet_server' + - 'encrypted_saved_object_encryption_key_required' +required: + - isReady + - missing_requirements diff --git a/x-pack/plugins/fleet/common/openapi/paths/agents@setup.yaml b/x-pack/plugins/fleet/common/openapi/paths/agents@setup.yaml index 87556dca0afbb..773872ae3407a 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/agents@setup.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/agents@setup.yaml @@ -7,12 +7,7 @@ get: content: application/json: schema: - type: object - properties: - isInitialized: - type: boolean - required: - - isInitialized + $ref: ../components/schemas/fleet_status_response.yaml operationId: get-agents-setup security: - basicAuth: [] @@ -25,12 +20,7 @@ post: content: application/json: schema: - type: object - properties: - isInitialized: - type: boolean - required: - - isInitialized + $ref: ../components/schemas/fleet_setup_response.yaml requestBody: content: application/json: diff --git a/x-pack/plugins/fleet/common/openapi/paths/setup.yaml b/x-pack/plugins/fleet/common/openapi/paths/setup.yaml index a157dea8b38dc..d917059442baa 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/setup.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/setup.yaml @@ -7,10 +7,7 @@ post: content: application/json: schema: - type: object - properties: - isInitialized: - type: boolean + $ref: ../components/schemas/fleet_setup_response.yaml '500': description: Internal Server Error content: diff --git a/x-pack/plugins/fleet/common/types/rest_spec/fleet_setup.ts b/x-pack/plugins/fleet/common/types/rest_spec/fleet_setup.ts index 8bde56e5451c9..a637f19423a6b 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/fleet_setup.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/fleet_setup.ts @@ -5,8 +5,9 @@ * 2.0. */ -export interface CreateFleetSetupResponse { +export interface PostFleetSetupResponse { isInitialized: boolean; + nonFatalErrors: Array<{ name: string; message: string }>; } export interface GetFleetStatusResponse { diff --git a/x-pack/plugins/fleet/common/types/rest_spec/index.ts b/x-pack/plugins/fleet/common/types/rest_spec/index.ts index 870cf3f3f1b82..3ad6df3a97303 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/index.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/index.ts @@ -13,7 +13,6 @@ export * from './agent_policy'; export * from './fleet_setup'; export * from './epm'; export * from './enrollment_api_key'; -export * from './ingest_setup'; export * from './output'; export * from './settings'; export * from './app'; diff --git a/x-pack/plugins/fleet/common/types/rest_spec/ingest_setup.ts b/x-pack/plugins/fleet/common/types/rest_spec/ingest_setup.ts deleted file mode 100644 index 91a1915c4c518..0000000000000 --- a/x-pack/plugins/fleet/common/types/rest_spec/ingest_setup.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export interface PostIngestSetupResponse { - isInitialized: boolean; - nonFatalErrors?: Array<{ error: Error }>; -} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index ee529b6865e56..65496afc1a101 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -31,6 +31,7 @@ import { sendGetOnePackagePolicy, sendGetPackageInfoByKey, } from '../../../hooks'; +import { useBreadcrumbs as useIntegrationsBreadcrumbs } from '../../../../integrations/hooks'; import { Loading, Error, ExtensionWrapper } from '../../../components'; import { ConfirmDeployAgentPolicyModal } from '../components'; import { CreatePackagePolicyPageLayout } from '../create_package_policy_page/components'; @@ -492,6 +493,6 @@ const IntegrationsBreadcrumb = memo<{ policyName: string; pkgkey: string; }>(({ pkgTitle, policyName, pkgkey }) => { - useBreadcrumbs('integration_policy_edit', { policyName, pkgTitle, pkgkey }); + useIntegrationsBreadcrumbs('integration_policy_edit', { policyName, pkgTitle, pkgkey }); return null; }); diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index f4a347c2ab3f0..0606334737a2a 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -28,7 +28,7 @@ import type { LicensingPluginSetup } from '../../licensing/public'; import type { CloudSetup } from '../../cloud/public'; import type { GlobalSearchPluginSetup } from '../../global_search/public'; import { PLUGIN_ID, INTEGRATIONS_PLUGIN_ID, setupRouteService, appRoutesService } from '../common'; -import type { CheckPermissionsResponse, PostIngestSetupResponse } from '../common'; +import type { CheckPermissionsResponse, PostFleetSetupResponse } from '../common'; import type { FleetConfigType } from '../common/types'; @@ -223,7 +223,7 @@ export class FleetPlugin implements Plugin(setupRouteService.getSetupPath()) + .post(setupRouteService.getSetupPath()) .then(({ isInitialized }) => isInitialized ? Promise.resolve(true) diff --git a/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts b/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts index 809a045478b03..0e22f544ddfa3 100644 --- a/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts @@ -7,7 +7,7 @@ import { httpServerMock } from 'src/core/server/mocks'; -import type { PostIngestSetupResponse } from '../../../common'; +import type { PostFleetSetupResponse } from '../../../common'; import { RegistryError } from '../../errors'; import { createAppContextStartContractMock, xpackMocks } from '../../mocks'; import { appContextService } from '../../services/app_context'; @@ -53,7 +53,7 @@ describe('FleetSetupHandler', () => { ); await fleetSetupHandler(context, request, response); - const expectedBody: PostIngestSetupResponse = { isInitialized: true, nonFatalErrors: [] }; + const expectedBody: PostFleetSetupResponse = { isInitialized: true, nonFatalErrors: [] }; expect(response.customError).toHaveBeenCalledTimes(0); expect(response.ok).toHaveBeenCalledWith({ body: expectedBody }); }); diff --git a/x-pack/plugins/fleet/server/routes/setup/handlers.ts b/x-pack/plugins/fleet/server/routes/setup/handlers.ts index 370196cc202cd..fe1e30f9f05d6 100644 --- a/x-pack/plugins/fleet/server/routes/setup/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/setup/handlers.ts @@ -8,7 +8,7 @@ import type { RequestHandler } from 'src/core/server'; import { appContextService } from '../../services'; -import type { GetFleetStatusResponse, PostIngestSetupResponse } from '../../../common'; +import type { GetFleetStatusResponse, PostFleetSetupResponse } from '../../../common'; import { setupIngestManager } from '../../services/setup'; import { hasFleetServers } from '../../services/fleet_server'; import { defaultIngestErrorHandler } from '../../errors'; @@ -46,21 +46,20 @@ export const fleetSetupHandler: RequestHandler = async (context, request, respon try { const soClient = context.core.savedObjects.client; const esClient = context.core.elasticsearch.client.asCurrentUser; - const body: PostIngestSetupResponse = await setupIngestManager(soClient, esClient); + const setupStatus = await setupIngestManager(soClient, esClient); + const body: PostFleetSetupResponse = { + ...setupStatus, + nonFatalErrors: setupStatus.nonFatalErrors.map((e) => { + // JSONify the error object so it can be displayed properly in the UI + const error = e.error ?? e; + return { + name: error.name, + message: error.message, + }; + }), + }; - return response.ok({ - body: { - ...body, - nonFatalErrors: body.nonFatalErrors?.map((e) => { - // JSONify the error object so it can be displayed properly in the UI - const error = e.error ?? e; - return { - name: error.name, - message: error.message, - }; - }), - }, - }); + return response.ok({ body }); } catch (error) { return defaultIngestErrorHandler({ error, response }); } diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.test.ts b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts index cf06136c487e4..2361275563928 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.test.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts @@ -158,7 +158,7 @@ describe('policy preconfiguration', () => { const soClient = getPutPreconfiguredPackagesMock(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - const { policies, packages } = await ensurePreconfiguredPackagesAndPolicies( + const { policies, packages, nonFatalErrors } = await ensurePreconfiguredPackagesAndPolicies( soClient, esClient, [], @@ -168,13 +168,14 @@ describe('policy preconfiguration', () => { expect(policies.length).toBe(0); expect(packages.length).toBe(0); + expect(nonFatalErrors.length).toBe(0); }); it('should install packages successfully', async () => { const soClient = getPutPreconfiguredPackagesMock(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - const { policies, packages } = await ensurePreconfiguredPackagesAndPolicies( + const { policies, packages, nonFatalErrors } = await ensurePreconfiguredPackagesAndPolicies( soClient, esClient, [], @@ -184,13 +185,14 @@ describe('policy preconfiguration', () => { expect(policies.length).toBe(0); expect(packages).toEqual(expect.arrayContaining(['test_package-3.0.0'])); + expect(nonFatalErrors.length).toBe(0); }); it('should install packages and configure agent policies successfully', async () => { const soClient = getPutPreconfiguredPackagesMock(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - const { policies, packages } = await ensurePreconfiguredPackagesAndPolicies( + const { policies, packages, nonFatalErrors } = await ensurePreconfiguredPackagesAndPolicies( soClient, esClient, [ @@ -213,6 +215,7 @@ describe('policy preconfiguration', () => { expect(policies.length).toEqual(1); expect(policies[0].id).toBe('mocked-test-id'); expect(packages).toEqual(expect.arrayContaining(['test_package-3.0.0'])); + expect(nonFatalErrors.length).toBe(0); }); it('should throw an error when trying to install duplicate packages', async () => { @@ -235,11 +238,45 @@ describe('policy preconfiguration', () => { ); }); + it('should return nonFatalErrors', async () => { + const soClient = getPutPreconfiguredPackagesMock(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + const policies: PreconfiguredAgentPolicy[] = [ + { + name: 'Test policy', + namespace: 'default', + id: 'test-id', + package_policies: [ + { + package: { name: 'test_package' }, + name: 'Test package', + }, + ], + }, + ]; + + const { nonFatalErrors } = await ensurePreconfiguredPackagesAndPolicies( + soClient, + esClient, + policies, + [{ name: 'CANNOT_MATCH', version: 'x.y.z' }], + mockDefaultOutput + ); + + expect(nonFatalErrors.length).toBe(1); + expect(nonFatalErrors[0].agentPolicy).toEqual({ name: 'Test policy' }); + expect(nonFatalErrors[0].error.message).toEqual( + 'Test policy could not be added. test_package is not installed, add test_package to `xpack.fleet.packages` or remove it from Test package.' + ); + }); it('should not attempt to recreate or modify an agent policy if its ID is unchanged', async () => { const soClient = getPutPreconfiguredPackagesMock(); const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; - const { policies: policiesA } = await ensurePreconfiguredPackagesAndPolicies( + const { + policies: policiesA, + nonFatalErrors: nonFatalErrorsA, + } = await ensurePreconfiguredPackagesAndPolicies( soClient, esClient, [ @@ -256,8 +293,12 @@ describe('policy preconfiguration', () => { expect(policiesA.length).toEqual(1); expect(policiesA[0].id).toBe('mocked-test-id'); + expect(nonFatalErrorsA.length).toBe(0); - const { policies: policiesB } = await ensurePreconfiguredPackagesAndPolicies( + const { + policies: policiesB, + nonFatalErrors: nonFatalErrorsB, + } = await ensurePreconfiguredPackagesAndPolicies( soClient, esClient, [ @@ -280,6 +321,7 @@ describe('policy preconfiguration', () => { expect(policiesB.length).toEqual(1); expect(policiesB[0].id).toBe('mocked-test-id'); expect(policiesB[0].updated_at).toEqual(policiesA[0].updated_at); + expect(nonFatalErrorsB.length).toBe(0); }); }); diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.ts b/x-pack/plugins/fleet/server/services/preconfiguration.ts index 0e24871628dcd..b3597ade23633 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.ts @@ -36,7 +36,7 @@ import { agentPolicyService, addPackageToAgentPolicy } from './agent_policy'; interface PreconfigurationResult { policies: Array<{ id: string; updated_at: string }>; packages: string[]; - nonFatalErrors?: PreconfigurationError[]; + nonFatalErrors: PreconfigurationError[]; } export type InputsOverride = Partial & { diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index cfef04846d92e..1f3c3c5082b34 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -31,7 +31,7 @@ import { pkgToPkgKey } from './epm/registry'; export interface SetupStatus { isInitialized: boolean; - nonFatalErrors?: Array; + nonFatalErrors: Array; } export async function setupIngestManager( diff --git a/x-pack/plugins/infra/common/search_strategies/log_entries/log_entries.ts b/x-pack/plugins/infra/common/search_strategies/log_entries/log_entries.ts index 071432e4937c3..b9841abad6ab5 100644 --- a/x-pack/plugins/infra/common/search_strategies/log_entries/log_entries.ts +++ b/x-pack/plugins/infra/common/search_strategies/log_entries/log_entries.ts @@ -6,7 +6,7 @@ */ import * as rt from 'io-ts'; -import { DslQuery } from '../../../../../../src/plugins/data/common'; +import { DslQuery } from '@kbn/es-query'; import { logSourceColumnConfigurationRT } from '../../log_sources/log_source_configuration'; import { logEntryAfterCursorRT, diff --git a/x-pack/plugins/infra/public/alerting/inventory/index.ts b/x-pack/plugins/infra/public/alerting/inventory/index.ts index b66bd94553d3c..7d370c7106cb7 100644 --- a/x-pack/plugins/infra/public/alerting/inventory/index.ts +++ b/x-pack/plugins/infra/public/alerting/inventory/index.ts @@ -12,16 +12,18 @@ import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../server/lib/alerting/inventory_metric_threshold/types'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; + +import { ObservabilityRuleTypeModel } from '../../../../observability/public'; + import { AlertTypeParams } from '../../../../alerting/common'; import { validateMetricThreshold } from './components/validation'; +import { formatReason } from './rule_data_formatters'; interface InventoryMetricAlertTypeParams extends AlertTypeParams { criteria: InventoryMetricConditions[]; } -export function createInventoryMetricAlertType(): AlertTypeModel { +export function createInventoryMetricAlertType(): ObservabilityRuleTypeModel { return { id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, description: i18n.translate('xpack.infra.metrics.inventory.alertFlyout.alertDescription', { @@ -44,5 +46,6 @@ Reason: } ), requiresAppContext: false, + format: formatReason, }; } diff --git a/x-pack/plugins/infra/public/alerting/inventory/rule_data_formatters.ts b/x-pack/plugins/infra/public/alerting/inventory/rule_data_formatters.ts new file mode 100644 index 0000000000000..1d8414d6abd23 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/inventory/rule_data_formatters.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { ALERT_ID } from '@kbn/rule-data-utils'; +import { ObservabilityRuleTypeFormatter } from '../../../../observability/public'; + +export const formatReason: ObservabilityRuleTypeFormatter = ({ fields }) => { + const groupName = fields[ALERT_ID]; + const reason = i18n.translate('xpack.infra.metrics.alerting.inventory.alertReasonDescription', { + defaultMessage: 'Inventory alert for {groupName}.', // TEMP reason message, will be deleted once we index the reason field + values: { + groupName, + }, + }); + + const link = '/app/metrics/inventory'; + + return { + reason, + link, + }; +}; diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream_error_boundary.tsx b/x-pack/plugins/infra/public/components/log_stream/log_stream_error_boundary.tsx index c55e6d299127b..41a9e13dcc178 100644 --- a/x-pack/plugins/infra/public/components/log_stream/log_stream_error_boundary.tsx +++ b/x-pack/plugins/infra/public/components/log_stream/log_stream_error_boundary.tsx @@ -8,7 +8,7 @@ import { EuiCodeBlock, EuiEmptyPrompt } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { KQLSyntaxError } from '../../../../../../src/plugins/data/common'; +import { KQLSyntaxError } from '@kbn/es-query'; import { RenderErrorFunc, ResettableErrorBoundary } from '../resettable_error_boundary'; export const LogStreamErrorBoundary: React.FC<{ resetOnChange: any }> = ({ diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts index d5951d9ec9915..0eaeea60c63bf 100644 --- a/x-pack/plugins/infra/public/plugin.ts +++ b/x-pack/plugins/infra/public/plugin.ts @@ -34,12 +34,15 @@ export class Plugin implements InfraClientPluginClass { registerFeatures(pluginsSetup.home); } - pluginsSetup.triggersActionsUi.alertTypeRegistry.register(createInventoryMetricAlertType()); + pluginsSetup.observability.observabilityRuleTypeRegistry.register( + createInventoryMetricAlertType() + ); + pluginsSetup.observability.observabilityRuleTypeRegistry.register( createLogThresholdAlertType() ); - pluginsSetup.triggersActionsUi.alertTypeRegistry.register(createMetricThresholdAlertType()); + pluginsSetup.triggersActionsUi.alertTypeRegistry.register(createMetricThresholdAlertType()); pluginsSetup.observability.dashboard.register({ appName: 'infra_logs', hasData: getLogsHasDataFetcher(core.getStartServices), diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index 7a890ac14482a..025bc54e11cc9 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -12,12 +12,13 @@ import { getCustomMetricLabel } from '../../../../common/formatters/get_custom_m import { toMetricOpt } from '../../../../common/snapshot_metric_i18n'; import { AlertStates, InventoryMetricConditions } from './types'; import { + ActionGroupIdsOf, ActionGroup, AlertInstanceContext, AlertInstanceState, RecoveredActionGroup, } from '../../../../../alerting/common'; -import { AlertExecutorOptions } from '../../../../../alerting/server'; +import { AlertInstance, AlertTypeState } from '../../../../../alerting/server'; import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types'; import { InfraBackendLibs } from '../../infra_types'; import { METRIC_FORMATTERS } from '../../../../common/formatters/snapshot_metric_formats'; @@ -30,7 +31,6 @@ import { stateToAlertMessage, } from '../common/messages'; import { evaluateCondition } from './evaluate_condition'; -import { InventoryMetricThresholdAllowedActionGroups } from './register_inventory_metric_threshold_alert_type'; interface InventoryMetricThresholdParams { criteria: InventoryMetricConditions[]; @@ -40,145 +40,163 @@ interface InventoryMetricThresholdParams { alertOnNoData?: boolean; } -export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) => async ({ - services, - params, -}: AlertExecutorOptions< - /** - * TODO: Remove this use of `any` by utilizing a proper type - */ - Record, - Record, - AlertInstanceState, - AlertInstanceContext, +type InventoryMetricThresholdAllowedActionGroups = ActionGroupIdsOf< + typeof FIRED_ACTIONS | typeof WARNING_ACTIONS +>; + +export type InventoryMetricThresholdAlertTypeParams = Record; +export type InventoryMetricThresholdAlertTypeState = AlertTypeState; // no specific state used +export type InventoryMetricThresholdAlertInstanceState = AlertInstanceState; // no specific state used +export type InventoryMetricThresholdAlertInstanceContext = AlertInstanceContext; // no specific instance context used + +type InventoryMetricThresholdAlertInstance = AlertInstance< + InventoryMetricThresholdAlertInstanceState, + InventoryMetricThresholdAlertInstanceContext, InventoryMetricThresholdAllowedActionGroups ->) => { - const { - criteria, - filterQuery, - sourceId, - nodeType, - alertOnNoData, - } = params as InventoryMetricThresholdParams; - - if (criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions'); - - const source = await libs.sources.getSourceConfiguration( - services.savedObjectsClient, - sourceId || 'default' - ); - - const logQueryFields = await libs - .getLogQueryFields( - sourceId || 'default', - services.savedObjectsClient, - services.scopedClusterClient.asCurrentUser - ) - .catch(() => undefined); - - const compositeSize = libs.configuration.inventory.compositeSize; - - const results = await Promise.all( - criteria.map((condition) => - evaluateCondition({ - condition, - nodeType, - source, - logQueryFields, - esClient: services.scopedClusterClient.asCurrentUser, - compositeSize, - filterQuery, - }) - ) - ); - - const inventoryItems = Object.keys(first(results)!); - for (const item of inventoryItems) { - // AND logic; all criteria must be across the threshold - const shouldAlertFire = results.every((result) => - // Grab the result of the most recent bucket - last(result[item].shouldFire) +>; +type InventoryMetricThresholdAlertInstanceFactory = ( + id: string, + threshold?: number | undefined, + value?: number | undefined +) => InventoryMetricThresholdAlertInstance; + +export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) => + libs.metricsRules.createLifecycleRuleExecutor< + InventoryMetricThresholdAlertTypeParams, + InventoryMetricThresholdAlertTypeState, + InventoryMetricThresholdAlertInstanceState, + InventoryMetricThresholdAlertInstanceContext, + InventoryMetricThresholdAllowedActionGroups + >(async ({ services, params }) => { + const { + criteria, + filterQuery, + sourceId, + nodeType, + alertOnNoData, + } = params as InventoryMetricThresholdParams; + if (criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions'); + const { alertWithLifecycle, savedObjectsClient } = services; + const alertInstanceFactory: InventoryMetricThresholdAlertInstanceFactory = (id) => + alertWithLifecycle({ + id, + fields: {}, + }); + + const source = await libs.sources.getSourceConfiguration( + savedObjectsClient, + sourceId || 'default' ); - const shouldAlertWarn = results.every((result) => last(result[item].shouldWarn)); - - // AND logic; because we need to evaluate all criteria, if one of them reports no data then the - // whole alert is in a No Data/Error state - const isNoData = results.some((result) => last(result[item].isNoData)); - const isError = results.some((result) => result[item].isError); - - const nextState = isError - ? AlertStates.ERROR - : isNoData - ? AlertStates.NO_DATA - : shouldAlertFire - ? AlertStates.ALERT - : shouldAlertWarn - ? AlertStates.WARNING - : AlertStates.OK; - - let reason; - if (nextState === AlertStates.ALERT || nextState === AlertStates.WARNING) { - reason = results - .map((result) => - buildReasonWithVerboseMetricName( - result[item], - buildFiredAlertReason, - nextState === AlertStates.WARNING - ) - ) - .join('\n'); - /* - * Custom recovery actions aren't yet available in the alerting framework - * Uncomment the code below once they've been implemented - * Reference: https://github.com/elastic/kibana/issues/87048 - */ - // } else if (nextState === AlertStates.OK && prevState?.alertState === AlertStates.ALERT) { - // reason = results - // .map((result) => buildReasonWithVerboseMetricName(result[item], buildRecoveredAlertReason)) - // .join('\n'); - } - if (alertOnNoData) { - if (nextState === AlertStates.NO_DATA) { - reason = results - .filter((result) => result[item].isNoData) - .map((result) => buildReasonWithVerboseMetricName(result[item], buildNoDataAlertReason)) - .join('\n'); - } else if (nextState === AlertStates.ERROR) { + + const logQueryFields = await libs + .getLogQueryFields( + sourceId || 'default', + services.savedObjectsClient, + services.scopedClusterClient.asCurrentUser + ) + .catch(() => undefined); + + const compositeSize = libs.configuration.inventory.compositeSize; + const results = await Promise.all( + criteria.map((condition) => + evaluateCondition({ + condition, + nodeType, + source, + logQueryFields, + esClient: services.scopedClusterClient.asCurrentUser, + compositeSize, + filterQuery, + }) + ) + ); + const inventoryItems = Object.keys(first(results)!); + for (const item of inventoryItems) { + // AND logic; all criteria must be across the threshold + const shouldAlertFire = results.every((result) => { + // Grab the result of the most recent bucket + return last(result[item].shouldFire); + }); + const shouldAlertWarn = results.every((result) => last(result[item].shouldWarn)); + + // AND logic; because we need to evaluate all criteria, if one of them reports no data then the + // whole alert is in a No Data/Error state + const isNoData = results.some((result) => last(result[item].isNoData)); + const isError = results.some((result) => result[item].isError); + + const nextState = isError + ? AlertStates.ERROR + : isNoData + ? AlertStates.NO_DATA + : shouldAlertFire + ? AlertStates.ALERT + : shouldAlertWarn + ? AlertStates.WARNING + : AlertStates.OK; + let reason; + if (nextState === AlertStates.ALERT || nextState === AlertStates.WARNING) { reason = results - .filter((result) => result[item].isError) - .map((result) => buildReasonWithVerboseMetricName(result[item], buildErrorAlertReason)) + .map((result) => + buildReasonWithVerboseMetricName( + result[item], + buildFiredAlertReason, + nextState === AlertStates.WARNING + ) + ) .join('\n'); - } - } - if (reason) { - const actionGroupId = - nextState === AlertStates.OK - ? RecoveredActionGroup.id - : nextState === AlertStates.WARNING - ? WARNING_ACTIONS.id - : FIRED_ACTIONS.id; - const alertInstance = services.alertInstanceFactory(`${item}`); - alertInstance.scheduleActions( - /** - * TODO: We're lying to the compiler here as explicitly calling `scheduleActions` on - * the RecoveredActionGroup isn't allowed + /* + * Custom recovery actions aren't yet available in the alerting framework + * Uncomment the code below once they've been implemented + * Reference: https://github.com/elastic/kibana/issues/87048 */ - (actionGroupId as unknown) as InventoryMetricThresholdAllowedActionGroups, - { - group: item, - alertState: stateToAlertMessage[nextState], - reason, - timestamp: moment().toISOString(), - value: mapToConditionsLookup(results, (result) => - formatMetric(result[item].metric, result[item].currentValue) - ), - threshold: mapToConditionsLookup(criteria, (c) => c.threshold), - metric: mapToConditionsLookup(criteria, (c) => c.metric), + // } else if (nextState === AlertStates.OK && prevState?.alertState === AlertStates.ALERT) { + // reason = results + // .map((result) => buildReasonWithVerboseMetricName(result[item], buildRecoveredAlertReason)) + // .join('\n'); + } + if (alertOnNoData) { + if (nextState === AlertStates.NO_DATA) { + reason = results + .filter((result) => result[item].isNoData) + .map((result) => buildReasonWithVerboseMetricName(result[item], buildNoDataAlertReason)) + .join('\n'); + } else if (nextState === AlertStates.ERROR) { + reason = results + .filter((result) => result[item].isError) + .map((result) => buildReasonWithVerboseMetricName(result[item], buildErrorAlertReason)) + .join('\n'); } - ); + } + if (reason) { + const actionGroupId = + nextState === AlertStates.OK + ? RecoveredActionGroup.id + : nextState === AlertStates.WARNING + ? WARNING_ACTIONS.id + : FIRED_ACTIONS.id; + + const alertInstance = alertInstanceFactory(`${item}`); + alertInstance.scheduleActions( + /** + * TODO: We're lying to the compiler here as explicitly calling `scheduleActions` on + * the RecoveredActionGroup isn't allowed + */ + (actionGroupId as unknown) as InventoryMetricThresholdAllowedActionGroups, + { + group: item, + alertState: stateToAlertMessage[nextState], + reason, + timestamp: moment().toISOString(), + value: mapToConditionsLookup(results, (result) => + formatMetric(result[item].metric, result[item].currentValue) + ), + threshold: mapToConditionsLookup(criteria, (c) => c.threshold), + metric: mapToConditionsLookup(criteria, (c) => c.metric), + } + ); + } } - } -}; + }); const buildReasonWithVerboseMetricName = ( resultItem: any, diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts index 5410353ac46a0..5d516f3591419 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts @@ -7,12 +7,7 @@ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; -import { - AlertType, - AlertInstanceState, - AlertInstanceContext, - ActionGroupIdsOf, -} from '../../../../../alerting/server'; +import { PluginSetupContract } from '../../../../../alerting/server'; import { createInventoryMetricThresholdExecutor, FIRED_ACTIONS, @@ -51,56 +46,45 @@ const condition = schema.object({ ), }); -export type InventoryMetricThresholdAllowedActionGroups = ActionGroupIdsOf< - typeof FIRED_ACTIONS | typeof WARNING_ACTIONS ->; - -export const registerMetricInventoryThresholdAlertType = ( +export async function registerMetricInventoryThresholdAlertType( + alertingPlugin: PluginSetupContract, libs: InfraBackendLibs -): AlertType< - /** - * TODO: Remove this use of `any` by utilizing a proper type - */ - Record, - never, // Only use if defining useSavedObjectReferences hook - Record, - AlertInstanceState, - AlertInstanceContext, - InventoryMetricThresholdAllowedActionGroups -> => ({ - id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, - name: i18n.translate('xpack.infra.metrics.inventory.alertName', { - defaultMessage: 'Inventory', - }), - validate: { - params: schema.object( - { - criteria: schema.arrayOf(condition), - nodeType: schema.string(), - filterQuery: schema.maybe( - schema.string({ validate: validateIsStringElasticsearchJSONFilter }) - ), - sourceId: schema.string(), - alertOnNoData: schema.maybe(schema.boolean()), - }, - { unknowns: 'allow' } - ), - }, - defaultActionGroupId: FIRED_ACTIONS_ID, - actionGroups: [FIRED_ACTIONS, WARNING_ACTIONS], - producer: 'infrastructure', - minimumLicenseRequired: 'basic', - isExportable: true, - executor: createInventoryMetricThresholdExecutor(libs), - actionVariables: { - context: [ - { name: 'group', description: groupActionVariableDescription }, - { name: 'alertState', description: alertStateActionVariableDescription }, - { name: 'reason', description: reasonActionVariableDescription }, - { name: 'timestamp', description: timestampActionVariableDescription }, - { name: 'value', description: valueActionVariableDescription }, - { name: 'metric', description: metricActionVariableDescription }, - { name: 'threshold', description: thresholdActionVariableDescription }, - ], - }, -}); +) { + alertingPlugin.registerType({ + id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, + name: i18n.translate('xpack.infra.metrics.inventory.alertName', { + defaultMessage: 'Inventory', + }), + validate: { + params: schema.object( + { + criteria: schema.arrayOf(condition), + nodeType: schema.string(), + filterQuery: schema.maybe( + schema.string({ validate: validateIsStringElasticsearchJSONFilter }) + ), + sourceId: schema.string(), + alertOnNoData: schema.maybe(schema.boolean()), + }, + { unknowns: 'allow' } + ), + }, + defaultActionGroupId: FIRED_ACTIONS_ID, + actionGroups: [FIRED_ACTIONS, WARNING_ACTIONS], + producer: 'infrastructure', + minimumLicenseRequired: 'basic', + isExportable: true, + executor: createInventoryMetricThresholdExecutor(libs), + actionVariables: { + context: [ + { name: 'group', description: groupActionVariableDescription }, + { name: 'alertState', description: alertStateActionVariableDescription }, + { name: 'reason', description: reasonActionVariableDescription }, + { name: 'timestamp', description: timestampActionVariableDescription }, + { name: 'value', description: valueActionVariableDescription }, + { name: 'metric', description: metricActionVariableDescription }, + { name: 'threshold', description: thresholdActionVariableDescription }, + ], + }, + }); +} diff --git a/x-pack/plugins/infra/server/lib/alerting/register_alert_types.ts b/x-pack/plugins/infra/server/lib/alerting/register_alert_types.ts index b5e6f714de77e..d7df2afd8038b 100644 --- a/x-pack/plugins/infra/server/lib/alerting/register_alert_types.ts +++ b/x-pack/plugins/infra/server/lib/alerting/register_alert_types.ts @@ -21,10 +21,9 @@ const registerAlertTypes = ( ) => { if (alertingPlugin) { alertingPlugin.registerType(registerMetricThresholdAlertType(libs)); - alertingPlugin.registerType(registerMetricInventoryThresholdAlertType(libs)); alertingPlugin.registerType(registerMetricAnomalyAlertType(libs, ml)); - const registerFns = [registerLogThresholdAlertType]; + const registerFns = [registerLogThresholdAlertType, registerMetricInventoryThresholdAlertType]; registerFns.forEach((fn) => { fn(alertingPlugin, libs); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/error_helper.test.ts b/x-pack/plugins/lens/public/editor_frame_service/error_helper.test.ts index 41974149b9c03..7fae35057421c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/error_helper.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/error_helper.test.ts @@ -141,6 +141,17 @@ const scriptedFieldError = { }, }; +const networkError = { + stack: 'Error: Batch request failed with status 0', + message: '[lens_merge_tables] > [esaggs] > Batch request failed with status 0', + name: 'Error', + original: { + name: 'Error', + message: 'Batch request failed with status 0', + stack: 'Error: Batch request failed with status 0', + }, +}; + // EsAggs will report an internal error when user attempts to use a runtime field on an indexpattern he has no access to const indexpatternAccessError = { stack: "TypeError: Cannot read property 'values' of undefined\n", @@ -175,5 +186,11 @@ describe('lens_error_helpers', () => { indexpatternAccessError.message, ]); }); + + it("should report a network custom message when there's a network/connection problem", () => { + expect(getOriginalRequestErrorMessages(networkError as Error)).toEqual([ + 'Network error, try again later or contact your administrator.', + ]); + }); }); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/error_helper.ts b/x-pack/plugins/lens/public/editor_frame_service/error_helper.ts index 42470e5cb6162..b19a295b68407 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/error_helper.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/error_helper.ts @@ -28,6 +28,10 @@ interface EsAggError { stack: string; } +const isNetworkError = (e: Error): boolean => { + return e.message === 'Batch request failed with status 0'; // Note: 0 here means Network error +}; + const isRequestError = (e: Error | RequestError): e is RequestError => { if ('body' in e) { return e.body?.attributes?.error?.caused_by !== undefined; @@ -101,7 +105,15 @@ export function getOriginalRequestErrorMessages(error?: ExpressionRenderError | const errorMessages = []; if (error && 'original' in error && error.original) { if (isEsAggError(error.original)) { - errorMessages.push(error.message); + if (isNetworkError(error.original)) { + errorMessages.push( + i18n.translate('xpack.lens.editorFrame.networkErrorMessage', { + defaultMessage: 'Network error, try again later or contact your administrator.', + }) + ); + } else { + errorMessages.push(error.message); + } } else { const rootErrors = uniqWith(getErrorSources(error.original), isEqual); for (const rootError of rootErrors) { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx index a0a6b30e541a7..ec1421577dc2b 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx @@ -23,9 +23,10 @@ import { EuiButtonIcon, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { EsQueryConfig, Query, Filter } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n/react'; import { CoreStart } from 'kibana/public'; -import { DataPublicPluginStart, EsQueryConfig, Query, Filter } from 'src/plugins/data/public'; +import { DataPublicPluginStart } from 'src/plugins/data/public'; import { htmlIdGenerator } from '@elastic/eui'; import { DatasourceDataPanelProps, DataType, StateSetter } from '../types'; import { ChildDragDropProvider, DragContextState } from '../drag_drop'; diff --git a/x-pack/plugins/lists/server/services/utils/get_query_filter.ts b/x-pack/plugins/lists/server/services/utils/get_query_filter.ts index 25c8f9880063f..ff0e42870ab30 100644 --- a/x-pack/plugins/lists/server/services/utils/get_query_filter.ts +++ b/x-pack/plugins/lists/server/services/utils/get_query_filter.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { DslQuery, EsQueryConfig } from 'src/plugins/data/common'; +import { DslQuery, EsQueryConfig } from '@kbn/es-query'; import { Filter, Query, esQuery } from '../../../../../../src/plugins/data/server'; diff --git a/x-pack/plugins/maps/common/elasticsearch_util/spatial_filter_utils.ts b/x-pack/plugins/maps/common/elasticsearch_util/spatial_filter_utils.ts index 9a2b2c21136df..0e2c38bf7afe4 100644 --- a/x-pack/plugins/maps/common/elasticsearch_util/spatial_filter_utils.ts +++ b/x-pack/plugins/maps/common/elasticsearch_util/spatial_filter_utils.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { Feature, Geometry, Polygon, Position } from 'geojson'; // @ts-expect-error import turfCircle from '@turf/circle'; -import { FilterMeta, FILTERS } from '../../../../../src/plugins/data/common'; +import { FilterMeta, FILTERS } from '@kbn/es-query'; import { MapExtent } from '../descriptor_types'; import { ES_SPATIAL_RELATIONS } from '../constants'; import { getEsSpatialRelationLabel } from '../i18n_getters'; diff --git a/x-pack/plugins/ml/common/types/es_client.ts b/x-pack/plugins/ml/common/types/es_client.ts index 29a7a81aa5693..433deac02bc9c 100644 --- a/x-pack/plugins/ml/common/types/es_client.ts +++ b/x-pack/plugins/ml/common/types/es_client.ts @@ -8,8 +8,7 @@ import { estypes } from '@elastic/elasticsearch'; import { JsonObject } from '@kbn/common-utils'; -import { buildEsQuery } from '../../../../../src/plugins/data/common/es_query/es_query'; -import type { DslQuery } from '../../../../../src/plugins/data/common/es_query/kuery'; +import { buildEsQuery, DslQuery } from '@kbn/es-query'; import { isPopulatedObject } from '../util/object_utils'; diff --git a/x-pack/plugins/ml/public/embeddables/common/process_filters.ts b/x-pack/plugins/ml/public/embeddables/common/process_filters.ts index 8ff75205b4d48..e054df09bd95b 100644 --- a/x-pack/plugins/ml/public/embeddables/common/process_filters.ts +++ b/x-pack/plugins/ml/public/embeddables/common/process_filters.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { Filter } from '../../../../../../src/plugins/data/common/es_query/filters'; -import { Query } from '../../../../../../src/plugins/data/common/query'; +import { Filter, Query } from '@kbn/es-query'; import { esKuery, esQuery } from '../../../../../../src/plugins/data/public'; export function processFilters(filters: Filter[], query: Query, controlledBy?: string) { diff --git a/x-pack/plugins/ml/public/embeddables/types.ts b/x-pack/plugins/ml/public/embeddables/types.ts index 60355dae5baf4..436eee0698708 100644 --- a/x-pack/plugins/ml/public/embeddables/types.ts +++ b/x-pack/plugins/ml/public/embeddables/types.ts @@ -6,14 +6,10 @@ */ import type { CoreStart } from 'kibana/public'; +import type { Filter, Query } from '@kbn/es-query'; import type { JobId } from '../../common/types/anomaly_detection_jobs'; import type { SwimlaneType } from '../application/explorer/explorer_constants'; -import type { Filter } from '../../../../../src/plugins/data/common/es_query/filters'; -import type { - Query, - RefreshInterval, - TimeRange, -} from '../../../../../src/plugins/data/common/query'; +import type { RefreshInterval, TimeRange } from '../../../../../src/plugins/data/common'; import type { EmbeddableInput, EmbeddableOutput, diff --git a/x-pack/plugins/ml/server/models/job_service/index.ts b/x-pack/plugins/ml/server/models/job_service/index.ts index 94dc669bfd946..94c25581e8e84 100644 --- a/x-pack/plugins/ml/server/models/job_service/index.ts +++ b/x-pack/plugins/ml/server/models/job_service/index.ts @@ -13,16 +13,16 @@ import { newJobCapsProvider } from './new_job_caps'; import { newJobChartsProvider, topCategoriesProvider } from './new_job'; import { modelSnapshotProvider } from './model_snapshots'; import type { MlClient } from '../../lib/ml_client'; -import type { AlertsClient } from '../../../../alerting/server'; +import type { RulesClient } from '../../../../alerting/server'; export function jobServiceProvider( client: IScopedClusterClient, mlClient: MlClient, - alertsClient?: AlertsClient + rulesClient?: RulesClient ) { return { ...datafeedsProvider(client, mlClient), - ...jobsProvider(client, mlClient, alertsClient), + ...jobsProvider(client, mlClient, rulesClient), ...groupsProvider(mlClient), ...newJobCapsProvider(client), ...newJobChartsProvider(client), diff --git a/x-pack/plugins/ml/server/models/job_service/jobs.ts b/x-pack/plugins/ml/server/models/job_service/jobs.ts index 22bac1cb08e19..6732c8fe7e2f1 100644 --- a/x-pack/plugins/ml/server/models/job_service/jobs.ts +++ b/x-pack/plugins/ml/server/models/job_service/jobs.ts @@ -40,7 +40,7 @@ import { import { groupsProvider } from './groups'; import type { MlClient } from '../../lib/ml_client'; import { isPopulatedObject } from '../../../common/util/object_utils'; -import type { AlertsClient } from '../../../../alerting/server'; +import type { RulesClient } from '../../../../alerting/server'; import { ML_ALERT_TYPES } from '../../../common/constants/alerts'; import { MlAnomalyDetectionAlertParams } from '../../routes/schemas/alerting_schema'; @@ -54,7 +54,7 @@ interface Results { export function jobsProvider( client: IScopedClusterClient, mlClient: MlClient, - alertsClient?: AlertsClient + rulesClient?: RulesClient ) { const { asInternalUser } = client; @@ -421,8 +421,8 @@ export function jobsProvider( jobs.push(tempJob); }); - if (alertsClient) { - const mlAlertingRules = await alertsClient.find({ + if (rulesClient) { + const mlAlertingRules = await rulesClient.find({ options: { filter: `alert.attributes.alertTypeId:${ML_ALERT_TYPES.ANOMALY_DETECTION}`, perPage: 1000, diff --git a/x-pack/plugins/ml/server/routes/job_service.ts b/x-pack/plugins/ml/server/routes/job_service.ts index 992822f6d6eb8..023997a35a6a6 100644 --- a/x-pack/plugins/ml/server/routes/job_service.ts +++ b/x-pack/plugins/ml/server/routes/job_service.ts @@ -235,7 +235,7 @@ export function jobServiceRoutes({ router, routeGuard }: RouteInitialization) { const { jobsSummary } = jobServiceProvider( client, mlClient, - context.alerting?.getAlertsClient() + context.alerting?.getRulesClient() ); const { jobIds } = request.body; const resp = await jobsSummary(jobIds); @@ -340,7 +340,7 @@ export function jobServiceRoutes({ router, routeGuard }: RouteInitialization) { const { createFullJobsList } = jobServiceProvider( client, mlClient, - context.alerting?.getAlertsClient() + context.alerting?.getRulesClient() ); const { jobIds } = request.body; const resp = await createFullJobsList(jobIds); diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts b/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts index bd18d285e29aa..8cda6a15b5393 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts @@ -17,16 +17,16 @@ jest.mock('../static_globals', () => ({ })); describe('AlertsFactory', () => { - const alertsClient = { + const rulesClient = { find: jest.fn(), }; afterEach(() => { - alertsClient.find.mockReset(); + rulesClient.find.mockReset(); }); it('should get by type', async () => { - alertsClient.find = jest.fn().mockImplementation(() => { + rulesClient.find = jest.fn().mockImplementation(() => { return { total: 1, data: [ @@ -36,20 +36,20 @@ describe('AlertsFactory', () => { ], }; }); - const alert = await AlertsFactory.getByType(ALERT_CPU_USAGE, alertsClient as any); + const alert = await AlertsFactory.getByType(ALERT_CPU_USAGE, rulesClient as any); expect(alert).not.toBeNull(); expect(alert?.getId()).toBe(ALERT_CPU_USAGE); }); it('should pass in the correct filters', async () => { let filter = null; - alertsClient.find = jest.fn().mockImplementation(({ options }) => { + rulesClient.find = jest.fn().mockImplementation(({ options }) => { filter = options.filter; return { total: 0, }; }); - await AlertsFactory.getByType(ALERT_CPU_USAGE, alertsClient as any); + await AlertsFactory.getByType(ALERT_CPU_USAGE, rulesClient as any); expect(filter).toBe(`alert.attributes.alertTypeId:${ALERT_CPU_USAGE}`); }); }); diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts b/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts index 0bb8fe39fd9a0..fad3da83e36a5 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts @@ -38,7 +38,7 @@ import { ALERT_CCR_READ_EXCEPTIONS, ALERT_LARGE_SHARD_SIZE, } from '../../common/constants'; -import { AlertsClient } from '../../../alerting/server'; +import { RulesClient } from '../../../alerting/server'; import { Alert } from '../../../alerting/common'; const BY_TYPE = { @@ -61,13 +61,13 @@ const BY_TYPE = { export class AlertsFactory { public static async getByType( type: string, - alertsClient: AlertsClient | undefined + rulesClient: RulesClient | undefined ): Promise { const alertCls = BY_TYPE[type]; - if (!alertCls || !alertsClient) { + if (!alertCls || !rulesClient) { return; } - const alertClientAlerts = await alertsClient.find({ + const alertClientAlerts = await rulesClient.find({ options: { filter: `alert.attributes.alertTypeId:${type}`, }, diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.test.ts index c89890397be7c..3fe4eac712487 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_alert.test.ts @@ -19,7 +19,7 @@ describe('BaseAlert', () => { describe('create', () => { it('should create an alert if it does not exist', async () => { const alert = new BaseAlert(); - const alertsClient = { + const rulesClient = { create: jest.fn(), find: jest.fn().mockImplementation(() => { return { @@ -41,8 +41,8 @@ describe('BaseAlert', () => { }, ]; - await alert.createIfDoesNotExist(alertsClient as any, actionsClient as any, actions); - expect(alertsClient.create).toHaveBeenCalledWith({ + await alert.createIfDoesNotExist(rulesClient as any, actionsClient as any, actions); + expect(rulesClient.create).toHaveBeenCalledWith({ data: { actions: [ { @@ -73,7 +73,7 @@ describe('BaseAlert', () => { it('should not create an alert if it exists', async () => { const alert = new BaseAlert(); - const alertsClient = { + const rulesClient = { create: jest.fn(), find: jest.fn().mockImplementation(() => { return { @@ -96,14 +96,14 @@ describe('BaseAlert', () => { }, ]; - await alert.createIfDoesNotExist(alertsClient as any, actionsClient as any, actions); - expect(alertsClient.create).not.toHaveBeenCalled(); + await alert.createIfDoesNotExist(rulesClient as any, actionsClient as any, actions); + expect(rulesClient.create).not.toHaveBeenCalled(); }); }); describe('getStates', () => { it('should get alert states', async () => { - const alertsClient = { + const rulesClient = { getAlertState: jest.fn().mockImplementation(() => { return { alertInstances: { @@ -117,7 +117,7 @@ describe('BaseAlert', () => { const id = '456def'; const filters: any[] = []; const alert = new BaseAlert(); - const states = await alert.getStates(alertsClient as any, id, filters); + const states = await alert.getStates(rulesClient as any, id, filters); expect(states).toStrictEqual({ abc123: { id: 'foobar', @@ -126,7 +126,7 @@ describe('BaseAlert', () => { }); it('should return nothing if no states are available', async () => { - const alertsClient = { + const rulesClient = { getAlertState: jest.fn().mockImplementation(() => { return null; }), @@ -134,7 +134,7 @@ describe('BaseAlert', () => { const id = '456def'; const filters: any[] = []; const alert = new BaseAlert(); - const states = await alert.getStates(alertsClient as any, id, filters); + const states = await alert.getStates(rulesClient as any, id, filters); expect(states).toStrictEqual({}); }); }); diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.ts index 0020ef779838f..7bc5d4242d0bd 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_alert.ts @@ -11,7 +11,7 @@ import { AlertType, AlertExecutorOptions, AlertInstance, - AlertsClient, + RulesClient, AlertServices, } from '../../../alerting/server'; import { Alert, AlertTypeParams, RawAlertInstance, SanitizedAlert } from '../../../alerting/common'; @@ -114,11 +114,11 @@ export class BaseAlert { } public async createIfDoesNotExist( - alertsClient: AlertsClient, + rulesClient: RulesClient, actionsClient: ActionsClient, actions: AlertEnableAction[] ): Promise> { - const existingAlertData = await alertsClient.find({ + const existingAlertData = await rulesClient.find({ options: { search: this.alertOptions.id, }, @@ -152,7 +152,7 @@ export class BaseAlert { throttle = '1d', interval = '1m', } = this.alertOptions; - return await alertsClient.create({ + return await rulesClient.create({ data: { enabled: true, tags: [], @@ -169,11 +169,11 @@ export class BaseAlert { } public async getStates( - alertsClient: AlertsClient, + rulesClient: RulesClient, id: string, filters: CommonAlertFilter[] ): Promise<{ [instanceId: string]: RawAlertInstance }> { - const states = await alertsClient.getAlertState({ id }); + const states = await rulesClient.getAlertState({ id }); if (!states || !states.alertInstances) { return {}; } diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts index 0d2d9fdbed635..ffcab27568597 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts @@ -47,7 +47,7 @@ describe('fetchStatus', () => { }; let alertStates: AlertState[] = []; const licenseService = null; - const alertsClient = { + const rulesClient = { find: jest.fn(() => ({ total: 1, data: [ @@ -68,13 +68,13 @@ describe('fetchStatus', () => { }; afterEach(() => { - (alertsClient.find as jest.Mock).mockClear(); - (alertsClient.getAlertState as jest.Mock).mockClear(); + (rulesClient.find as jest.Mock).mockClear(); + (rulesClient.getAlertState as jest.Mock).mockClear(); alertStates.length = 0; }); it('should fetch from the alerts client', async () => { - const status = await fetchStatus(alertsClient as any, licenseService as any, alertTypes, [ + const status = await fetchStatus(rulesClient as any, licenseService as any, alertTypes, [ defaultClusterState.clusterUuid, ]); expect(status).toEqual({ @@ -96,7 +96,7 @@ describe('fetchStatus', () => { }, ]; - const status = await fetchStatus(alertsClient as any, licenseService as any, alertTypes, [ + const status = await fetchStatus(rulesClient as any, licenseService as any, alertTypes, [ defaultClusterState.clusterUuid, ]); expect(Object.values(status).length).toBe(1); @@ -105,32 +105,32 @@ describe('fetchStatus', () => { }); it('should pass in the right filter to the alerts client', async () => { - await fetchStatus(alertsClient as any, licenseService as any, alertTypes, [ + await fetchStatus(rulesClient as any, licenseService as any, alertTypes, [ defaultClusterState.clusterUuid, ]); - expect((alertsClient.find as jest.Mock).mock.calls[0][0].options.filter).toBe( + expect((rulesClient.find as jest.Mock).mock.calls[0][0].options.filter).toBe( `alert.attributes.alertTypeId:${alertType}` ); }); it('should return nothing if no alert state is found', async () => { - alertsClient.getAlertState = jest.fn(() => ({ + rulesClient.getAlertState = jest.fn(() => ({ alertTypeState: null, })) as any; - const status = await fetchStatus(alertsClient as any, licenseService as any, alertTypes, [ + const status = await fetchStatus(rulesClient as any, licenseService as any, alertTypes, [ defaultClusterState.clusterUuid, ]); expect(status[alertType].states.length).toEqual(0); }); it('should return nothing if no alerts are found', async () => { - alertsClient.find = jest.fn(() => ({ + rulesClient.find = jest.fn(() => ({ total: 0, data: [], })) as any; - const status = await fetchStatus(alertsClient as any, licenseService as any, alertTypes, [ + const status = await fetchStatus(rulesClient as any, licenseService as any, alertTypes, [ defaultClusterState.clusterUuid, ]); expect(status).toEqual({}); @@ -145,7 +145,7 @@ describe('fetchStatus', () => { })), }; await fetchStatus( - alertsClient as any, + rulesClient as any, customLicenseService as any, [ALERT_CLUSTER_HEALTH], [defaultClusterState.clusterUuid] @@ -154,7 +154,7 @@ describe('fetchStatus', () => { }); it('should sort the alerts', async () => { - const customAlertsClient = { + const customRulesClient = { find: jest.fn(() => ({ total: 1, data: [ @@ -182,7 +182,7 @@ describe('fetchStatus', () => { })), }; const status = await fetchStatus( - customAlertsClient as any, + customRulesClient as any, licenseService as any, [ALERT_CPU_USAGE, ALERT_DISK_USAGE, ALERT_MISSING_MONITORING_DATA], [defaultClusterState.clusterUuid] diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts index a7b2457130744..f4fd792ddf922 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts @@ -6,7 +6,7 @@ */ import { AlertInstanceState } from '../../../common/types/alerts'; -import { AlertsClient } from '../../../../alerting/server'; +import { RulesClient } from '../../../../alerting/server'; import { AlertsFactory } from '../../alerts'; import { CommonAlertStatus, @@ -17,7 +17,7 @@ import { ALERTS } from '../../../common/constants'; import { MonitoringLicenseService } from '../../types'; export async function fetchStatus( - alertsClient: AlertsClient, + rulesClient: RulesClient, licenseService: MonitoringLicenseService, alertTypes: string[] | undefined, clusterUuids: string[], @@ -27,7 +27,7 @@ export async function fetchStatus( const byType: { [type: string]: CommonAlertStatus } = {}; await Promise.all( (alertTypes || ALERTS).map(async (type) => { - const alert = await AlertsFactory.getByType(type, alertsClient); + const alert = await AlertsFactory.getByType(type, rulesClient); if (!alert || !alert.rawAlert) { return; } @@ -45,7 +45,7 @@ export async function fetchStatus( } // Now that we have the id, we can get the state - const states = await alert.getStates(alertsClient, id, filters); + const states = await alert.getStates(rulesClient, id, filters); if (!states) { return result; } diff --git a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js index 77b71d3e92f4c..9ef309cee7312 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js @@ -117,16 +117,16 @@ export async function getClustersFromRequest( // add alerts data if (isInCodePath(codePaths, [CODE_PATH_ALERTS])) { - const alertsClient = req.getAlertsClient(); + const rulesClient = req.getRulesClient(); const alertStatus = await fetchStatus( - alertsClient, + rulesClient, req.server.plugins.monitoring.info, undefined, clusters.map((cluster) => get(cluster, 'elasticsearch.cluster.id', cluster.cluster_uuid)) ); for (const cluster of clusters) { - if (!alertsClient) { + if (!rulesClient) { cluster.alerts = { list: {}, alertsMeta: { diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index ca8d87bd300a0..aed48b7391529 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -347,9 +347,9 @@ export class MonitoringPlugin getKibanaStatsCollector: () => this.legacyShimDependencies.kibanaStatsCollector, getUiSettingsService: () => context.core.uiSettings.client, getActionTypeRegistry: () => context.actions?.listTypes(), - getAlertsClient: () => { + getRulesClient: () => { try { - return plugins.alerting.getAlertsClientWithRequest(req); + return plugins.alerting.getRulesClientWithRequest(req); } catch (err) { // If security is disabled, this call will throw an error unless a certain config is set for dist builds return null; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts index e21304e8458e3..ef6e03e7c999c 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts @@ -56,10 +56,10 @@ export function enableAlertsRoute(server: LegacyServer, npRoute: RouteDependenci } } - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const actionsClient = context.actions?.getActionsClient(); const types = context.actions?.listTypes(); - if (!alertsClient || !actionsClient || !types) { + if (!rulesClient || !actionsClient || !types) { return response.ok({ body: undefined }); } @@ -99,7 +99,7 @@ export function enableAlertsRoute(server: LegacyServer, npRoute: RouteDependenci if (disabledWatcherClusterAlerts) { createdAlerts = await Promise.all( - alerts.map((alert) => alert.createIfDoesNotExist(alertsClient, actionsClient, actions)) + alerts.map((alert) => alert.createIfDoesNotExist(rulesClient, actionsClient, actions)) ); } diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts index 95e2cb63bec86..f77630e5d61a5 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts @@ -34,13 +34,13 @@ export function alertStatusRoute(server: any, npRoute: RouteDependencies) { try { const { clusterUuid } = request.params; const { alertTypeIds, filters } = request.body; - const alertsClient = context.alerting?.getAlertsClient(); - if (!alertsClient) { + const rulesClient = context.alerting?.getRulesClient(); + if (!rulesClient) { return response.ok({ body: undefined }); } const status = await fetchStatus( - alertsClient, + rulesClient, npRoute.licenseService, alertTypeIds, [clusterUuid], diff --git a/x-pack/plugins/monitoring/server/types.ts b/x-pack/plugins/monitoring/server/types.ts index b920f2bfacf80..07e56f0c00232 100644 --- a/x-pack/plugins/monitoring/server/types.ts +++ b/x-pack/plugins/monitoring/server/types.ts @@ -115,7 +115,7 @@ export interface LegacyRequest { getKibanaStatsCollector: () => any; getUiSettingsService: () => any; getActionTypeRegistry: () => any; - getAlertsClient: () => any; + getRulesClient: () => any; getActionsClient: () => any; server: LegacyServer; } diff --git a/x-pack/plugins/observability/kibana.json b/x-pack/plugins/observability/kibana.json index 6bd96e012548d..f4b3754e4253e 100644 --- a/x-pack/plugins/observability/kibana.json +++ b/x-pack/plugins/observability/kibana.json @@ -8,6 +8,7 @@ ], "optionalPlugins": [ "home", + "discover", "lens", "licensing", "usageCollection" diff --git a/x-pack/plugins/observability/public/components/shared/add_data_buttons/mobile_add_data.tsx b/x-pack/plugins/observability/public/components/shared/add_data_buttons/mobile_add_data.tsx new file mode 100644 index 0000000000000..0e17c6277618b --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/add_data_buttons/mobile_add_data.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiHeaderLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { useKibana } from '../../../utils/kibana_react'; + +export function MobileAddData() { + const kibana = useKibana(); + + return ( + + {ADD_DATA_LABEL} + + ); +} + +const ADD_DATA_LABEL = i18n.translate('xpack.observability.mobile.addDataButtonLabel', { + defaultMessage: 'Add Mobile data', +}); diff --git a/x-pack/plugins/observability/public/components/shared/add_data_buttons/synthetics_add_data.tsx b/x-pack/plugins/observability/public/components/shared/add_data_buttons/synthetics_add_data.tsx new file mode 100644 index 0000000000000..af91624769e6b --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/add_data_buttons/synthetics_add_data.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiHeaderLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { useKibana } from '../../../utils/kibana_react'; + +export function SyntheticsAddData() { + const kibana = useKibana(); + + return ( + + {ADD_DATA_LABEL} + + ); +} + +const ADD_DATA_LABEL = i18n.translate('xpack.observability..synthetics.addDataButtonLabel', { + defaultMessage: 'Add synthetics data', +}); diff --git a/x-pack/plugins/observability/public/components/shared/add_data_buttons/ux_add_data.tsx b/x-pack/plugins/observability/public/components/shared/add_data_buttons/ux_add_data.tsx new file mode 100644 index 0000000000000..c6aa0742466f1 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/add_data_buttons/ux_add_data.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiHeaderLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { useKibana } from '../../../utils/kibana_react'; + +export function UXAddData() { + const kibana = useKibana(); + + return ( + + {ADD_DATA_LABEL} + + ); +} + +const ADD_DATA_LABEL = i18n.translate('xpack.observability.ux.addDataButtonLabel', { + defaultMessage: 'Add UX data', +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/action_menu/action_menu.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/action_menu/action_menu.test.tsx new file mode 100644 index 0000000000000..329192abc99d2 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/action_menu/action_menu.test.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render } from '../../rtl_helpers'; +import { fireEvent, screen } from '@testing-library/dom'; +import React from 'react'; +import { sampleAttribute } from '../../configurations/test_data/sample_attribute'; +import * as pluginHook from '../../../../../hooks/use_plugin_context'; +import { TypedLensByValueInput } from '../../../../../../../lens/public'; +import { ExpViewActionMenuContent } from './action_menu'; + +jest.spyOn(pluginHook, 'usePluginContext').mockReturnValue({ + appMountParameters: { + setHeaderActionMenu: jest.fn(), + }, +} as any); + +describe('Action Menu', function () { + it('should be able to click open in lens', async function () { + const { findByText, core } = render( + + ); + + expect(await screen.findByText('Open in Lens')).toBeInTheDocument(); + + fireEvent.click(await findByText('Open in Lens')); + + expect(core.lens?.navigateToPrefilledEditor).toHaveBeenCalledTimes(1); + expect(core.lens?.navigateToPrefilledEditor).toHaveBeenCalledWith( + { + id: '', + attributes: sampleAttribute, + timeRange: { to: 'now', from: 'now-10m' }, + }, + true + ); + }); + + it('should be able to click save', async function () { + const { findByText } = render( + + ); + + expect(await screen.findByText('Save')).toBeInTheDocument(); + + fireEvent.click(await findByText('Save')); + + expect(await screen.findByText('Lens Save Modal Component')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/action_menu/action_menu.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/action_menu/action_menu.tsx new file mode 100644 index 0000000000000..38011eb5f8ffb --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/action_menu/action_menu.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { LensEmbeddableInput, TypedLensByValueInput } from '../../../../../../../lens/public'; +import { ObservabilityAppServices } from '../../../../../application/types'; +import { useKibana } from '../../../../../../../../../src/plugins/kibana_react/public'; + +export function ExpViewActionMenuContent({ + timeRange, + lensAttributes, +}: { + timeRange?: { from: string; to: string }; + lensAttributes: TypedLensByValueInput['attributes'] | null; +}) { + const kServices = useKibana().services; + + const { lens } = kServices; + + const [isSaveOpen, setIsSaveOpen] = useState(false); + + const LensSaveModalComponent = lens.SaveModalComponent; + + return ( + <> + + + { + if (lensAttributes) { + lens.navigateToPrefilledEditor( + { + id: '', + timeRange, + attributes: lensAttributes, + }, + true + ); + } + }} + > + {i18n.translate('xpack.observability.expView.heading.openInLens', { + defaultMessage: 'Open in Lens', + })} + + + + { + if (lensAttributes) { + setIsSaveOpen(true); + } + }} + size="s" + > + {i18n.translate('xpack.observability.expView.heading.saveLensVisualization', { + defaultMessage: 'Save', + })} + + + + + {isSaveOpen && lensAttributes && ( + setIsSaveOpen(false)} + // if we want to do anything after the viz is saved + // right now there is no action, so an empty function + onSave={() => {}} + /> + )} + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/action_menu/index.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/action_menu/index.tsx new file mode 100644 index 0000000000000..23500b63e900a --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/action_menu/index.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { ExpViewActionMenuContent } from './action_menu'; +import HeaderMenuPortal from '../../../header_menu_portal'; +import { usePluginContext } from '../../../../../hooks/use_plugin_context'; +import { TypedLensByValueInput } from '../../../../../../../lens/public'; + +interface Props { + timeRange?: { from: string; to: string }; + lensAttributes: TypedLensByValueInput['attributes'] | null; +} +export function ExpViewActionMenu(props: Props) { + const { appMountParameters } = usePluginContext(); + + return ( + + + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/date_range_picker.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/date_range_picker.tsx similarity index 58% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/date_range_picker.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/components/date_range_picker.tsx index c30863585b3b0..0b8e1c1785c7f 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/date_range_picker.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/date_range_picker.tsx @@ -6,48 +6,48 @@ */ import React from 'react'; -import { i18n } from '@kbn/i18n'; import { EuiDatePicker, EuiDatePickerRange } from '@elastic/eui'; -import DateMath from '@elastic/datemath'; import { Moment } from 'moment'; +import DateMath from '@elastic/datemath'; +import { i18n } from '@kbn/i18n'; import { useSeriesStorage } from '../hooks/use_series_storage'; import { useUiSetting } from '../../../../../../../../src/plugins/kibana_react/public'; +import { SeriesUrl } from '../types'; +import { ReportTypes } from '../configurations/constants'; export const parseAbsoluteDate = (date: string, options = {}) => { return DateMath.parse(date, options)!; }; -export function DateRangePicker({ seriesId }: { seriesId: string }) { - const { firstSeriesId, getSeries, setSeries } = useSeriesStorage(); +export function DateRangePicker({ seriesId, series }: { seriesId: number; series: SeriesUrl }) { + const { firstSeries, setSeries, reportType } = useSeriesStorage(); const dateFormat = useUiSetting('dateFormat'); - const { - time: { from, to }, - reportType, - } = getSeries(firstSeriesId); + const seriesFrom = series.time?.from; + const seriesTo = series.time?.to; - const series = getSeries(seriesId); + const { from: mainFrom, to: mainTo } = firstSeries!.time; - const { - time: { from: seriesFrom, to: seriesTo }, - } = series; + const startDate = parseAbsoluteDate(seriesFrom ?? mainFrom)!; + const endDate = parseAbsoluteDate(seriesTo ?? mainTo, { roundUp: true })!; - const startDate = parseAbsoluteDate(seriesFrom ?? from)!; - const endDate = parseAbsoluteDate(seriesTo ?? to, { roundUp: true })!; + const getTotalDuration = () => { + const mainStartDate = parseAbsoluteDate(mainTo)!; + const mainEndDate = parseAbsoluteDate(mainTo, { roundUp: true })!; + return mainEndDate.diff(mainStartDate, 'millisecond'); + }; - const onStartChange = (newDate: Moment) => { - if (reportType === 'kpi-over-time') { - const mainStartDate = parseAbsoluteDate(from)!; - const mainEndDate = parseAbsoluteDate(to, { roundUp: true })!; - const totalDuration = mainEndDate.diff(mainStartDate, 'millisecond'); - const newFrom = newDate.toISOString(); - const newTo = newDate.add(totalDuration, 'millisecond').toISOString(); + const onStartChange = (newStartDate: Moment) => { + if (reportType === ReportTypes.KPI) { + const totalDuration = getTotalDuration(); + const newFrom = newStartDate.toISOString(); + const newTo = newStartDate.add(totalDuration, 'millisecond').toISOString(); setSeries(seriesId, { ...series, time: { from: newFrom, to: newTo }, }); } else { - const newFrom = newDate.toISOString(); + const newFrom = newStartDate.toISOString(); setSeries(seriesId, { ...series, @@ -55,20 +55,19 @@ export function DateRangePicker({ seriesId }: { seriesId: string }) { }); } }; - const onEndChange = (newDate: Moment) => { - if (reportType === 'kpi-over-time') { - const mainStartDate = parseAbsoluteDate(from)!; - const mainEndDate = parseAbsoluteDate(to, { roundUp: true })!; - const totalDuration = mainEndDate.diff(mainStartDate, 'millisecond'); - const newTo = newDate.toISOString(); - const newFrom = newDate.subtract(totalDuration, 'millisecond').toISOString(); + + const onEndChange = (newEndDate: Moment) => { + if (reportType === ReportTypes.KPI) { + const totalDuration = getTotalDuration(); + const newTo = newEndDate.toISOString(); + const newFrom = newEndDate.subtract(totalDuration, 'millisecond').toISOString(); setSeries(seriesId, { ...series, time: { from: newFrom, to: newTo }, }); } else { - const newTo = newDate.toISOString(); + const newTo = newEndDate.toISOString(); setSeries(seriesId, { ...series, @@ -90,7 +89,7 @@ export function DateRangePicker({ seriesId }: { seriesId: string }) { aria-label={i18n.translate('xpack.observability.expView.dateRanger.startDate', { defaultMessage: 'Start date', })} - dateFormat={dateFormat} + dateFormat={dateFormat.replace('ss.SSS', 'ss')} showTimeSelect /> } @@ -104,7 +103,7 @@ export function DateRangePicker({ seriesId }: { seriesId: string }) { aria-label={i18n.translate('xpack.observability.expView.dateRanger.endDate', { defaultMessage: 'End date', })} - dateFormat={dateFormat} + dateFormat={dateFormat.replace('ss.SSS', 'ss')} showTimeSelect /> } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/empty_view.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/empty_view.tsx index 3566835b1701c..d17e451ef702c 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/empty_view.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/empty_view.tsx @@ -10,19 +10,19 @@ import { isEmpty } from 'lodash'; import { EuiFlexGroup, EuiFlexItem, EuiProgress, EuiSpacer, EuiText } from '@elastic/eui'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; -import { LOADING_VIEW } from '../series_builder/series_builder'; -import { SeriesUrl } from '../types'; +import { LOADING_VIEW } from '../series_editor/series_editor'; +import { ReportViewType, SeriesUrl } from '../types'; export function EmptyView({ loading, - height, series, + reportType, }: { loading: boolean; - height: string; - series: SeriesUrl; + series?: SeriesUrl; + reportType: ReportViewType; }) { - const { dataType, reportType, reportDefinitions } = series ?? {}; + const { dataType, reportDefinitions } = series ?? {}; let emptyMessage = EMPTY_LABEL; @@ -45,7 +45,7 @@ export function EmptyView({ } return ( - + {loading && ( ` +const Wrapper = styled.div` text-align: center; - height: ${(props) => props.height}; position: relative; `; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.test.tsx index fe2953edd36d6..03fd23631f755 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { fireEvent, screen, waitFor } from '@testing-library/react'; -import { mockAppIndexPattern, mockIndexPattern, render } from '../rtl_helpers'; +import { mockAppIndexPattern, mockIndexPattern, mockUxSeries, render } from '../rtl_helpers'; import { FilterLabel } from './filter_label'; import * as useSeriesHook from '../hooks/use_series_filters'; import { buildFilterLabel } from '../../filter_value_label/filter_value_label'; @@ -27,9 +27,10 @@ describe('FilterLabel', function () { value={'elastic-co'} label={'Web Application'} negate={false} - seriesId={'kpi-over-time'} + seriesId={0} removeFilter={jest.fn()} indexPattern={mockIndexPattern} + series={mockUxSeries} /> ); @@ -51,9 +52,10 @@ describe('FilterLabel', function () { value={'elastic-co'} label={'Web Application'} negate={false} - seriesId={'kpi-over-time'} + seriesId={0} removeFilter={removeFilter} indexPattern={mockIndexPattern} + series={mockUxSeries} /> ); @@ -74,9 +76,10 @@ describe('FilterLabel', function () { value={'elastic-co'} label={'Web Application'} negate={false} - seriesId={'kpi-over-time'} + seriesId={0} removeFilter={removeFilter} indexPattern={mockIndexPattern} + series={mockUxSeries} /> ); @@ -100,9 +103,10 @@ describe('FilterLabel', function () { value={'elastic-co'} label={'Web Application'} negate={true} - seriesId={'kpi-over-time'} + seriesId={0} removeFilter={jest.fn()} indexPattern={mockIndexPattern} + series={mockUxSeries} /> ); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.tsx index a08e777c5ea71..c6254a85de9ac 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/filter_label.tsx @@ -9,21 +9,24 @@ import React from 'react'; import { IndexPattern } from '../../../../../../../../src/plugins/data/public'; import { useSeriesFilters } from '../hooks/use_series_filters'; import { FilterValueLabel } from '../../filter_value_label/filter_value_label'; +import { SeriesUrl } from '../types'; interface Props { field: string; label: string; - value: string; - seriesId: string; + value: string | string[]; + seriesId: number; + series: SeriesUrl; negate: boolean; definitionFilter?: boolean; indexPattern: IndexPattern; - removeFilter: (field: string, value: string, notVal: boolean) => void; + removeFilter: (field: string, value: string | string[], notVal: boolean) => void; } export function FilterLabel({ label, seriesId, + series, field, value, negate, @@ -31,7 +34,7 @@ export function FilterLabel({ removeFilter, definitionFilter, }: Props) { - const { invertFilter } = useSeriesFilters({ seriesId }); + const { invertFilter } = useSeriesFilters({ seriesId, series }); return indexPattern ? ( { + setSeries(seriesId, { ...series, color: colorN }); + }; + + const color = + series.color ?? ((theme.eui as unknown) as Record)[`euiColorVis${seriesId}`]; + + const button = ( + + setIsOpen((prevState) => !prevState)} hasArrow={false}> + + + + ); + + return ( + setIsOpen(false)}> + + + + + ); +} + +const PICK_A_COLOR_LABEL = i18n.translate( + 'xpack.observability.overview.exploratoryView.pickColor', + { + defaultMessage: 'Pick a color', + } +); + +const EDIT_SERIES_COLOR_LABEL = i18n.translate( + 'xpack.observability.overview.exploratoryView.editSeriesColor', + { + defaultMessage: 'Edit color for series', + } +); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/components/series_date_picker/index.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/series_date_picker/index.tsx new file mode 100644 index 0000000000000..23d6589fecbcb --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/series_date_picker/index.tsx @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import { EuiSuperDatePicker, EuiText } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +import { useHasData } from '../../../../../hooks/use_has_data'; +import { useSeriesStorage } from '../../hooks/use_series_storage'; +import { useQuickTimeRanges } from '../../../../../hooks/use_quick_time_ranges'; +import { parseTimeParts } from '../../series_viewer/columns/utils'; +import { useUiSetting } from '../../../../../../../../../src/plugins/kibana_react/public'; +import { SeriesUrl } from '../../types'; +import { ReportTypes } from '../../configurations/constants'; + +export interface TimePickerTime { + from: string; + to: string; +} + +export interface TimePickerQuickRange extends TimePickerTime { + display: string; +} + +interface Props { + seriesId: number; + series: SeriesUrl; + readonly?: boolean; +} +const readableUnit: Record = { + m: i18n.translate('xpack.observability.overview.exploratoryView.minutes', { + defaultMessage: 'Minutes', + }), + h: i18n.translate('xpack.observability.overview.exploratoryView.hour', { + defaultMessage: 'Hour', + }), + d: i18n.translate('xpack.observability.overview.exploratoryView.day', { + defaultMessage: 'Day', + }), +}; + +export function SeriesDatePicker({ series, seriesId, readonly = true }: Props) { + const { onRefreshTimeRange } = useHasData(); + + const commonlyUsedRanges = useQuickTimeRanges(); + + const { setSeries, reportType, allSeries, firstSeries } = useSeriesStorage(); + + function onTimeChange({ start, end }: { start: string; end: string }) { + onRefreshTimeRange(); + if (reportType === ReportTypes.KPI) { + allSeries.forEach((currSeries, seriesIndex) => { + setSeries(seriesIndex, { ...currSeries, time: { from: start, to: end } }); + }); + } else { + setSeries(seriesId, { ...series, time: { from: start, to: end } }); + } + } + + const seriesTime = series.time ?? firstSeries!.time; + + const dateFormat = useUiSetting('dateFormat').replace('ss.SSS', 'ss'); + + if (readonly) { + const timeParts = parseTimeParts(seriesTime?.from, seriesTime?.to); + + if (timeParts) { + const { + timeTense: timeTenseDefault, + timeUnits: timeUnitsDefault, + timeValue: timeValueDefault, + } = timeParts; + + return ( + {`${timeTenseDefault} ${timeValueDefault} ${ + readableUnit?.[timeUnitsDefault] ?? timeUnitsDefault + }`} + ); + } else { + return ( + + {i18n.translate('xpack.observability.overview.exploratoryView.dateRangeReadonly', { + defaultMessage: '{start} to {end}', + values: { + start: moment(seriesTime.from).format(dateFormat), + end: moment(seriesTime.to).format(dateFormat), + }, + })} + + ); + } + } + + return ( + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/series_date_picker.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/series_date_picker/series_date_picker.test.tsx similarity index 50% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/series_date_picker.test.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/components/series_date_picker/series_date_picker.test.tsx index 931dfbe07cd23..3517508300e4b 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/series_date_picker.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/components/series_date_picker/series_date_picker.test.tsx @@ -6,67 +6,48 @@ */ import React from 'react'; -import { mockUseHasData, render } from '../rtl_helpers'; +import { mockUseHasData, render } from '../../rtl_helpers'; import { fireEvent, waitFor } from '@testing-library/react'; import { SeriesDatePicker } from './index'; -import { DEFAULT_TIME } from '../configurations/constants'; describe('SeriesDatePicker', function () { it('should render properly', function () { const initSeries = { - data: { - 'uptime-pings-histogram': { + data: [ + { + name: 'uptime-pings-histogram', dataType: 'synthetics' as const, - reportType: 'data-distribution' as const, breakdown: 'monitor.status', time: { from: 'now-30m', to: 'now' }, }, - }, + ], }; - const { getByText } = render(, { initSeries }); - - getByText('Last 30 minutes'); - }); - - it('should set defaults', async function () { - const initSeries = { - data: { - 'uptime-pings-histogram': { - reportType: 'kpi-over-time' as const, - dataType: 'synthetics' as const, - breakdown: 'monitor.status', - }, - }, - }; - const { setSeries: setSeries1 } = render( - , - { initSeries: initSeries as any } - ); - expect(setSeries1).toHaveBeenCalledTimes(1); - expect(setSeries1).toHaveBeenCalledWith('uptime-pings-histogram', { - breakdown: 'monitor.status', - dataType: 'synthetics' as const, - reportType: 'kpi-over-time' as const, - time: DEFAULT_TIME, + const { getByText } = render(, { + initSeries, }); + + getByText('Last 30 Minutes'); }); it('should set series data', async function () { const initSeries = { - data: { - 'uptime-pings-histogram': { + data: [ + { + name: 'uptime-pings-histogram', dataType: 'synthetics' as const, - reportType: 'kpi-over-time' as const, breakdown: 'monitor.status', time: { from: 'now-30m', to: 'now' }, }, - }, + ], }; const { onRefreshTimeRange } = mockUseHasData(); - const { getByTestId, setSeries } = render(, { - initSeries, - }); + const { getByTestId, setSeries } = render( + , + { + initSeries, + } + ); await waitFor(function () { fireEvent.click(getByTestId('superDatePickerToggleQuickMenuButton')); @@ -76,10 +57,10 @@ describe('SeriesDatePicker', function () { expect(onRefreshTimeRange).toHaveBeenCalledTimes(1); - expect(setSeries).toHaveBeenCalledWith('series-id', { + expect(setSeries).toHaveBeenCalledWith(0, { + name: 'uptime-pings-histogram', breakdown: 'monitor.status', dataType: 'synthetics', - reportType: 'kpi-over-time', time: { from: 'now/d', to: 'now/d' }, }); expect(setSeries).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/constants.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/constants.ts index ba1f2214223e3..bf5feb7d5863c 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/constants.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/constants.ts @@ -94,6 +94,19 @@ export const DataViewLabels: Record = { 'device-data-distribution': DEVICE_DISTRIBUTION_LABEL, }; +export enum ReportTypes { + KPI = 'kpi-over-time', + DISTRIBUTION = 'data-distribution', + CORE_WEB_VITAL = 'core-web-vitals', + DEVICE_DISTRIBUTION = 'device-data-distribution', +} + +export enum DataTypes { + SYNTHETICS = 'synthetics', + UX = 'ux', + MOBILE = 'mobile', +} + export const USE_BREAK_DOWN_COLUMN = 'USE_BREAK_DOWN_COLUMN'; export const FILTER_RECORDS = 'FILTER_RECORDS'; export const TERMS_COLUMN = 'TERMS_COLUMN'; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/url_constants.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/url_constants.ts index 6f990015fbc62..55ac75b47c056 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/url_constants.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/url_constants.ts @@ -8,10 +8,12 @@ export enum URL_KEYS { DATA_TYPE = 'dt', OPERATION_TYPE = 'op', - REPORT_TYPE = 'rt', SERIES_TYPE = 'st', BREAK_DOWN = 'bd', FILTERS = 'ft', REPORT_DEFINITIONS = 'rdf', SELECTED_METRIC = 'mt', + HIDDEN = 'h', + NAME = 'n', + COLOR = 'c', } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts index 574a9f6a2bc10..3f6551986527c 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts @@ -15,6 +15,7 @@ import { getCoreWebVitalsConfig } from './rum/core_web_vitals_config'; import { getMobileKPIConfig } from './mobile/kpi_over_time_config'; import { getMobileKPIDistributionConfig } from './mobile/distribution_config'; import { getMobileDeviceDistributionConfig } from './mobile/device_distribution_config'; +import { DataTypes, ReportTypes } from './constants'; interface Props { reportType: ReportViewType; @@ -24,24 +25,24 @@ interface Props { export const getDefaultConfigs = ({ reportType, dataType, indexPattern }: Props) => { switch (dataType) { - case 'ux': - if (reportType === 'data-distribution') { + case DataTypes.UX: + if (reportType === ReportTypes.DISTRIBUTION) { return getRumDistributionConfig({ indexPattern }); } - if (reportType === 'core-web-vitals') { + if (reportType === ReportTypes.CORE_WEB_VITAL) { return getCoreWebVitalsConfig({ indexPattern }); } return getKPITrendsLensConfig({ indexPattern }); - case 'synthetics': - if (reportType === 'data-distribution') { + case DataTypes.SYNTHETICS: + if (reportType === ReportTypes.DISTRIBUTION) { return getSyntheticsDistributionConfig({ indexPattern }); } return getSyntheticsKPIConfig({ indexPattern }); - case 'mobile': - if (reportType === 'data-distribution') { + case DataTypes.MOBILE: + if (reportType === ReportTypes.DISTRIBUTION) { return getMobileKPIDistributionConfig({ indexPattern }); } - if (reportType === 'device-data-distribution') { + if (reportType === ReportTypes.DEVICE_DISTRIBUTION) { return getMobileDeviceDistributionConfig({ indexPattern }); } return getMobileKPIConfig({ indexPattern }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts index ae70bbdcfa3b8..08d2da4714e47 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.test.ts @@ -16,7 +16,7 @@ import { } from './constants/elasticsearch_fieldnames'; import { buildExistsFilter, buildPhrasesFilter } from './utils'; import { sampleAttributeKpi } from './test_data/sample_attribute_kpi'; -import { REPORT_METRIC_FIELD } from './constants'; +import { RECORDS_FIELD, REPORT_METRIC_FIELD, ReportTypes } from './constants'; describe('Lens Attribute', () => { mockAppIndexPattern(); @@ -38,6 +38,9 @@ describe('Lens Attribute', () => { indexPattern: mockIndexPattern, reportDefinitions: {}, time: { from: 'now-15m', to: 'now' }, + color: 'green', + name: 'test-series', + selectedMetricField: TRANSACTION_DURATION, }; beforeEach(() => { @@ -50,7 +53,7 @@ describe('Lens Attribute', () => { it('should return expected json for kpi report type', function () { const seriesConfigKpi = getDefaultConfigs({ - reportType: 'kpi-over-time', + reportType: ReportTypes.KPI, dataType: 'ux', indexPattern: mockIndexPattern, }); @@ -63,6 +66,9 @@ describe('Lens Attribute', () => { indexPattern: mockIndexPattern, reportDefinitions: { 'service.name': ['elastic-co'] }, time: { from: 'now-15m', to: 'now' }, + color: 'green', + name: 'test-series', + selectedMetricField: RECORDS_FIELD, }, ]); @@ -135,6 +141,9 @@ describe('Lens Attribute', () => { indexPattern: mockIndexPattern, reportDefinitions: { 'performance.metric': [LCP_FIELD] }, time: { from: 'now-15m', to: 'now' }, + color: 'green', + name: 'test-series', + selectedMetricField: TRANSACTION_DURATION, }; lnsAttr = new LensAttributes([layerConfig1]); @@ -277,7 +286,7 @@ describe('Lens Attribute', () => { 'transaction.type: page-load and processor.event: transaction and transaction.type : *', }, isBucketed: false, - label: 'Pages loaded', + label: 'test-series', operationType: 'formula', params: { format: { @@ -383,7 +392,7 @@ describe('Lens Attribute', () => { palette: undefined, seriesType: 'line', xAccessor: 'x-axis-column-layer0', - yConfig: [{ forAccessor: 'y-axis-column-layer0' }], + yConfig: [{ color: 'green', forAccessor: 'y-axis-column-layer0' }], }, ], legend: { isVisible: true, position: 'right' }, @@ -403,6 +412,9 @@ describe('Lens Attribute', () => { reportDefinitions: { 'performance.metric': [LCP_FIELD] }, breakdown: USER_AGENT_NAME, time: { from: 'now-15m', to: 'now' }, + color: 'green', + name: 'test-series', + selectedMetricField: TRANSACTION_DURATION, }; lnsAttr = new LensAttributes([layerConfig1]); @@ -422,7 +434,7 @@ describe('Lens Attribute', () => { seriesType: 'line', splitAccessor: 'breakdown-column-layer0', xAccessor: 'x-axis-column-layer0', - yConfig: [{ forAccessor: 'y-axis-column-layer0' }], + yConfig: [{ color: 'green', forAccessor: 'y-axis-column-layer0' }], }, ]); @@ -483,7 +495,7 @@ describe('Lens Attribute', () => { 'transaction.type: page-load and processor.event: transaction and transaction.type : *', }, isBucketed: false, - label: 'Pages loaded', + label: 'test-series', operationType: 'formula', params: { format: { @@ -589,6 +601,9 @@ describe('Lens Attribute', () => { indexPattern: mockIndexPattern, reportDefinitions: { 'performance.metric': [LCP_FIELD] }, time: { from: 'now-15m', to: 'now' }, + color: 'green', + name: 'test-series', + selectedMetricField: TRANSACTION_DURATION, }; const filters = lnsAttr.getLayerFilters(layerConfig1, 2); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts index dfb17ee470d35..5426d3bcd4233 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { capitalize } from 'lodash'; + import { CountIndexPatternColumn, DateHistogramIndexPatternColumn, @@ -36,10 +37,11 @@ import { REPORT_METRIC_FIELD, RECORDS_FIELD, RECORDS_PERCENTAGE_FIELD, + ReportTypes, } from './constants'; import { ColumnFilter, SeriesConfig, UrlFilter, URLReportDefinition } from '../types'; import { PersistableFilter } from '../../../../../../lens/common'; -import { parseAbsoluteDate } from '../series_date_picker/date_range_picker'; +import { parseAbsoluteDate } from '../components/date_range_picker'; import { getDistributionInPercentageColumn } from './lens_columns/overall_column'; function getLayerReferenceName(layerId: string) { @@ -73,14 +75,6 @@ export const parseCustomFieldName = (seriesConfig: SeriesConfig, selectedMetricF timeScale = currField?.timeScale; columnLabel = currField?.label; } - } else if (metricOptions?.[0].field || metricOptions?.[0].id) { - const firstMetricOption = metricOptions?.[0]; - - selectedMetricField = firstMetricOption.field || firstMetricOption.id; - columnType = firstMetricOption.columnType; - columnFilters = firstMetricOption.columnFilters; - timeScale = firstMetricOption.timeScale; - columnLabel = firstMetricOption.label; } return { fieldName: selectedMetricField!, columnType, columnFilters, timeScale, columnLabel }; @@ -95,7 +89,9 @@ export interface LayerConfig { reportDefinitions: URLReportDefinition; time: { to: string; from: string }; indexPattern: IndexPattern; - selectedMetricField?: string; + selectedMetricField: string; + color: string; + name: string; } export class LensAttributes { @@ -471,14 +467,15 @@ export class LensAttributes { getLayerFilters(layerConfig: LayerConfig, totalLayers: number) { const { filters, - time: { from, to }, + time, seriesConfig: { baseFilters: layerFilters, reportType }, } = layerConfig; let baseFilters = ''; - if (reportType !== 'kpi-over-time' && totalLayers > 1) { + + if (reportType !== ReportTypes.KPI && totalLayers > 1 && time) { // for kpi over time, we don't need to add time range filters // since those are essentially plotted along the x-axis - baseFilters += `@timestamp >= ${from} and @timestamp <= ${to}`; + baseFilters += `@timestamp >= ${time.from} and @timestamp <= ${time.to}`; } layerFilters?.forEach((filter: PersistableFilter | ExistsFilter) => { @@ -534,7 +531,11 @@ export class LensAttributes { } getTimeShift(mainLayerConfig: LayerConfig, layerConfig: LayerConfig, index: number) { - if (index === 0 || mainLayerConfig.seriesConfig.reportType !== 'kpi-over-time') { + if ( + index === 0 || + mainLayerConfig.seriesConfig.reportType !== ReportTypes.KPI || + !layerConfig.time + ) { return null; } @@ -546,11 +547,14 @@ export class LensAttributes { time: { from }, } = layerConfig; - const inDays = parseAbsoluteDate(mainFrom).diff(parseAbsoluteDate(from), 'days'); + const inDays = Math.abs(parseAbsoluteDate(mainFrom).diff(parseAbsoluteDate(from), 'days')); if (inDays > 1) { return inDays + 'd'; } - const inHours = parseAbsoluteDate(mainFrom).diff(parseAbsoluteDate(from), 'hours'); + const inHours = Math.abs(parseAbsoluteDate(mainFrom).diff(parseAbsoluteDate(from), 'hours')); + if (inHours === 0) { + return null; + } return inHours + 'h'; } @@ -568,6 +572,12 @@ export class LensAttributes { const { sourceField } = seriesConfig.xAxisColumn; + let label = timeShift ? `${mainYAxis.label}(${timeShift})` : mainYAxis.label; + + if (layerConfig.seriesConfig.reportType !== ReportTypes.CORE_WEB_VITAL && layerConfig.name) { + label = layerConfig.name; + } + layers[layerId] = { columnOrder: [ `x-axis-column-${layerId}`, @@ -581,7 +591,7 @@ export class LensAttributes { [`x-axis-column-${layerId}`]: this.getXAxis(layerConfig, layerId), [`y-axis-column-${layerId}`]: { ...mainYAxis, - label: timeShift ? `${mainYAxis.label}(${timeShift})` : mainYAxis.label, + label, filter: { query: columnFilter, language: 'kuery' }, ...(timeShift ? { timeShift } : {}), }, @@ -624,7 +634,7 @@ export class LensAttributes { seriesType: layerConfig.seriesType || layerConfig.seriesConfig.defaultSeriesType, palette: layerConfig.seriesConfig.palette, yConfig: layerConfig.seriesConfig.yConfig || [ - { forAccessor: `y-axis-column-layer${index}` }, + { forAccessor: `y-axis-column-layer${index}`, color: layerConfig.color }, ], xAccessor: `x-axis-column-layer${index}`, ...(layerConfig.breakdown && @@ -638,7 +648,7 @@ export class LensAttributes { }; } - getJSON(): TypedLensByValueInput['attributes'] { + getJSON(refresh?: number): TypedLensByValueInput['attributes'] { const uniqueIndexPatternsIds = Array.from( new Set([...this.layerConfigs.map(({ indexPattern }) => indexPattern.id)]) ); @@ -647,7 +657,7 @@ export class LensAttributes { return { title: 'Prefilled from exploratory view app', - description: '', + description: String(refresh), visualizationType: 'lnsXY', references: [ ...uniqueIndexPatternsIds.map((patternId) => ({ diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/device_distribution_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/device_distribution_config.ts index d1612a08f5551..4e178bba7e02a 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/device_distribution_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/device_distribution_config.ts @@ -6,7 +6,7 @@ */ import { ConfigProps, SeriesConfig } from '../../types'; -import { FieldLabels, REPORT_METRIC_FIELD, USE_BREAK_DOWN_COLUMN } from '../constants'; +import { FieldLabels, REPORT_METRIC_FIELD, ReportTypes, USE_BREAK_DOWN_COLUMN } from '../constants'; import { buildPhraseFilter } from '../utils'; import { SERVICE_NAME } from '../constants/elasticsearch_fieldnames'; import { MOBILE_APP, NUMBER_OF_DEVICES } from '../constants/labels'; @@ -14,7 +14,7 @@ import { MobileFields } from './mobile_fields'; export function getMobileDeviceDistributionConfig({ indexPattern }: ConfigProps): SeriesConfig { return { - reportType: 'device-data-distribution', + reportType: ReportTypes.DEVICE_DISTRIBUTION, defaultSeriesType: 'bar', seriesTypes: ['bar', 'bar_horizontal'], xAxisColumn: { @@ -38,13 +38,13 @@ export function getMobileDeviceDistributionConfig({ indexPattern }: ConfigProps) ...MobileFields, [SERVICE_NAME]: MOBILE_APP, }, + definitionFields: [SERVICE_NAME], metricOptions: [ { - id: 'labels.device_id', field: 'labels.device_id', + id: 'labels.device_id', label: NUMBER_OF_DEVICES, }, ], - definitionFields: [SERVICE_NAME], }; } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/distribution_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/distribution_config.ts index 9b1c4c8da3e9b..1da27be4fcc95 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/distribution_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/distribution_config.ts @@ -6,7 +6,7 @@ */ import { ConfigProps, SeriesConfig } from '../../types'; -import { FieldLabels, RECORDS_FIELD, REPORT_METRIC_FIELD } from '../constants'; +import { FieldLabels, RECORDS_FIELD, REPORT_METRIC_FIELD, ReportTypes } from '../constants'; import { buildPhrasesFilter } from '../utils'; import { METRIC_SYSTEM_CPU_USAGE, @@ -21,7 +21,7 @@ import { MobileFields } from './mobile_fields'; export function getMobileKPIDistributionConfig({ indexPattern }: ConfigProps): SeriesConfig { return { - reportType: 'data-distribution', + reportType: ReportTypes.DISTRIBUTION, defaultSeriesType: 'bar', seriesTypes: ['line', 'bar'], xAxisColumn: { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/kpi_over_time_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/kpi_over_time_config.ts index 945a631078a33..3ee5b3125fcda 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/kpi_over_time_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/mobile/kpi_over_time_config.ts @@ -6,7 +6,13 @@ */ import { ConfigProps, SeriesConfig } from '../../types'; -import { FieldLabels, OPERATION_COLUMN, RECORDS_FIELD, REPORT_METRIC_FIELD } from '../constants'; +import { + FieldLabels, + OPERATION_COLUMN, + RECORDS_FIELD, + REPORT_METRIC_FIELD, + ReportTypes, +} from '../constants'; import { buildPhrasesFilter } from '../utils'; import { METRIC_SYSTEM_CPU_USAGE, @@ -26,7 +32,7 @@ import { MobileFields } from './mobile_fields'; export function getMobileKPIConfig({ indexPattern }: ConfigProps): SeriesConfig { return { - reportType: 'kpi-over-time', + reportType: ReportTypes.KPI, defaultSeriesType: 'line', seriesTypes: ['line', 'bar', 'area'], xAxisColumn: { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.test.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.test.ts index 07bb13f957e45..35e094996f6f2 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.test.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.test.ts @@ -9,7 +9,7 @@ import { mockAppIndexPattern, mockIndexPattern } from '../../rtl_helpers'; import { getDefaultConfigs } from '../default_configs'; import { LayerConfig, LensAttributes } from '../lens_attributes'; import { sampleAttributeCoreWebVital } from '../test_data/sample_attribute_cwv'; -import { SERVICE_NAME, USER_AGENT_OS } from '../constants/elasticsearch_fieldnames'; +import { LCP_FIELD, SERVICE_NAME, USER_AGENT_OS } from '../constants/elasticsearch_fieldnames'; describe('Core web vital config test', function () { mockAppIndexPattern(); @@ -24,10 +24,13 @@ describe('Core web vital config test', function () { const layerConfig: LayerConfig = { seriesConfig, + color: 'green', + name: 'test-series', + breakdown: USER_AGENT_OS, indexPattern: mockIndexPattern, - reportDefinitions: { [SERVICE_NAME]: ['elastic-co'] }, time: { from: 'now-15m', to: 'now' }, - breakdown: USER_AGENT_OS, + reportDefinitions: { [SERVICE_NAME]: ['elastic-co'] }, + selectedMetricField: LCP_FIELD, }; beforeEach(() => { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.ts index 62455df248085..e8d620388a89e 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/core_web_vitals_config.ts @@ -11,6 +11,7 @@ import { FieldLabels, FILTER_RECORDS, REPORT_METRIC_FIELD, + ReportTypes, USE_BREAK_DOWN_COLUMN, } from '../constants'; import { buildPhraseFilter } from '../utils'; @@ -38,7 +39,7 @@ export function getCoreWebVitalsConfig({ indexPattern }: ConfigProps): SeriesCon return { defaultSeriesType: 'bar_horizontal_percentage_stacked', - reportType: 'core-web-vitals', + reportType: ReportTypes.CORE_WEB_VITAL, seriesTypes: ['bar_horizontal_percentage_stacked'], xAxisColumn: { sourceField: USE_BREAK_DOWN_COLUMN, @@ -153,5 +154,6 @@ export function getCoreWebVitalsConfig({ indexPattern }: ConfigProps): SeriesCon { color: statusPallete[1], forAccessor: 'y-axis-column-1' }, { color: statusPallete[2], forAccessor: 'y-axis-column-2' }, ], + query: { query: 'transaction.type: "page-load"', language: 'kuery' }, }; } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/data_distribution_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/data_distribution_config.ts index f34c8db6c197d..de6f2c67b2aeb 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/data_distribution_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/data_distribution_config.ts @@ -6,7 +6,12 @@ */ import { ConfigProps, SeriesConfig } from '../../types'; -import { FieldLabels, REPORT_METRIC_FIELD, RECORDS_PERCENTAGE_FIELD } from '../constants'; +import { + FieldLabels, + REPORT_METRIC_FIELD, + RECORDS_PERCENTAGE_FIELD, + ReportTypes, +} from '../constants'; import { buildPhraseFilter } from '../utils'; import { CLIENT_GEO_COUNTRY_NAME, @@ -41,7 +46,7 @@ import { export function getRumDistributionConfig({ indexPattern }: ConfigProps): SeriesConfig { return { - reportType: 'data-distribution', + reportType: ReportTypes.DISTRIBUTION, defaultSeriesType: 'line', seriesTypes: [], xAxisColumn: { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/kpi_over_time_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/kpi_over_time_config.ts index 5899b16d12b4f..9112778eadaa7 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/kpi_over_time_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/rum/kpi_over_time_config.ts @@ -6,7 +6,13 @@ */ import { ConfigProps, SeriesConfig } from '../../types'; -import { FieldLabels, OPERATION_COLUMN, RECORDS_FIELD, REPORT_METRIC_FIELD } from '../constants'; +import { + FieldLabels, + OPERATION_COLUMN, + RECORDS_FIELD, + REPORT_METRIC_FIELD, + ReportTypes, +} from '../constants'; import { buildPhraseFilter } from '../utils'; import { CLIENT_GEO_COUNTRY_NAME, @@ -43,7 +49,7 @@ export function getKPITrendsLensConfig({ indexPattern }: ConfigProps): SeriesCon return { defaultSeriesType: 'bar_stacked', seriesTypes: [], - reportType: 'kpi-over-time', + reportType: ReportTypes.KPI, xAxisColumn: { sourceField: '@timestamp', }, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/data_distribution_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/data_distribution_config.ts index 730e742f9d8c5..da90f45d15201 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/data_distribution_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/data_distribution_config.ts @@ -6,7 +6,12 @@ */ import { ConfigProps, SeriesConfig } from '../../types'; -import { FieldLabels, REPORT_METRIC_FIELD, RECORDS_PERCENTAGE_FIELD } from '../constants'; +import { + FieldLabels, + REPORT_METRIC_FIELD, + RECORDS_PERCENTAGE_FIELD, + ReportTypes, +} from '../constants'; import { CLS_LABEL, DCL_LABEL, @@ -30,7 +35,7 @@ export function getSyntheticsDistributionConfig({ indexPattern, }: ConfigProps): SeriesConfig { return { - reportType: 'data-distribution', + reportType: ReportTypes.DISTRIBUTION, defaultSeriesType: series?.seriesType || 'line', seriesTypes: [], xAxisColumn: { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts index 4ee22181d4334..65b43a83a8fb5 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts @@ -6,7 +6,7 @@ */ import { ConfigProps, SeriesConfig } from '../../types'; -import { FieldLabels, OPERATION_COLUMN, REPORT_METRIC_FIELD } from '../constants'; +import { FieldLabels, OPERATION_COLUMN, REPORT_METRIC_FIELD, ReportTypes } from '../constants'; import { CLS_LABEL, DCL_LABEL, @@ -30,7 +30,7 @@ const SUMMARY_DOWN = 'summary.down'; export function getSyntheticsKPIConfig({ indexPattern }: ConfigProps): SeriesConfig { return { - reportType: 'kpi-over-time', + reportType: ReportTypes.KPI, defaultSeriesType: 'bar_stacked', seriesTypes: [], xAxisColumn: { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute.ts index 569d68ad4ebff..a5898f33e0ec0 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute.ts @@ -5,12 +5,18 @@ * 2.0. */ export const sampleAttribute = { - title: 'Prefilled from exploratory view app', - description: '', - visualizationType: 'lnsXY', + description: 'undefined', references: [ - { id: 'apm-*', name: 'indexpattern-datasource-current-indexpattern', type: 'index-pattern' }, - { id: 'apm-*', name: 'indexpattern-datasource-layer-layer0', type: 'index-pattern' }, + { + id: 'apm-*', + name: 'indexpattern-datasource-current-indexpattern', + type: 'index-pattern', + }, + { + id: 'apm-*', + name: 'indexpattern-datasource-layer-layer0', + type: 'index-pattern', + }, ], state: { datasourceStates: { @@ -28,17 +34,23 @@ export const sampleAttribute = { ], columns: { 'x-axis-column-layer0': { - sourceField: 'transaction.duration.us', - label: 'Page load time', dataType: 'number', - operationType: 'range', isBucketed: true, - scale: 'interval', + label: 'Page load time', + operationType: 'range', params: { - type: 'histogram', - ranges: [{ from: 0, to: 1000, label: '' }], maxBars: 'auto', + ranges: [ + { + from: 0, + label: '', + to: 1000, + }, + ], + type: 'histogram', }, + scale: 'interval', + sourceField: 'transaction.duration.us', }, 'y-axis-column-layer0': { dataType: 'number', @@ -48,7 +60,7 @@ export const sampleAttribute = { 'transaction.type: page-load and processor.event: transaction and transaction.type : *', }, isBucketed: false, - label: 'Pages loaded', + label: 'test-series', operationType: 'formula', params: { format: { @@ -81,16 +93,16 @@ export const sampleAttribute = { 'y-axis-column-layer0X1': { customLabel: true, dataType: 'number', - isBucketed: false, - label: 'Part of count() / overall_sum(count())', - operationType: 'count', - scale: 'ratio', - sourceField: 'Records', filter: { language: 'kuery', query: 'transaction.type: page-load and processor.event: transaction and transaction.type : *', }, + isBucketed: false, + label: 'Part of count() / overall_sum(count())', + operationType: 'count', + scale: 'ratio', + sourceField: 'Records', }, 'y-axis-column-layer0X2': { customLabel: true, @@ -141,26 +153,51 @@ export const sampleAttribute = { }, }, }, + filters: [], + query: { + language: 'kuery', + query: 'transaction.duration.us < 60000000', + }, visualization: { - legend: { isVisible: true, position: 'right' }, - valueLabels: 'hide', - fittingFunction: 'Linear', + axisTitlesVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, curveType: 'CURVE_MONOTONE_X', - axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, - tickLabelsVisibilitySettings: { x: true, yLeft: true, yRight: true }, - gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, - preferredSeriesType: 'line', + fittingFunction: 'Linear', + gridlinesVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, layers: [ { accessors: ['y-axis-column-layer0'], layerId: 'layer0', seriesType: 'line', - yConfig: [{ forAccessor: 'y-axis-column-layer0' }], xAccessor: 'x-axis-column-layer0', + yConfig: [ + { + color: 'green', + forAccessor: 'y-axis-column-layer0', + }, + ], }, ], + legend: { + isVisible: true, + position: 'right', + }, + preferredSeriesType: 'line', + tickLabelsVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, + valueLabels: 'hide', }, - query: { query: 'transaction.duration.us < 60000000', language: 'kuery' }, - filters: [], }, + title: 'Prefilled from exploratory view app', + visualizationType: 'lnsXY', }; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts index 2087b85b81886..425bf069cc87f 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts @@ -5,7 +5,7 @@ * 2.0. */ export const sampleAttributeCoreWebVital = { - description: '', + description: 'undefined', references: [ { id: 'apm-*', @@ -94,7 +94,7 @@ export const sampleAttributeCoreWebVital = { filters: [], query: { language: 'kuery', - query: '', + query: 'transaction.type: "page-load"', }, visualization: { axisTitlesVisibilitySettings: { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_kpi.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_kpi.ts index 7f066caf66bf1..85bafdecabde0 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_kpi.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_kpi.ts @@ -5,12 +5,18 @@ * 2.0. */ export const sampleAttributeKpi = { - title: 'Prefilled from exploratory view app', - description: '', - visualizationType: 'lnsXY', + description: 'undefined', references: [ - { id: 'apm-*', name: 'indexpattern-datasource-current-indexpattern', type: 'index-pattern' }, - { id: 'apm-*', name: 'indexpattern-datasource-layer-layer0', type: 'index-pattern' }, + { + id: 'apm-*', + name: 'indexpattern-datasource-current-indexpattern', + type: 'index-pattern', + }, + { + id: 'apm-*', + name: 'indexpattern-datasource-layer-layer0', + type: 'index-pattern', + }, ], state: { datasourceStates: { @@ -20,25 +26,27 @@ export const sampleAttributeKpi = { columnOrder: ['x-axis-column-layer0', 'y-axis-column-layer0'], columns: { 'x-axis-column-layer0': { - sourceField: '@timestamp', dataType: 'date', isBucketed: true, label: '@timestamp', operationType: 'date_histogram', - params: { interval: 'auto' }, + params: { + interval: 'auto', + }, scale: 'interval', + sourceField: '@timestamp', }, 'y-axis-column-layer0': { dataType: 'number', + filter: { + language: 'kuery', + query: 'transaction.type: page-load and processor.event: transaction', + }, isBucketed: false, - label: 'Page views', + label: 'test-series', operationType: 'count', scale: 'ratio', sourceField: 'Records', - filter: { - query: 'transaction.type: page-load and processor.event: transaction', - language: 'kuery', - }, }, }, incompleteColumns: {}, @@ -46,26 +54,51 @@ export const sampleAttributeKpi = { }, }, }, + filters: [], + query: { + language: 'kuery', + query: '', + }, visualization: { - legend: { isVisible: true, position: 'right' }, - valueLabels: 'hide', - fittingFunction: 'Linear', + axisTitlesVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, curveType: 'CURVE_MONOTONE_X', - axisTitlesVisibilitySettings: { x: true, yLeft: true, yRight: true }, - tickLabelsVisibilitySettings: { x: true, yLeft: true, yRight: true }, - gridlinesVisibilitySettings: { x: true, yLeft: true, yRight: true }, - preferredSeriesType: 'line', + fittingFunction: 'Linear', + gridlinesVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, layers: [ { accessors: ['y-axis-column-layer0'], layerId: 'layer0', seriesType: 'line', - yConfig: [{ forAccessor: 'y-axis-column-layer0' }], xAccessor: 'x-axis-column-layer0', + yConfig: [ + { + color: 'green', + forAccessor: 'y-axis-column-layer0', + }, + ], }, ], + legend: { + isVisible: true, + position: 'right', + }, + preferredSeriesType: 'line', + tickLabelsVisibilitySettings: { + x: true, + yLeft: true, + yRight: true, + }, + valueLabels: 'hide', }, - query: { query: '', language: 'kuery' }, - filters: [], }, + title: 'Prefilled from exploratory view app', + visualizationType: 'lnsXY', }; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts index f7df2939d9909..694250e5749cb 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts @@ -5,7 +5,7 @@ * 2.0. */ import rison, { RisonValue } from 'rison-node'; -import type { SeriesUrl, UrlFilter } from '../types'; +import type { ReportViewType, SeriesUrl, UrlFilter } from '../types'; import type { AllSeries, AllShortSeries } from '../hooks/use_series_storage'; import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/index_patterns'; import { esFilters, ExistsFilter } from '../../../../../../../../src/plugins/data/public'; @@ -16,40 +16,43 @@ export function convertToShortUrl(series: SeriesUrl) { const { operationType, seriesType, - reportType, breakdown, filters, reportDefinitions, dataType, selectedMetricField, + hidden, + name, + color, ...restSeries } = series; return { [URL_KEYS.OPERATION_TYPE]: operationType, - [URL_KEYS.REPORT_TYPE]: reportType, [URL_KEYS.SERIES_TYPE]: seriesType, [URL_KEYS.BREAK_DOWN]: breakdown, [URL_KEYS.FILTERS]: filters, [URL_KEYS.REPORT_DEFINITIONS]: reportDefinitions, [URL_KEYS.DATA_TYPE]: dataType, [URL_KEYS.SELECTED_METRIC]: selectedMetricField, + [URL_KEYS.HIDDEN]: hidden, + [URL_KEYS.NAME]: name, + [URL_KEYS.COLOR]: color, ...restSeries, }; } -export function createExploratoryViewUrl(allSeries: AllSeries, baseHref = '') { - const allSeriesIds = Object.keys(allSeries); - - const allShortSeries: AllShortSeries = {}; - - allSeriesIds.forEach((seriesKey) => { - allShortSeries[seriesKey] = convertToShortUrl(allSeries[seriesKey]); - }); +export function createExploratoryViewUrl( + { reportType, allSeries }: { reportType: ReportViewType; allSeries: AllSeries }, + baseHref = '' +) { + const allShortSeries: AllShortSeries = allSeries.map((series) => convertToShortUrl(series)); return ( baseHref + - `/app/observability/exploratory-view#?sr=${rison.encode(allShortSeries as RisonValue)}` + `/app/observability/exploratory-view/configure#?reportType=${reportType}&sr=${rison.encode( + (allShortSeries as unknown) as RisonValue + )}` ); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx index 989ebf17c2062..21c749258bebe 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx @@ -11,6 +11,13 @@ import { render, mockCore, mockAppIndexPattern } from './rtl_helpers'; import { ExploratoryView } from './exploratory_view'; import { getStubIndexPattern } from '../../../../../../../src/plugins/data/public/test_utils'; import * as obsvInd from './utils/observability_index_patterns'; +import * as pluginHook from '../../../hooks/use_plugin_context'; + +jest.spyOn(pluginHook, 'usePluginContext').mockReturnValue({ + appMountParameters: { + setHeaderActionMenu: jest.fn(), + }, +} as any); describe('ExploratoryView', () => { mockAppIndexPattern(); @@ -41,29 +48,18 @@ describe('ExploratoryView', () => { it('renders exploratory view', async () => { render(); - expect(await screen.findByText(/open in lens/i)).toBeInTheDocument(); + expect(await screen.findByText(/Preview/i)).toBeInTheDocument(); + expect(await screen.findByText(/Configure series/i)).toBeInTheDocument(); + expect(await screen.findByText(/Hide chart/i)).toBeInTheDocument(); + expect(await screen.findByText(/Refresh/i)).toBeInTheDocument(); expect( await screen.findByRole('heading', { name: /Performance Distribution/i }) ).toBeInTheDocument(); }); it('renders lens component when there is series', async () => { - const initSeries = { - data: { - 'ux-series': { - isNew: true, - dataType: 'ux' as const, - reportType: 'data-distribution' as const, - breakdown: 'user_agent .name', - reportDefinitions: { 'service.name': ['elastic-co'] }, - time: { from: 'now-15m', to: 'now' }, - }, - }, - }; - - render(, { initSeries }); + render(); - expect(await screen.findByText(/open in lens/i)).toBeInTheDocument(); expect((await screen.findAllByText('Performance distribution'))[0]).toBeInTheDocument(); expect(await screen.findByText(/Lens Embeddable Component/i)).toBeInTheDocument(); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx index af04108c56790..cb901b8b588f3 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx @@ -4,11 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { i18n } from '@kbn/i18n'; import React, { useEffect, useRef, useState } from 'react'; -import { EuiPanel, EuiTitle } from '@elastic/eui'; +import { EuiButtonEmpty, EuiPanel, EuiResizableContainer, EuiTitle } from '@elastic/eui'; import styled from 'styled-components'; -import { isEmpty } from 'lodash'; +import { useRouteMatch } from 'react-router-dom'; +import { PanelDirection } from '@elastic/eui/src/components/resizable_container/types'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { ObservabilityPublicPluginsStart } from '../../../plugin'; import { ExploratoryViewHeader } from './header/header'; @@ -16,40 +18,15 @@ import { useSeriesStorage } from './hooks/use_series_storage'; import { useLensAttributes } from './hooks/use_lens_attributes'; import { TypedLensByValueInput } from '../../../../../lens/public'; import { useAppIndexPatternContext } from './hooks/use_app_index_pattern'; -import { SeriesBuilder } from './series_builder/series_builder'; -import { SeriesUrl } from './types'; +import { SeriesViews } from './views/series_views'; import { LensEmbeddable } from './lens_embeddable'; import { EmptyView } from './components/empty_view'; -export const combineTimeRanges = ( - allSeries: Record, - firstSeries?: SeriesUrl -) => { - let to: string = ''; - let from: string = ''; - if (firstSeries?.reportType === 'kpi-over-time') { - return firstSeries.time; - } - Object.values(allSeries ?? {}).forEach((series) => { - if (series.dataType && series.reportType && !isEmpty(series.reportDefinitions)) { - const seriesTo = new Date(series.time.to); - const seriesFrom = new Date(series.time.from); - if (!to || seriesTo > new Date(to)) { - to = series.time.to; - } - if (!from || seriesFrom < new Date(from)) { - from = series.time.from; - } - } - }); - return { to, from }; -}; +export type PanelId = 'seriesPanel' | 'chartPanel'; export function ExploratoryView({ saveAttributes, - multiSeries, }: { - multiSeries?: boolean; saveAttributes?: (attr: TypedLensByValueInput['attributes'] | null) => void; }) { const { @@ -69,20 +46,19 @@ export function ExploratoryView({ const { loadIndexPattern, loading } = useAppIndexPatternContext(); - const { firstSeries, firstSeriesId, allSeries } = useSeriesStorage(); + const { firstSeries, allSeries, lastRefresh, reportType } = useSeriesStorage(); const lensAttributesT = useLensAttributes(); const setHeightOffset = () => { if (seriesBuilderRef?.current && wrapperRef.current) { const headerOffset = wrapperRef.current.getBoundingClientRect().top; - const seriesOffset = seriesBuilderRef.current.getBoundingClientRect().height; - setHeight(`calc(100vh - ${seriesOffset + headerOffset + 40}px)`); + setHeight(`calc(100vh - ${headerOffset + 40}px)`); } }; useEffect(() => { - Object.values(allSeries).forEach((seriesT) => { + allSeries.forEach((seriesT) => { loadIndexPattern({ dataType: seriesT.dataType, }); @@ -96,38 +72,104 @@ export function ExploratoryView({ } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [JSON.stringify(lensAttributesT ?? {})]); + }, [JSON.stringify(lensAttributesT ?? {}), lastRefresh]); useEffect(() => { setHeightOffset(); }); + const collapseFn = useRef<(id: PanelId, direction: PanelDirection) => void>(); + + const [hiddenPanel, setHiddenPanel] = useState(''); + + const isPreview = !!useRouteMatch('/exploratory-view/preview'); + + const onCollapse = (panelId: string) => { + setHiddenPanel((prevState) => (panelId === prevState ? '' : panelId)); + }; + + const onChange = (panelId: PanelId) => { + onCollapse(panelId); + if (collapseFn.current) { + collapseFn.current(panelId, panelId === 'seriesPanel' ? 'right' : 'left'); + } + }; + return ( {lens ? ( <> - + - {lensAttributes ? ( - - ) : ( - + + {(EuiResizablePanel, EuiResizableButton, { togglePanel }) => { + collapseFn.current = (id, direction) => togglePanel?.(id, { direction }); + + return ( + <> + + {lensAttributes ? ( + + ) : ( + + )} + + + + {!isPreview && + (hiddenPanel === 'chartPanel' ? ( + onChange('chartPanel')} iconType="arrowDown"> + {SHOW_CHART_LABEL} + + ) : ( + onChange('chartPanel')} + iconType="arrowUp" + color="text" + > + {HIDE_CHART_LABEL} + + ))} + + + + ); + }} + + {hiddenPanel === 'seriesPanel' && ( + onChange('seriesPanel')} iconType="arrowUp"> + {PREVIEW_LABEL} + )} - ) : ( -

- {i18n.translate('xpack.observability.overview.exploratoryView.lensDisabled', { - defaultMessage: - 'Lens app is not available, please enable Lens to use exploratory view.', - })} -

+

{LENS_NOT_AVAILABLE}

)}
@@ -147,4 +189,39 @@ const Wrapper = styled(EuiPanel)` margin: 0 auto; width: 100%; overflow-x: auto; + position: relative; +`; + +const ShowPreview = styled(EuiButtonEmpty)` + position: absolute; + bottom: 34px; +`; +const HideChart = styled(EuiButtonEmpty)` + position: absolute; + top: -35px; + right: 50px; `; +const ShowChart = styled(EuiButtonEmpty)` + position: absolute; + top: -10px; + right: 50px; +`; + +const HIDE_CHART_LABEL = i18n.translate('xpack.observability.overview.exploratoryView.hideChart', { + defaultMessage: 'Hide chart', +}); + +const SHOW_CHART_LABEL = i18n.translate('xpack.observability.overview.exploratoryView.showChart', { + defaultMessage: 'Show chart', +}); + +const PREVIEW_LABEL = i18n.translate('xpack.observability.overview.exploratoryView.preview', { + defaultMessage: 'Preview', +}); + +const LENS_NOT_AVAILABLE = i18n.translate( + 'xpack.observability.overview.exploratoryView.lensDisabled', + { + defaultMessage: 'Lens app is not available, please enable Lens to use exploratory view.', + } +); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.test.tsx index 8cd8977fcf741..1f910b946deb3 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.test.tsx @@ -8,51 +8,22 @@ import React from 'react'; import { render } from '../rtl_helpers'; import { ExploratoryViewHeader } from './header'; -import { fireEvent } from '@testing-library/dom'; +import * as pluginHook from '../../../../hooks/use_plugin_context'; + +jest.spyOn(pluginHook, 'usePluginContext').mockReturnValue({ + appMountParameters: { + setHeaderActionMenu: jest.fn(), + }, +} as any); describe('ExploratoryViewHeader', function () { it('should render properly', function () { const { getByText } = render( ); - getByText('Open in Lens'); - }); - - it('should be able to click open in lens', function () { - const initSeries = { - data: { - 'uptime-pings-histogram': { - dataType: 'synthetics' as const, - reportType: 'kpi-over-time' as const, - breakdown: 'monitor.status', - time: { from: 'now-15m', to: 'now' }, - }, - }, - }; - - const { getByText, core } = render( - , - { initSeries } - ); - fireEvent.click(getByText('Open in Lens')); - - expect(core?.lens?.navigateToPrefilledEditor).toHaveBeenCalledTimes(1); - expect(core?.lens?.navigateToPrefilledEditor).toHaveBeenCalledWith( - { - attributes: { title: 'Performance distribution' }, - id: '', - timeRange: { - from: 'now-15m', - to: 'now', - }, - }, - true - ); + getByText('Refresh'); }); }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx index ded56ec9e817f..bec8673f88b4e 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx @@ -5,43 +5,37 @@ * 2.0. */ -import React, { useState } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiBetaBadge, EuiButton, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import { TypedLensByValueInput, LensEmbeddableInput } from '../../../../../../lens/public'; -import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { TypedLensByValueInput } from '../../../../../../lens/public'; import { DataViewLabels } from '../configurations/constants'; -import { ObservabilityAppServices } from '../../../../application/types'; import { useSeriesStorage } from '../hooks/use_series_storage'; -import { combineTimeRanges } from '../exploratory_view'; +import { LastUpdated } from './last_updated'; +import { combineTimeRanges } from '../lens_embeddable'; +import { ExpViewActionMenu } from '../components/action_menu'; interface Props { - seriesId: string; + seriesId?: number; + lastUpdated?: number; lensAttributes: TypedLensByValueInput['attributes'] | null; } -export function ExploratoryViewHeader({ seriesId, lensAttributes }: Props) { - const kServices = useKibana().services; +export function ExploratoryViewHeader({ seriesId, lensAttributes, lastUpdated }: Props) { + const { getSeries, allSeries, setLastRefresh, reportType } = useSeriesStorage(); - const { lens } = kServices; + const series = seriesId ? getSeries(seriesId) : undefined; - const { getSeries, allSeries } = useSeriesStorage(); - - const series = getSeries(seriesId); - - const [isSaveOpen, setIsSaveOpen] = useState(false); - - const LensSaveModalComponent = lens.SaveModalComponent; - - const timeRange = combineTimeRanges(allSeries, series); + const timeRange = combineTimeRanges(reportType, allSeries, series); return ( <> +

- {DataViewLabels[series.reportType] ?? + {DataViewLabels[reportType] ?? i18n.translate('xpack.observability.expView.heading.label', { defaultMessage: 'Analyze data', })}{' '} @@ -57,53 +51,18 @@ export function ExploratoryViewHeader({ seriesId, lensAttributes }: Props) { - { - if (lensAttributes) { - lens.navigateToPrefilledEditor( - { - id: '', - timeRange, - attributes: lensAttributes, - }, - true - ); - } - }} - > - {i18n.translate('xpack.observability.expView.heading.openInLens', { - defaultMessage: 'Open in Lens', - })} - + - { - if (lensAttributes) { - setIsSaveOpen(true); - } - }} - > - {i18n.translate('xpack.observability.expView.heading.saveLensVisualization', { - defaultMessage: 'Save', - })} + setLastRefresh(Date.now())}> + {REFRESH_LABEL} - - {isSaveOpen && lensAttributes && ( - setIsSaveOpen(false)} - onSave={() => {}} - /> - )} ); } + +const REFRESH_LABEL = i18n.translate('xpack.observability.overview.exploratoryView.refresh', { + defaultMessage: 'Refresh', +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/last_updated.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/last_updated.tsx similarity index 55% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/last_updated.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/header/last_updated.tsx index 874171de123d2..c352ec0423dd8 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/last_updated.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/last_updated.tsx @@ -8,6 +8,7 @@ import React, { useEffect, useState } from 'react'; import { EuiIcon, EuiText } from '@elastic/eui'; import moment from 'moment'; +import { FormattedMessage } from '@kbn/i18n/react'; interface Props { lastUpdated?: number; @@ -18,20 +19,34 @@ export function LastUpdated({ lastUpdated }: Props) { useEffect(() => { const interVal = setInterval(() => { setRefresh(Date.now()); - }, 1000); + }, 5000); return () => { clearInterval(interVal); }; }, []); + useEffect(() => { + setRefresh(Date.now()); + }, [lastUpdated]); + if (!lastUpdated) { return null; } + const isWarning = moment().diff(moment(lastUpdated), 'minute') > 5; + const isDanger = moment().diff(moment(lastUpdated), 'minute') > 10; + return ( - - Last Updated: {moment(lastUpdated).from(refresh)} + + + ); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx index 7a5f12a72b1f0..d65917093d129 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx @@ -27,7 +27,7 @@ interface ProviderProps { } type HasAppDataState = Record; -type IndexPatternState = Record; +export type IndexPatternState = Record; type LoadingState = Record; export function IndexPatternContextProvider({ children }: ProviderProps) { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_discover_link.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_discover_link.tsx new file mode 100644 index 0000000000000..e86144c124949 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_discover_link.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useEffect, useState } from 'react'; +import { useKibana } from '../../../../utils/kibana_react'; +import { SeriesConfig, SeriesUrl } from '../types'; +import { useAppIndexPatternContext } from './use_app_index_pattern'; +import { buildExistsFilter, buildPhraseFilter, buildPhrasesFilter } from '../configurations/utils'; +import { getFiltersFromDefs } from './use_lens_attributes'; +import { RECORDS_FIELD, RECORDS_PERCENTAGE_FIELD } from '../configurations/constants'; + +interface UseDiscoverLink { + seriesConfig: SeriesConfig; + series: SeriesUrl; +} + +export const useDiscoverLink = ({ series, seriesConfig }: UseDiscoverLink) => { + const kServices = useKibana().services; + const { + application: { navigateToUrl }, + } = kServices; + + const { indexPatterns } = useAppIndexPatternContext(); + + const urlGenerator = kServices.discover?.urlGenerator; + const [discoverUrl, setDiscoverUrl] = useState(''); + + useEffect(() => { + const indexPattern = indexPatterns?.[series.dataType]; + + const definitions = series.reportDefinitions ?? {}; + const filters = [...(seriesConfig?.baseFilters ?? [])]; + + const definitionFilters = getFiltersFromDefs(definitions); + + definitionFilters.forEach(({ field, values = [] }) => { + if (values.length > 1) { + filters.push(buildPhrasesFilter(field, values, indexPattern)[0]); + } else { + filters.push(buildPhraseFilter(field, values[0], indexPattern)[0]); + } + }); + + const selectedMetricField = series.selectedMetricField; + + if ( + selectedMetricField && + selectedMetricField !== RECORDS_FIELD && + selectedMetricField !== RECORDS_PERCENTAGE_FIELD + ) { + filters.push(buildExistsFilter(selectedMetricField, indexPattern)[0]); + } + + const getDiscoverUrl = async () => { + if (!urlGenerator?.createUrl) return; + + const newUrl = await urlGenerator.createUrl({ + filters, + indexPatternId: indexPattern?.id, + }); + setDiscoverUrl(newUrl); + }; + getDiscoverUrl(); + }, [ + indexPatterns, + series.dataType, + series.reportDefinitions, + series.selectedMetricField, + seriesConfig?.baseFilters, + urlGenerator, + ]); + + const onClick = useCallback( + (event: React.MouseEvent) => { + if (discoverUrl) { + event.preventDefault(); + + return navigateToUrl(discoverUrl); + } + }, + [discoverUrl, navigateToUrl] + ); + + return { + href: discoverUrl, + onClick, + }; +}; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.ts index 8bb265b4f6d89..71945734eeabc 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.ts @@ -9,12 +9,18 @@ import { useMemo } from 'react'; import { isEmpty } from 'lodash'; import { TypedLensByValueInput } from '../../../../../../lens/public'; import { LayerConfig, LensAttributes } from '../configurations/lens_attributes'; -import { useSeriesStorage } from './use_series_storage'; +import { + AllSeries, + allSeriesKey, + convertAllShortSeries, + useSeriesStorage, +} from './use_series_storage'; import { getDefaultConfigs } from '../configurations/default_configs'; import { SeriesUrl, UrlFilter } from '../types'; import { useAppIndexPatternContext } from './use_app_index_pattern'; import { ALL_VALUES_SELECTED } from '../../field_value_suggestions/field_value_combobox'; +import { useTheme } from '../../../../hooks/use_theme'; export const getFiltersFromDefs = (reportDefinitions: SeriesUrl['reportDefinitions']) => { return Object.entries(reportDefinitions ?? {}) @@ -28,41 +34,56 @@ export const getFiltersFromDefs = (reportDefinitions: SeriesUrl['reportDefinitio }; export const useLensAttributes = (): TypedLensByValueInput['attributes'] | null => { - const { allSeriesIds, allSeries } = useSeriesStorage(); + const { storage, autoApply, allSeries, lastRefresh, reportType } = useSeriesStorage(); const { indexPatterns } = useAppIndexPatternContext(); + const theme = useTheme(); + return useMemo(() => { - if (isEmpty(indexPatterns) || isEmpty(allSeriesIds)) { + if (isEmpty(indexPatterns) || isEmpty(allSeries) || !reportType) { return null; } + const allSeriesT: AllSeries = autoApply + ? allSeries + : convertAllShortSeries(storage.get(allSeriesKey) ?? []); + const layerConfigs: LayerConfig[] = []; - allSeriesIds.forEach((seriesIdT) => { - const seriesT = allSeries[seriesIdT]; - const indexPattern = indexPatterns?.[seriesT?.dataType]; - if (indexPattern && seriesT.reportType && !isEmpty(seriesT.reportDefinitions)) { + allSeriesT.forEach((series, seriesIndex) => { + const indexPattern = indexPatterns?.[series?.dataType]; + + if ( + indexPattern && + !isEmpty(series.reportDefinitions) && + !series.hidden && + series.selectedMetricField + ) { const seriesConfig = getDefaultConfigs({ - reportType: seriesT.reportType, - dataType: seriesT.dataType, + reportType, indexPattern, + dataType: series.dataType, }); - const filters: UrlFilter[] = (seriesT.filters ?? []).concat( - getFiltersFromDefs(seriesT.reportDefinitions) + const filters: UrlFilter[] = (series.filters ?? []).concat( + getFiltersFromDefs(series.reportDefinitions) ); + const color = `euiColorVis${seriesIndex}`; + layerConfigs.push({ filters, indexPattern, seriesConfig, - time: seriesT.time, - breakdown: seriesT.breakdown, - seriesType: seriesT.seriesType, - operationType: seriesT.operationType, - reportDefinitions: seriesT.reportDefinitions ?? {}, - selectedMetricField: seriesT.selectedMetricField, + time: series.time, + name: series.name, + breakdown: series.breakdown, + seriesType: series.seriesType, + operationType: series.operationType, + reportDefinitions: series.reportDefinitions ?? {}, + selectedMetricField: series.selectedMetricField, + color: series.color ?? ((theme.eui as unknown) as Record)[color], }); } }); @@ -73,6 +94,6 @@ export const useLensAttributes = (): TypedLensByValueInput['attributes'] | null const lensAttributes = new LensAttributes(layerConfigs); - return lensAttributes.getJSON(); - }, [indexPatterns, allSeriesIds, allSeries]); + return lensAttributes.getJSON(lastRefresh); + }, [indexPatterns, allSeries, reportType, autoApply, storage, theme, lastRefresh]); }; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_filters.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_filters.ts index 2d2618bc46152..f2a6130cdc59d 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_filters.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_filters.ts @@ -6,18 +6,16 @@ */ import { useSeriesStorage } from './use_series_storage'; -import { UrlFilter } from '../types'; +import { SeriesUrl, UrlFilter } from '../types'; export interface UpdateFilter { field: string; - value: string; + value: string | string[]; negate?: boolean; } -export const useSeriesFilters = ({ seriesId }: { seriesId: string }) => { - const { getSeries, setSeries } = useSeriesStorage(); - - const series = getSeries(seriesId); +export const useSeriesFilters = ({ seriesId, series }: { seriesId: number; series: SeriesUrl }) => { + const { setSeries } = useSeriesStorage(); const filters = series.filters ?? []; @@ -26,10 +24,14 @@ export const useSeriesFilters = ({ seriesId }: { seriesId: string }) => { .map((filter) => { if (filter.field === field) { if (negate) { - const notValuesN = filter.notValues?.filter((val) => val !== value); + const notValuesN = filter.notValues?.filter((val) => + value instanceof Array ? !value.includes(val) : val !== value + ); return { ...filter, notValues: notValuesN }; } else { - const valuesN = filter.values?.filter((val) => val !== value); + const valuesN = filter.values?.filter((val) => + value instanceof Array ? !value.includes(val) : val !== value + ); return { ...filter, values: valuesN }; } } @@ -43,9 +45,9 @@ export const useSeriesFilters = ({ seriesId }: { seriesId: string }) => { const addFilter = ({ field, value, negate }: UpdateFilter) => { const currFilter: UrlFilter = { field }; if (negate) { - currFilter.notValues = [value]; + currFilter.notValues = value instanceof Array ? value : [value]; } else { - currFilter.values = [value]; + currFilter.values = value instanceof Array ? value : [value]; } if (filters.length === 0) { setSeries(seriesId, { ...series, filters: [currFilter] }); @@ -65,13 +67,26 @@ export const useSeriesFilters = ({ seriesId }: { seriesId: string }) => { const currNotValues = currFilter.notValues ?? []; const currValues = currFilter.values ?? []; - const notValues = currNotValues.filter((val) => val !== value); - const values = currValues.filter((val) => val !== value); + const notValues = currNotValues.filter((val) => + value instanceof Array ? !value.includes(val) : val !== value + ); + + const values = currValues.filter((val) => + value instanceof Array ? !value.includes(val) : val !== value + ); if (negate) { - notValues.push(value); + if (value instanceof Array) { + notValues.push(...value); + } else { + notValues.push(value); + } } else { - values.push(value); + if (value instanceof Array) { + values.push(...value); + } else { + values.push(value); + } } currFilter.notValues = notValues.length > 0 ? notValues : undefined; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.test.tsx index c32acc47abd1b..ce6d7bd94d8e4 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.test.tsx @@ -6,37 +6,39 @@ */ import React, { useEffect } from 'react'; - -import { UrlStorageContextProvider, useSeriesStorage } from './use_series_storage'; +import { Route, Router } from 'react-router-dom'; import { render } from '@testing-library/react'; +import { UrlStorageContextProvider, useSeriesStorage } from './use_series_storage'; +import { getHistoryFromUrl } from '../rtl_helpers'; -const mockSingleSeries = { - 'performance-distribution': { - reportType: 'data-distribution', +const mockSingleSeries = [ + { + name: 'performance-distribution', dataType: 'ux', breakdown: 'user_agent.name', time: { from: 'now-15m', to: 'now' }, }, -}; +]; -const mockMultipleSeries = { - 'performance-distribution': { - reportType: 'data-distribution', +const mockMultipleSeries = [ + { + name: 'performance-distribution', dataType: 'ux', breakdown: 'user_agent.name', time: { from: 'now-15m', to: 'now' }, }, - 'kpi-over-time': { - reportType: 'kpi-over-time', + { + name: 'kpi-over-time', dataType: 'synthetics', breakdown: 'user_agent.name', time: { from: 'now-15m', to: 'now' }, }, -}; +]; -describe('userSeries', function () { +describe('userSeriesStorage', function () { function setupTestComponent(seriesData: any) { const setData = jest.fn(); + function TestComponent() { const data = useSeriesStorage(); @@ -48,11 +50,20 @@ describe('userSeries', function () { } render( - - - + + + (key === 'sr' ? seriesData : null)), + set: jest.fn(), + }} + > + + + + ); return setData; @@ -63,22 +74,20 @@ describe('userSeries', function () { expect(setData).toHaveBeenCalledTimes(2); expect(setData).toHaveBeenLastCalledWith( expect.objectContaining({ - allSeries: { - 'performance-distribution': { - breakdown: 'user_agent.name', + allSeries: [ + { + name: 'performance-distribution', dataType: 'ux', - reportType: 'data-distribution', + breakdown: 'user_agent.name', time: { from: 'now-15m', to: 'now' }, }, - }, - allSeriesIds: ['performance-distribution'], + ], firstSeries: { - breakdown: 'user_agent.name', + name: 'performance-distribution', dataType: 'ux', - reportType: 'data-distribution', + breakdown: 'user_agent.name', time: { from: 'now-15m', to: 'now' }, }, - firstSeriesId: 'performance-distribution', }) ); }); @@ -89,42 +98,38 @@ describe('userSeries', function () { expect(setData).toHaveBeenCalledTimes(2); expect(setData).toHaveBeenLastCalledWith( expect.objectContaining({ - allSeries: { - 'performance-distribution': { - breakdown: 'user_agent.name', + allSeries: [ + { + name: 'performance-distribution', dataType: 'ux', - reportType: 'data-distribution', + breakdown: 'user_agent.name', time: { from: 'now-15m', to: 'now' }, }, - 'kpi-over-time': { - reportType: 'kpi-over-time', + { + name: 'kpi-over-time', dataType: 'synthetics', breakdown: 'user_agent.name', time: { from: 'now-15m', to: 'now' }, }, - }, - allSeriesIds: ['performance-distribution', 'kpi-over-time'], + ], firstSeries: { - breakdown: 'user_agent.name', + name: 'performance-distribution', dataType: 'ux', - reportType: 'data-distribution', + breakdown: 'user_agent.name', time: { from: 'now-15m', to: 'now' }, }, - firstSeriesId: 'performance-distribution', }) ); }); it('should return expected result when there are no series', function () { - const setData = setupTestComponent({}); + const setData = setupTestComponent([]); - expect(setData).toHaveBeenCalledTimes(2); + expect(setData).toHaveBeenCalledTimes(1); expect(setData).toHaveBeenLastCalledWith( expect.objectContaining({ - allSeries: {}, - allSeriesIds: [], + allSeries: [], firstSeries: undefined, - firstSeriesId: undefined, }) ); }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.tsx index a47a124d14b4d..04f8751e2a0b6 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.tsx @@ -6,6 +6,7 @@ */ import React, { createContext, useContext, useState, useEffect, useCallback } from 'react'; +import { useRouteMatch } from 'react-router-dom'; import { IKbnUrlStateStorage, ISessionStorageStateStorage, @@ -22,13 +23,19 @@ import { OperationType, SeriesType } from '../../../../../../lens/public'; import { URL_KEYS } from '../configurations/constants/url_constants'; export interface SeriesContextValue { - firstSeries: SeriesUrl; - firstSeriesId: string; - allSeriesIds: string[]; + firstSeries?: SeriesUrl; + autoApply: boolean; + lastRefresh: number; + setLastRefresh: (val: number) => void; + setAutoApply: (val: boolean) => void; + applyChanges: () => void; allSeries: AllSeries; - setSeries: (seriesIdN: string, newValue: SeriesUrl) => void; - getSeries: (seriesId: string) => SeriesUrl; - removeSeries: (seriesId: string) => void; + setSeries: (seriesIndex: number, newValue: SeriesUrl) => void; + getSeries: (seriesIndex: number) => SeriesUrl | undefined; + removeSeries: (seriesIndex: number) => void; + setReportType: (reportType: string) => void; + storage: IKbnUrlStateStorage | ISessionStorageStateStorage; + reportType: ReportViewType; } export const UrlStorageContext = createContext({} as SeriesContextValue); @@ -36,72 +43,112 @@ interface ProviderProps { storage: IKbnUrlStateStorage | ISessionStorageStateStorage; } -function convertAllShortSeries(allShortSeries: AllShortSeries) { - const allSeriesIds = Object.keys(allShortSeries); - const allSeriesN: AllSeries = {}; - allSeriesIds.forEach((seriesKey) => { - allSeriesN[seriesKey] = convertFromShortUrl(allShortSeries[seriesKey]); - }); - - return allSeriesN; +export function convertAllShortSeries(allShortSeries: AllShortSeries) { + return (allShortSeries ?? []).map((shortSeries) => convertFromShortUrl(shortSeries)); } +export const allSeriesKey = 'sr'; +const autoApplyKey = 'autoApply'; +const reportTypeKey = 'reportType'; + export function UrlStorageContextProvider({ children, storage, }: ProviderProps & { children: JSX.Element }) { - const allSeriesKey = 'sr'; - - const [allShortSeries, setAllShortSeries] = useState( - () => storage.get(allSeriesKey) ?? {} - ); const [allSeries, setAllSeries] = useState(() => - convertAllShortSeries(storage.get(allSeriesKey) ?? {}) + convertAllShortSeries(storage.get(allSeriesKey) ?? []) + ); + + const [autoApply, setAutoApply] = useState(() => storage.get(autoApplyKey) ?? true); + const [lastRefresh, setLastRefresh] = useState(() => Date.now()); + + const [reportType, setReportType] = useState( + () => (storage as IKbnUrlStateStorage).get(reportTypeKey) ?? '' ); - const [firstSeriesId, setFirstSeriesId] = useState(''); + const [firstSeries, setFirstSeries] = useState(); + const isPreview = !!useRouteMatch('/exploratory-view/preview'); useEffect(() => { - const allSeriesIds = Object.keys(allShortSeries); - const allSeriesN: AllSeries = convertAllShortSeries(allShortSeries ?? {}); + const allShortSeries = allSeries.map((series) => convertToShortUrl(series)); - setAllSeries(allSeriesN); - setFirstSeriesId(allSeriesIds?.[0]); - setFirstSeries(allSeriesN?.[allSeriesIds?.[0]]); - (storage as IKbnUrlStateStorage).set(allSeriesKey, allShortSeries); - }, [allShortSeries, storage]); + const firstSeriesT = allSeries?.[0]; - const setSeries = (seriesIdN: string, newValue: SeriesUrl) => { - setAllShortSeries((prevState) => { - prevState[seriesIdN] = convertToShortUrl(newValue); - return { ...prevState }; - }); - }; + setFirstSeries(firstSeriesT); - const removeSeries = (seriesIdN: string) => { - setAllShortSeries((prevState) => { - delete prevState[seriesIdN]; - return { ...prevState }; + if (autoApply) { + (storage as IKbnUrlStateStorage).set(allSeriesKey, allShortSeries); + } + }, [allSeries, autoApply, storage]); + + useEffect(() => { + // needed for tab change + const allShortSeries = allSeries.map((series) => convertToShortUrl(series)); + + (storage as IKbnUrlStateStorage).set(allSeriesKey, allShortSeries); + (storage as IKbnUrlStateStorage).set(reportTypeKey, reportType); + // this is only needed for tab change, so we will not add allSeries into dependencies + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isPreview, storage]); + + const setSeries = useCallback((seriesIndex: number, newValue: SeriesUrl) => { + setAllSeries((prevAllSeries) => { + const newStateRest = prevAllSeries.map((series, index) => { + if (index === seriesIndex) { + return newValue; + } + return series; + }); + + if (prevAllSeries.length === seriesIndex) { + return [...newStateRest, newValue]; + } + + return [...newStateRest]; }); - }; + }, []); + + useEffect(() => { + (storage as IKbnUrlStateStorage).set(reportTypeKey, reportType); + }, [reportType, storage]); - const allSeriesIds = Object.keys(allShortSeries); + const removeSeries = useCallback((seriesIndex: number) => { + setAllSeries((prevAllSeries) => + prevAllSeries.filter((seriesT, index) => index !== seriesIndex) + ); + }, []); const getSeries = useCallback( - (seriesId?: string) => { - return seriesId ? allSeries?.[seriesId] ?? {} : ({} as SeriesUrl); + (seriesIndex: number) => { + return allSeries[seriesIndex]; }, [allSeries] ); + const applyChanges = useCallback(() => { + const allShortSeries = allSeries.map((series) => convertToShortUrl(series)); + + (storage as IKbnUrlStateStorage).set(allSeriesKey, allShortSeries); + setLastRefresh(Date.now()); + }, [allSeries, storage]); + + useEffect(() => { + (storage as IKbnUrlStateStorage).set(autoApplyKey, autoApply); + }, [autoApply, storage]); + const value = { + autoApply, + setAutoApply, + applyChanges, storage, getSeries, setSeries, removeSeries, - firstSeriesId, allSeries, - allSeriesIds, + lastRefresh, + setLastRefresh, + setReportType, + reportType: storage.get(reportTypeKey) as ReportViewType, firstSeries: firstSeries!, }; return {children}; @@ -112,10 +159,9 @@ export function useSeriesStorage() { } function convertFromShortUrl(newValue: ShortUrlSeries): SeriesUrl { - const { dt, op, st, rt, bd, ft, time, rdf, mt, ...restSeries } = newValue; + const { dt, op, st, bd, ft, time, rdf, mt, h, n, c, ...restSeries } = newValue; return { operationType: op, - reportType: rt!, seriesType: st, breakdown: bd, filters: ft!, @@ -123,26 +169,31 @@ function convertFromShortUrl(newValue: ShortUrlSeries): SeriesUrl { reportDefinitions: rdf, dataType: dt!, selectedMetricField: mt, + hidden: h, + name: n, + color: c, ...restSeries, }; } interface ShortUrlSeries { [URL_KEYS.OPERATION_TYPE]?: OperationType; - [URL_KEYS.REPORT_TYPE]?: ReportViewType; [URL_KEYS.DATA_TYPE]?: AppDataType; [URL_KEYS.SERIES_TYPE]?: SeriesType; [URL_KEYS.BREAK_DOWN]?: string; [URL_KEYS.FILTERS]?: UrlFilter[]; [URL_KEYS.REPORT_DEFINITIONS]?: URLReportDefinition; [URL_KEYS.SELECTED_METRIC]?: string; + [URL_KEYS.HIDDEN]?: boolean; + [URL_KEYS.NAME]: string; + [URL_KEYS.COLOR]?: string; time?: { to: string; from: string; }; } -export type AllShortSeries = Record; -export type AllSeries = Record; +export type AllShortSeries = ShortUrlSeries[]; +export type AllSeries = SeriesUrl[]; -export const NEW_SERIES_KEY = 'new-series-key'; +export const NEW_SERIES_KEY = 'new-series'; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/index.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/index.tsx index e55752ceb62ba..3de29b02853e8 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/index.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/index.tsx @@ -25,11 +25,9 @@ import { TypedLensByValueInput } from '../../../../../lens/public'; export function ExploratoryViewPage({ saveAttributes, - multiSeries = false, useSessionStorage = false, }: { useSessionStorage?: boolean; - multiSeries?: boolean; saveAttributes?: (attr: TypedLensByValueInput['attributes'] | null) => void; }) { useTrackPageview({ app: 'observability-overview', path: 'exploratory-view' }); @@ -61,7 +59,7 @@ export function ExploratoryViewPage({ - + diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx index 4cb586fe94ceb..9e4d9486dc155 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx @@ -7,16 +7,51 @@ import { i18n } from '@kbn/i18n'; import React, { Dispatch, SetStateAction, useCallback } from 'react'; -import { combineTimeRanges } from './exploratory_view'; +import styled from 'styled-components'; +import { isEmpty } from 'lodash'; import { TypedLensByValueInput } from '../../../../../lens/public'; import { useSeriesStorage } from './hooks/use_series_storage'; import { ObservabilityPublicPluginsStart } from '../../../plugin'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { ReportViewType, SeriesUrl } from './types'; +import { ReportTypes } from './configurations/constants'; interface Props { lensAttributes: TypedLensByValueInput['attributes']; setLastUpdated: Dispatch>; } +export const combineTimeRanges = ( + reportType: ReportViewType, + allSeries: SeriesUrl[], + firstSeries?: SeriesUrl +) => { + let to: string = ''; + let from: string = ''; + + if (reportType === ReportTypes.KPI) { + return firstSeries?.time; + } + + allSeries.forEach((series) => { + if ( + series.dataType && + series.selectedMetricField && + !isEmpty(series.reportDefinitions) && + series.time + ) { + const seriesTo = new Date(series.time.to); + const seriesFrom = new Date(series.time.from); + if (!to || seriesTo > new Date(to)) { + to = series.time.to; + } + if (!from || seriesFrom < new Date(from)) { + from = series.time.from; + } + } + }); + + return { to, from }; +}; export function LensEmbeddable(props: Props) { const { lensAttributes, setLastUpdated } = props; @@ -27,9 +62,11 @@ export function LensEmbeddable(props: Props) { const LensComponent = lens?.EmbeddableComponent; - const { firstSeriesId, firstSeries: series, setSeries, allSeries } = useSeriesStorage(); + const { firstSeries, setSeries, allSeries, reportType } = useSeriesStorage(); - const timeRange = combineTimeRanges(allSeries, series); + const firstSeriesId = 0; + + const timeRange = firstSeries ? combineTimeRanges(reportType, allSeries, firstSeries) : null; const onLensLoad = useCallback(() => { setLastUpdated(Date.now()); @@ -37,9 +74,9 @@ export function LensEmbeddable(props: Props) { const onBrushEnd = useCallback( ({ range }: { range: number[] }) => { - if (series?.reportType !== 'data-distribution') { + if (reportType !== 'data-distribution' && firstSeries) { setSeries(firstSeriesId, { - ...series, + ...firstSeries, time: { from: new Date(range[0]).toISOString(), to: new Date(range[1]).toISOString(), @@ -53,16 +90,30 @@ export function LensEmbeddable(props: Props) { ); } }, - [notifications?.toasts, series, firstSeriesId, setSeries] + [reportType, setSeries, firstSeries, notifications?.toasts] ); + if (timeRange === null || !firstSeries) { + return null; + } + return ( - + + + ); } + +const LensWrapper = styled.div` + height: 100%; + + &&& > div { + height: 100%; + } +`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/rtl_helpers.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/rtl_helpers.tsx index 972e3beb4b722..0e609cbe6c9e5 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/rtl_helpers.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/rtl_helpers.tsx @@ -10,7 +10,7 @@ import React, { ReactElement } from 'react'; import { stringify } from 'query-string'; // eslint-disable-next-line import/no-extraneous-dependencies import { render as reactTestLibRender, RenderOptions } from '@testing-library/react'; -import { Router } from 'react-router-dom'; +import { Route, Router } from 'react-router-dom'; import { createMemoryHistory, History } from 'history'; import { CoreStart } from 'kibana/public'; import { I18nProvider } from '@kbn/i18n/react'; @@ -24,7 +24,7 @@ import { EuiThemeProvider } from '../../../../../../../src/plugins/kibana_react/ import { lensPluginMock } from '../../../../../lens/public/mocks'; import * as useAppIndexPatternHook from './hooks/use_app_index_pattern'; import { IndexPatternContextProvider } from './hooks/use_app_index_pattern'; -import { AllSeries, UrlStorageContext } from './hooks/use_series_storage'; +import { AllSeries, SeriesContextValue, UrlStorageContext } from './hooks/use_series_storage'; import * as fetcherHook from '../../../hooks/use_fetcher'; import * as useSeriesFilterHook from './hooks/use_series_filters'; @@ -39,9 +39,10 @@ import { IndexPattern, IndexPatternsContract, } from '../../../../../../../src/plugins/data/common/index_patterns/index_patterns'; -import { AppDataType, UrlFilter } from './types'; +import { AppDataType, SeriesUrl, UrlFilter } from './types'; import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; import { ListItem } from '../../../hooks/use_values_list'; +import { TRANSACTION_DURATION } from './configurations/constants/elasticsearch_fieldnames'; interface KibanaProps { services?: KibanaServices; @@ -158,9 +159,11 @@ export function MockRouter({ }: MockRouterProps) { return ( - - {children} - + + + {children} + + ); } @@ -173,7 +176,7 @@ export function render( core: customCore, kibanaProps, renderOptions, - url, + url = '/app/observability/exploratory-view/configure#?autoApply=!t', initSeries = {}, }: RenderRouterOptions = {} ) { @@ -203,7 +206,7 @@ export function render( }; } -const getHistoryFromUrl = (url: Url) => { +export const getHistoryFromUrl = (url: Url) => { if (typeof url === 'string') { return createMemoryHistory({ initialEntries: [url], @@ -252,6 +255,15 @@ export const mockUseValuesList = (values?: ListItem[]) => { return { spy, onRefreshTimeRange }; }; +export const mockUxSeries = { + name: 'performance-distribution', + dataType: 'ux', + breakdown: 'user_agent.name', + time: { from: 'now-15m', to: 'now' }, + reportDefinitions: { 'service.name': ['elastic-co'] }, + selectedMetricField: TRANSACTION_DURATION, +} as SeriesUrl; + function mockSeriesStorageContext({ data, filters, @@ -261,34 +273,34 @@ function mockSeriesStorageContext({ filters?: UrlFilter[]; breakdown?: string; }) { - const mockDataSeries = data || { - 'performance-distribution': { - reportType: 'data-distribution', - dataType: 'ux', - breakdown: breakdown || 'user_agent.name', - time: { from: 'now-15m', to: 'now' }, - ...(filters ? { filters } : {}), - }, + const testSeries = { + ...mockUxSeries, + breakdown: breakdown || 'user_agent.name', + ...(filters ? { filters } : {}), }; - const allSeriesIds = Object.keys(mockDataSeries); - const firstSeriesId = allSeriesIds?.[0]; - const series = mockDataSeries[firstSeriesId]; + const mockDataSeries = data || [testSeries]; const removeSeries = jest.fn(); const setSeries = jest.fn(); - const getSeries = jest.fn().mockReturnValue(series); + const getSeries = jest.fn().mockReturnValue(testSeries); return { - firstSeriesId, - allSeriesIds, removeSeries, setSeries, getSeries, - firstSeries: mockDataSeries[firstSeriesId], + autoApply: true, + reportType: 'data-distribution', + lastRefresh: Date.now(), + setLastRefresh: jest.fn(), + setAutoApply: jest.fn(), + applyChanges: jest.fn(), + firstSeries: mockDataSeries[0], allSeries: mockDataSeries, - }; + setReportType: jest.fn(), + storage: { get: jest.fn() } as any, + } as SeriesContextValue; } export function mockUseSeriesFilter() { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.test.tsx deleted file mode 100644 index b10702ebded57..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.test.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { fireEvent, screen } from '@testing-library/react'; -import { mockAppIndexPattern, render } from '../../rtl_helpers'; -import { dataTypes, DataTypesCol } from './data_types_col'; - -describe('DataTypesCol', function () { - const seriesId = 'test-series-id'; - - mockAppIndexPattern(); - - it('should render properly', function () { - const { getByText } = render(); - - dataTypes.forEach(({ label }) => { - getByText(label); - }); - }); - - it('should set series on change', function () { - const { setSeries } = render(); - - fireEvent.click(screen.getByText(/user experience \(rum\)/i)); - - expect(setSeries).toHaveBeenCalledTimes(1); - expect(setSeries).toHaveBeenCalledWith(seriesId, { - dataType: 'ux', - isNew: true, - time: { - from: 'now-15m', - to: 'now', - }, - }); - }); - - it('should set series on change on already selected', function () { - const initSeries = { - data: { - [seriesId]: { - dataType: 'synthetics' as const, - reportType: 'kpi-over-time' as const, - breakdown: 'monitor.status', - time: { from: 'now-15m', to: 'now' }, - }, - }, - }; - - render(, { initSeries }); - - const button = screen.getByRole('button', { - name: /Synthetic Monitoring/i, - }); - - expect(button.classList).toContain('euiButton--fill'); - }); -}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.tsx deleted file mode 100644 index f386f62d9ed73..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/data_types_col.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import styled from 'styled-components'; -import { AppDataType } from '../../types'; -import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; -import { useSeriesStorage } from '../../hooks/use_series_storage'; - -export const dataTypes: Array<{ id: AppDataType; label: string }> = [ - { id: 'synthetics', label: 'Synthetic Monitoring' }, - { id: 'ux', label: 'User Experience (RUM)' }, - { id: 'mobile', label: 'Mobile Experience' }, - // { id: 'infra_logs', label: 'Logs' }, - // { id: 'infra_metrics', label: 'Metrics' }, - // { id: 'apm', label: 'APM' }, -]; - -export function DataTypesCol({ seriesId }: { seriesId: string }) { - const { getSeries, setSeries, removeSeries } = useSeriesStorage(); - - const series = getSeries(seriesId); - const { loading } = useAppIndexPatternContext(); - - const onDataTypeChange = (dataType?: AppDataType) => { - if (!dataType) { - removeSeries(seriesId); - } else { - setSeries(seriesId || `${dataType}-series`, { - dataType, - isNew: true, - time: series.time, - } as any); - } - }; - - const selectedDataType = series.dataType; - - return ( - - {dataTypes.map(({ id: dataTypeId, label }) => ( - - - - ))} - - ); -} - -const FlexGroup = styled(EuiFlexGroup)` - width: 100%; -`; - -const Button = styled(EuiButton)` - will-change: transform; -`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/date_picker_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/date_picker_col.tsx deleted file mode 100644 index 6be78084ae195..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/date_picker_col.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import styled from 'styled-components'; -import { SeriesDatePicker } from '../../series_date_picker'; -import { DateRangePicker } from '../../series_date_picker/date_range_picker'; -import { useSeriesStorage } from '../../hooks/use_series_storage'; - -interface Props { - seriesId: string; -} -export function DatePickerCol({ seriesId }: Props) { - const { firstSeriesId, getSeries } = useSeriesStorage(); - const { reportType } = getSeries(firstSeriesId); - - return ( - - {firstSeriesId === seriesId || reportType !== 'kpi-over-time' ? ( - - ) : ( - - )} - - ); -} - -const Wrapper = styled.div` - .euiSuperDatePicker__flexWrapper { - width: 100%; - > .euiFlexItem { - margin-right: 0px; - } - } -`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.test.tsx deleted file mode 100644 index a5e5ad3900ded..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.test.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { fireEvent, screen } from '@testing-library/react'; -import { getDefaultConfigs } from '../../configurations/default_configs'; -import { mockIndexPattern, render } from '../../rtl_helpers'; -import { ReportBreakdowns } from './report_breakdowns'; -import { USER_AGENT_OS } from '../../configurations/constants/elasticsearch_fieldnames'; - -describe('Series Builder ReportBreakdowns', function () { - const seriesId = 'test-series-id'; - const dataViewSeries = getDefaultConfigs({ - reportType: 'data-distribution', - dataType: 'ux', - indexPattern: mockIndexPattern, - }); - - it('should render properly', function () { - render(); - - screen.getByText('Select an option: , is selected'); - screen.getAllByText('Browser family'); - }); - - it('should set new series breakdown on change', function () { - const { setSeries } = render( - - ); - - const btn = screen.getByRole('button', { - name: /select an option: Browser family , is selected/i, - hidden: true, - }); - - fireEvent.click(btn); - - fireEvent.click(screen.getByText(/operating system/i)); - - expect(setSeries).toHaveBeenCalledTimes(1); - expect(setSeries).toHaveBeenCalledWith(seriesId, { - breakdown: USER_AGENT_OS, - dataType: 'ux', - reportType: 'data-distribution', - time: { from: 'now-15m', to: 'now' }, - }); - }); - it('should set undefined on new series on no select breakdown', function () { - const { setSeries } = render( - - ); - - const btn = screen.getByRole('button', { - name: /select an option: Browser family , is selected/i, - hidden: true, - }); - - fireEvent.click(btn); - - fireEvent.click(screen.getByText(/no breakdown/i)); - - expect(setSeries).toHaveBeenCalledTimes(1); - expect(setSeries).toHaveBeenCalledWith(seriesId, { - breakdown: undefined, - dataType: 'ux', - reportType: 'data-distribution', - time: { from: 'now-15m', to: 'now' }, - }); - }); -}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.tsx deleted file mode 100644 index fa2d01691ce1d..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_breakdowns.tsx +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { Breakdowns } from '../../series_editor/columns/breakdowns'; -import { SeriesConfig } from '../../types'; - -export function ReportBreakdowns({ - seriesId, - seriesConfig, -}: { - seriesConfig: SeriesConfig; - seriesId: string; -}) { - return ( - - ); -} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.tsx deleted file mode 100644 index 0c620abf56e8a..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.tsx +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; -import styled from 'styled-components'; -import { useSeriesStorage } from '../../hooks/use_series_storage'; -import { ReportMetricOptions } from '../report_metric_options'; -import { SeriesConfig } from '../../types'; -import { SeriesChartTypesSelect } from './chart_types'; -import { OperationTypeSelect } from './operation_type_select'; -import { DatePickerCol } from './date_picker_col'; -import { parseCustomFieldName } from '../../configurations/lens_attributes'; -import { ReportDefinitionField } from './report_definition_field'; - -function getColumnType(seriesConfig: SeriesConfig, selectedMetricField?: string) { - const { columnType } = parseCustomFieldName(seriesConfig, selectedMetricField); - - return columnType; -} - -export function ReportDefinitionCol({ - seriesConfig, - seriesId, -}: { - seriesConfig: SeriesConfig; - seriesId: string; -}) { - const { getSeries, setSeries } = useSeriesStorage(); - - const series = getSeries(seriesId); - - const { reportDefinitions: selectedReportDefinitions = {}, selectedMetricField } = series ?? {}; - - const { - definitionFields, - defaultSeriesType, - hasOperationType, - yAxisColumns, - metricOptions, - } = seriesConfig; - - const onChange = (field: string, value?: string[]) => { - if (!value?.[0]) { - delete selectedReportDefinitions[field]; - setSeries(seriesId, { - ...series, - reportDefinitions: { ...selectedReportDefinitions }, - }); - } else { - setSeries(seriesId, { - ...series, - reportDefinitions: { ...selectedReportDefinitions, [field]: value }, - }); - } - }; - - const columnType = getColumnType(seriesConfig, selectedMetricField); - - return ( - - - - - - {definitionFields.map((field) => ( - - - - ))} - {metricOptions && ( - - - - )} - {(hasOperationType || columnType === 'operation') && ( - - - - )} - - - - - ); -} - -const FlexGroup = styled(EuiFlexGroup)` - width: 100%; -`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.test.tsx deleted file mode 100644 index 0b183b5f20c03..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { screen } from '@testing-library/react'; -import { ReportFilters } from './report_filters'; -import { getDefaultConfigs } from '../../configurations/default_configs'; -import { mockIndexPattern, render } from '../../rtl_helpers'; - -describe('Series Builder ReportFilters', function () { - const seriesId = 'test-series-id'; - - const dataViewSeries = getDefaultConfigs({ - reportType: 'data-distribution', - indexPattern: mockIndexPattern, - dataType: 'ux', - }); - - it('should render properly', function () { - render(); - - screen.getByText('Add filter'); - }); -}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.tsx deleted file mode 100644 index d5938c5387e8f..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_filters.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { SeriesFilter } from '../../series_editor/columns/series_filter'; -import { SeriesConfig } from '../../types'; - -export function ReportFilters({ - seriesConfig, - seriesId, -}: { - seriesConfig: SeriesConfig; - seriesId: string; -}) { - return ( - - ); -} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.test.tsx deleted file mode 100644 index 12ae8560453c9..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.test.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { fireEvent, screen } from '@testing-library/react'; -import { mockAppIndexPattern, render } from '../../rtl_helpers'; -import { ReportTypesCol, SELECTED_DATA_TYPE_FOR_REPORT } from './report_types_col'; -import { ReportTypes } from '../series_builder'; -import { DEFAULT_TIME } from '../../configurations/constants'; - -describe('ReportTypesCol', function () { - const seriesId = 'performance-distribution'; - - mockAppIndexPattern(); - - it('should render properly', function () { - render(); - screen.getByText('Performance distribution'); - screen.getByText('KPI over time'); - }); - - it('should display empty message', function () { - render(); - screen.getByText(SELECTED_DATA_TYPE_FOR_REPORT); - }); - - it('should set series on change', function () { - const { setSeries } = render( - - ); - - fireEvent.click(screen.getByText(/KPI over time/i)); - - expect(setSeries).toHaveBeenCalledWith(seriesId, { - dataType: 'ux', - selectedMetricField: undefined, - reportType: 'kpi-over-time', - time: { from: 'now-15m', to: 'now' }, - }); - expect(setSeries).toHaveBeenCalledTimes(1); - }); - - it('should set selected as filled', function () { - const initSeries = { - data: { - [seriesId]: { - dataType: 'synthetics' as const, - reportType: 'kpi-over-time' as const, - breakdown: 'monitor.status', - time: { from: 'now-15m', to: 'now' }, - isNew: true, - }, - }, - }; - - const { setSeries } = render( - , - { initSeries } - ); - - const button = screen.getByRole('button', { - name: /KPI over time/i, - }); - - expect(button.classList).toContain('euiButton--fill'); - fireEvent.click(button); - - // undefined on click selected - expect(setSeries).toHaveBeenCalledWith(seriesId, { - dataType: 'synthetics', - time: DEFAULT_TIME, - isNew: true, - }); - }); -}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.tsx deleted file mode 100644 index c4eebbfaca3eb..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_types_col.tsx +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { map } from 'lodash'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import styled from 'styled-components'; -import { ReportViewType, SeriesUrl } from '../../types'; -import { useSeriesStorage } from '../../hooks/use_series_storage'; -import { DEFAULT_TIME } from '../../configurations/constants'; -import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; -import { ReportTypeItem } from '../series_builder'; - -interface Props { - seriesId: string; - reportTypes: ReportTypeItem[]; -} - -export function ReportTypesCol({ seriesId, reportTypes }: Props) { - const { setSeries, getSeries, firstSeries, firstSeriesId } = useSeriesStorage(); - - const { reportType: selectedReportType, ...restSeries } = getSeries(seriesId); - - const { loading, hasData } = useAppIndexPatternContext(restSeries.dataType); - - if (!restSeries.dataType) { - return ( - - ); - } - - if (!loading && !hasData) { - return ( - - ); - } - - const disabledReportTypes: ReportViewType[] = map( - reportTypes.filter( - ({ reportType }) => firstSeriesId !== seriesId && reportType !== firstSeries.reportType - ), - 'reportType' - ); - - return reportTypes?.length > 0 ? ( - - {reportTypes.map(({ reportType, label }) => ( - - - - ))} - - ) : ( - {SELECTED_DATA_TYPE_FOR_REPORT} - ); -} - -export const SELECTED_DATA_TYPE_FOR_REPORT = i18n.translate( - 'xpack.observability.expView.reportType.noDataType', - { defaultMessage: 'No data type selected.' } -); - -const FlexGroup = styled(EuiFlexGroup)` - width: 100%; -`; - -const Button = styled(EuiButton)` - will-change: transform; -`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/report_metric_options.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/report_metric_options.tsx deleted file mode 100644 index a2a3e34c21834..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/report_metric_options.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiSuperSelect } from '@elastic/eui'; -import { useSeriesStorage } from '../hooks/use_series_storage'; -import { SeriesConfig } from '../types'; - -interface Props { - seriesId: string; - defaultValue?: string; - options: SeriesConfig['metricOptions']; -} - -export function ReportMetricOptions({ seriesId, options: opts }: Props) { - const { getSeries, setSeries } = useSeriesStorage(); - - const series = getSeries(seriesId); - - const onChange = (value: string) => { - setSeries(seriesId, { - ...series, - selectedMetricField: value, - }); - }; - - const options = opts ?? []; - - return ( - ({ - value: fd || id, - inputDisplay: label, - }))} - valueOfSelected={series.selectedMetricField || options?.[0].field || options?.[0].id} - onChange={(value) => onChange(value)} - /> - ); -} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/series_builder.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/series_builder.tsx deleted file mode 100644 index 684cf3a210a51..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/series_builder.tsx +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { RefObject, useEffect, useState } from 'react'; -import { isEmpty } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { - EuiBasicTable, - EuiButton, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiSwitch, -} from '@elastic/eui'; -import { rgba } from 'polished'; -import { AppDataType, SeriesConfig, ReportViewType, SeriesUrl } from '../types'; -import { DataTypesCol } from './columns/data_types_col'; -import { ReportTypesCol } from './columns/report_types_col'; -import { ReportDefinitionCol } from './columns/report_definition_col'; -import { ReportFilters } from './columns/report_filters'; -import { ReportBreakdowns } from './columns/report_breakdowns'; -import { NEW_SERIES_KEY, useSeriesStorage } from '../hooks/use_series_storage'; -import { useAppIndexPatternContext } from '../hooks/use_app_index_pattern'; -import { getDefaultConfigs } from '../configurations/default_configs'; -import { SeriesEditor } from '../series_editor/series_editor'; -import { SeriesActions } from '../series_editor/columns/series_actions'; -import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; -import { LastUpdated } from './last_updated'; -import { - CORE_WEB_VITALS_LABEL, - DEVICE_DISTRIBUTION_LABEL, - KPI_OVER_TIME_LABEL, - PERF_DIST_LABEL, -} from '../configurations/constants/labels'; - -export interface ReportTypeItem { - id: string; - reportType: ReportViewType; - label: string; -} - -export const ReportTypes: Record = { - synthetics: [ - { id: 'kpi', reportType: 'kpi-over-time', label: KPI_OVER_TIME_LABEL }, - { id: 'dist', reportType: 'data-distribution', label: PERF_DIST_LABEL }, - ], - ux: [ - { id: 'kpi', reportType: 'kpi-over-time', label: KPI_OVER_TIME_LABEL }, - { id: 'dist', reportType: 'data-distribution', label: PERF_DIST_LABEL }, - { id: 'cwv', reportType: 'core-web-vitals', label: CORE_WEB_VITALS_LABEL }, - ], - mobile: [ - { id: 'kpi', reportType: 'kpi-over-time', label: KPI_OVER_TIME_LABEL }, - { id: 'dist', reportType: 'data-distribution', label: PERF_DIST_LABEL }, - { id: 'mdd', reportType: 'device-data-distribution', label: DEVICE_DISTRIBUTION_LABEL }, - ], - apm: [], - infra_logs: [], - infra_metrics: [], -}; - -interface BuilderItem { - id: string; - series: SeriesUrl; - seriesConfig?: SeriesConfig; -} - -export function SeriesBuilder({ - seriesBuilderRef, - lastUpdated, - multiSeries, -}: { - seriesBuilderRef: RefObject; - lastUpdated?: number; - multiSeries?: boolean; -}) { - const [editorItems, setEditorItems] = useState([]); - const { getSeries, allSeries, allSeriesIds, setSeries, removeSeries } = useSeriesStorage(); - - const { loading, indexPatterns } = useAppIndexPatternContext(); - - useEffect(() => { - const getDataViewSeries = (dataType: AppDataType, reportType: SeriesUrl['reportType']) => { - if (indexPatterns?.[dataType]) { - return getDefaultConfigs({ - dataType, - indexPattern: indexPatterns[dataType], - reportType: reportType!, - }); - } - }; - - const seriesToEdit: BuilderItem[] = - allSeriesIds - .filter((sId) => { - return allSeries?.[sId]?.isNew; - }) - .map((sId) => { - const series = getSeries(sId); - const seriesConfig = getDataViewSeries(series.dataType, series.reportType); - - return { id: sId, series, seriesConfig }; - }) ?? []; - const initSeries: BuilderItem[] = [{ id: 'series-id', series: {} as SeriesUrl }]; - setEditorItems(multiSeries || seriesToEdit.length > 0 ? seriesToEdit : initSeries); - }, [allSeries, allSeriesIds, getSeries, indexPatterns, loading, multiSeries]); - - const columns = [ - { - name: i18n.translate('xpack.observability.expView.seriesBuilder.dataType', { - defaultMessage: 'Data Type', - }), - field: 'id', - width: '15%', - render: (seriesId: string) => , - }, - { - name: i18n.translate('xpack.observability.expView.seriesBuilder.report', { - defaultMessage: 'Report', - }), - width: '15%', - field: 'id', - render: (seriesId: string, { series: { dataType } }: BuilderItem) => ( - - ), - }, - { - name: i18n.translate('xpack.observability.expView.seriesBuilder.definition', { - defaultMessage: 'Definition', - }), - width: '30%', - field: 'id', - render: ( - seriesId: string, - { series: { dataType, reportType }, seriesConfig }: BuilderItem - ) => { - if (dataType && seriesConfig) { - return loading ? ( - LOADING_VIEW - ) : reportType ? ( - - ) : ( - SELECT_REPORT_TYPE - ); - } - - return null; - }, - }, - { - name: i18n.translate('xpack.observability.expView.seriesBuilder.filters', { - defaultMessage: 'Filters', - }), - width: '20%', - field: 'id', - render: (seriesId: string, { series: { reportType }, seriesConfig }: BuilderItem) => - reportType && seriesConfig ? ( - - ) : null, - }, - { - name: i18n.translate('xpack.observability.expView.seriesBuilder.breakdown', { - defaultMessage: 'Breakdowns', - }), - width: '20%', - field: 'id', - render: (seriesId: string, { series: { reportType }, seriesConfig }: BuilderItem) => - reportType && seriesConfig ? ( - - ) : null, - }, - ...(multiSeries - ? [ - { - name: i18n.translate('xpack.observability.expView.seriesBuilder.actions', { - defaultMessage: 'Actions', - }), - align: 'center' as const, - width: '10%', - field: 'id', - render: (seriesId: string, item: BuilderItem) => ( - - ), - }, - ] - : []), - ]; - - const applySeries = () => { - editorItems.forEach(({ series, id: seriesId }) => { - const { reportType, reportDefinitions, isNew, ...restSeries } = series; - - if (reportType && !isEmpty(reportDefinitions)) { - const reportDefId = Object.values(reportDefinitions ?? {})[0]; - const newSeriesId = `${reportDefId}-${reportType}`; - - const newSeriesN: SeriesUrl = { - ...restSeries, - reportType, - reportDefinitions, - }; - - setSeries(newSeriesId, newSeriesN); - removeSeries(seriesId); - } - }); - }; - - const addSeries = () => { - const prevSeries = allSeries?.[allSeriesIds?.[0]]; - setSeries( - `${NEW_SERIES_KEY}-${editorItems.length + 1}`, - prevSeries - ? ({ isNew: true, time: prevSeries.time } as SeriesUrl) - : ({ isNew: true } as SeriesUrl) - ); - }; - - return ( - - {multiSeries && ( - - - - - - {}} - compressed - /> - - - applySeries()} isDisabled={true} size="s"> - {i18n.translate('xpack.observability.expView.seriesBuilder.apply', { - defaultMessage: 'Apply changes', - })} - - - - addSeries()} size="s"> - {i18n.translate('xpack.observability.expView.seriesBuilder.addSeries', { - defaultMessage: 'Add Series', - })} - - - - )} -
- {multiSeries && } - {editorItems.length > 0 && ( - - )} - -
-
- ); -} - -const Wrapper = euiStyled.div` - max-height: 50vh; - overflow-y: scroll; - overflow-x: clip; - &::-webkit-scrollbar { - height: ${({ theme }) => theme.eui.euiScrollBar}; - width: ${({ theme }) => theme.eui.euiScrollBar}; - } - &::-webkit-scrollbar-thumb { - background-clip: content-box; - background-color: ${({ theme }) => rgba(theme.eui.euiColorDarkShade, 0.5)}; - border: ${({ theme }) => theme.eui.euiScrollBarCorner} solid transparent; - } - &::-webkit-scrollbar-corner, - &::-webkit-scrollbar-track { - background-color: transparent; - } -`; - -export const LOADING_VIEW = i18n.translate( - 'xpack.observability.expView.seriesBuilder.loadingView', - { - defaultMessage: 'Loading view ...', - } -); - -export const SELECT_REPORT_TYPE = i18n.translate( - 'xpack.observability.expView.seriesBuilder.selectReportType', - { - defaultMessage: 'No report type selected', - } -); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/index.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/index.tsx deleted file mode 100644 index e21da424b58c8..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_date_picker/index.tsx +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiSuperDatePicker } from '@elastic/eui'; -import React, { useEffect } from 'react'; -import { useHasData } from '../../../../hooks/use_has_data'; -import { useSeriesStorage } from '../hooks/use_series_storage'; -import { useQuickTimeRanges } from '../../../../hooks/use_quick_time_ranges'; -import { DEFAULT_TIME } from '../configurations/constants'; - -export interface TimePickerTime { - from: string; - to: string; -} - -export interface TimePickerQuickRange extends TimePickerTime { - display: string; -} - -interface Props { - seriesId: string; -} - -export function SeriesDatePicker({ seriesId }: Props) { - const { onRefreshTimeRange } = useHasData(); - - const commonlyUsedRanges = useQuickTimeRanges(); - - const { getSeries, setSeries } = useSeriesStorage(); - - const series = getSeries(seriesId); - - function onTimeChange({ start, end }: { start: string; end: string }) { - onRefreshTimeRange(); - setSeries(seriesId, { ...series, time: { from: start, to: end } }); - } - - useEffect(() => { - if (!series || !series.time) { - setSeries(seriesId, { ...series, time: DEFAULT_TIME }); - } - }, [series, seriesId, setSeries]); - - return ( - - ); -} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/chart_edit_options.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/chart_edit_options.tsx deleted file mode 100644 index 207a53e13f1ad..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/chart_edit_options.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { Breakdowns } from './columns/breakdowns'; -import { SeriesConfig } from '../types'; -import { ChartOptions } from './columns/chart_options'; - -interface Props { - seriesConfig: SeriesConfig; - seriesId: string; - breakdownFields: string[]; -} -export function ChartEditOptions({ seriesConfig, seriesId, breakdownFields }: Props) { - return ( - - - - - - - - - ); -} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_options.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_options.tsx deleted file mode 100644 index f2a6377fd9b71..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_options.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { SeriesConfig } from '../../types'; -import { OperationTypeSelect } from '../../series_builder/columns/operation_type_select'; -import { SeriesChartTypesSelect } from '../../series_builder/columns/chart_types'; - -interface Props { - seriesConfig: SeriesConfig; - seriesId: string; -} - -export function ChartOptions({ seriesConfig, seriesId }: Props) { - return ( - - - - - {seriesConfig.hasOperationType && ( - - - - )} - - ); -} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/chart_types.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.test.tsx similarity index 85% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/chart_types.test.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.test.tsx index c054853d9c877..8f196b8a05dda 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/chart_types.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.test.tsx @@ -7,12 +7,12 @@ import React from 'react'; import { fireEvent, screen, waitFor } from '@testing-library/react'; -import { render } from '../../rtl_helpers'; +import { mockUxSeries, render } from '../../rtl_helpers'; import { SeriesChartTypesSelect, XYChartTypesSelect } from './chart_types'; describe.skip('SeriesChartTypesSelect', function () { it('should render properly', async function () { - render(); + render(); await waitFor(() => { screen.getByText(/chart type/i); @@ -21,7 +21,7 @@ describe.skip('SeriesChartTypesSelect', function () { it('should call set series on change', async function () { const { setSeries } = render( - + ); await waitFor(() => { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/chart_types.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.tsx similarity index 77% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/chart_types.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.tsx index 50c2f91e6067d..27d846502dbe6 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/chart_types.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/chart_types.tsx @@ -6,11 +6,11 @@ */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSuperSelect } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiIcon, EuiSuperSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../../../../../../../../../src/plugins/kibana_react/public'; import { ObservabilityPublicPluginsStart } from '../../../../../plugin'; -import { useFetcher } from '../../../../..'; +import { SeriesUrl, useFetcher } from '../../../../..'; import { useSeriesStorage } from '../../hooks/use_series_storage'; import { SeriesType } from '../../../../../../../lens/public'; @@ -20,16 +20,14 @@ const CHART_TYPE_LABEL = i18n.translate('xpack.observability.expView.chartTypes. export function SeriesChartTypesSelect({ seriesId, - seriesTypes, + series, defaultChartType, }: { - seriesId: string; - seriesTypes?: SeriesType[]; + seriesId: number; + series: SeriesUrl; defaultChartType: SeriesType; }) { - const { getSeries, setSeries } = useSeriesStorage(); - - const series = getSeries(seriesId); + const { setSeries } = useSeriesStorage(); const seriesType = series?.seriesType ?? defaultChartType; @@ -42,17 +40,15 @@ export function SeriesChartTypesSelect({ onChange={onChange} value={seriesType} excludeChartTypes={['bar_percentage_stacked']} - includeChartTypes={ - seriesTypes || [ - 'bar', - 'bar_horizontal', - 'line', - 'area', - 'bar_stacked', - 'area_stacked', - 'bar_horizontal_percentage_stacked', - ] - } + includeChartTypes={[ + 'bar', + 'bar_horizontal', + 'line', + 'area', + 'bar_stacked', + 'area_stacked', + 'bar_horizontal_percentage_stacked', + ]} label={CHART_TYPE_LABEL} /> ); @@ -105,14 +101,14 @@ export function XYChartTypesSelect({ }); return ( - + + + ); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/data_type_select.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/data_type_select.test.tsx new file mode 100644 index 0000000000000..838631e1f05df --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/data_type_select.test.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { fireEvent, screen } from '@testing-library/react'; +import { mockAppIndexPattern, mockUxSeries, render } from '../../rtl_helpers'; +import { DataTypesLabels, DataTypesSelect } from './data_type_select'; +import { DataTypes } from '../../configurations/constants'; + +describe('DataTypeSelect', function () { + const seriesId = 0; + + mockAppIndexPattern(); + + it('should render properly', function () { + render(); + }); + + it('should set series on change', async function () { + const { setSeries } = render(); + + fireEvent.click(await screen.findByText(DataTypesLabels[DataTypes.UX])); + fireEvent.click(await screen.findByText(DataTypesLabels[DataTypes.SYNTHETICS])); + + expect(setSeries).toHaveBeenCalledTimes(1); + expect(setSeries).toHaveBeenCalledWith(seriesId, { + dataType: 'synthetics', + name: 'synthetics-series-1', + time: { + from: 'now-15m', + to: 'now', + }, + }); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/data_type_select.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/data_type_select.tsx new file mode 100644 index 0000000000000..b0a6e3b5e26b0 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/data_type_select.tsx @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiSuperSelect } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { useSeriesStorage } from '../../hooks/use_series_storage'; +import { AppDataType, SeriesUrl } from '../../types'; +import { DataTypes, ReportTypes } from '../../configurations/constants'; + +interface Props { + seriesId: number; + series: SeriesUrl; +} + +export const DataTypesLabels = { + [DataTypes.UX]: i18n.translate('xpack.observability.overview.exploratoryView.uxLabel', { + defaultMessage: 'User experience (RUM)', + }), + + [DataTypes.SYNTHETICS]: i18n.translate( + 'xpack.observability.overview.exploratoryView.syntheticsLabel', + { + defaultMessage: 'Synthetics monitoring', + } + ), + + [DataTypes.MOBILE]: i18n.translate( + 'xpack.observability.overview.exploratoryView.mobileExperienceLabel', + { + defaultMessage: 'Mobile experience', + } + ), +}; + +export const dataTypes: Array<{ id: AppDataType; label: string }> = [ + { + id: DataTypes.SYNTHETICS, + label: DataTypesLabels[DataTypes.SYNTHETICS], + }, + { + id: DataTypes.UX, + label: DataTypesLabels[DataTypes.UX], + }, + { + id: DataTypes.MOBILE, + label: DataTypesLabels[DataTypes.MOBILE], + }, +]; + +const SELECT_DATA_TYPE = 'SELECT_DATA_TYPE'; + +export function DataTypesSelect({ seriesId, series }: Props) { + const { setSeries, reportType } = useSeriesStorage(); + + const onDataTypeChange = (dataType: AppDataType) => { + if (String(dataType) !== SELECT_DATA_TYPE) { + setSeries(seriesId, { + dataType, + time: series.time, + name: `${dataType}-series-${seriesId + 1}`, + }); + } + }; + + const options = dataTypes + .filter(({ id }) => { + if (reportType === ReportTypes.DEVICE_DISTRIBUTION) { + return id === DataTypes.MOBILE; + } + if (reportType === ReportTypes.CORE_WEB_VITAL) { + return id === DataTypes.UX; + } + return true; + }) + .map(({ id, label }) => ({ + value: id, + inputDisplay: label, + })); + + return ( + onDataTypeChange(value as AppDataType)} + style={{ minWidth: 220 }} + /> + ); +} + +const SELECT_DATA_TYPE_LABEL = i18n.translate( + 'xpack.observability.overview.exploratoryView.selectDataType', + { + defaultMessage: 'Select data type', + } +); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx index 41e83f407af2b..032eb66dcfa4f 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/date_picker_col.tsx @@ -6,24 +6,84 @@ */ import React from 'react'; -import { SeriesDatePicker } from '../../series_date_picker'; +import styled from 'styled-components'; +import { i18n } from '@kbn/i18n'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useSeriesStorage } from '../../hooks/use_series_storage'; -import { DateRangePicker } from '../../series_date_picker/date_range_picker'; +import { DateRangePicker } from '../../components/date_range_picker'; +import { SeriesDatePicker } from '../../components/series_date_picker'; +import { AppDataType, SeriesUrl } from '../../types'; +import { ReportTypes } from '../../configurations/constants'; +import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; +import { SyntheticsAddData } from '../../../add_data_buttons/synthetics_add_data'; +import { MobileAddData } from '../../../add_data_buttons/mobile_add_data'; +import { UXAddData } from '../../../add_data_buttons/ux_add_data'; interface Props { - seriesId: string; + seriesId: number; + series: SeriesUrl; } -export function DatePickerCol({ seriesId }: Props) { - const { firstSeriesId, getSeries } = useSeriesStorage(); - const { reportType } = getSeries(firstSeriesId); + +const AddDataComponents: Record = { + mobile: MobileAddData, + ux: UXAddData, + synthetics: SyntheticsAddData, + apm: null, + infra_logs: null, + infra_metrics: null, +}; + +export function DatePickerCol({ seriesId, series }: Props) { + const { reportType } = useSeriesStorage(); + + const { hasAppData } = useAppIndexPatternContext(); + + if (!series.dataType) { + return null; + } + + const AddDataButton = AddDataComponents[series.dataType]; + if (hasAppData[series.dataType] === false && AddDataButton !== null) { + return ( + + + + {i18n.translate('xpack.observability.overview.exploratoryView.noDataAvailable', { + defaultMessage: 'No {dataType} data available.', + values: { + dataType: series.dataType, + }, + })} + + + + + + + ); + } + + if (!series.selectedMetricField) { + return null; + } return ( -
- {firstSeriesId === seriesId || reportType !== 'kpi-over-time' ? ( - + + {seriesId === 0 || reportType !== ReportTypes.KPI ? ( + ) : ( - + )} -
+ ); } + +const Wrapper = styled.div` + width: 100%; + .euiSuperDatePicker__flexWrapper { + width: 100%; + > .euiFlexItem { + margin-right: 0; + } + } +`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/operation_type_select.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.test.tsx similarity index 69% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/operation_type_select.test.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.test.tsx index 516f04e3812ba..ced4d3af057ff 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/operation_type_select.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.test.tsx @@ -7,62 +7,66 @@ import React from 'react'; import { fireEvent, screen } from '@testing-library/react'; -import { render } from '../../rtl_helpers'; +import { mockUxSeries, render } from '../../rtl_helpers'; import { OperationTypeSelect } from './operation_type_select'; describe('OperationTypeSelect', function () { it('should render properly', function () { - render(); + render(); screen.getByText('Select an option: , is selected'); }); it('should display selected value', function () { const initSeries = { - data: { - 'performance-distribution': { + data: [ + { + name: 'performance-distribution', dataType: 'ux' as const, - reportType: 'kpi-over-time' as const, operationType: 'median' as const, time: { from: 'now-15m', to: 'now' }, }, - }, + ], }; - render(, { initSeries }); + render(, { + initSeries, + }); screen.getByText('Median'); }); it('should call set series on change', function () { const initSeries = { - data: { - 'series-id': { + data: [ + { + name: 'performance-distribution', dataType: 'ux' as const, - reportType: 'kpi-over-time' as const, operationType: 'median' as const, time: { from: 'now-15m', to: 'now' }, }, - }, + ], }; - const { setSeries } = render(, { initSeries }); + const { setSeries } = render(, { + initSeries, + }); fireEvent.click(screen.getByTestId('operationTypeSelect')); - expect(setSeries).toHaveBeenCalledWith('series-id', { + expect(setSeries).toHaveBeenCalledWith(0, { operationType: 'median', dataType: 'ux', - reportType: 'kpi-over-time', time: { from: 'now-15m', to: 'now' }, + name: 'performance-distribution', }); fireEvent.click(screen.getByText('95th Percentile')); - expect(setSeries).toHaveBeenCalledWith('series-id', { + expect(setSeries).toHaveBeenCalledWith(0, { operationType: '95th', dataType: 'ux', - reportType: 'kpi-over-time', time: { from: 'now-15m', to: 'now' }, + name: 'performance-distribution', }); }); }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/operation_type_select.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx similarity index 91% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/operation_type_select.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx index fce1383f30f34..4c10c9311704d 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/operation_type_select.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx @@ -11,17 +11,18 @@ import { EuiSuperSelect } from '@elastic/eui'; import { useSeriesStorage } from '../../hooks/use_series_storage'; import { OperationType } from '../../../../../../../lens/public'; +import { SeriesUrl } from '../../types'; export function OperationTypeSelect({ seriesId, + series, defaultOperationType, }: { - seriesId: string; + seriesId: number; + series: SeriesUrl; defaultOperationType?: OperationType; }) { - const { getSeries, setSeries } = useSeriesStorage(); - - const series = getSeries(seriesId); + const { setSeries } = useSeriesStorage(); const operationType = series?.operationType; @@ -83,11 +84,7 @@ export function OperationTypeSelect({ return ( { - removeSeries(seriesId); - }; - return ( - - ); -} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.test.tsx similarity index 69% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.test.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.test.tsx index cac1eccada311..ee04ee8891302 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_col.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.test.tsx @@ -12,14 +12,14 @@ import { mockAppIndexPattern, mockIndexPattern, mockUseValuesList, + mockUxSeries, render, } from '../../rtl_helpers'; import { ReportDefinitionCol } from './report_definition_col'; -import { SERVICE_NAME } from '../../configurations/constants/elasticsearch_fieldnames'; describe('Series Builder ReportDefinitionCol', function () { mockAppIndexPattern(); - const seriesId = 'test-series-id'; + const seriesId = 0; const seriesConfig = getDefaultConfigs({ reportType: 'data-distribution', @@ -27,34 +27,22 @@ describe('Series Builder ReportDefinitionCol', function () { dataType: 'ux', }); - const initSeries = { - data: { - [seriesId]: { - dataType: 'ux' as const, - reportType: 'data-distribution' as const, - time: { from: 'now-30d', to: 'now' }, - reportDefinitions: { [SERVICE_NAME]: ['elastic-co'] }, - }, - }, - }; - mockUseValuesList([{ label: 'elastic-co', count: 10 }]); it('should render properly', async function () { - render(, { - initSeries, - }); + render( + + ); screen.getByText('Web Application'); screen.getByText('Environment'); - screen.getByText('Select an option: Page load time, is selected'); - screen.getByText('Page load time'); + screen.getByText('Search Environment'); }); it('should render selected report definitions', async function () { - render(, { - initSeries, - }); + render( + + ); expect(await screen.findByText('elastic-co')).toBeInTheDocument(); @@ -63,8 +51,7 @@ describe('Series Builder ReportDefinitionCol', function () { it('should be able to remove selected definition', async function () { const { setSeries } = render( - , - { initSeries } + ); expect( @@ -78,11 +65,14 @@ describe('Series Builder ReportDefinitionCol', function () { fireEvent.click(removeBtn); expect(setSeries).toHaveBeenCalledTimes(1); + expect(setSeries).toHaveBeenCalledWith(seriesId, { dataType: 'ux', + name: 'performance-distribution', + breakdown: 'user_agent.name', reportDefinitions: {}, - reportType: 'data-distribution', - time: { from: 'now-30d', to: 'now' }, + selectedMetricField: 'transaction.duration.us', + time: { from: 'now-15m', to: 'now' }, }); }); }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.tsx new file mode 100644 index 0000000000000..dad2a7da2367b --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { useSeriesStorage } from '../../hooks/use_series_storage'; +import { SeriesConfig, SeriesUrl } from '../../types'; +import { ReportDefinitionField } from './report_definition_field'; + +export function ReportDefinitionCol({ + seriesId, + series, + seriesConfig, +}: { + seriesId: number; + series: SeriesUrl; + seriesConfig: SeriesConfig; +}) { + const { setSeries } = useSeriesStorage(); + + const { reportDefinitions: selectedReportDefinitions = {} } = series; + + const { definitionFields } = seriesConfig; + + const onChange = (field: string, value?: string[]) => { + if (!value?.[0]) { + delete selectedReportDefinitions[field]; + setSeries(seriesId, { + ...series, + reportDefinitions: { ...selectedReportDefinitions }, + }); + } else { + setSeries(seriesId, { + ...series, + reportDefinitions: { ...selectedReportDefinitions, [field]: value }, + }); + } + }; + + return ( + + {definitionFields.map((field) => ( + + + + ))} + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_field.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx similarity index 66% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_field.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx index d137b36a7e8c7..3651b4b7f075b 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_builder/columns/report_definition_field.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx @@ -6,30 +6,25 @@ */ import React, { useMemo } from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { isEmpty } from 'lodash'; +import { ExistsFilter } from '@kbn/es-query'; import FieldValueSuggestions from '../../../field_value_suggestions'; -import { useSeriesStorage } from '../../hooks/use_series_storage'; import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; import { ESFilter } from '../../../../../../../../../src/core/types/elasticsearch'; import { PersistableFilter } from '../../../../../../../lens/common'; -import { ExistsFilter } from '../../../../../../../../../src/plugins/data/common/es_query/filters'; import { buildPhrasesFilter } from '../../configurations/utils'; -import { SeriesConfig } from '../../types'; +import { SeriesConfig, SeriesUrl } from '../../types'; import { ALL_VALUES_SELECTED } from '../../../field_value_suggestions/field_value_combobox'; interface Props { - seriesId: string; + seriesId: number; + series: SeriesUrl; field: string; seriesConfig: SeriesConfig; onChange: (field: string, value?: string[]) => void; } -export function ReportDefinitionField({ seriesId, field, seriesConfig, onChange }: Props) { - const { getSeries } = useSeriesStorage(); - - const series = getSeries(seriesId); - +export function ReportDefinitionField({ series, field, seriesConfig, onChange }: Props) { const { indexPattern } = useAppIndexPatternContext(series.dataType); const { reportDefinitions: selectedReportDefinitions = {} } = series; @@ -64,23 +59,26 @@ export function ReportDefinitionField({ seriesId, field, seriesConfig, onChange // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(selectedReportDefinitions), JSON.stringify(baseFilters)]); + if (!indexPattern) { + return null; + } + return ( - - - {indexPattern && ( - onChange(field, val)} - filters={queryFilters} - time={series.time} - fullWidth={true} - allowAllValuesSelection={true} - /> - )} - - + onChange(field, val)} + filters={queryFilters} + time={series.time} + fullWidth={true} + asCombobox={true} + allowExclusions={false} + allowAllValuesSelection={true} + usePrependLabel={false} + compressed={false} + required={isEmpty(selectedReportDefinitions)} + /> ); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_type_select.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_type_select.tsx new file mode 100644 index 0000000000000..01c9fce7637bb --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_type_select.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiSuperSelect } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { useSeriesStorage } from '../../hooks/use_series_storage'; +import { ReportViewType } from '../../types'; +import { + CORE_WEB_VITALS_LABEL, + DEVICE_DISTRIBUTION_LABEL, + KPI_OVER_TIME_LABEL, + PERF_DIST_LABEL, +} from '../../configurations/constants/labels'; + +const SELECT_REPORT_TYPE = 'SELECT_REPORT_TYPE'; + +export const reportTypesList: Array<{ + reportType: ReportViewType | typeof SELECT_REPORT_TYPE; + label: string; +}> = [ + { + reportType: SELECT_REPORT_TYPE, + label: i18n.translate('xpack.observability.expView.reportType.selectLabel', { + defaultMessage: 'Select report type', + }), + }, + { reportType: 'kpi-over-time', label: KPI_OVER_TIME_LABEL }, + { reportType: 'data-distribution', label: PERF_DIST_LABEL }, + { reportType: 'core-web-vitals', label: CORE_WEB_VITALS_LABEL }, + { reportType: 'device-data-distribution', label: DEVICE_DISTRIBUTION_LABEL }, +]; + +export function ReportTypesSelect() { + const { setReportType, reportType: selectedReportType, allSeries } = useSeriesStorage(); + + const onReportTypeChange = (reportType: ReportViewType) => { + setReportType(reportType); + }; + + const options = reportTypesList + .filter(({ reportType }) => (selectedReportType ? reportType !== SELECT_REPORT_TYPE : true)) + .map(({ reportType, label }) => ({ + value: reportType, + inputDisplay: reportType === SELECT_REPORT_TYPE ? label : {label}, + dropdownDisplay: label, + })); + + return ( + onReportTypeChange(value as ReportViewType)} + style={{ minWidth: 200 }} + isInvalid={!selectedReportType && allSeries.length > 0} + disabled={allSeries.length > 0} + /> + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_actions.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_actions.tsx deleted file mode 100644 index 51ebe6c6bd9d5..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_actions.tsx +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { isEmpty } from 'lodash'; -import { RemoveSeries } from './remove_series'; -import { useSeriesStorage } from '../../hooks/use_series_storage'; -import { SeriesUrl } from '../../types'; - -interface Props { - seriesId: string; - editorMode?: boolean; -} -export function SeriesActions({ seriesId, editorMode = false }: Props) { - const { getSeries, setSeries, allSeriesIds, removeSeries } = useSeriesStorage(); - const series = getSeries(seriesId); - - const onEdit = () => { - setSeries(seriesId, { ...series, isNew: true }); - }; - - const copySeries = () => { - let copySeriesId: string = `${seriesId}-copy`; - if (allSeriesIds.includes(copySeriesId)) { - copySeriesId = copySeriesId + allSeriesIds.length; - } - setSeries(copySeriesId, series); - }; - - const { reportType, reportDefinitions, isNew, ...restSeries } = series; - const isSaveAble = reportType && !isEmpty(reportDefinitions); - - const saveSeries = () => { - if (isSaveAble) { - const reportDefId = Object.values(reportDefinitions ?? {})[0]; - let newSeriesId = `${reportDefId}-${reportType}`; - - if (allSeriesIds.includes(newSeriesId)) { - newSeriesId = `${newSeriesId}-${allSeriesIds.length}`; - } - const newSeriesN: SeriesUrl = { - ...restSeries, - reportType, - reportDefinitions, - }; - - setSeries(newSeriesId, newSeriesN); - removeSeries(seriesId); - } - }; - - return ( - - {!editorMode && ( - - - - )} - {editorMode && ( - - - - )} - {editorMode && ( - - - - )} - - - - - ); -} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_filter.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_filter.tsx deleted file mode 100644 index 02144c6929b38..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_filter.tsx +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import React, { useState, Fragment } from 'react'; -import { - EuiButton, - EuiPopover, - EuiSpacer, - EuiButtonEmpty, - EuiFlexItem, - EuiFlexGroup, -} from '@elastic/eui'; -import { FilterExpanded } from './filter_expanded'; -import { SeriesConfig } from '../../types'; -import { FieldLabels } from '../../configurations/constants/constants'; -import { SelectedFilters } from '../selected_filters'; -import { useSeriesStorage } from '../../hooks/use_series_storage'; - -interface Props { - seriesId: string; - filterFields: SeriesConfig['filterFields']; - baseFilters: SeriesConfig['baseFilters']; - seriesConfig: SeriesConfig; - isNew?: boolean; - labels?: Record; -} - -export interface Field { - label: string; - field: string; - nested?: string; - isNegated?: boolean; -} - -export function SeriesFilter({ - seriesConfig, - isNew, - seriesId, - filterFields = [], - baseFilters, - labels, -}: Props) { - const [isPopoverVisible, setIsPopoverVisible] = useState(false); - - const [selectedField, setSelectedField] = useState(); - - const options: Field[] = filterFields.map((field) => { - if (typeof field === 'string') { - return { label: labels?.[field] ?? FieldLabels[field], field }; - } - - return { - field: field.field, - nested: field.nested, - isNegated: field.isNegated, - label: labels?.[field.field] ?? FieldLabels[field.field], - }; - }); - - const { setSeries, getSeries } = useSeriesStorage(); - const urlSeries = getSeries(seriesId); - - const button = ( - { - setIsPopoverVisible((prevState) => !prevState); - }} - size="s" - > - {i18n.translate('xpack.observability.expView.seriesEditor.addFilter', { - defaultMessage: 'Add filter', - })} - - ); - - const mainPanel = ( - <> - - {options.map((opt) => ( - - { - setSelectedField(opt); - }} - > - {opt.label} - - - - ))} - - ); - - const childPanel = selectedField ? ( - { - setSelectedField(undefined); - }} - filters={baseFilters} - /> - ) : null; - - const closePopover = () => { - setIsPopoverVisible(false); - setSelectedField(undefined); - }; - - return ( - - - - - {!selectedField ? mainPanel : childPanel} - - - {(urlSeries.filters ?? []).length > 0 && ( - - { - setSeries(seriesId, { ...urlSeries, filters: undefined }); - }} - size="s" - > - {i18n.translate('xpack.observability.expView.seriesEditor.clearFilter', { - defaultMessage: 'Clear filters', - })} - - - )} - - ); -} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/expanded_series_row.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/expanded_series_row.tsx new file mode 100644 index 0000000000000..801c885ec9a62 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/expanded_series_row.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSpacer } from '@elastic/eui'; +import { SeriesConfig, SeriesUrl } from '../types'; +import { ReportDefinitionCol } from './columns/report_definition_col'; +import { OperationTypeSelect } from './columns/operation_type_select'; +import { parseCustomFieldName } from '../configurations/lens_attributes'; +import { SeriesFilter } from '../series_viewer/columns/series_filter'; + +function getColumnType(seriesConfig: SeriesConfig, selectedMetricField?: string) { + const { columnType } = parseCustomFieldName(seriesConfig, selectedMetricField); + + return columnType; +} + +interface Props { + seriesId: number; + series: SeriesUrl; + seriesConfig: SeriesConfig; +} +export function ExpandedSeriesRow({ seriesId, series, seriesConfig }: Props) { + if (!seriesConfig) { + return null; + } + + const { selectedMetricField } = series ?? {}; + + const { hasOperationType, yAxisColumns } = seriesConfig; + + const columnType = getColumnType(seriesConfig, selectedMetricField); + + return ( +
+ + + + + + + + + + + + + {(hasOperationType || columnType === 'operation') && ( + + + + + + )} + + +
+ ); +} + +const FILTERS_LABEL = i18n.translate('xpack.observability.expView.seriesBuilder.selectFilters', { + defaultMessage: 'Filters', +}); + +const OPERATION_LABEL = i18n.translate('xpack.observability.expView.seriesBuilder.operation', { + defaultMessage: 'Operation', +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.tsx new file mode 100644 index 0000000000000..85eb85e0fc30a --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.tsx @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiSuperSelect, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { useSeriesStorage } from '../hooks/use_series_storage'; +import { SeriesConfig, SeriesUrl } from '../types'; +import { useAppIndexPatternContext } from '../hooks/use_app_index_pattern'; +import { RECORDS_FIELD, RECORDS_PERCENTAGE_FIELD } from '../configurations/constants'; + +interface Props { + seriesId: number; + series: SeriesUrl; + defaultValue?: string; + metricOptions: SeriesConfig['metricOptions']; +} + +const SELECT_REPORT_METRIC = 'SELECT_REPORT_METRIC'; + +export function ReportMetricOptions({ seriesId, series, metricOptions }: Props) { + const { setSeries } = useSeriesStorage(); + + const { indexPatterns } = useAppIndexPatternContext(); + + const onChange = (value: string) => { + setSeries(seriesId, { + ...series, + selectedMetricField: value, + }); + }; + + if (!series.dataType) { + return null; + } + + const indexPattern = indexPatterns?.[series.dataType]; + + const options = (metricOptions ?? []).map(({ label, field, id }) => { + let disabled = false; + + if (field !== RECORDS_FIELD && field !== RECORDS_PERCENTAGE_FIELD && field) { + disabled = !Boolean(indexPattern?.getFieldByName(field)); + } + return { + disabled, + value: field || id, + dropdownDisplay: disabled ? ( + {field}, + }} + /> + } + > + {label} + + ) : ( + label + ), + inputDisplay: label, + }; + }); + + return ( + onChange(value)} + style={{ minWidth: 220 }} + /> + ); +} + +const SELECT_REPORT_METRIC_LABEL = i18n.translate( + 'xpack.observability.expView.seriesEditor.selectReportMetric', + { + defaultMessage: 'Select report metric', + } +); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.tsx deleted file mode 100644 index 5d2ce6ba84951..0000000000000 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.tsx +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { Fragment } from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { useSeriesStorage } from '../hooks/use_series_storage'; -import { FilterLabel } from '../components/filter_label'; -import { SeriesConfig, UrlFilter } from '../types'; -import { useAppIndexPatternContext } from '../hooks/use_app_index_pattern'; -import { useSeriesFilters } from '../hooks/use_series_filters'; -import { getFiltersFromDefs } from '../hooks/use_lens_attributes'; - -interface Props { - seriesId: string; - seriesConfig: SeriesConfig; - isNew?: boolean; -} -export function SelectedFilters({ seriesId, isNew, seriesConfig }: Props) { - const { getSeries } = useSeriesStorage(); - - const series = getSeries(seriesId); - - const { reportDefinitions = {} } = series; - - const { labels } = seriesConfig; - - const filters: UrlFilter[] = series.filters ?? []; - - let definitionFilters: UrlFilter[] = getFiltersFromDefs(reportDefinitions); - - // we don't want to display report definition filters in new series view - if (isNew) { - definitionFilters = []; - } - - const { removeFilter } = useSeriesFilters({ seriesId }); - - const { indexPattern } = useAppIndexPatternContext(series.dataType); - - return (filters.length > 0 || definitionFilters.length > 0) && indexPattern ? ( - - - {filters.map(({ field, values, notValues }) => ( - - {(values ?? []).map((val) => ( - - removeFilter({ field, value: val, negate: false })} - negate={false} - indexPattern={indexPattern} - /> - - ))} - {(notValues ?? []).map((val) => ( - - removeFilter({ field, value: val, negate: true })} - indexPattern={indexPattern} - /> - - ))} - - ))} - - {definitionFilters.map(({ field, values }) => ( - - {(values ?? []).map((val) => ( - - { - // FIXME handle this use case - }} - negate={false} - definitionFilter={true} - indexPattern={indexPattern} - /> - - ))} - - ))} - - - ) : null; -} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/series_editor.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/series_editor.tsx index c3cc8484d1751..80fe400830832 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/series_editor.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/series_editor.tsx @@ -5,134 +5,399 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiBasicTable, EuiIcon, EuiSpacer, EuiText } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { SeriesFilter } from './columns/series_filter'; -import { SeriesConfig } from '../types'; -import { NEW_SERIES_KEY, useSeriesStorage } from '../hooks/use_series_storage'; +import { + EuiBasicTable, + EuiButtonIcon, + EuiSpacer, + EuiFormRow, + EuiFlexItem, + EuiFlexGroup, + EuiButtonEmpty, +} from '@elastic/eui'; +import { rgba } from 'polished'; +import classNames from 'classnames'; +import { isEmpty } from 'lodash'; +import { euiStyled } from './../../../../../../../../src/plugins/kibana_react/common'; +import { AppDataType, SeriesConfig, ReportViewType, SeriesUrl } from '../types'; +import { SeriesContextValue, useSeriesStorage } from '../hooks/use_series_storage'; +import { IndexPatternState, useAppIndexPatternContext } from '../hooks/use_app_index_pattern'; import { getDefaultConfigs } from '../configurations/default_configs'; +import { SeriesActions } from '../series_viewer/columns/series_actions'; +import { SeriesInfo } from '../series_viewer/columns/series_info'; +import { DataTypesSelect } from './columns/data_type_select'; import { DatePickerCol } from './columns/date_picker_col'; -import { useAppIndexPatternContext } from '../hooks/use_app_index_pattern'; -import { SeriesActions } from './columns/series_actions'; -import { ChartEditOptions } from './chart_edit_options'; +import { ExpandedSeriesRow } from './expanded_series_row'; +import { SeriesName } from '../series_viewer/columns/series_name'; +import { ReportTypesSelect } from './columns/report_type_select'; +import { ViewActions } from '../views/view_actions'; +import { ReportMetricOptions } from './report_metric_options'; +import { Breakdowns } from '../series_viewer/columns/breakdowns'; -interface EditItem { - seriesConfig: SeriesConfig; +export interface ReportTypeItem { id: string; + reportType: ReportViewType; + label: string; +} + +export interface BuilderItem { + id: number; + series: SeriesUrl; + seriesConfig: SeriesConfig; } -export function SeriesEditor() { - const { allSeries, allSeriesIds } = useSeriesStorage(); +type ExpandedRowMap = Record; + +export const getSeriesToEdit = ({ + indexPatterns, + allSeries, + reportType, +}: { + allSeries: SeriesContextValue['allSeries']; + indexPatterns: IndexPatternState; + reportType: ReportViewType; +}): BuilderItem[] => { + const getDataViewSeries = (dataType: AppDataType) => { + if (indexPatterns?.[dataType]) { + return getDefaultConfigs({ + dataType, + reportType, + indexPattern: indexPatterns[dataType], + }); + } + }; + + return allSeries.map((series, seriesIndex) => { + const seriesConfig = getDataViewSeries(series.dataType)!; + + return { id: seriesIndex, series, seriesConfig }; + }); +}; + +export const SeriesEditor = React.memo(function () { + const [editorItems, setEditorItems] = useState([]); + + const { getSeries, allSeries, reportType, removeSeries } = useSeriesStorage(); + + const { loading, indexPatterns } = useAppIndexPatternContext(); + + const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState>( + {} + ); + + useEffect(() => { + const newExpandRows: ExpandedRowMap = {}; + + setEditorItems((prevState) => { + const newEditorItems = getSeriesToEdit({ + reportType, + allSeries, + indexPatterns, + }); + + newEditorItems.forEach(({ series, id, seriesConfig }) => { + const prevSeriesItem = prevState.find(({ id: prevId }) => prevId === id); + if ( + prevSeriesItem && + series.selectedMetricField && + prevSeriesItem.series.selectedMetricField !== series.selectedMetricField + ) { + newExpandRows[id] = ( + + ); + } + }); + return [...newEditorItems]; + }); + + setItemIdToExpandedRowMap((prevState) => { + return { ...prevState, ...newExpandRows }; + }); + }, [allSeries, getSeries, indexPatterns, loading, reportType]); + + useEffect(() => { + setItemIdToExpandedRowMap((prevState) => { + const itemIdToExpandedRowMapValues = { ...prevState }; + + const newEditorItems = getSeriesToEdit({ + reportType, + allSeries, + indexPatterns, + }); + + newEditorItems.forEach((item) => { + if (itemIdToExpandedRowMapValues[item.id]) { + itemIdToExpandedRowMapValues[item.id] = ( + + ); + } + }); + return itemIdToExpandedRowMapValues; + }); + }, [allSeries, editorItems, indexPatterns, reportType]); + + const toggleDetails = (item: BuilderItem) => { + const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap }; + if (itemIdToExpandedRowMapValues[item.id]) { + delete itemIdToExpandedRowMapValues[item.id]; + } else { + itemIdToExpandedRowMapValues[item.id] = ( + + ); + } + setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues); + }; const columns = [ + { + align: 'left' as const, + width: '40px', + isExpander: true, + field: 'id', + name: '', + render: (id: number, item: BuilderItem) => + item.series.dataType && item.series.selectedMetricField ? ( + toggleDetails(item)} + isDisabled={!item.series.dataType || !item.series.selectedMetricField} + aria-label={itemIdToExpandedRowMap[item.id] ? COLLAPSE_LABEL : EXPAND_LABEL} + iconType={itemIdToExpandedRowMap[item.id] ? 'arrowUp' : 'arrowDown'} + /> + ) : null, + }, + { + name: '', + field: 'id', + width: '40px', + render: (seriesId: number, { seriesConfig, series }: BuilderItem) => ( + + ), + }, { name: i18n.translate('xpack.observability.expView.seriesEditor.name', { defaultMessage: 'Name', }), field: 'id', - width: '15%', - render: (seriesId: string) => ( - - {' '} - {seriesId === NEW_SERIES_KEY ? 'series-preview' : seriesId} - + width: '20%', + render: (seriesId: number, { series }: BuilderItem) => ( + ), }, { - name: i18n.translate('xpack.observability.expView.seriesEditor.filters', { - defaultMessage: 'Filters', + name: i18n.translate('xpack.observability.expView.seriesEditor.dataType', { + defaultMessage: 'Data type', }), - field: 'defaultFilters', + field: 'id', width: '15%', - render: (seriesId: string, { seriesConfig, id }: EditItem) => ( - + render: (seriesId: number, { series }: BuilderItem) => ( + ), }, { - name: i18n.translate('xpack.observability.expView.seriesEditor.breakdowns', { - defaultMessage: 'Breakdowns', + name: i18n.translate('xpack.observability.expView.seriesEditor.reportMetric', { + defaultMessage: 'Report metric', }), field: 'id', - width: '25%', - render: (seriesId: string, { seriesConfig, id }: EditItem) => ( - ( + ), }, { - name: ( -
- -
+ name: i18n.translate('xpack.observability.expView.seriesEditor.time', { + defaultMessage: 'Time', + }), + field: 'id', + width: '27%', + render: (seriesId: number, { series }: BuilderItem) => ( + ), - width: '20%', + }, + + { + name: i18n.translate('xpack.observability.expView.seriesBuilder.breakdownBy', { + defaultMessage: 'Breakdown by', + }), + width: '10%', field: 'id', - align: 'right' as const, - render: (seriesId: string, item: EditItem) => , + render: (seriesId: number, { series, seriesConfig }: BuilderItem) => ( + + ), }, + { - name: i18n.translate('xpack.observability.expView.seriesEditor.actions', { + name: i18n.translate('xpack.observability.expView.seriesBuilder.actions', { defaultMessage: 'Actions', }), align: 'center' as const, - width: '10%', + width: '8%', field: 'id', - render: (seriesId: string, item: EditItem) => , + render: (seriesId: number, { series, seriesConfig }: BuilderItem) => ( + + ), }, ]; - const { indexPatterns } = useAppIndexPatternContext(); - const items: EditItem[] = []; - - allSeriesIds.forEach((seriesKey) => { - const series = allSeries[seriesKey]; - if (series?.reportType && indexPatterns[series.dataType] && !series.isNew) { - items.push({ - id: seriesKey, - seriesConfig: getDefaultConfigs({ - indexPattern: indexPatterns[series.dataType], - reportType: series.reportType, - dataType: series.dataType, - }), - }); - } - }); + const getRowProps = (item: BuilderItem) => { + const { dataType, reportDefinitions, selectedMetricField } = item.series; - if (items.length === 0 && allSeriesIds.length > 0) { - return null; - } + return { + className: classNames({ + isExpanded: itemIdToExpandedRowMap[item.id], + isIncomplete: !dataType || isEmpty(reportDefinitions) || !selectedMetricField, + }), + // commenting this for now, since adding on click on row, blocks adding space + // into text field for name column + // ...(dataType && selectedMetricField + // ? { + // onClick: (evt: MouseEvent) => { + // const targetElem = evt.target as HTMLElement; + // + // if ( + // targetElem.classList.contains('euiTableCellContent') && + // targetElem.tagName !== 'BUTTON' + // ) { + // toggleDetails(item); + // } + // evt.stopPropagation(); + // evt.preventDefault(); + // }, + // } + // : {}), + }; + }; + + const resetView = () => { + const totalSeries = allSeries.length; + for (let i = totalSeries; i >= 0; i--) { + removeSeries(i); + } + setEditorItems([]); + setItemIdToExpandedRowMap({}); + }; return ( - <> - - - - + +
+ + + + + + + + {reportType && ( + + resetView()} color="text"> + {RESET_LABEL} + + + )} + + + + + + + + {editorItems.length > 0 && ( + + )} + +
+
); -} +}); + +const Wrapper = euiStyled.div` + max-height: 50vh; + &::-webkit-scrollbar { + height: ${({ theme }) => theme.eui.euiScrollBar}; + width: ${({ theme }) => theme.eui.euiScrollBar}; + } + &::-webkit-scrollbar-thumb { + background-clip: content-box; + background-color: ${({ theme }) => rgba(theme.eui.euiColorDarkShade, 0.5)}; + border: ${({ theme }) => theme.eui.euiScrollBarCorner} solid transparent; + } + &::-webkit-scrollbar-corner, + &::-webkit-scrollbar-track { + background-color: transparent; + } + + &&& { + .euiTableRow-isExpandedRow .euiTableRowCell { + border-top: none; + background-color: #FFFFFF; + border-bottom: 2px solid #d3dae6; + border-right: 2px solid rgb(211, 218, 230); + border-left: 2px solid rgb(211, 218, 230); + } + + .isExpanded { + border-right: 2px solid rgb(211, 218, 230); + border-left: 2px solid rgb(211, 218, 230); + .euiTableRowCell { + border-bottom: none; + } + } + .isIncomplete .euiTableRowCell { + background-color: rgba(254, 197, 20, 0.1); + } + } +`; + +export const LOADING_VIEW = i18n.translate( + 'xpack.observability.expView.seriesBuilder.loadingView', + { + defaultMessage: 'Loading view ...', + } +); + +export const SELECT_REPORT_TYPE = i18n.translate( + 'xpack.observability.expView.seriesBuilder.selectReportType', + { + defaultMessage: 'No report type selected', + } +); + +export const RESET_LABEL = i18n.translate('xpack.observability.expView.seriesBuilder.reset', { + defaultMessage: 'Reset', +}); + +export const REPORT_TYPE_LABEL = i18n.translate( + 'xpack.observability.expView.seriesBuilder.reportType', + { + defaultMessage: 'Report type', + } +); + +const COLLAPSE_LABEL = i18n.translate('xpack.observability.expView.seriesBuilder.collapse', { + defaultMessage: 'Collapse', +}); + +const EXPAND_LABEL = i18n.translate('xpack.observability.expView.seriesBuilder.expand', { + defaultMessage: 'Exapnd', +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/breakdowns.test.tsx similarity index 74% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.test.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/breakdowns.test.tsx index 84568e1c5068a..21b766227a562 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/breakdowns.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { fireEvent, screen } from '@testing-library/react'; import { Breakdowns } from './breakdowns'; -import { mockIndexPattern, render } from '../../rtl_helpers'; +import { mockIndexPattern, mockUxSeries, render } from '../../rtl_helpers'; import { getDefaultConfigs } from '../../configurations/default_configs'; import { USER_AGENT_OS } from '../../configurations/constants/elasticsearch_fieldnames'; @@ -20,13 +20,7 @@ describe('Breakdowns', function () { }); it('should render properly', async function () { - render( - - ); + render(); screen.getAllByText('Browser family'); }); @@ -36,9 +30,9 @@ describe('Breakdowns', function () { const { setSeries } = render( , { initSeries } ); @@ -49,10 +43,14 @@ describe('Breakdowns', function () { fireEvent.click(screen.getByText('Browser family')); - expect(setSeries).toHaveBeenCalledWith('series-id', { + expect(setSeries).toHaveBeenCalledWith(0, { breakdown: 'user_agent.name', dataType: 'ux', - reportType: 'data-distribution', + name: 'performance-distribution', + reportDefinitions: { + 'service.name': ['elastic-co'], + }, + selectedMetricField: 'transaction.duration.us', time: { from: 'now-15m', to: 'now' }, }); expect(setSeries).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/breakdowns.tsx similarity index 71% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/breakdowns.tsx index 2237935d466ad..315f63e33bed0 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/breakdowns.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/breakdowns.tsx @@ -8,20 +8,20 @@ import React from 'react'; import { EuiSuperSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useRouteMatch } from 'react-router-dom'; import { useSeriesStorage } from '../../hooks/use_series_storage'; import { USE_BREAK_DOWN_COLUMN } from '../../configurations/constants'; -import { SeriesConfig } from '../../types'; +import { SeriesConfig, SeriesUrl } from '../../types'; interface Props { - seriesId: string; - breakdowns: string[]; + seriesId: number; + series: SeriesUrl; seriesConfig: SeriesConfig; } -export function Breakdowns({ seriesConfig, seriesId, breakdowns = [] }: Props) { - const { setSeries, getSeries } = useSeriesStorage(); - - const series = getSeries(seriesId); +export function Breakdowns({ seriesConfig, seriesId, series }: Props) { + const { setSeries } = useSeriesStorage(); + const isPreview = !!useRouteMatch('/exploratory-view/preview'); const selectedBreakdown = series.breakdown; const NO_BREAKDOWN = 'no_breakdown'; @@ -40,9 +40,13 @@ export function Breakdowns({ seriesConfig, seriesId, breakdowns = [] }: Props) { } }; + if (!seriesConfig) { + return null; + } + const hasUseBreakdownColumn = seriesConfig.xAxisColumn.sourceField === USE_BREAK_DOWN_COLUMN; - const items = breakdowns.map((breakdown) => ({ + const items = seriesConfig.breakdownFields.map((breakdown) => ({ id: breakdown, label: seriesConfig.labels[breakdown], })); @@ -50,14 +54,12 @@ export function Breakdowns({ seriesConfig, seriesId, breakdowns = [] }: Props) { if (!hasUseBreakdownColumn) { items.push({ id: NO_BREAKDOWN, - label: i18n.translate('xpack.observability.exp.breakDownFilter.noBreakdown', { - defaultMessage: 'No breakdown', - }), + label: NO_BREAK_DOWN_LABEL, }); } const options = items.map(({ id, label }) => ({ - inputDisplay: id === NO_BREAKDOWN ? label : {label}, + inputDisplay: label, value: id, dropdownDisplay: label, })); @@ -69,7 +71,7 @@ export function Breakdowns({ seriesConfig, seriesId, breakdowns = [] }: Props) {
onOptionChange(value)} @@ -78,3 +80,10 @@ export function Breakdowns({ seriesConfig, seriesId, breakdowns = [] }: Props) {
); } + +export const NO_BREAK_DOWN_LABEL = i18n.translate( + 'xpack.observability.exp.breakDownFilter.noBreakdown', + { + defaultMessage: 'No breakdown', + } +); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/chart_types.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/chart_types.tsx new file mode 100644 index 0000000000000..e6ba505c82091 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/chart_types.tsx @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { EuiPopover, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { + useKibana, + ToolbarButton, +} from '../../../../../../../../../src/plugins/kibana_react/public'; +import { ObservabilityPublicPluginsStart } from '../../../../../plugin'; +import { SeriesUrl, useFetcher } from '../../../../..'; +import { SeriesConfig } from '../../types'; +import { SeriesChartTypesSelect } from '../../series_editor/columns/chart_types'; + +interface Props { + seriesId: number; + series: SeriesUrl; + seriesConfig: SeriesConfig; +} + +export function SeriesChartTypes({ seriesId, series, seriesConfig }: Props) { + const seriesType = series?.seriesType ?? seriesConfig.defaultSeriesType; + + const { + services: { lens }, + } = useKibana(); + + const { data = [] } = useFetcher(() => lens.getXyVisTypes(), [lens]); + + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + return ( + setIsPopoverOpen(false)} + button={ + + id === seriesType)?.icon!} + aria-label={CHART_TYPE_LABEL} + onClick={() => setIsPopoverOpen((prevState) => !prevState)} + /> + + } + > + + + ); +} + +const EDIT_CHART_TYPE_LABEL = i18n.translate( + 'xpack.observability.expView.seriesEditor.editChartSeriesLabel', + { + defaultMessage: 'Edit chart type for series', + } +); + +const CHART_TYPE_LABEL = i18n.translate('xpack.observability.expView.chartTypes.label', { + defaultMessage: 'Chart type', +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/filter_expanded.test.tsx similarity index 73% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/filter_expanded.test.tsx index 2fadb0e56433e..2657765bde8e3 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/filter_expanded.test.tsx @@ -8,20 +8,24 @@ import React from 'react'; import { fireEvent, screen } from '@testing-library/react'; import { FilterExpanded } from './filter_expanded'; -import { mockAppIndexPattern, mockUseValuesList, render } from '../../rtl_helpers'; +import { mockUxSeries, mockAppIndexPattern, mockUseValuesList, render } from '../../rtl_helpers'; import { USER_AGENT_NAME } from '../../configurations/constants/elasticsearch_fieldnames'; describe('FilterExpanded', function () { + const filters = [{ field: USER_AGENT_NAME, values: ['Chrome'] }]; + + const mockSeries = { ...mockUxSeries, filters }; + it('should render properly', async function () { - const initSeries = { filters: [{ field: USER_AGENT_NAME, values: ['Chrome'] }] }; + const initSeries = { filters }; mockAppIndexPattern(); render( , { initSeries } @@ -30,42 +34,36 @@ describe('FilterExpanded', function () { screen.getByText('Browser Family'); }); it('should call go back on click', async function () { - const initSeries = { filters: [{ field: USER_AGENT_NAME, values: ['Chrome'] }] }; - const goBack = jest.fn(); + const initSeries = { filters }; render( , { initSeries } ); fireEvent.click(screen.getByText('Browser Family')); - - expect(goBack).toHaveBeenCalledTimes(1); - expect(goBack).toHaveBeenCalledWith(); }); it('should call useValuesList on load', async function () { - const initSeries = { filters: [{ field: USER_AGENT_NAME, values: ['Chrome'] }] }; + const initSeries = { filters }; const { spy } = mockUseValuesList([ { label: 'Chrome', count: 10 }, { label: 'Firefox', count: 5 }, ]); - const goBack = jest.fn(); - render( , { initSeries } @@ -80,7 +78,7 @@ describe('FilterExpanded', function () { ); }); it('should filter display values', async function () { - const initSeries = { filters: [{ field: USER_AGENT_NAME, values: ['Chrome'] }] }; + const initSeries = { filters }; mockUseValuesList([ { label: 'Chrome', count: 10 }, @@ -89,15 +87,17 @@ describe('FilterExpanded', function () { render( , { initSeries } ); + fireEvent.click(screen.getByText('Browser Family')); + expect(screen.queryByText('Firefox')).toBeTruthy(); fireEvent.input(screen.getByRole('searchbox'), { target: { value: 'ch' } }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/filter_expanded.tsx similarity index 54% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/filter_expanded.tsx index 6f9d8efdc0681..1ef25722aff5c 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/filter_expanded.tsx @@ -6,48 +6,56 @@ */ import React, { useState, Fragment } from 'react'; -import { EuiFieldSearch, EuiSpacer, EuiButtonEmpty, EuiFilterGroup, EuiText } from '@elastic/eui'; +import { + EuiFieldSearch, + EuiSpacer, + EuiFilterGroup, + EuiText, + EuiPopover, + EuiFilterButton, +} from '@elastic/eui'; import styled from 'styled-components'; import { rgba } from 'polished'; import { i18n } from '@kbn/i18n'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/api/types'; import { map } from 'lodash'; +import { ExistsFilter } from '@kbn/es-query'; import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; -import { useSeriesStorage } from '../../hooks/use_series_storage'; -import { SeriesConfig, UrlFilter } from '../../types'; +import { SeriesConfig, SeriesUrl, UrlFilter } from '../../types'; import { FilterValueButton } from './filter_value_btn'; import { useValuesList } from '../../../../../hooks/use_values_list'; import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import { ESFilter } from '../../../../../../../../../src/core/types/elasticsearch'; import { PersistableFilter } from '../../../../../../../lens/common'; -import { ExistsFilter } from '../../../../../../../../../src/plugins/data/common/es_query/filters'; interface Props { - seriesId: string; + seriesId: number; + series: SeriesUrl; label: string; field: string; isNegated?: boolean; - goBack: () => void; nestedField?: string; filters: SeriesConfig['baseFilters']; } +export interface NestedFilterOpen { + value: string; + negate: boolean; +} + export function FilterExpanded({ seriesId, + series, field, label, - goBack, nestedField, isNegated, filters: defaultFilters, }: Props) { const [value, setValue] = useState(''); - const [isOpen, setIsOpen] = useState({ value: '', negate: false }); - - const { getSeries } = useSeriesStorage(); - - const series = getSeries(seriesId); + const [isOpen, setIsOpen] = useState(false); + const [isNestedOpen, setIsNestedOpen] = useState({ value: '', negate: false }); const queryFilters: ESFilter[] = []; @@ -81,62 +89,71 @@ export function FilterExpanded({ ); return ( - - goBack()}> - {label} - - { - setValue(evt.target.value); - }} - placeholder={i18n.translate('xpack.observability.filters.expanded.search', { - defaultMessage: 'Search for {label}', - values: { label }, - })} - /> - - - {displayValues.length === 0 && !loading && ( - - {i18n.translate('xpack.observability.filters.expanded.noFilter', { - defaultMessage: 'No filters found.', - })} - - )} - {displayValues.map((opt) => ( - - - {isNegated !== false && ( + setIsOpen((prevState) => !prevState)} iconType="arrowDown"> + {label} + + } + isOpen={isOpen} + closePopover={() => setIsOpen(false)} + > + + { + setValue(evt.target.value); + }} + placeholder={i18n.translate('xpack.observability.filters.expanded.search', { + defaultMessage: 'Search for {label}', + values: { label }, + })} + /> + + + {displayValues.length === 0 && !loading && ( + + {i18n.translate('xpack.observability.filters.expanded.noFilter', { + defaultMessage: 'No filters found.', + })} + + )} + {displayValues.map((opt) => ( + + + {isNegated !== false && ( + + )} - )} - - - - - ))} - - + + + + ))} + + + ); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/filter_value_btn.test.tsx similarity index 91% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.test.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/filter_value_btn.test.tsx index c1790fea8c0c4..409b9d93444b7 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/filter_value_btn.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { fireEvent, screen } from '@testing-library/react'; import { FilterValueButton } from './filter_value_btn'; -import { mockUseSeriesFilter, mockUseValuesList, render } from '../../rtl_helpers'; +import { mockUxSeries, mockUseSeriesFilter, mockUseValuesList, render } from '../../rtl_helpers'; import { USER_AGENT_NAME, USER_AGENT_VERSION, @@ -19,11 +19,12 @@ describe('FilterValueButton', function () { render( ); @@ -34,11 +35,12 @@ describe('FilterValueButton', function () { render( ); @@ -54,12 +56,13 @@ describe('FilterValueButton', function () { render( ); @@ -80,12 +83,13 @@ describe('FilterValueButton', function () { render( ); @@ -104,12 +108,13 @@ describe('FilterValueButton', function () { render( ); @@ -129,13 +134,14 @@ describe('FilterValueButton', function () { render( ); @@ -160,13 +166,14 @@ describe('FilterValueButton', function () { render( ); @@ -194,13 +201,14 @@ describe('FilterValueButton', function () { render( ); @@ -219,13 +227,14 @@ describe('FilterValueButton', function () { render( ); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/filter_value_btn.tsx similarity index 92% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/filter_value_btn.tsx index bf4ca6eb83d94..111f915a95f46 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_value_btn.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/filter_value_btn.tsx @@ -8,10 +8,11 @@ import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; import { EuiFilterButton, hexToRgb } from '@elastic/eui'; import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; -import { useSeriesStorage } from '../../hooks/use_series_storage'; import { useSeriesFilters } from '../../hooks/use_series_filters'; import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; import FieldValueSuggestions from '../../../field_value_suggestions'; +import { SeriesUrl } from '../../types'; +import { NestedFilterOpen } from './filter_expanded'; interface Props { value: string; @@ -19,12 +20,13 @@ interface Props { allSelectedValues?: string[]; negate: boolean; nestedField?: string; - seriesId: string; + seriesId: number; + series: SeriesUrl; isNestedOpen: { value: string; negate: boolean; }; - setIsNestedOpen: (val: { value: string; negate: boolean }) => void; + setIsNestedOpen: (val: NestedFilterOpen) => void; } export function FilterValueButton({ @@ -34,16 +36,13 @@ export function FilterValueButton({ field, negate, seriesId, + series, nestedField, allSelectedValues, }: Props) { - const { getSeries } = useSeriesStorage(); - - const series = getSeries(seriesId); - const { indexPatterns } = useAppIndexPatternContext(series.dataType); - const { setFilter, removeFilter } = useSeriesFilters({ seriesId }); + const { setFilter, removeFilter } = useSeriesFilters({ seriesId, series }); const hasActiveFilters = (allSelectedValues ?? []).includes(value); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/remove_series.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/remove_series.tsx new file mode 100644 index 0000000000000..2d38b81e12c9f --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/remove_series.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; +import { useSeriesStorage } from '../../hooks/use_series_storage'; + +interface Props { + seriesId: number; +} + +export function RemoveSeries({ seriesId }: Props) { + const { removeSeries, allSeries } = useSeriesStorage(); + + const onClick = () => { + removeSeries(seriesId); + }; + + const isDisabled = seriesId === 0 && allSeries.length > 1; + + return ( + + + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/series_actions.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/series_actions.tsx new file mode 100644 index 0000000000000..72ae111f002b1 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/series_actions.tsx @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { RemoveSeries } from './remove_series'; +import { useSeriesStorage } from '../../hooks/use_series_storage'; +import { SeriesConfig, SeriesUrl } from '../../types'; +import { useDiscoverLink } from '../../hooks/use_discover_link'; + +interface Props { + seriesId: number; + series: SeriesUrl; + seriesConfig: SeriesConfig; +} +export function SeriesActions({ seriesId, series, seriesConfig }: Props) { + const { setSeries, allSeries } = useSeriesStorage(); + + const { href: discoverHref } = useDiscoverLink({ series, seriesConfig }); + + const copySeries = () => { + let copySeriesId: string = `${series.name}-copy`; + if (allSeries.find(({ name }) => name === copySeriesId)) { + copySeriesId = copySeriesId + allSeries.length; + } + setSeries(allSeries.length, { ...series, name: copySeriesId }); + }; + + const toggleSeries = () => { + if (series.hidden) { + setSeries(seriesId, { ...series, hidden: undefined }); + } else { + setSeries(seriesId, { ...series, hidden: true }); + } + }; + + return ( + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/series_filter.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/series_filter.tsx new file mode 100644 index 0000000000000..87c17d03282c3 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/series_filter.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFilterGroup, EuiSpacer } from '@elastic/eui'; +import { useRouteMatch } from 'react-router-dom'; +import { FilterExpanded } from './filter_expanded'; +import { SeriesConfig, SeriesUrl } from '../../types'; +import { FieldLabels } from '../../configurations/constants/constants'; +import { SelectedFilters } from '../selected_filters'; + +interface Props { + seriesId: number; + seriesConfig: SeriesConfig; + series: SeriesUrl; +} + +export interface Field { + label: string; + field: string; + nested?: string; + isNegated?: boolean; +} + +export function SeriesFilter({ series, seriesConfig, seriesId }: Props) { + const isPreview = !!useRouteMatch('/exploratory-view/preview'); + + const options: Field[] = seriesConfig.filterFields.map((field) => { + if (typeof field === 'string') { + return { label: seriesConfig.labels?.[field] ?? FieldLabels[field], field }; + } + + return { + field: field.field, + nested: field.nested, + isNegated: field.isNegated, + label: seriesConfig.labels?.[field.field] ?? FieldLabels[field.field], + }; + }); + + return ( + <> + {!isPreview && ( + <> + + {options.map((opt) => ( + + ))} + + + + )} + + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/series_info.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/series_info.tsx new file mode 100644 index 0000000000000..3506acbeb528d --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/series_info.tsx @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { isEmpty } from 'lodash'; +import { EuiBadge, EuiBadgeGroup, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { useRouteMatch } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { SeriesChartTypes } from './chart_types'; +import { SeriesConfig, SeriesUrl } from '../../types'; +import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; +import { SeriesColorPicker } from '../../components/series_color_picker'; +import { dataTypes } from '../../series_editor/columns/data_type_select'; + +interface Props { + seriesId: number; + series: SeriesUrl; + seriesConfig?: SeriesConfig; +} + +export function SeriesInfo({ seriesId, series, seriesConfig }: Props) { + const isConfigure = !!useRouteMatch('/exploratory-view/configure'); + + const { dataType, reportDefinitions, selectedMetricField } = series; + + const { loading } = useAppIndexPatternContext(); + + const isIncomplete = + (!dataType || isEmpty(reportDefinitions) || !selectedMetricField) && !loading; + + if (!seriesConfig) { + return null; + } + + const { definitionFields, labels } = seriesConfig; + + const incompleteDefinition = isEmpty(reportDefinitions) + ? i18n.translate('xpack.observability.overview.exploratoryView.missingReportDefinition', { + defaultMessage: 'Missing {reportDefinition}', + values: { reportDefinition: labels?.[definitionFields[0]] }, + }) + : ''; + + let incompleteMessage = !selectedMetricField ? MISSING_REPORT_METRIC_LABEL : incompleteDefinition; + + if (!dataType) { + incompleteMessage = MISSING_DATA_TYPE_LABEL; + } + + if (!isIncomplete && seriesConfig && isConfigure) { + return ( + + + + + + + + + ); + } + + return ( + + + {isIncomplete && {incompleteMessage}} + + {!isConfigure && ( + + + {dataTypes.find(({ id }) => id === dataType)!.label} + + + )} + + ); +} + +const MISSING_REPORT_METRIC_LABEL = i18n.translate( + 'xpack.observability.overview.exploratoryView.missingReportMetric', + { + defaultMessage: 'Missing report metric', + } +); + +const MISSING_DATA_TYPE_LABEL = i18n.translate( + 'xpack.observability.overview.exploratoryView.missingDataType', + { + defaultMessage: 'Missing data type', + } +); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/series_name.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/series_name.tsx new file mode 100644 index 0000000000000..e35966a9fb0d2 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/series_name.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, ChangeEvent, useEffect } from 'react'; +import { EuiFieldText } from '@elastic/eui'; +import { useSeriesStorage } from '../../hooks/use_series_storage'; +import { SeriesUrl } from '../../types'; + +interface Props { + seriesId: number; + series: SeriesUrl; +} + +export function SeriesName({ series, seriesId }: Props) { + const { setSeries } = useSeriesStorage(); + + const [value, setValue] = useState(series.name); + + const onChange = (e: ChangeEvent) => { + setValue(e.target.value); + }; + + const onSave = () => { + if (value !== series.name) { + setSeries(seriesId, { ...series, name: value }); + } + }; + + useEffect(() => { + setValue(series.name); + }, [series.name]); + + return ; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/utils.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/utils.ts new file mode 100644 index 0000000000000..b9ee53a7e8e2d --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/columns/utils.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import dateMath from '@elastic/datemath'; +import _isString from 'lodash/isString'; + +const LAST = 'Last'; +const NEXT = 'Next'; + +const isNow = (value: string) => value === 'now'; + +export const isString = (value: any): value is string => _isString(value); +export interface QuickSelect { + timeTense: string; + timeValue: number; + timeUnits: TimeUnitId; +} +export type TimeUnitFromNowId = 's+' | 'm+' | 'h+' | 'd+' | 'w+' | 'M+' | 'y+'; +export type TimeUnitId = 's' | 'm' | 'h' | 'd' | 'w' | 'M' | 'y'; + +export interface RelativeOption { + text: string; + value: TimeUnitId | TimeUnitFromNowId; +} + +export const relativeOptions: RelativeOption[] = [ + { text: 'Seconds ago', value: 's' }, + { text: 'Minutes ago', value: 'm' }, + { text: 'Hours ago', value: 'h' }, + { text: 'Days ago', value: 'd' }, + { text: 'Weeks ago', value: 'w' }, + { text: 'Months ago', value: 'M' }, + { text: 'Years ago', value: 'y' }, + + { text: 'Seconds from now', value: 's+' }, + { text: 'Minutes from now', value: 'm+' }, + { text: 'Hours from now', value: 'h+' }, + { text: 'Days from now', value: 'd+' }, + { text: 'Weeks from now', value: 'w+' }, + { text: 'Months from now', value: 'M+' }, + { text: 'Years from now', value: 'y+' }, +]; + +const timeUnitIds = relativeOptions + .map(({ value }) => value) + .filter((value) => !value.includes('+')) as TimeUnitId[]; + +export const relativeUnitsFromLargestToSmallest = timeUnitIds.reverse(); + +/** + * This function returns time value, time unit and time tense for a given time string. + * + * For example: for `now-40m` it will parse output as time value to `40` time unit to `m` and time unit to `last`. + * + * If given a datetime string it will return a default value. + * + * If the given string is in the format such as `now/d` it will parse the string to moment object and find the time value, time unit and time tense using moment + * + * This function accepts two strings start and end time. I the start value is now then it uses the end value to parse. + */ +export function parseTimeParts(start: string, end: string): QuickSelect | null { + const value = isNow(start) ? end : start; + + const matches = isString(value) && value.match(/now(([-+])(\d+)([smhdwMy])(\/[smhdwMy])?)?/); + + if (!matches) { + return null; + } + + const operator = matches[2]; + const matchedTimeValue = matches[3]; + const timeUnits = matches[4] as TimeUnitId; + + if (matchedTimeValue && timeUnits && operator) { + return { + timeTense: operator === '+' ? NEXT : LAST, + timeUnits, + timeValue: parseInt(matchedTimeValue, 10), + }; + } + + const duration = moment.duration(moment().diff(dateMath.parse(value))); + let unitOp = ''; + for (let i = 0; i < relativeUnitsFromLargestToSmallest.length; i++) { + const as = duration.as(relativeUnitsFromLargestToSmallest[i]); + if (as < 0) { + unitOp = '+'; + } + if (Math.abs(as) > 1) { + return { + timeValue: Math.round(Math.abs(as)), + timeUnits: relativeUnitsFromLargestToSmallest[i], + timeTense: unitOp === '+' ? NEXT : LAST, + }; + } + } + + return null; +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/selected_filters.test.tsx similarity index 71% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.test.tsx rename to x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/selected_filters.test.tsx index eb76772a66c7e..8fc5ae95fd41b 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/selected_filters.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/selected_filters.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { screen, waitFor } from '@testing-library/react'; -import { mockAppIndexPattern, mockIndexPattern, render } from '../rtl_helpers'; +import { mockAppIndexPattern, mockIndexPattern, mockUxSeries, render } from '../rtl_helpers'; import { SelectedFilters } from './selected_filters'; import { getDefaultConfigs } from '../configurations/default_configs'; import { USER_AGENT_NAME } from '../configurations/constants/elasticsearch_fieldnames'; @@ -22,11 +22,19 @@ describe('SelectedFilters', function () { }); it('should render properly', async function () { - const initSeries = { filters: [{ field: USER_AGENT_NAME, values: ['Chrome'] }] }; + const filters = [{ field: USER_AGENT_NAME, values: ['Chrome'] }]; + const initSeries = { filters }; - render(, { - initSeries, - }); + render( + , + { + initSeries, + } + ); await waitFor(() => { screen.getByText('Chrome'); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/selected_filters.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/selected_filters.tsx new file mode 100644 index 0000000000000..46adba1dbde55 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/selected_filters.tsx @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { Fragment } from 'react'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { useRouteMatch } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { FilterLabel } from '../components/filter_label'; +import { SeriesConfig, SeriesUrl, UrlFilter } from '../types'; +import { useAppIndexPatternContext } from '../hooks/use_app_index_pattern'; +import { useSeriesFilters } from '../hooks/use_series_filters'; +import { getFiltersFromDefs } from '../hooks/use_lens_attributes'; +import { useSeriesStorage } from '../hooks/use_series_storage'; + +interface Props { + seriesId: number; + series: SeriesUrl; + seriesConfig: SeriesConfig; +} +export function SelectedFilters({ seriesId, series, seriesConfig }: Props) { + const { setSeries } = useSeriesStorage(); + + const isPreview = !!useRouteMatch('/exploratory-view/preview'); + + const { reportDefinitions = {} } = series; + + const { labels } = seriesConfig; + + const filters: UrlFilter[] = series.filters ?? []; + + let definitionFilters: UrlFilter[] = getFiltersFromDefs(reportDefinitions); + + const isConfigure = !!useRouteMatch('/exploratory-view/configure'); + + // we don't want to display report definition filters in new series view + if (isConfigure) { + definitionFilters = []; + } + + const { removeFilter } = useSeriesFilters({ seriesId, series }); + + const { indexPattern } = useAppIndexPatternContext(series.dataType); + + if ((filters.length === 0 && definitionFilters.length === 0) || !indexPattern) { + return null; + } + + return ( + + {filters.map(({ field, values, notValues }) => ( + + {(values ?? []).length > 0 && ( + + { + values?.forEach((val) => { + removeFilter({ field, value: val, negate: false }); + }); + }} + negate={false} + indexPattern={indexPattern} + /> + + )} + {(notValues ?? []).length > 0 && ( + + { + values?.forEach((val) => { + removeFilter({ field, value: val, negate: false }); + }); + }} + indexPattern={indexPattern} + /> + + )} + + ))} + + {definitionFilters.map(({ field, values }) => + values ? ( + + {}} + negate={false} + definitionFilter={true} + indexPattern={indexPattern} + /> + + ) : null + )} + + {(series.filters ?? []).length > 0 && !isPreview && ( + + { + setSeries(seriesId, { ...series, filters: undefined }); + }} + size="s" + > + {i18n.translate('xpack.observability.expView.seriesEditor.clearFilter', { + defaultMessage: 'Clear filters', + })} + + + )} + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/series_viewer.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/series_viewer.tsx new file mode 100644 index 0000000000000..85d65dcac6ac3 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_viewer/series_viewer.tsx @@ -0,0 +1,120 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { isEmpty } from 'lodash'; +import { EuiBasicTable, EuiSpacer, EuiText } from '@elastic/eui'; +import { SeriesFilter } from './columns/series_filter'; +import { SeriesConfig, SeriesUrl } from '../types'; +import { useSeriesStorage } from '../hooks/use_series_storage'; +import { getDefaultConfigs } from '../configurations/default_configs'; +import { useAppIndexPatternContext } from '../hooks/use_app_index_pattern'; +import { SeriesInfo } from './columns/series_info'; +import { SeriesDatePicker } from '../components/series_date_picker'; +import { NO_BREAK_DOWN_LABEL } from './columns/breakdowns'; + +interface EditItem { + id: number; + series: SeriesUrl; + seriesConfig: SeriesConfig; +} + +export function SeriesViewer() { + const { allSeries, reportType } = useSeriesStorage(); + + const columns = [ + { + name: '', + field: 'id', + width: '10%', + render: (seriesId: number, { seriesConfig, series }: EditItem) => ( + + ), + }, + { + name: i18n.translate('xpack.observability.expView.seriesEditor.name', { + defaultMessage: 'Name', + }), + field: 'id', + width: '15%', + render: (seriesId: number, { series }: EditItem) => {series.name}, + }, + { + name: i18n.translate('xpack.observability.expView.seriesEditor.filters', { + defaultMessage: 'Filters', + }), + field: 'id', + width: '35%', + render: (seriesId: number, { series, seriesConfig }: EditItem) => ( + + ), + }, + { + name: i18n.translate('xpack.observability.expView.seriesEditor.breakdownBy', { + defaultMessage: 'Breakdown by', + }), + field: 'seriesId', + width: '10%', + render: (seriesId: number, { seriesConfig: { labels }, series }: EditItem) => ( + {series.breakdown ? labels[series.breakdown] : NO_BREAK_DOWN_LABEL} + ), + }, + { + name: i18n.translate('xpack.observability.expView.seriesEditor.time', { + defaultMessage: 'Time', + }), + width: '30%', + field: 'id', + render: (seriesId: number, { series }: EditItem) => ( + + ), + }, + ]; + + const { indexPatterns } = useAppIndexPatternContext(); + const items: EditItem[] = []; + + allSeries.forEach((series, seriesIndex) => { + if (indexPatterns[series.dataType] && !isEmpty(series.reportDefinitions)) { + items.push({ + series, + id: seriesIndex, + seriesConfig: getDefaultConfigs({ + reportType, + dataType: series.dataType, + indexPattern: indexPatterns[series.dataType], + }), + }); + } + }); + + if (items.length === 0 && allSeries.length > 0) { + return null; + } + + return ( + <> + + + + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts index 717d98715453d..4bba0c221f3c5 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts @@ -6,6 +6,7 @@ */ import { PaletteOutput } from 'src/plugins/charts/public'; +import { ExistsFilter, PhraseFilter } from '@kbn/es-query'; import { LastValueIndexPatternColumn, DateHistogramIndexPatternColumn, @@ -16,8 +17,7 @@ import { } from '../../../../../lens/public'; import { PersistableFilter } from '../../../../../lens/common'; -import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns'; -import { ExistsFilter } from '../../../../../../../src/plugins/data/common/es_query/filters'; +import { IIndexPattern } from '../../../../../../../src/plugins/data/public'; export const ReportViewTypes = { dist: 'data-distribution', @@ -42,7 +42,7 @@ export interface MetricOption { field?: string; label: string; description?: string; - columnType?: 'range' | 'operation' | 'FILTER_RECORDS' | 'TERMS_COLUMN'; + columnType?: 'range' | 'operation' | 'FILTER_RECORDS' | 'TERMS_COLUMN' | 'unique_count'; columnFilters?: ColumnFilter[]; timeScale?: string; } @@ -55,7 +55,7 @@ export interface SeriesConfig { defaultSeriesType: SeriesType; filterFields: Array; seriesTypes: SeriesType[]; - baseFilters?: PersistableFilter[] | ExistsFilter[]; + baseFilters?: Array; definitionFields: string[]; metricOptions?: MetricOption[]; labels: Record; @@ -69,6 +69,7 @@ export interface SeriesConfig { export type URLReportDefinition = Record; export interface SeriesUrl { + name: string; time: { to: string; from: string; @@ -76,12 +77,12 @@ export interface SeriesUrl { breakdown?: string; filters?: UrlFilter[]; seriesType?: SeriesType; - reportType: ReportViewType; operationType?: OperationType; dataType: AppDataType; reportDefinitions?: URLReportDefinition; selectedMetricField?: string; - isNew?: boolean; + hidden?: boolean; + color?: string; } export interface UrlFilter { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/views/series_views.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/views/series_views.tsx new file mode 100644 index 0000000000000..e0b46102caba0 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/views/series_views.tsx @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { RefObject, useEffect, useState } from 'react'; + +import { EuiTabs, EuiTab, EuiButtonIcon } from '@elastic/eui'; +import { useHistory, useParams } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { SeriesEditor } from '../series_editor/series_editor'; +import { SeriesViewer } from '../series_viewer/series_viewer'; +import { PanelId } from '../exploratory_view'; + +const tabs = [ + { + id: 'preview' as const, + name: i18n.translate('xpack.observability.overview.exploratoryView.preview', { + defaultMessage: 'Preview', + }), + }, + { + id: 'configure' as const, + name: i18n.translate('xpack.observability.overview.exploratoryView.configureSeries', { + defaultMessage: 'Configure series', + }), + }, +]; + +type ViewTab = 'preview' | 'configure'; + +export function SeriesViews({ + seriesBuilderRef, + onSeriesPanelCollapse, +}: { + seriesBuilderRef: RefObject; + onSeriesPanelCollapse: (panel: PanelId) => void; +}) { + const params = useParams<{ mode: ViewTab }>(); + + const history = useHistory(); + + const [selectedTabId, setSelectedTabId] = useState('configure'); + + const onSelectedTabChanged = (id: ViewTab) => { + setSelectedTabId(id); + history.push('/exploratory-view/' + id); + }; + + useEffect(() => { + setSelectedTabId(params.mode); + }, [params.mode]); + + const renderTabs = () => { + return tabs.map((tab, index) => ( + onSelectedTabChanged(tab.id)} + isSelected={tab.id === selectedTabId} + key={index} + > + {tab.id === 'preview' && selectedTabId === 'preview' ? ( + + onSeriesPanelCollapse('seriesPanel')} + /> +  {tab.name} + + ) : ( + tab.name + )} + + )); + }; + + return ( +
+ {renderTabs()} + {selectedTabId === 'preview' && } + {selectedTabId === 'configure' && } +
+ ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/views/view_actions.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/views/view_actions.tsx new file mode 100644 index 0000000000000..db1f23ad9b6e3 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/views/view_actions.tsx @@ -0,0 +1,119 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useEffect, useState } from 'react'; +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSwitch, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { isEqual } from 'lodash'; +import { + allSeriesKey, + convertAllShortSeries, + NEW_SERIES_KEY, + useSeriesStorage, +} from '../hooks/use_series_storage'; +import { SeriesUrl } from '../types'; +import { useAppIndexPatternContext } from '../hooks/use_app_index_pattern'; +import { BuilderItem, getSeriesToEdit } from '../series_editor/series_editor'; +import { DEFAULT_TIME, ReportTypes } from '../configurations/constants'; + +export function ViewActions() { + const [editorItems, setEditorItems] = useState([]); + const { + getSeries, + allSeries, + setSeries, + storage, + reportType, + autoApply, + setAutoApply, + applyChanges, + } = useSeriesStorage(); + + const { loading, indexPatterns } = useAppIndexPatternContext(); + + useEffect(() => { + setEditorItems(getSeriesToEdit({ allSeries, indexPatterns, reportType })); + }, [allSeries, getSeries, indexPatterns, loading, reportType]); + + const addSeries = () => { + const prevSeries = allSeries?.[0]; + const name = `${NEW_SERIES_KEY}-${editorItems.length + 1}`; + const nextSeries = { name } as SeriesUrl; + + const nextSeriesId = allSeries.length; + + if (reportType === 'data-distribution') { + setSeries(nextSeriesId, { + ...nextSeries, + time: prevSeries?.time || DEFAULT_TIME, + } as SeriesUrl); + } else { + setSeries( + nextSeriesId, + prevSeries ? nextSeries : ({ ...nextSeries, time: DEFAULT_TIME } as SeriesUrl) + ); + } + }; + + const noChanges = isEqual(allSeries, convertAllShortSeries(storage.get(allSeriesKey) ?? [])); + + const isAddDisabled = + !reportType || + ((reportType === ReportTypes.CORE_WEB_VITAL || + reportType === ReportTypes.DEVICE_DISTRIBUTION) && + allSeries.length > 0); + + return ( + + + setAutoApply(!autoApply)} + compressed + /> + + {!autoApply && ( + + applyChanges()} isDisabled={autoApply || noChanges} fill> + {i18n.translate('xpack.observability.expView.seriesBuilder.apply', { + defaultMessage: 'Apply changes', + })} + + + )} + + + addSeries()} isDisabled={isAddDisabled}> + {i18n.translate('xpack.observability.expView.seriesBuilder.addSeries', { + defaultMessage: 'Add series', + })} + + + + + ); +} + +const AUTO_APPLY_LABEL = i18n.translate('xpack.observability.expView.seriesBuilder.autoApply', { + defaultMessage: 'Auto apply', +}); diff --git a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_combobox.tsx b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_combobox.tsx index fc562fa80e26d..0735df53888aa 100644 --- a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_combobox.tsx +++ b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_combobox.tsx @@ -6,15 +6,24 @@ */ import React, { useEffect, useState } from 'react'; -import { union } from 'lodash'; -import { EuiComboBox, EuiFormControlLayout, EuiComboBoxOptionOption } from '@elastic/eui'; +import { union, isEmpty } from 'lodash'; +import { + EuiComboBox, + EuiFormControlLayout, + EuiComboBoxOptionOption, + EuiFormRow, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; import { FieldValueSelectionProps } from './types'; export const ALL_VALUES_SELECTED = 'ALL_VALUES'; const formatOptions = (values?: string[], allowAllValuesSelection?: boolean) => { const uniqueValues = Array.from( - new Set(allowAllValuesSelection ? ['ALL_VALUES', ...(values ?? [])] : values) + new Set( + allowAllValuesSelection && (values ?? []).length > 0 + ? ['ALL_VALUES', ...(values ?? [])] + : values + ) ); return (uniqueValues ?? []).map((label) => ({ @@ -30,7 +39,9 @@ export function FieldValueCombobox({ loading, values, setQuery, + usePrependLabel = true, compressed = true, + required = true, allowAllValuesSelection, onChange: onSelectionChange, }: FieldValueSelectionProps) { @@ -54,29 +65,35 @@ export function FieldValueCombobox({ onSelectionChange(selectedValuesN.map(({ label: lbl }) => lbl)); }; - return ( + const comboBox = ( + { + setQuery(searchVal); + }} + options={options} + selectedOptions={options.filter((opt) => selectedValue?.includes(opt.label))} + onChange={onChange} + isInvalid={required && isEmpty(selectedValue)} + /> + ); + + return usePrependLabel ? ( - { - setQuery(searchVal); - }} - options={options} - selectedOptions={options.filter((opt) => selectedValue?.includes(opt.label))} - onChange={onChange} - /> + {comboBox} + ) : ( + + {comboBox} + ); } diff --git a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_selection.tsx b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_selection.tsx index f713af9768229..cee3ab8aea28b 100644 --- a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_selection.tsx +++ b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_selection.tsx @@ -70,6 +70,7 @@ export function FieldValueSelection({ values = [], selectedValue, excludedValue, + allowExclusions = true, compressed = true, onChange: onSelectionChange, }: FieldValueSelectionProps) { @@ -173,8 +174,8 @@ export function FieldValueSelection({ }} options={options} onChange={onChange} + allowExclusions={allowExclusions} isLoading={loading && !query && options.length === 0} - allowExclusions={true} > {(list, search) => (
diff --git a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/index.test.tsx b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/index.test.tsx index 3c7d0851531b4..1dd43d45c40e6 100644 --- a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/index.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/index.test.tsx @@ -96,6 +96,7 @@ describe.skip('FieldValueSuggestions', () => { selectedValue={[]} filters={[]} asCombobox={false} + allowExclusions={true} /> ); @@ -118,10 +119,13 @@ describe.skip('FieldValueSuggestions', () => { excludedValue={['Pak']} filters={[]} asCombobox={false} + allowExclusions={true} /> ); + fireEvent.click(screen.getByText('Service name')); + fireEvent.click(await screen.findByText('US')); fireEvent.click(await screen.findByText('Pak')); fireEvent.click(await screen.findByText('Apply')); diff --git a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/index.tsx b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/index.tsx index 54114c7604644..65e1d0932e4ed 100644 --- a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/index.tsx +++ b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/index.tsx @@ -28,7 +28,10 @@ export function FieldValueSuggestions({ singleSelection, compressed, asFilterButton, + usePrependLabel, allowAllValuesSelection, + required, + allowExclusions = true, asCombobox = true, onChange: onSelectionChange, }: FieldValueSuggestionsProps) { @@ -64,7 +67,10 @@ export function FieldValueSuggestions({ width={width} compressed={compressed} asFilterButton={asFilterButton} + usePrependLabel={usePrependLabel} + allowExclusions={allowExclusions} allowAllValuesSelection={allowAllValuesSelection} + required={required} /> ); } diff --git a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts index d857b39b074ac..73b3d78ce8700 100644 --- a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts +++ b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts @@ -23,7 +23,10 @@ interface CommonProps { compressed?: boolean; asFilterButton?: boolean; showCount?: boolean; + usePrependLabel?: boolean; + allowExclusions?: boolean; allowAllValuesSelection?: boolean; + required?: boolean; } export type FieldValueSuggestionsProps = CommonProps & { diff --git a/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx b/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx index 01d727071770d..82392b5c23bf9 100644 --- a/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx +++ b/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx @@ -18,21 +18,25 @@ export function buildFilterLabel({ negate, }: { label: string; - value: string; + value: string | string[]; negate: boolean; field: string; indexPattern: IndexPattern; }) { const indexField = indexPattern.getFieldByName(field)!; - const filter = esFilters.buildPhraseFilter(indexField, value, indexPattern); + const filter = + value instanceof Array && value.length > 1 + ? esFilters.buildPhrasesFilter(indexField, value, indexPattern) + : esFilters.buildPhraseFilter(indexField, value, indexPattern); - filter.meta.value = value; + filter.meta.type = value instanceof Array && value.length > 1 ? 'phrases' : 'phrase'; + + filter.meta.value = value as string; filter.meta.key = label; filter.meta.alias = null; filter.meta.negate = negate; filter.meta.disabled = false; - filter.meta.type = 'phrase'; return filter; } @@ -40,10 +44,10 @@ export function buildFilterLabel({ interface Props { field: string; label: string; - value: string; + value: string | string[]; negate: boolean; - removeFilter: (field: string, value: string, notVal: boolean) => void; - invertFilter: (val: { field: string; value: string; negate: boolean }) => void; + removeFilter: (field: string, value: string | string[], notVal: boolean) => void; + invertFilter: (val: { field: string; value: string | string[]; negate: boolean }) => void; indexPattern: IndexPattern; allowExclusion?: boolean; } diff --git a/x-pack/plugins/observability/public/components/shared/index.tsx b/x-pack/plugins/observability/public/components/shared/index.tsx index 9d557a40b7987..afc053604fcdf 100644 --- a/x-pack/plugins/observability/public/components/shared/index.tsx +++ b/x-pack/plugins/observability/public/components/shared/index.tsx @@ -6,6 +6,7 @@ */ import React, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; import type { CoreVitalProps, HeaderMenuPortalProps } from './types'; import type { FieldValueSuggestionsProps } from './field_value_suggestions/types'; @@ -26,7 +27,7 @@ const HeaderMenuPortalLazy = lazy(() => import('./header_menu_portal')); export function HeaderMenuPortal(props: HeaderMenuPortalProps) { return ( - + }> ); diff --git a/x-pack/plugins/observability/public/hooks/use_quick_time_ranges.tsx b/x-pack/plugins/observability/public/hooks/use_quick_time_ranges.tsx index 82a0fc39b8519..198b4092b0ed6 100644 --- a/x-pack/plugins/observability/public/hooks/use_quick_time_ranges.tsx +++ b/x-pack/plugins/observability/public/hooks/use_quick_time_ranges.tsx @@ -7,7 +7,7 @@ import { useUiSetting } from '../../../../../src/plugins/kibana_react/public'; import { UI_SETTINGS } from '../../../../../src/plugins/data/common'; -import { TimePickerQuickRange } from '../components/shared/exploratory_view/series_date_picker'; +import { TimePickerQuickRange } from '../components/shared/exploratory_view/components/series_date_picker'; export function useQuickTimeRanges() { const timePickerQuickRanges = useUiSetting( diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index 7b1ef5b0bad7a..f2ff2a01ebed0 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -24,6 +24,7 @@ import type { DataPublicPluginSetup, DataPublicPluginStart, } from '../../../../src/plugins/data/public'; +import type { DiscoverStart } from '../../../../src/plugins/discover/public'; import type { HomePublicPluginSetup, HomePublicPluginStart, @@ -56,6 +57,7 @@ export interface ObservabilityPublicPluginsStart { triggersActionsUi: TriggersAndActionsUIPublicPluginStart; data: DataPublicPluginStart; lens: LensPublicStart; + discover: DiscoverStart; } export type ObservabilityPublicStart = ReturnType; diff --git a/x-pack/plugins/observability/public/routes/index.tsx b/x-pack/plugins/observability/public/routes/index.tsx index f97e3fb996441..09d22496c98ff 100644 --- a/x-pack/plugins/observability/public/routes/index.tsx +++ b/x-pack/plugins/observability/public/routes/index.tsx @@ -7,6 +7,7 @@ import * as t from 'io-ts'; import React from 'react'; +import { Redirect } from 'react-router-dom'; import { alertStatusRt } from '../../common/typings'; import { ExploratoryViewPage } from '../components/shared/exploratory_view'; import { AlertsPage } from '../pages/alerts'; @@ -99,7 +100,20 @@ export const routes = { }), }, }, - '/exploratory-view': { + '/exploratory-view/': { + handler: () => { + return ; + }, + params: { + query: t.partial({ + rangeFrom: t.string, + rangeTo: t.string, + refreshPaused: jsonRt.pipe(t.boolean), + refreshInterval: jsonRt.pipe(t.number), + }), + }, + }, + '/exploratory-view/:mode': { handler: () => { return ; }, @@ -112,18 +126,4 @@ export const routes = { }), }, }, - // enable this to test multi series architecture - // '/exploratory-view/multi': { - // handler: () => { - // return ; - // }, - // params: { - // query: t.partial({ - // rangeFrom: t.string, - // rangeTo: t.string, - // refreshPaused: jsonRt.pipe(t.boolean), - // refreshInterval: jsonRt.pipe(t.number), - // }), - // }, - // }, }; diff --git a/x-pack/plugins/osquery/common/typed_json.ts b/x-pack/plugins/osquery/common/typed_json.ts index 8ce6907beb80b..7ef7469a5ebe7 100644 --- a/x-pack/plugins/osquery/common/typed_json.ts +++ b/x-pack/plugins/osquery/common/typed_json.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { DslQuery, Filter } from 'src/plugins/data/common'; - +import { DslQuery, Filter } from '@kbn/es-query'; import { JsonObject } from '@kbn/common-utils'; export type ESQuery = diff --git a/x-pack/plugins/osquery/public/saved_queries/use_create_saved_query.ts b/x-pack/plugins/osquery/public/saved_queries/use_create_saved_query.ts index 1d10d80bd6fbf..ddc25630079cd 100644 --- a/x-pack/plugins/osquery/public/saved_queries/use_create_saved_query.ts +++ b/x-pack/plugins/osquery/public/saved_queries/use_create_saved_query.ts @@ -36,16 +36,15 @@ export const useCreateSavedQuery = ({ withRedirect }: UseCreateSavedQueryProps) if (!currentUser) { throw new Error('CurrentUser is missing'); } - + // @ts-expect-error update types + const payloadId = payload.id; const conflictingEntries = await savedObjects.client.find({ type: savedQuerySavedObjectType, - // @ts-expect-error update types - search: payload.id, + search: payloadId, searchFields: ['id'], }); if (conflictingEntries.savedObjects.length) { - // @ts-expect-error update types - throw new Error(`Saved query with id ${payload.id} already exists.`); + throw new Error(`Saved query with id ${payloadId} already exists.`); } return savedObjects.client.create(savedQuerySavedObjectType, { // @ts-expect-error update types @@ -58,6 +57,12 @@ export const useCreateSavedQuery = ({ withRedirect }: UseCreateSavedQueryProps) }, { onError: (error) => { + if (error instanceof Error) { + return setErrorToast(error, { + title: 'Saved query creation error', + toastMessage: error.message, + }); + } // @ts-expect-error update types setErrorToast(error, { title: error.body.error, toastMessage: error.body.message }); }, diff --git a/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts b/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts index fe0d38648b23c..45b7a4c2d6b9a 100644 --- a/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts +++ b/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts @@ -37,15 +37,21 @@ export const useUpdateSavedQuery = ({ savedQueryId }: UseUpdateSavedQueryProps) throw new Error('CurrentUser is missing'); } + // @ts-expect-error update types + const payloadId = payload.id; const conflictingEntries = await savedObjects.client.find({ type: savedQuerySavedObjectType, - // @ts-expect-error update types - search: payload.id, + search: payloadId, searchFields: ['id'], }); - if (conflictingEntries.savedObjects.length) { - // @ts-expect-error update types - throw new Error(`Saved query with id ${payload.id} already exists.`); + const conflictingObjects = conflictingEntries.savedObjects; + // we some how have more than one object with the same id + const updateConflicts = + conflictingObjects.length > 1 || + // or the one we conflict with isn't the same one we are updating + (conflictingObjects.length && conflictingObjects[0].id !== savedQueryId); + if (updateConflicts) { + throw new Error(`Saved query with id ${payloadId} already exists.`); } return savedObjects.client.update(savedQuerySavedObjectType, savedQueryId, { @@ -57,6 +63,12 @@ export const useUpdateSavedQuery = ({ savedQueryId }: UseUpdateSavedQueryProps) }, { onError: (error) => { + if (error instanceof Error) { + return setErrorToast(error, { + title: 'Saved query update error', + toastMessage: error.message, + }); + } // @ts-expect-error update types setErrorToast(error, { title: error.body.error, toastMessage: error.body.message }); }, diff --git a/x-pack/plugins/reporting/kibana.json b/x-pack/plugins/reporting/kibana.json index 6a8f3a3e4e5ec..4bddfae96756d 100644 --- a/x-pack/plugins/reporting/kibana.json +++ b/x-pack/plugins/reporting/kibana.json @@ -2,6 +2,11 @@ "id": "reporting", "version": "8.0.0", "kibanaVersion": "kibana", + "owner": { + "name": "Kibana Reporting Services", + "githubTeam": "kibana-reporting-services" + }, + "description": "Reporting Services enables applications to feature reports that the user can automate with Watcher and download later.", "optionalPlugins": ["security", "spaces", "usageCollection"], "configPath": ["xpack", "reporting"], "requiredPlugins": [ diff --git a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts index 1e7bcb0002dad..69f21e605627b 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts @@ -11,12 +11,8 @@ import type { CreateExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; import { buildExceptionFilter } from '@kbn/securitysolution-list-utils'; -import { - Filter, - IIndexPattern, - buildEsQuery, - EsQueryConfig, -} from '../../../../../src/plugins/data/common'; +import { Filter, EsQueryConfig, IndexPatternBase, buildEsQuery } from '@kbn/es-query'; + import { ESBoolQuery } from '../typed_json'; import { Query, Index, TimestampOverrideOrUndefined } from './schemas/common/schemas'; @@ -28,7 +24,7 @@ export const getQueryFilter = ( lists: Array, excludeExceptions: boolean = true ): ESBoolQuery => { - const indexPattern: IIndexPattern = { + const indexPattern: IndexPatternBase = { fields: [], title: index.join(), }; diff --git a/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts b/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts index 85d339970dc59..f7f3df31db237 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts @@ -5,12 +5,9 @@ * 2.0. */ -import { IIndexPattern } from 'src/plugins/data/public'; -import { - IEsSearchRequest, - IEsSearchResponse, - IFieldSubType, -} from '../../../../../../src/plugins/data/common'; +import { IFieldSubType } from '@kbn/es-query'; +import type { IIndexPattern } from 'src/plugins/data/public'; +import { IEsSearchRequest, IEsSearchResponse } from '../../../../../../src/plugins/data/common'; import { DocValueFields, Maybe } from '../common'; interface FieldInfo { diff --git a/x-pack/plugins/security_solution/common/typed_json.ts b/x-pack/plugins/security_solution/common/typed_json.ts index 8ce6907beb80b..1c42ab3a6fd24 100644 --- a/x-pack/plugins/security_solution/common/typed_json.ts +++ b/x-pack/plugins/security_solution/common/typed_json.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { DslQuery, Filter } from 'src/plugins/data/common'; +import { DslQuery, Filter } from '@kbn/es-query'; import { JsonObject } from '@kbn/common-utils'; diff --git a/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts b/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts index 229bbcce87696..24de882bdfb90 100644 --- a/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts +++ b/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts @@ -65,8 +65,8 @@ describe('Alert details with unmapped fields', () => { cy.get(TABLE_ROWS) .eq(expectedUnmmappedField.row) .within(() => { - cy.get(CELL_TEXT).eq(0).should('have.text', expectedUnmmappedField.field); - cy.get(CELL_TEXT).eq(1).should('have.text', expectedUnmmappedField.text); + cy.get(CELL_TEXT).eq(2).should('have.text', expectedUnmmappedField.field); + cy.get(CELL_TEXT).eq(4).should('have.text', expectedUnmmappedField.text); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts index 825cc7f8081e5..3b524bd252cdd 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts @@ -61,8 +61,8 @@ describe('Alert details with unmapped fields', () => { cy.get(TABLE_ROWS) .eq(expectedUnmmappedField.row) .within(() => { - cy.get(CELL_TEXT).eq(0).should('have.text', expectedUnmmappedField.field); - cy.get(CELL_TEXT).eq(1).should('have.text', expectedUnmmappedField.text); + cy.get(CELL_TEXT).eq(2).should('have.text', expectedUnmmappedField.field); + cy.get(CELL_TEXT).eq(4).should('have.text', expectedUnmmappedField.text); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/overview/cti_link_panel.spec.ts b/x-pack/plugins/security_solution/cypress/integration/overview/cti_link_panel.spec.ts index 514c4b4e12bb3..095401ff31422 100644 --- a/x-pack/plugins/security_solution/cypress/integration/overview/cti_link_panel.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/overview/cti_link_panel.spec.ts @@ -31,11 +31,9 @@ describe('CTI Link Panel', () => { cy.get(`${OVERVIEW_CTI_VIEW_DASHBOARD_BUTTON}`).should('be.disabled'); cy.get(`${OVERVIEW_CTI_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 indicators'); cy.get(`${OVERVIEW_CTI_ENABLE_MODULE_BUTTON}`).should('exist'); - cy.get(`${OVERVIEW_CTI_ENABLE_MODULE_BUTTON}`).should( - 'have.attr', - 'href', - 'https://www.elastic.co/guide/en/beats/filebeat/master/filebeat-module-threatintel.html' - ); + cy.get(`${OVERVIEW_CTI_ENABLE_MODULE_BUTTON}`) + .should('have.attr', 'href') + .and('match', /filebeat-module-threatintel.html/); }); describe('enabled threat intel module', () => { diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/toggle_column.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/toggle_column.spec.ts index 786d153fd94d6..b44856a469cfb 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/toggle_column.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/toggle_column.spec.ts @@ -5,24 +5,18 @@ * 2.0. */ -import { - ID_HEADER_FIELD, - ID_TOGGLE_FIELD, - TIMESTAMP_HEADER_FIELD, - TIMESTAMP_TOGGLE_FIELD, -} from '../../screens/timeline'; +import { ID_HEADER_FIELD, TIMESTAMP_HEADER_FIELD } from '../../screens/timeline'; import { cleanKibana } from '../../tasks/common'; import { loginAndWaitForPage } from '../../tasks/login'; import { openTimelineUsingToggle } from '../../tasks/security_main'; import { - checkIdToggleField, + clickIdToggleField, closeTimeline, createNewTimeline, - dragAndDropIdToggleFieldToTimeline, expandFirstTimelineEventDetails, populateTimeline, - uncheckTimestampToggleField, + clickTimestampToggleField, } from '../../tasks/timeline'; import { HOSTS_URL } from '../../urls/navigation'; @@ -44,33 +38,16 @@ describe('toggle column in timeline', () => { closeTimeline(); }); - it('displays a checked Toggle field checkbox for `@timestamp`, a default timeline column', () => { - expandFirstTimelineEventDetails(); - cy.get(TIMESTAMP_TOGGLE_FIELD).should('be.checked'); - }); - - it('displays an Unchecked Toggle field checkbox for `_id`, because it is NOT a default timeline column', () => { - expandFirstTimelineEventDetails(); - cy.get(ID_TOGGLE_FIELD).should('not.be.checked'); - }); - it('removes the @timestamp field from the timeline when the user un-checks the toggle', () => { expandFirstTimelineEventDetails(); - uncheckTimestampToggleField(); + clickTimestampToggleField(); cy.get(TIMESTAMP_HEADER_FIELD).should('not.exist'); }); it('adds the _id field to the timeline when the user checks the field', () => { expandFirstTimelineEventDetails(); - checkIdToggleField(); - - cy.get(ID_HEADER_FIELD).should('exist'); - }); - - it('adds the _id field to the timeline via drag and drop', () => { - expandFirstTimelineEventDetails(); - dragAndDropIdToggleFieldToTimeline(); + clickIdToggleField(); cy.get(ID_HEADER_FIELD).should('exist'); }); diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index af7a7bb5d4c71..e6f2fb30bede8 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -189,10 +189,10 @@ export const attachTimelineToExistingCase = () => { cy.get(ATTACH_TIMELINE_TO_EXISTING_CASE_ICON).click({ force: true }); }; -export const checkIdToggleField = () => { +export const clickIdToggleField = () => { cy.get(ID_HEADER_FIELD).should('not.exist'); - cy.get(ID_TOGGLE_FIELD).check({ + cy.get(ID_TOGGLE_FIELD).click({ force: true, }); }; @@ -294,10 +294,10 @@ export const unpinFirstEvent = () => { cy.get(PIN_EVENT).first().click({ force: true }); }; -export const uncheckTimestampToggleField = () => { +export const clickTimestampToggleField = () => { cy.get(TIMESTAMP_TOGGLE_FIELD).should('exist'); - cy.get(TIMESTAMP_TOGGLE_FIELD).uncheck({ force: true }); + cy.get(TIMESTAMP_TOGGLE_FIELD).click({ force: true }); }; export const dragAndDropIdToggleFieldToTimeline = () => { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/columns.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/columns.test.tsx index 198277e1fb941..9d254b0d27f6b 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/columns.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/columns.test.tsx @@ -12,9 +12,10 @@ import { useMountAppended } from '../../utils/use_mount_appended'; import { mockBrowserFields } from '../../containers/source/mock'; import { EventFieldsData } from './types'; +jest.mock('../../lib/kibana'); interface Column { field: string; - name: string; + name: string | JSX.Element; sortable: boolean; render: (field: string, data: EventFieldsData) => JSX.Element; } @@ -42,39 +43,71 @@ describe('getColumns', () => { }); }); - describe('column checkbox', () => { - let checkboxColumn: Column; + describe('column actions', () => { + let actionsColumn: Column; const mockDataToUse = mockBrowserFields.agent; + const testValue = 'testValue'; const testData = { type: 'someType', category: 'agent', + field: 'agent.id', ...mockDataToUse, } as EventFieldsData; beforeEach(() => { - checkboxColumn = getColumns(defaultProps)[0] as Column; + actionsColumn = getColumns(defaultProps)[0] as Column; }); - test('should be enabled when the field does not exist', () => { - const testField = 'nonExistingField'; - const wrapper = mount( - {checkboxColumn.render(testField, testData)} - ) as ReactWrapper; - expect( - wrapper.find(`[data-test-subj="toggle-field-${testField}"]`).first().prop('disabled') - ).toBe(false); + describe('filter in', () => { + test('it renders a filter for (+) button', () => { + const wrapper = mount( + {actionsColumn.render(testValue, testData)} + ) as ReactWrapper; + + expect(wrapper.find('[data-test-subj="hover-actions-filter-for"]').exists()).toBeTruthy(); + }); + }); + + describe('filter out', () => { + test('it renders a filter out (-) button', () => { + const wrapper = mount( + {actionsColumn.render(testValue, testData)} + ) as ReactWrapper; + + expect(wrapper.find('[data-test-subj="hover-actions-filter-out"]').exists()).toBeTruthy(); + }); }); - test('should be enabled when the field does exist', () => { - const testField = mockDataToUse.fields - ? Object.keys(mockDataToUse.fields)[0] - : 'agent.hostname'; - const wrapper = mount( - {checkboxColumn.render(testField, testData)} - ) as ReactWrapper; - expect( - wrapper.find(`[data-test-subj="toggle-field-${testField}"]`).first().prop('disabled') - ).toBe(false); + describe('add to timeline', () => { + test('it renders an add to timeline button', () => { + const wrapper = mount( + {actionsColumn.render(testValue, testData)} + ) as ReactWrapper; + + expect(wrapper.find('[data-test-subj="hover-actions-add-timeline"]').exists()).toBeTruthy(); + }); + }); + + describe('column toggle', () => { + test('it renders a column toggle button', () => { + const wrapper = mount( + {actionsColumn.render(testValue, testData)} + ) as ReactWrapper; + + expect( + wrapper.find('[data-test-subj="hover-actions-toggle-column"]').exists() + ).toBeTruthy(); + }); + }); + + describe('copy', () => { + test('it renders a copy button', () => { + const wrapper = mount( + {actionsColumn.render(testValue, testData)} + ) as ReactWrapper; + + expect(wrapper.find('[data-test-subj="hover-actions-copy-button"]').exists()).toBeTruthy(); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx index 1be05cc560552..b8c10cf506476 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/columns.tsx @@ -7,37 +7,19 @@ /* eslint-disable react/display-name */ -import { - EuiCheckbox, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiPanel, - EuiToolTip, - EuiIconTip, - EuiText, -} from '@elastic/eui'; -import { get, isEmpty } from 'lodash'; +import { EuiPanel, EuiText } from '@elastic/eui'; +import { get } from 'lodash'; import memoizeOne from 'memoize-one'; import React from 'react'; import styled from 'styled-components'; -import { onFocusReFocusDraggable } from '../../../../../timelines/public'; import { BrowserFields } from '../../containers/source'; -import { DragEffects } from '../drag_and_drop/draggable_wrapper'; -import { DroppableWrapper } from '../drag_and_drop/droppable_wrapper'; -import { DRAG_TYPE_FIELD, getDroppableId } from '../drag_and_drop/helpers'; -import { DraggableFieldBadge } from '../draggables/field_badge'; -import { DraggableFieldsBrowserField } from '../../../timelines/components/fields_browser/field_items'; -import { OverflowField } from '../tables/helpers'; -import { defaultColumnHeaderType } from '../../../timelines/components/timeline/body/column_headers/default_headers'; -import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants'; -import { MESSAGE_FIELD_NAME } from '../../../timelines/components/timeline/body/renderers/constants'; -import { FormattedFieldValue } from '../../../timelines/components/timeline/body/renderers/formatted_field'; import { OnUpdateColumns } from '../../../timelines/components/timeline/events'; -import { getIconFromType, getExampleText } from './helpers'; import * as i18n from './translations'; import { EventFieldsData } from './types'; import { ColumnHeaderOptions } from '../../../../common'; +import { FieldValueCell } from './table/field_value_cell'; +import { FieldNameCell } from './table/field_name_cell'; +import { ActionCell } from './table/action_cell'; const HoverActionsContainer = styled(EuiPanel)` align-items: center; @@ -53,13 +35,6 @@ const HoverActionsContainer = styled(EuiPanel)` HoverActionsContainer.displayName = 'HoverActionsContainer'; -const FullWidthFlexGroup = styled(EuiFlexGroup)` - width: 100%; -`; - -const FullWidthFlexItem = styled(EuiFlexItem)` - width: 100%; -`; export const getFieldFromBrowserField = memoizeOne( (keys: string[], browserFields: BrowserFields): BrowserFields => get(browserFields, keys), (newArgs, lastArgs) => newArgs[0].join() === lastArgs[0].join() @@ -84,37 +59,46 @@ export const getColumns = ({ getLinkValue: (field: string) => string | null; }) => [ { - field: 'field', - name: '', + field: 'values', + name: ( + + {i18n.ACTIONS} + + ), sortable: false, truncateText: false, - width: '30px', - render: (field: string, data: EventFieldsData) => { - const label = data.isObjectArray ? i18n.NESTED_COLUMN(field) : i18n.VIEW_COLUMN(field); + width: '180px', + render: (values: string[] | null | undefined, data: EventFieldsData) => { + const label = data.isObjectArray + ? i18n.NESTED_COLUMN(data.field) + : i18n.VIEW_COLUMN(data.field); + const fieldFromBrowserField = getFieldFromBrowserField( + [data.category, 'fields', data.field], + browserFields + ); return ( - - c.id === field) !== -1} - data-test-subj={`toggle-field-${field}`} - data-colindex={1} - id={field} - onChange={() => - toggleColumn({ - columnHeaderType: defaultColumnHeaderType, - id: field, - initialWidth: DEFAULT_COLUMN_MIN_WIDTH, - }) - } - disabled={data.isObjectArray && data.type !== 'geo_point'} - /> - + ); }, }, { field: 'field', - name: i18n.FIELD, + className: 'eventFieldsTable__fieldNameCell', + name: ( + + {i18n.FIELD} + + ), sortable: true, truncateText: false, render: (field: string, data: EventFieldsData) => { @@ -123,67 +107,23 @@ export const getColumns = ({ browserFields ); return ( - - - - - - - - {(data.isObjectArray && data.type !== 'geo_point') || fieldFromBrowserField == null ? ( - {field} - ) : ( - ( -
- - - -
- )} - > - -
- )} -
- {!isEmpty(data.description) && ( - - - - )} -
+ ); }, }, { field: 'values', - name: i18n.VALUE, + className: 'eventFieldsTable__fieldValueCell', + name: ( + + {i18n.VALUE} + + ), sortable: true, truncateText: false, render: (values: string[] | null | undefined, data: EventFieldsData) => { @@ -192,57 +132,15 @@ export const getColumns = ({ browserFields ); return ( - - {values != null && - values.map((value, i) => { - if (fieldFromBrowserField == null) { - return {value}; - } - return ( - -
- {data.field === MESSAGE_FIELD_NAME ? ( - - ) : ( - - )} -
-
- ); - })} -
+ ); }, }, - { - field: 'valuesConcatenated', - name: i18n.BLANK, - render: () => null, - sortable: false, - truncateText: true, - width: '1px', - }, ]; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx index 555b67da953d6..80c014771ae68 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx @@ -10,7 +10,6 @@ import React from 'react'; import '../../mock/match_media'; import { mockDetailItemData, mockDetailItemDataId } from '../../mock/mock_detail_item'; import { TestProviders } from '../../mock/test_providers'; -import { timelineActions } from '../../../timelines/store/timeline'; import { EventFieldsBrowser } from './event_fields_browser'; import { mockBrowserFields } from '../../containers/source/mock'; import { useMountAppended } from '../../utils/use_mount_appended'; @@ -43,7 +42,7 @@ describe('EventFieldsBrowser', () => { const mount = useMountAppended(); describe('column headers', () => { - ['Field', 'Value'].forEach((header) => { + ['Actions', 'Field', 'Value'].forEach((header) => { test(`it renders the ${header} column header`, () => { const wrapper = mount( @@ -57,7 +56,7 @@ describe('EventFieldsBrowser', () => { ); - expect(wrapper.find('thead').containsMatchingElement({header})).toBeTruthy(); + expect(wrapper.find('thead').contains(header)).toBeTruthy(); }); }); }); @@ -82,12 +81,10 @@ describe('EventFieldsBrowser', () => { }); }); - describe('toggle column checkbox', () => { + describe('Hover Actions', () => { const eventId = 'pEMaMmkBUV60JmNWmWVi'; - test('it renders an UNchecked checkbox for a field that is not a member of columnHeaders', () => { - const field = 'agent.id'; - + test('it renders a filter for (+) button', () => { const wrapper = mount( { ); - expect(wrapper.find(`[data-test-subj="toggle-field-${field}"]`).first().props().checked).toBe( - false - ); + expect(wrapper.find('[data-test-subj="hover-actions-filter-for"]').exists()).toBeTruthy(); }); - test('it renders an checked checkbox for a field that is a member of columnHeaders', () => { - const field = '@timestamp'; - + test('it renders a filter out (-) button', () => { const wrapper = mount( { ); - expect(wrapper.find(`[data-test-subj="toggle-field-${field}"]`).first().props().checked).toBe( - true - ); + expect(wrapper.find('[data-test-subj="hover-actions-filter-out"]').exists()).toBeTruthy(); }); - test('it invokes toggleColumn when the checkbox is clicked', () => { - const field = '@timestamp'; + test('it renders an add to timeline button', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="hover-actions-add-timeline"]').exists()).toBeTruthy(); + }); + test('it renders a column toggle button', () => { const wrapper = mount( { ); - wrapper - .find(`[data-test-subj="toggle-field-${field}"]`) - .find(`input[type="checkbox"]`) - .first() - .simulate('change', { - target: { checked: true }, - }); - wrapper.update(); + expect( + wrapper.find('[data-test-subj="hover-actions-toggle-column"]').first().exists() + ).toBeTruthy(); + }); - expect(mockDispatch).toBeCalledWith( - timelineActions.removeColumn({ - columnId: '@timestamp', - id: 'test', - }) + test('it renders a copy button', () => { + const wrapper = mount( + + + ); + + expect(wrapper.find('[data-test-subj="hover-actions-copy-button"]').exists()).toBeTruthy(); }); }); @@ -219,7 +228,7 @@ describe('EventFieldsBrowser', () => { .find('[data-euiicon-type]') .last() .prop('data-euiicon-type') - ).toEqual('iInCircle'); + ).toEqual('tokenDate'); }); }); @@ -257,12 +266,7 @@ describe('EventFieldsBrowser', () => { ); expect( - wrapper - .find('.euiTableRow') - .find('.euiTableRowCell') - .at(1) - .find('EuiIconTip') - .prop('content') + wrapper.find('[data-test-subj="field-name-cell"]').at(0).find('EuiToolTip').prop('content') ).toContain( 'Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events. Example: 2016-05-23T08:05:34.853Z' ); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx index 3ad7e9aef19dc..46b7bd5a60b8d 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx @@ -9,7 +9,6 @@ import { getOr, noop, sortBy } from 'lodash/fp'; import { EuiInMemoryTable } from '@elastic/eui'; import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { useDispatch } from 'react-redux'; -import { rgba } from 'polished'; import styled from 'styled-components'; import { arrayIndexToAriaIndex, @@ -38,42 +37,74 @@ interface Props { timelineTabType: TimelineTabs | 'flyout'; } -const TableWrapper = styled.div` - display: flex; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const StyledEuiInMemoryTable = styled(EuiInMemoryTable as any)` flex: 1; - overflow: hidden; + overflow: auto; - > div { - display: flex; - flex-direction: column; - flex: 1; - overflow: hidden; + .eventFieldsTable__fieldIcon { + padding-top: ${({ theme }) => parseFloat(theme.eui.euiSizeXS) * 1.5}px; + } - > .euiFlexGroup:first-of-type { - flex: 0; - } + .eventFieldsTable__fieldName { + line-height: ${({ theme }) => theme.eui.euiLineHeight}; + padding: ${({ theme }) => theme.eui.euiSizeXS}; } -`; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const StyledEuiInMemoryTable = styled(EuiInMemoryTable as any)` - flex: 1; - overflow: auto; + // TODO: Use this logic from discover + /* .eventFieldsTable__multiFieldBadge { + font: ${({ theme }) => theme.eui.euiFont}; + } */ - &::-webkit-scrollbar { - height: ${({ theme }) => theme.eui.euiScrollBar}; - width: ${({ theme }) => theme.eui.euiScrollBar}; + .eventFieldsTable__tableRow { + font-size: ${({ theme }) => theme.eui.euiFontSizeXS}; + font-family: ${({ theme }) => theme.eui.euiCodeFontFamily}; + + .eventFieldsTable__hoverActionButtons { + &:focus-within { + .timelines__hoverActionButton, + .securitySolution__hoverActionButton { + opacity: 1; + } + } + } + &:hover { + .timelines__hoverActionButton, + .securitySolution__hoverActionButton { + opacity: 1; + } + } + .timelines__hoverActionButton, + .securitySolution__hoverActionButton { + // TODO: Using this logic from discover + /* @include euiBreakpoint('m', 'l', 'xl') { + opacity: 0; + } */ + opacity: 0; + &:focus { + opacity: 1; + } + } } - &::-webkit-scrollbar-thumb { - background-clip: content-box; - background-color: ${({ theme }) => rgba(theme.eui.euiColorDarkShade, 0.5)}; - border: ${({ theme }) => theme.eui.euiScrollBarCorner} solid transparent; + .eventFieldsTable__actionCell, + .eventFieldsTable__fieldNameCell { + align-items: flex-start; + padding: ${({ theme }) => theme.eui.euiSizeXS}; } - &::-webkit-scrollbar-corner, - &::-webkit-scrollbar-track { - background-color: transparent; + .eventFieldsTable__fieldValue { + display: inline-block; + word-break: break-all; + word-wrap: break-word; + white-space: pre-wrap; + line-height: ${({ theme }) => theme.eui.euiLineHeight}; + color: ${({ theme }) => theme.eui.euiColorFullShade}; + vertical-align: top; + } + + .kbnDocViewer__warning { + margin-right: ${({ theme }) => theme.eui.euiSizeS}; } `; @@ -81,10 +112,6 @@ const StyledEuiInMemoryTable = styled(EuiInMemoryTable as any)` * This callback, invoked via `EuiInMemoryTable`'s `rowProps, assigns * attributes to every ``. */ -const getAriaRowindex = (timelineEventsDetailsItem: TimelineEventsDetailsItem) => - timelineEventsDetailsItem.ariaRowindex != null - ? { 'data-rowindex': timelineEventsDetailsItem.ariaRowindex } - : {}; /** Renders a table view or JSON view of the `ECS` `data` */ export const EventFieldsBrowser = React.memo( @@ -144,6 +171,15 @@ export const EventFieldsBrowser = React.memo( [columnHeaders, dispatch, timelineId] ); + const onSetRowProps = useCallback(({ ariaRowindex, field }: TimelineEventsDetailsItem) => { + const rowIndex = ariaRowindex != null ? { 'data-rowindex': ariaRowindex } : {}; + return { + ...rowIndex, + className: 'eventFieldsTable__tableRow', + 'data-test-subj': `event-fields-table-row-${field}`, + }; + }, []); + const onUpdateColumns = useCallback( (columns) => dispatch(timelineActions.updateColumns({ id: timelineId, columns })), [dispatch, timelineId] @@ -212,17 +248,17 @@ export const EventFieldsBrowser = React.memo( }, [focusSearchInput]); return ( - +
- +
); } ); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/table/action_cell.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/table/action_cell.tsx new file mode 100644 index 0000000000000..795ecb266b092 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/event_details/table/action_cell.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useState, useRef } from 'react'; +import { getDraggableId } from '@kbn/securitysolution-t-grid'; +import { HoverActions } from '../../hover_actions'; +import { useActionCellDataProvider } from './use_action_cell_data_provider'; +import { EventFieldsData } from '../types'; +import { useGetTimelineId } from '../../drag_and_drop/draggable_wrapper_hover_content'; +import { ColumnHeaderOptions } from '../../../../../common/types/timeline'; +import { BrowserField } from '../../../containers/source'; + +interface Props { + contextId: string; + data: EventFieldsData; + disabled?: boolean; + eventId: string; + fieldFromBrowserField: Readonly>>; + getLinkValue: (field: string) => string | null; + onFilterAdded?: () => void; + timelineId?: string; + toggleColumn?: (column: ColumnHeaderOptions) => void; + values: string[] | null | undefined; +} + +export const ActionCell: React.FC = React.memo( + ({ + contextId, + data, + eventId, + fieldFromBrowserField, + getLinkValue, + onFilterAdded, + timelineId, + toggleColumn, + values, + }) => { + const actionCellConfig = useActionCellDataProvider({ + contextId, + eventId, + field: data.field, + fieldFormat: data.format, + fieldFromBrowserField, + fieldType: data.type, + isObjectArray: data.isObjectArray, + linkValue: getLinkValue(data.field), + values, + }); + + const draggableRef = useRef(null); + const [showTopN, setShowTopN] = useState(false); + const [goGetTimelineId, setGoGetTimelineId] = useState(false); + const timelineIdFind = useGetTimelineId(draggableRef, goGetTimelineId); + const [hoverActionsOwnFocus] = useState(false); + + const toggleTopN = useCallback(() => { + setShowTopN((prevShowTopN) => { + const newShowTopN = !prevShowTopN; + return newShowTopN; + }); + }, []); + + const draggableIds = actionCellConfig?.idList.map((id) => getDraggableId(id)); + return ( + + ); + } +); + +ActionCell.displayName = 'ActionCell'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx new file mode 100644 index 0000000000000..e62d7f90b9f1d --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiBadge, EuiText, EuiToolTip } from '@elastic/eui'; +import { isEmpty } from 'lodash'; +import * as i18n from '../translations'; +import { FieldIcon } from '../../../../../../../../src/plugins/kibana_react/public'; +import { IndexPatternField } from '../../../../../../../../src/plugins/data/public'; +import { getExampleText } from '../helpers'; +import { BrowserField } from '../../../containers/source'; +import { EventFieldsData } from '../types'; +import { getFieldTypeName } from './get_field_type_name'; + +export interface FieldNameCellProps { + data: EventFieldsData; + field: string; + fieldFromBrowserField: Readonly>>; + fieldMapping?: IndexPatternField; + scripted?: boolean; +} +export const FieldNameCell = React.memo( + ({ data, field, fieldMapping, scripted }: FieldNameCellProps) => { + const typeName = getFieldTypeName(data.type); + // TODO: We don't have fieldMapping or isMultiField until kibana indexPatterns is implemented. Will default to field for now + const displayName = fieldMapping && fieldMapping.displayName ? fieldMapping.displayName : field; + const defaultTooltip = displayName !== field ? `${field} (${displayName})` : field; + // TODO: Remove. This is what was used to show the plaintext fieldName vs the tooltip one + // const showPlainTextName = + // (data.isObjectArray && data.type !== 'geo_point') || fieldFromBrowserField == null; + const isMultiField = !!fieldMapping?.spec?.subType?.multi; + return ( + <> + + + + + + + + {field} + + + + {isMultiField && ( + + + {i18n.MULTI_FIELD_BADGE} + + + )} + + + ); + } +); + +FieldNameCell.displayName = 'FieldNameCell'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx new file mode 100644 index 0000000000000..b6524a8c9c895 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiText } from '@elastic/eui'; +import { BrowserField } from '../../../containers/source'; +import { OverflowField } from '../../tables/helpers'; +import { FormattedFieldValue } from '../../../../timelines/components/timeline/body/renderers/formatted_field'; +import { MESSAGE_FIELD_NAME } from '../../../../timelines/components/timeline/body/renderers/constants'; +import { EventFieldsData } from '../types'; + +export interface FieldValueCellProps { + contextId: string; + data: EventFieldsData; + eventId: string; + fieldFromBrowserField: Readonly>>; + getLinkValue: (field: string) => string | null; + values: string[] | null | undefined; +} + +export const FieldValueCell = React.memo( + ({ + contextId, + data, + eventId, + fieldFromBrowserField, + getLinkValue, + values, + }: FieldValueCellProps) => { + return ( +
+ {values != null && + values.map((value, i) => { + if (fieldFromBrowserField == null) { + return ( + + {value} + + ); + } + return ( +
+ {data.field === MESSAGE_FIELD_NAME ? ( + + ) : ( + + )} +
+ ); + })} +
+ ); + } +); + +FieldValueCell.displayName = 'FieldValueCell'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/table/get_field_type_name.ts b/x-pack/plugins/security_solution/public/common/components/event_details/table/get_field_type_name.ts new file mode 100644 index 0000000000000..cd753b7e76e95 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/event_details/table/get_field_type_name.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export function getFieldTypeName(type: string) { + switch (type) { + case 'boolean': + return i18n.translate('xpack.securitySolution.fieldNameIcons.booleanAriaLabel', { + defaultMessage: 'Boolean field', + }); + case 'conflict': + return i18n.translate('xpack.securitySolution.fieldNameIcons.conflictFieldAriaLabel', { + defaultMessage: 'Conflicting field', + }); + case 'date': + return i18n.translate('xpack.securitySolution.fieldNameIcons.dateFieldAriaLabel', { + defaultMessage: 'Date field', + }); + case 'geo_point': + return i18n.translate('xpack.securitySolution.fieldNameIcons.geoPointFieldAriaLabel', { + defaultMessage: 'Geo point field', + }); + case 'geo_shape': + return i18n.translate('xpack.securitySolution.fieldNameIcons.geoShapeFieldAriaLabel', { + defaultMessage: 'Geo shape field', + }); + case 'ip': + return i18n.translate('xpack.securitySolution.fieldNameIcons.ipAddressFieldAriaLabel', { + defaultMessage: 'IP address field', + }); + case 'murmur3': + return i18n.translate('xpack.securitySolution.fieldNameIcons.murmur3FieldAriaLabel', { + defaultMessage: 'Murmur3 field', + }); + case 'number': + return i18n.translate('xpack.securitySolution.fieldNameIcons.numberFieldAriaLabel', { + defaultMessage: 'Number field', + }); + case 'source': + // Note that this type is currently not provided, type for _source is undefined + return i18n.translate('xpack.securitySolution.fieldNameIcons.sourceFieldAriaLabel', { + defaultMessage: 'Source field', + }); + case 'string': + return i18n.translate('xpack.securitySolution.fieldNameIcons.stringFieldAriaLabel', { + defaultMessage: 'String field', + }); + case 'nested': + return i18n.translate('xpack.securitySolution.fieldNameIcons.nestedFieldAriaLabel', { + defaultMessage: 'Nested field', + }); + default: + return i18n.translate('xpack.securitySolution.fieldNameIcons.unknownFieldAriaLabel', { + defaultMessage: 'Unknown field', + }); + } +} diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/table/use_action_cell_data_provider.ts b/x-pack/plugins/security_solution/public/common/components/event_details/table/use_action_cell_data_provider.ts new file mode 100644 index 0000000000000..e580ae6c1fdef --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/event_details/table/use_action_cell_data_provider.ts @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { escapeDataProviderId } from '@kbn/securitysolution-t-grid'; +import { isArray, isEmpty, isString } from 'lodash/fp'; +import { + AGENT_STATUS_FIELD_NAME, + EVENT_MODULE_FIELD_NAME, + EVENT_URL_FIELD_NAME, + GEO_FIELD_TYPE, + HOST_NAME_FIELD_NAME, + IP_FIELD_TYPE, + MESSAGE_FIELD_NAME, + REFERENCE_URL_FIELD_NAME, + RULE_REFERENCE_FIELD_NAME, + SIGNAL_RULE_NAME_FIELD_NAME, + SIGNAL_STATUS_FIELD_NAME, +} from '../../../../timelines/components/timeline/body/renderers/constants'; +import { BYTES_FORMAT } from '../../../../timelines/components/timeline/body/renderers/bytes'; +import { EVENT_DURATION_FIELD_NAME } from '../../../../timelines/components/duration'; +import { PORT_NAMES } from '../../../../network/components/port'; +import { INDICATOR_REFERENCE } from '../../../../../common/cti/constants'; +import { BrowserField } from '../../../containers/source'; + +export interface UseActionCellDataProvider { + contextId?: string; + eventId?: string; + field: string; + fieldFormat?: string; + fieldFromBrowserField: Readonly>>; + fieldType?: string; + isObjectArray?: boolean; + linkValue?: string | null; + values: string[] | null | undefined; +} + +export const useActionCellDataProvider = ({ + contextId, + eventId, + field, + fieldFormat, + fieldFromBrowserField, + fieldType, + isObjectArray, + linkValue, + values, +}: UseActionCellDataProvider): { idList: string[]; stringValues: string[] } | null => { + if (values === null || values === undefined) return null; + + const stringifiedValues: string[] = []; + const arrayValues = Array.isArray(values) ? values : [values]; + + const idList: string[] = arrayValues + .map((value, index) => { + // if (fieldFromBrowserField) return ''; + let id = null; + let valueAsString: string = isString(value) ? value : `${values}`; + const appendedUniqueId = `${contextId}-${eventId}-${field}-${index}-${value}-${eventId}-${field}-${value}`; + if (isObjectArray || fieldType === GEO_FIELD_TYPE || [MESSAGE_FIELD_NAME].includes(field)) { + stringifiedValues.push(valueAsString); + return ''; + } else if (fieldType === IP_FIELD_TYPE) { + id = `formatted-ip-data-provider-${contextId}-${field}-${value}-${eventId}`; + if (isString(value) && !isEmpty(value)) { + try { + const addresses = JSON.parse(value); + if (isArray(addresses)) { + valueAsString = addresses.join(','); + } + } catch (_) { + // Default to keeping the existing string value + } + } + } else if (PORT_NAMES.some((portName) => field === portName)) { + id = `port-default-draggable-${appendedUniqueId}`; + } else if (field === EVENT_DURATION_FIELD_NAME) { + id = `duration-default-draggable-${appendedUniqueId}`; + } else if (field === HOST_NAME_FIELD_NAME) { + id = `event-details-value-default-draggable-${appendedUniqueId}`; + } else if (fieldFormat === BYTES_FORMAT) { + id = `bytes-default-draggable-${appendedUniqueId}`; + } else if (field === SIGNAL_RULE_NAME_FIELD_NAME) { + id = `event-details-value-default-draggable-${appendedUniqueId}-${linkValue}`; + } else if (field === EVENT_MODULE_FIELD_NAME) { + id = `event-details-value-default-draggable-${appendedUniqueId}-${value}`; + } else if (field === SIGNAL_STATUS_FIELD_NAME) { + id = `alert-details-value-default-draggable-${appendedUniqueId}`; + } else if (field === AGENT_STATUS_FIELD_NAME) { + const valueToUse = typeof value === 'string' ? value : ''; + id = `event-details-value-default-draggable-${appendedUniqueId}`; + valueAsString = valueToUse; + } else if ( + [ + RULE_REFERENCE_FIELD_NAME, + REFERENCE_URL_FIELD_NAME, + EVENT_URL_FIELD_NAME, + INDICATOR_REFERENCE, + ].includes(field) + ) { + id = `event-details-value-default-draggable-${appendedUniqueId}-${value}`; + } else { + id = `event-details-value-default-draggable-${appendedUniqueId}`; + } + stringifiedValues.push(valueAsString); + return escapeDataProviderId(id); + }) + .filter((id) => id !== ''); + + return { + idList, + stringValues: stringifiedValues, + }; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts b/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts index 56d5009c34d72..306580ef8e3e5 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/event_details/translations.ts @@ -83,3 +83,21 @@ export const NESTED_COLUMN = (field: string) => export const AGENT_STATUS = i18n.translate('xpack.securitySolution.detections.alerts.agentStatus', { defaultMessage: 'Agent status', }); + +export const MULTI_FIELD_TOOLTIP = i18n.translate( + 'xpack.securitySolution.eventDetails.multiFieldTooltipContent', + { + defaultMessage: 'Multi-fields can have multiple values per field', + } +); + +export const MULTI_FIELD_BADGE = i18n.translate( + 'xpack.securitySolution.eventDetails.multiFieldBadge', + { + defaultMessage: 'multi-field', + } +); + +export const ACTIONS = i18n.translate('xpack.securitySolution.eventDetails.table.actions', { + defaultMessage: 'Actions', +}); diff --git a/x-pack/plugins/security_solution/public/common/components/hover_actions/actions/show_top_n.tsx b/x-pack/plugins/security_solution/public/common/components/hover_actions/actions/show_top_n.tsx index fba9dd7346004..6e284289243f0 100644 --- a/x-pack/plugins/security_solution/public/common/components/hover_actions/actions/show_top_n.tsx +++ b/x-pack/plugins/security_solution/public/common/components/hover_actions/actions/show_top_n.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; +import { EuiButtonIcon, EuiPopover, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { StatefulTopN } from '../../top_n'; import { TimelineId } from '../../../../../common/types/timeline'; @@ -44,17 +44,28 @@ export const ShowTopNButton: React.FC = React.memo( ? SourcererScopeName.detections : SourcererScopeName.default; const { browserFields, indexPattern } = useSourcererScope(activeScope); - - return showTopN ? ( - + ); + return showTopN ? ( + + + ) : ( ` - padding: ${(props) => (props.$showTopN ? 'none' : props.theme.eui.paddingSizes.s)}; + padding: ${(props) => (props.$showTopN ? 'none' : `0 ${props.theme.eui.paddingSizes.s}`)}; + display: flex; &:focus-within { .timelines__hoverActionButton, @@ -57,10 +58,6 @@ const StyledHoverActionsContainer = styled.div<{ $showTopN: boolean }>` .timelines__hoverActionButton, .securitySolution__hoverActionButton { - // TODO: Using this logic from discover - /* @include euiBreakpoint('m', 'l', 'xl') { - opacity: 0; - } */ opacity: 0; &:focus { @@ -72,17 +69,17 @@ const StyledHoverActionsContainer = styled.div<{ $showTopN: boolean }>` interface Props { additionalContent?: React.ReactNode; dataType?: string; - draggableId?: DraggableId; + draggableIds?: DraggableId[]; field: string; - isObjectArray: boolean; goGetTimelineId?: (args: boolean) => void; + isObjectArray: boolean; onFilterAdded?: () => void; ownFocus: boolean; showTopN: boolean; timelineId?: string | null; toggleColumn?: (column: ColumnHeaderOptions) => void; toggleTopN: () => void; - value?: string[] | string | null; + values?: string[] | string | null; } /** Returns a value for the `disabled` prop of `EuiFocusTrap` */ @@ -104,7 +101,7 @@ export const HoverActions: React.FC = React.memo( ({ additionalContent = null, dataType, - draggableId, + draggableIds, field, goGetTimelineId, isObjectArray, @@ -114,7 +111,7 @@ export const HoverActions: React.FC = React.memo( timelineId, toggleColumn, toggleTopN, - value, + values, }) => { const kibana = useKibana(); const { timelines } = kibana.services; @@ -172,17 +169,21 @@ export const HoverActions: React.FC = React.memo( : SourcererScopeName.default; const { browserFields } = useSourcererScope(activeScope); - const handleStartDragToTimeline = useGetHandleStartDragToTimeline({ draggableId, field }); + const handleStartDragToTimeline = (() => { + const handleStartDragToTimelineFns = draggableIds?.map((draggableId) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + return useGetHandleStartDragToTimeline({ draggableId, field }); + }); + return () => handleStartDragToTimelineFns?.forEach((dragFn) => dragFn()); + })(); - const handleFilterForValue = useCallback( - () => filterForValueFn({ field, value, filterManager, onFilterAdded }), - [filterForValueFn, field, value, filterManager, onFilterAdded] - ); + const handleFilterForValue = useCallback(() => { + filterForValueFn({ field, value: values, filterManager, onFilterAdded }); + }, [filterForValueFn, field, values, filterManager, onFilterAdded]); - const handleFilterOutValue = useCallback( - () => filterOutValueFn({ field, value, filterManager, onFilterAdded }), - [filterOutValueFn, field, value, filterManager, onFilterAdded] - ); + const handleFilterOutValue = useCallback(() => { + filterOutValueFn({ field, value: values, filterManager, onFilterAdded }); + }, [filterOutValueFn, field, values, filterManager, onFilterAdded]); const handleToggleColumn = useCallback( () => (toggleColumn ? columnToggleFn({ toggleColumn, field }) : null), @@ -252,7 +253,6 @@ export const HoverActions: React.FC = React.memo( break; } }, - [ addToTimelineKeyboardShortcut, columnToggleKeyboardShortcut, @@ -268,7 +268,7 @@ export const HoverActions: React.FC = React.memo( ] ); - const showFilters = !showTopN && value != null; + const showFilters = !showTopN && values != null; return ( @@ -287,40 +287,44 @@ export const HoverActions: React.FC = React.memo( {showFilters && ( <> )} {toggleColumn && ( )} - {showFilters && draggableId != null && ( + {showFilters && draggableIds != null && ( )} {allowTopN({ @@ -328,17 +332,25 @@ export const HoverActions: React.FC = React.memo( fieldName: field, }) && ( )} {!showTopN && ( - + )} diff --git a/x-pack/plugins/security_solution/public/common/mock/endpoint/http_handler_mock_factory.ts b/x-pack/plugins/security_solution/public/common/mock/endpoint/http_handler_mock_factory.ts index c064b1808734d..12a7de96ba0d1 100644 --- a/x-pack/plugins/security_solution/public/common/mock/endpoint/http_handler_mock_factory.ts +++ b/x-pack/plugins/security_solution/public/common/mock/endpoint/http_handler_mock_factory.ts @@ -25,7 +25,7 @@ export type ResponseProviderCallback any = (...args: * * @example * type FleetSetupResponseProvidersMock = ResponseProvidersInterface<{ - * fleetSetup: () => PostIngestSetupResponse; + * fleetSetup: () => PostFleetSetupResponse; * }>; */ export type ResponseProvidersInterface< diff --git a/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts b/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts index 224d38c8462f2..d19d6ee734654 100644 --- a/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts +++ b/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { IIndexPattern } from '../../../../../../src/plugins/data/common/index_patterns'; +import { IIndexPattern } from '../../../../../../src/plugins/data/common'; export const mockIndexPattern: IIndexPattern = { fields: [ diff --git a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts index 806031b07e0c9..f271f49e56a29 100644 --- a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts +++ b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FilterStateStore } from '../../../../../../src/plugins/data/common/es_query/filters/meta_filter'; +import { FilterStateStore } from '../../../../../../src/plugins/data/common'; import { TimelineId, diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts index fa1eec9ee0e82..26497c7f6ee3b 100644 --- a/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns'; +import { IIndexPattern } from '../../../../../../../src/plugins/data/common'; import { DocValueFields } from '../../../../common/search_strategy/common'; import { BrowserFields, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx index 601e0509009ce..245aa67d677be 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx @@ -12,7 +12,7 @@ import { getOr, isEmpty } from 'lodash/fp'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; -import type { Filter } from '../../../../../../../src/plugins/data/common/es_query/filters'; +import { FilterStateStore, Filter } from '@kbn/es-query'; import { KueryFilterQueryKind, TimelineId, @@ -49,7 +49,6 @@ import { DataProvider, QueryOperator, } from '../../../timelines/components/timeline/data_providers/data_provider'; -import { esFilters } from '../../../../../../../src/plugins/data/public'; import { getTimelineTemplate } from '../../../timelines/containers/api'; export const getUpdateAlertsQuery = (eventIds: Readonly) => { @@ -283,7 +282,7 @@ export const buildAlertsKqlFilter = ( params: alertIds, }, $state: { - store: esFilters.FilterStateStore.APP_STATE, + store: FilterStateStore.APP_STATE, }, }, ]; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/autocomplete_field/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/autocomplete_field/index.tsx index 503a568f13744..51404f65dc7d4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/autocomplete_field/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/autocomplete_field/index.tsx @@ -10,7 +10,7 @@ import { EuiFormRow } from '@elastic/eui'; import { FieldHook } from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; import { FieldComponent } from '../../../../common/components/autocomplete/field'; import { IFieldType } from '../../../../../../../../src/plugins/data/common/index_patterns/fields'; -import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/index_patterns'; +import { IIndexPattern } from '../../../../../../../../src/plugins/data/common'; interface AutocompleteFieldProps { dataTestSubj: string; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts index ec9ee47bcb087..8fd2b5f437bcd 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts @@ -29,7 +29,7 @@ import { } from '../../../../../common/detection_engine/schemas/request/rule_schemas.mock'; import { getPatchRulesSchemaMock } from '../../../../../common/detection_engine/schemas/request/patch_rules_schema.mock'; import { rulesMock } from './mock'; -import { buildEsQuery } from 'src/plugins/data/common'; +import { buildEsQuery } from '@kbn/es-query'; const abortCtrl = new AbortController(); const mockKibanaServices = KibanaServices.get as jest.Mock; jest.mock('../../../../common/lib/kibana'); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts index 8379bbcb590e1..28ed14774acf6 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts @@ -17,7 +17,7 @@ export const BACK_TO_DETECTIONS = i18n.translate( export const IMPORT_RULE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.importRuleTitle', { - defaultMessage: 'Import rule', + defaultMessage: 'Import rules', } ); @@ -532,7 +532,7 @@ export const IMPORT_RULE_BTN_TITLE = i18n.translate( export const SELECT_RULE = i18n.translate( 'xpack.securitySolution.detectionEngine.components.importRuleModal.selectRuleDescription', { - defaultMessage: 'Select a Security rule (as exported from the Detection Engine view) to import', + defaultMessage: 'Select Security rules (as exported from the Detection Rules page) to import', } ); @@ -546,7 +546,7 @@ export const INITIAL_PROMPT_TEXT = i18n.translate( export const OVERWRITE_WITH_SAME_NAME = i18n.translate( 'xpack.securitySolution.detectionEngine.components.importRuleModal.overwriteDescription', { - defaultMessage: 'Automatically overwrite saved objects with the same rule ID', + defaultMessage: 'Overwrite existing detection rules with conflicting Rule ID', } ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_provider.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_provider.ts index d706aff6f6aa7..b96fe3c0db0f9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_provider.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_provider.ts @@ -60,6 +60,10 @@ export interface DataProvider { * Returns a DataProviderType */ type?: DataProviderType.default | DataProviderType.template; + /** + * Array of multiple values for a field + */ + valueArray?: string[] | null; } export type DataProvidersAnd = Pick>; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx index 7c38474d39dba..2c85f1547dbeb 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx @@ -8,6 +8,7 @@ import { isEmpty, get } from 'lodash/fp'; import memoizeOne from 'memoize-one'; +import { EsQueryConfig, Filter, Query } from '@kbn/es-query'; import { handleSkipFocus, elementOrChildrenHasFocus, @@ -24,12 +25,7 @@ import { EXISTS_OPERATOR, } from './data_providers/data_provider'; import { BrowserFields } from '../../../common/containers/source'; -import { - IIndexPattern, - Query, - EsQueryConfig, - Filter, -} from '../../../../../../../src/plugins/data/public'; +import { IIndexPattern } from '../../../../../../../src/plugins/data/public'; import { EVENTS_TABLE_CLASS_NAME } from './styles'; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts index 66ac744b3a50c..9071351ab541a 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts @@ -23,9 +23,8 @@ import { import { BulkInstallPackageInfo, BulkInstallPackagesResponse, - CreateFleetSetupResponse, IBulkInstallPackageHTTPError, - PostIngestSetupResponse, + PostFleetSetupResponse, } from '../../../fleet/common/types/rest_spec'; import { KbnClientWithApiKeySupport } from './kbn_client_with_api_key_support'; @@ -63,7 +62,7 @@ async function doIngestSetup(kbnClient: KbnClient) { const setupResponse = (await kbnClient.request({ path: SETUP_API_ROUTE, method: 'POST', - })) as AxiosResponse; + })) as AxiosResponse; if (!setupResponse.data.isInitialized) { console.error(setupResponse.data); @@ -79,7 +78,7 @@ async function doIngestSetup(kbnClient: KbnClient) { const setupResponse = (await kbnClient.request({ path: AGENTS_SETUP_API_ROUTES.CREATE_PATTERN, method: 'POST', - })) as AxiosResponse; + })) as AxiosResponse; if (!setupResponse.data.isInitialized) { console.error(setupResponse.data); diff --git a/x-pack/plugins/security_solution/server/fleet_integration/handlers/install_prepackaged_rules.ts b/x-pack/plugins/security_solution/server/fleet_integration/handlers/install_prepackaged_rules.ts index 02815d7e214f7..d8adf4ea6a1ca 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/handlers/install_prepackaged_rules.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/handlers/install_prepackaged_rules.ts @@ -71,7 +71,7 @@ export const installPrepackagedRules = async ({ // @ts-expect-error context, appClient, - alerts.getAlertsClientWithRequest(request), + alerts.getRulesClientWithRequest(request), frameworkRequest, maxTimelineImportExportSize, prebuiltRulesFromFileSystem, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.test.ts index f9244add61651..33721c055cb89 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.test.ts @@ -5,21 +5,21 @@ * 2.0. */ -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { createNotifications } from './create_notifications'; describe('createNotifications', () => { - let alertsClient: ReturnType; + let rulesClient: ReturnType; beforeEach(() => { - alertsClient = alertsClientMock.create(); + rulesClient = rulesClientMock.create(); }); - it('calls the alertsClient with proper params', async () => { + it('calls the rulesClient with proper params', async () => { const ruleAlertId = 'rule-04128c15-0d1b-4716-a4c5-46997ac7f3bd'; await createNotifications({ - alertsClient, + rulesClient, actions: [], ruleAlertId, enabled: true, @@ -27,7 +27,7 @@ describe('createNotifications', () => { name: '', }); - expect(alertsClient.create).toHaveBeenCalledWith( + expect(rulesClient.create).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ params: expect.objectContaining({ @@ -38,7 +38,7 @@ describe('createNotifications', () => { ); }); - it('calls the alertsClient with transformed actions', async () => { + it('calls the rulesClient with transformed actions', async () => { const action = { group: 'default', id: '99403909-ca9b-49ba-9d7a-7e5320e68d05', @@ -46,7 +46,7 @@ describe('createNotifications', () => { action_type_id: '.slack', }; await createNotifications({ - alertsClient, + rulesClient, actions: [action], ruleAlertId: 'new-rule-id', enabled: true, @@ -54,7 +54,7 @@ describe('createNotifications', () => { name: '', }); - expect(alertsClient.create).toHaveBeenCalledWith( + expect(rulesClient.create).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ actions: expect.arrayContaining([ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts index c445c33566289..907976062b519 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts @@ -12,14 +12,14 @@ import { addTags } from './add_tags'; import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; export const createNotifications = async ({ - alertsClient, + rulesClient, actions, enabled, ruleAlertId, interval, name, }: CreateNotificationParams): Promise> => - alertsClient.create({ + rulesClient.create({ data: { name, tags: addTags([], ruleAlertId), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/delete_notifications.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/delete_notifications.test.ts index d277f04fb4826..9cd01df6bcdf3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/delete_notifications.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/delete_notifications.test.ts @@ -5,25 +5,25 @@ * 2.0. */ -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { deleteNotifications } from './delete_notifications'; import { readNotifications } from './read_notifications'; jest.mock('./read_notifications'); describe('deleteNotifications', () => { - let alertsClient: ReturnType; + let rulesClient: ReturnType; const notificationId = 'notification-52128c15-0d1b-4716-a4c5-46997ac7f3bd'; const ruleAlertId = 'rule-04128c15-0d1b-4716-a4c5-46997ac7f3bd'; beforeEach(() => { - alertsClient = alertsClientMock.create(); + rulesClient = rulesClientMock.create(); }); it('should return null if notification was not found', async () => { (readNotifications as jest.Mock).mockResolvedValue(null); const result = await deleteNotifications({ - alertsClient, + rulesClient, id: notificationId, ruleAlertId, }); @@ -31,18 +31,18 @@ describe('deleteNotifications', () => { expect(result).toBe(null); }); - it('should call alertsClient.delete if notification was found', async () => { + it('should call rulesClient.delete if notification was found', async () => { (readNotifications as jest.Mock).mockResolvedValue({ id: notificationId, }); const result = await deleteNotifications({ - alertsClient, + rulesClient, id: notificationId, ruleAlertId, }); - expect(alertsClient.delete).toHaveBeenCalledWith( + expect(rulesClient.delete).toHaveBeenCalledWith( expect.objectContaining({ id: notificationId, }) @@ -50,18 +50,18 @@ describe('deleteNotifications', () => { expect(result).toEqual({ id: notificationId }); }); - it('should call alertsClient.delete if notification.id was null', async () => { + it('should call rulesClient.delete if notification.id was null', async () => { (readNotifications as jest.Mock).mockResolvedValue({ id: null, }); const result = await deleteNotifications({ - alertsClient, + rulesClient, id: notificationId, ruleAlertId, }); - expect(alertsClient.delete).toHaveBeenCalledWith( + expect(rulesClient.delete).toHaveBeenCalledWith( expect.objectContaining({ id: notificationId, }) @@ -69,24 +69,24 @@ describe('deleteNotifications', () => { expect(result).toEqual({ id: null }); }); - it('should return null if alertsClient.delete rejects with 404 if notification.id was null', async () => { + it('should return null if rulesClient.delete rejects with 404 if notification.id was null', async () => { (readNotifications as jest.Mock).mockResolvedValue({ id: null, }); - alertsClient.delete.mockRejectedValue({ + rulesClient.delete.mockRejectedValue({ output: { statusCode: 404, }, }); const result = await deleteNotifications({ - alertsClient, + rulesClient, id: notificationId, ruleAlertId, }); - expect(alertsClient.delete).toHaveBeenCalledWith( + expect(rulesClient.delete).toHaveBeenCalledWith( expect.objectContaining({ id: notificationId, }) @@ -94,7 +94,7 @@ describe('deleteNotifications', () => { expect(result).toEqual(null); }); - it('should return error object if alertsClient.delete rejects with status different than 404 and if notification.id was null', async () => { + it('should return error object if rulesClient.delete rejects with status different than 404 and if notification.id was null', async () => { (readNotifications as jest.Mock).mockResolvedValue({ id: null, }); @@ -105,12 +105,12 @@ describe('deleteNotifications', () => { }, }; - alertsClient.delete.mockRejectedValue(errorObject); + rulesClient.delete.mockRejectedValue(errorObject); let errorResult; try { await deleteNotifications({ - alertsClient, + rulesClient, id: notificationId, ruleAlertId, }); @@ -118,7 +118,7 @@ describe('deleteNotifications', () => { errorResult = error; } - expect(alertsClient.delete).toHaveBeenCalledWith( + expect(rulesClient.delete).toHaveBeenCalledWith( expect.objectContaining({ id: notificationId, }) @@ -132,7 +132,7 @@ describe('deleteNotifications', () => { }); const result = await deleteNotifications({ - alertsClient, + rulesClient, id: undefined, ruleAlertId, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/delete_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/delete_notifications.ts index 09dc0044db02f..cf6812b7cacdc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/delete_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/delete_notifications.ts @@ -9,21 +9,21 @@ import { readNotifications } from './read_notifications'; import { DeleteNotificationParams } from './types'; export const deleteNotifications = async ({ - alertsClient, + rulesClient, id, ruleAlertId, }: DeleteNotificationParams) => { - const notification = await readNotifications({ alertsClient, id, ruleAlertId }); + const notification = await readNotifications({ rulesClient, id, ruleAlertId }); if (notification == null) { return null; } if (notification.id != null) { - await alertsClient.delete({ id: notification.id }); + await rulesClient.delete({ id: notification.id }); return notification; } else if (id != null) { try { - await alertsClient.delete({ id }); + await rulesClient.delete({ id }); return notification; } catch (err) { if (err.output.statusCode === 404) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts index 6f8b1de1d9325..1f3d4247a0ad9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts @@ -18,7 +18,7 @@ export const getFilter = (filter: string | null | undefined) => { }; export const findNotifications = async ({ - alertsClient, + rulesClient, perPage, page, fields, @@ -26,7 +26,7 @@ export const findNotifications = async ({ sortField, sortOrder, }: FindNotificationParams): Promise> => - alertsClient.find({ + rulesClient.find({ options: { fields, page, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.test.ts index 0ee09dd4074c8..0e87dc76bd1cf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.test.ts @@ -6,7 +6,7 @@ */ import { readNotifications } from './read_notifications'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { getNotificationResult, getFindNotificationsResultWithSingleHit, @@ -23,18 +23,18 @@ class TestError extends Error { } describe('read_notifications', () => { - let alertsClient: ReturnType; + let rulesClient: ReturnType; beforeEach(() => { - alertsClient = alertsClientMock.create(); + rulesClient = rulesClientMock.create(); }); describe('readNotifications', () => { - test('should return the output from alertsClient if id is set but ruleAlertId is undefined', async () => { - alertsClient.get.mockResolvedValue(getNotificationResult()); + test('should return the output from rulesClient if id is set but ruleAlertId is undefined', async () => { + rulesClient.get.mockResolvedValue(getNotificationResult()); const rule = await readNotifications({ - alertsClient, + rulesClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleAlertId: undefined, }); @@ -44,10 +44,10 @@ describe('read_notifications', () => { const result = getNotificationResult(); // @ts-expect-error delete result.alertTypeId; - alertsClient.get.mockResolvedValue(result); + rulesClient.get.mockResolvedValue(result); const rule = await readNotifications({ - alertsClient, + rulesClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleAlertId: undefined, }); @@ -55,12 +55,12 @@ describe('read_notifications', () => { }); test('should return error if alerts client throws 404 error on get', async () => { - alertsClient.get.mockImplementation(() => { + rulesClient.get.mockImplementation(() => { throw new TestError(); }); const rule = await readNotifications({ - alertsClient, + rulesClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleAlertId: undefined, }); @@ -68,12 +68,12 @@ describe('read_notifications', () => { }); test('should return error if alerts client throws error on get', async () => { - alertsClient.get.mockImplementation(() => { + rulesClient.get.mockImplementation(() => { throw new Error('Test error'); }); try { await readNotifications({ - alertsClient, + rulesClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleAlertId: undefined, }); @@ -82,47 +82,47 @@ describe('read_notifications', () => { } }); - test('should return the output from alertsClient if id is set but ruleAlertId is null', async () => { - alertsClient.get.mockResolvedValue(getNotificationResult()); + test('should return the output from rulesClient if id is set but ruleAlertId is null', async () => { + rulesClient.get.mockResolvedValue(getNotificationResult()); const rule = await readNotifications({ - alertsClient, + rulesClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleAlertId: null, }); expect(rule).toEqual(getNotificationResult()); }); - test('should return the output from alertsClient if id is undefined but ruleAlertId is set', async () => { - alertsClient.get.mockResolvedValue(getNotificationResult()); - alertsClient.find.mockResolvedValue(getFindNotificationsResultWithSingleHit()); + test('should return the output from rulesClient if id is undefined but ruleAlertId is set', async () => { + rulesClient.get.mockResolvedValue(getNotificationResult()); + rulesClient.find.mockResolvedValue(getFindNotificationsResultWithSingleHit()); const rule = await readNotifications({ - alertsClient, + rulesClient, id: undefined, ruleAlertId: 'rule-1', }); expect(rule).toEqual(getNotificationResult()); }); - test('should return null if the output from alertsClient with ruleAlertId set is empty', async () => { - alertsClient.get.mockResolvedValue(getNotificationResult()); - alertsClient.find.mockResolvedValue({ data: [], page: 0, perPage: 1, total: 0 }); + test('should return null if the output from rulesClient with ruleAlertId set is empty', async () => { + rulesClient.get.mockResolvedValue(getNotificationResult()); + rulesClient.find.mockResolvedValue({ data: [], page: 0, perPage: 1, total: 0 }); const rule = await readNotifications({ - alertsClient, + rulesClient, id: undefined, ruleAlertId: 'rule-1', }); expect(rule).toEqual(null); }); - test('should return the output from alertsClient if id is null but ruleAlertId is set', async () => { - alertsClient.get.mockResolvedValue(getNotificationResult()); - alertsClient.find.mockResolvedValue(getFindNotificationsResultWithSingleHit()); + test('should return the output from rulesClient if id is null but ruleAlertId is set', async () => { + rulesClient.get.mockResolvedValue(getNotificationResult()); + rulesClient.find.mockResolvedValue(getFindNotificationsResultWithSingleHit()); const rule = await readNotifications({ - alertsClient, + rulesClient, id: null, ruleAlertId: 'rule-1', }); @@ -130,11 +130,11 @@ describe('read_notifications', () => { }); test('should return null if id and ruleAlertId are null', async () => { - alertsClient.get.mockResolvedValue(getNotificationResult()); - alertsClient.find.mockResolvedValue(getFindNotificationsResultWithSingleHit()); + rulesClient.get.mockResolvedValue(getNotificationResult()); + rulesClient.find.mockResolvedValue(getFindNotificationsResultWithSingleHit()); const rule = await readNotifications({ - alertsClient, + rulesClient, id: null, ruleAlertId: null, }); @@ -142,11 +142,11 @@ describe('read_notifications', () => { }); test('should return null if id and ruleAlertId are undefined', async () => { - alertsClient.get.mockResolvedValue(getNotificationResult()); - alertsClient.find.mockResolvedValue(getFindNotificationsResultWithSingleHit()); + rulesClient.get.mockResolvedValue(getNotificationResult()); + rulesClient.find.mockResolvedValue(getFindNotificationsResultWithSingleHit()); const rule = await readNotifications({ - alertsClient, + rulesClient, id: undefined, ruleAlertId: undefined, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts index 2f14f36fc5e0a..a31281821d2d7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts @@ -11,13 +11,13 @@ import { findNotifications } from './find_notifications'; import { INTERNAL_RULE_ALERT_ID_KEY } from '../../../../common/constants'; export const readNotifications = async ({ - alertsClient, + rulesClient, id, ruleAlertId, }: ReadNotificationParams): Promise | null> => { if (id != null) { try { - const notification = await alertsClient.get({ id }); + const notification = await rulesClient.get({ id }); if (isAlertType(notification)) { return notification; } else { @@ -33,7 +33,7 @@ export const readNotifications = async ({ } } else if (ruleAlertId != null) { const notificationFromFind = await findNotifications({ - alertsClient, + rulesClient, filter: `alert.attributes.tags: "${INTERNAL_RULE_ALERT_ID_KEY}:${ruleAlertId}"`, page: 1, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts index 50ad98865544e..fb3eb715368e4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts @@ -6,7 +6,7 @@ */ import { - AlertsClient, + RulesClient, PartialAlert, AlertType, AlertTypeParams, @@ -25,7 +25,7 @@ export interface RuleNotificationAlertTypeParams extends AlertTypeParams { export type RuleNotificationAlertType = Alert; export interface FindNotificationParams { - alertsClient: AlertsClient; + rulesClient: RulesClient; perPage?: number; page?: number; sortField?: string; @@ -45,7 +45,7 @@ export interface FindNotificationsRequestParams { } export interface Clients { - alertsClient: AlertsClient; + rulesClient: RulesClient; } export type UpdateNotificationParams = Omit< @@ -73,7 +73,7 @@ export interface NotificationAlertParams { export type CreateNotificationParams = NotificationAlertParams & Clients; export interface ReadNotificationParams { - alertsClient: AlertsClient; + rulesClient: RulesClient; id?: string | null; ruleAlertId?: string | null; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.test.ts index 0d4af8cd029a0..a2a858b552c0d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { updateNotifications } from './update_notifications'; import { readNotifications } from './read_notifications'; import { createNotifications } from './create_notifications'; @@ -16,17 +16,17 @@ jest.mock('./create_notifications'); describe('updateNotifications', () => { const notification = getNotificationResult(); - let alertsClient: ReturnType; + let rulesClient: ReturnType; beforeEach(() => { - alertsClient = alertsClientMock.create(); + rulesClient = rulesClientMock.create(); }); it('should update the existing notification if interval provided', async () => { (readNotifications as jest.Mock).mockResolvedValue(notification); await updateNotifications({ - alertsClient, + rulesClient, actions: [], ruleAlertId: 'new-rule-id', enabled: true, @@ -34,7 +34,7 @@ describe('updateNotifications', () => { name: '', }); - expect(alertsClient.update).toHaveBeenCalledWith( + expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ id: notification.id, data: expect.objectContaining({ @@ -50,7 +50,7 @@ describe('updateNotifications', () => { (readNotifications as jest.Mock).mockResolvedValue(null); const params: UpdateNotificationParams = { - alertsClient, + rulesClient, actions: [], ruleAlertId: 'new-rule-id', enabled: true, @@ -67,7 +67,7 @@ describe('updateNotifications', () => { (readNotifications as jest.Mock).mockResolvedValue(notification); await updateNotifications({ - alertsClient, + rulesClient, actions: [], ruleAlertId: 'new-rule-id', enabled: true, @@ -75,14 +75,14 @@ describe('updateNotifications', () => { name: '', }); - expect(alertsClient.delete).toHaveBeenCalledWith( + expect(rulesClient.delete).toHaveBeenCalledWith( expect.objectContaining({ id: notification.id, }) ); }); - it('should call the alertsClient with transformed actions', async () => { + it('should call the rulesClient with transformed actions', async () => { (readNotifications as jest.Mock).mockResolvedValue(notification); const action = { group: 'default', @@ -91,7 +91,7 @@ describe('updateNotifications', () => { action_type_id: '.slack', }; await updateNotifications({ - alertsClient, + rulesClient, actions: [action], ruleAlertId: 'new-rule-id', enabled: true, @@ -99,7 +99,7 @@ describe('updateNotifications', () => { name: '', }); - expect(alertsClient.update).toHaveBeenCalledWith( + expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ actions: expect.arrayContaining([ @@ -120,7 +120,7 @@ describe('updateNotifications', () => { const ruleAlertId = 'rule-04128c15-0d1b-4716-a4c5-46997ac7f3bd'; const result = await updateNotifications({ - alertsClient, + rulesClient, actions: [], enabled: true, ruleAlertId, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts index 40c2845abb607..a568bfbc608e4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts @@ -13,17 +13,17 @@ import { createNotifications } from './create_notifications'; import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; export const updateNotifications = async ({ - alertsClient, + rulesClient, actions, enabled, ruleAlertId, name, interval, }: UpdateNotificationParams): Promise | null> => { - const notification = await readNotifications({ alertsClient, id: undefined, ruleAlertId }); + const notification = await readNotifications({ rulesClient, id: undefined, ruleAlertId }); if (interval && notification) { - return alertsClient.update({ + return rulesClient.update({ id: notification.id, data: { tags: addTags([], ruleAlertId), @@ -41,7 +41,7 @@ export const updateNotifications = async ({ }); } else if (interval && !notification) { return createNotifications({ - alertsClient, + rulesClient, enabled, name, interval, @@ -49,7 +49,7 @@ export const updateNotifications = async ({ ruleAlertId, }); } else if (!interval && notification) { - await alertsClient.delete({ id: notification.id }); + await rulesClient.delete({ id: notification.id }); return null; } else { return null; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/reference_rules/query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/reference_rules/query.ts index 4ca9448f5e3c7..d4ce280e73268 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/reference_rules/query.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/reference_rules/query.ts @@ -10,8 +10,9 @@ import { schema } from '@kbn/config-schema'; import { Logger } from '@kbn/logging'; import { ESSearchRequest } from 'src/core/types/elasticsearch'; -import { buildEsQuery, IIndexPattern } from '../../../../../../../src/plugins/data/common'; +import { buildEsQuery } from '@kbn/es-query'; +import type { IIndexPattern } from 'src/plugins/data/public'; import { RuleDataClient, createPersistenceRuleTypeFactory, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts index 2e33200ee7390..53c7f9d1fbb11 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -11,12 +11,12 @@ import { elasticsearchServiceMock, savedObjectsClientMock, } from '../../../../../../../../src/core/server/mocks'; -import { alertsClientMock } from '../../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../../alerting/server/mocks'; import { licensingMock } from '../../../../../../licensing/server/mocks'; import { siemMock } from '../../../../mocks'; const createMockClients = () => ({ - alertsClient: alertsClientMock.create(), + rulesClient: rulesClientMock.create(), licensing: { license: licensingMock.createLicenseMock() }, clusterClient: elasticsearchServiceMock.createScopedClusterClient(), savedObjectsClient: savedObjectsClientMock.create(), @@ -47,7 +47,7 @@ const createRequestContextMock = ( ): SecuritySolutionRequestHandlerContextMock => { const coreContext = coreMock.createRequestHandlerContext(); return ({ - alerting: { getAlertsClient: jest.fn(() => clients.alertsClient) }, + alerting: { getRulesClient: jest.fn(() => clients.rulesClient) }, core: { ...coreContext, elasticsearch: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts index 3f0faf4da6e8b..102d799984d15 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts @@ -89,7 +89,7 @@ describe('add_prepackaged_rules_route', () => { mockExceptionsClient = listMock.getExceptionListClient(); - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); (installPrepackagedTimelines as jest.Mock).mockReset(); (installPrepackagedTimelines as jest.Mock).mockResolvedValue({ @@ -107,15 +107,15 @@ describe('add_prepackaged_rules_route', () => { }); describe('status codes', () => { - test('returns 200 when creating with a valid actionClient and alertClient', async () => { + test('returns 200 when creating with a valid actionClient and rulesClient', async () => { const request = addPrepackagedRulesRequest(); const response = await server.inject(request, context); expect(response.status).toEqual(200); }); - test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + test('returns 404 if rulesClient is not available on the route', async () => { + context.alerting!.getRulesClient = jest.fn(); const request = addPrepackagedRulesRequest(); const response = await server.inject(request, context); @@ -156,7 +156,7 @@ describe('add_prepackaged_rules_route', () => { describe('responses', () => { test('1 rule is installed and 0 are updated when find results are empty', async () => { - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); const request = addPrepackagedRulesRequest(); const response = await server.inject(request, context); @@ -297,7 +297,7 @@ describe('add_prepackaged_rules_route', () => { await createPrepackagedRules( context, siemMockClient, - clients.alertsClient, + clients.rulesClient, {} as FrameworkRequest, 1200, config.prebuiltRulesFromFileSystem, @@ -316,7 +316,7 @@ describe('add_prepackaged_rules_route', () => { await createPrepackagedRules( myContext, siemMockClient, - clients.alertsClient, + clients.rulesClient, {} as FrameworkRequest, 1200, config.prebuiltRulesFromFileSystem, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts index b62034128de3e..48a847474eeed 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts @@ -34,7 +34,7 @@ import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackage import { ruleAssetSavedObjectsClientFactory } from '../../rules/rule_asset_saved_objects_client'; import { buildSiemResponse } from '../utils'; -import { AlertsClient } from '../../../../../../alerting/server'; +import { RulesClient } from '../../../../../../alerting/server'; import { FrameworkRequest } from '../../../framework'; import { ExceptionListClient } from '../../../../../../lists/server'; @@ -65,17 +65,17 @@ export const addPrepackedRulesRoute = ( const frameworkRequest = await buildFrameworkRequest(context, security, _); try { - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const siemClient = context.securitySolution?.getAppClient(); - if (!siemClient || !alertsClient) { + if (!siemClient || !rulesClient) { return siemResponse.error({ statusCode: 404 }); } const validated = await createPrepackagedRules( context, siemClient, - alertsClient, + rulesClient, frameworkRequest, config.maxTimelineImportExportSize, config.prebuiltRulesFromFileSystem, @@ -104,7 +104,7 @@ class PrepackagedRulesError extends Error { export const createPrepackagedRules = async ( context: SecuritySolutionRequestHandlerContext, siemClient: AppClient, - alertsClient: AlertsClient, + rulesClient: RulesClient, frameworkRequest: FrameworkRequest, maxTimelineImportExportSize: ConfigType['maxTimelineImportExportSize'], prebuiltRulesFromFileSystem: ConfigType['prebuiltRulesFromFileSystem'], @@ -116,7 +116,7 @@ export const createPrepackagedRules = async ( const exceptionsListClient = context.lists != null ? context.lists.getExceptionListClient() : exceptionsClient; const ruleAssetsClient = ruleAssetSavedObjectsClientFactory(savedObjectsClient); - if (!siemClient || !alertsClient) { + if (!siemClient || !rulesClient) { throw new PrepackagedRulesError('', 404); } @@ -130,7 +130,7 @@ export const createPrepackagedRules = async ( prebuiltRulesFromFileSystem, prebuiltRulesFromSavedObjects ); - const prepackagedRules = await getExistingPrepackagedRules({ alertsClient }); + const prepackagedRules = await getExistingPrepackagedRules({ rulesClient }); const rulesToInstall = getRulesToInstall(latestPrepackagedRules, prepackagedRules); const rulesToUpdate = getRulesToUpdate(latestPrepackagedRules, prepackagedRules); const signalsIndex = siemClient.getSignalsIndex(); @@ -144,7 +144,7 @@ export const createPrepackagedRules = async ( } } - await Promise.all(installPrepackagedRules(alertsClient, rulesToInstall, signalsIndex)); + await Promise.all(installPrepackagedRules(rulesClient, rulesToInstall, signalsIndex)); const timeline = await installPrepackagedTimelines( maxTimelineImportExportSize, frameworkRequest, @@ -154,7 +154,7 @@ export const createPrepackagedRules = async ( timeline, importTimelineResultSchema ); - await updatePrepackagedRules(alertsClient, savedObjectsClient, rulesToUpdate, signalsIndex); + await updatePrepackagedRules(rulesClient, savedObjectsClient, rulesToUpdate, signalsIndex); const prepackagedRulesOutput: PrePackagedRulesAndTimelinesSchema = { rules_installed: rulesToInstall.length, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts index bbb753f1f62de..3de2770972c82 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts @@ -34,8 +34,8 @@ describe('create_rules_bulk', () => { ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); // no existing rules - clients.alertsClient.create.mockResolvedValue(getAlertMock(getQueryRuleParams())); // successful creation + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no existing rules + clients.rulesClient.create.mockResolvedValue(getAlertMock(getQueryRuleParams())); // successful creation context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } }) @@ -50,7 +50,7 @@ describe('create_rules_bulk', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(getReadBulkRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); @@ -105,7 +105,7 @@ describe('create_rules_bulk', () => { }); test('returns a duplicate error if rule_id already exists', async () => { - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const response = await server.inject(getReadBulkRequest(), context); expect(response.status).toEqual(200); @@ -120,7 +120,7 @@ describe('create_rules_bulk', () => { }); test('catches error if creation throws', async () => { - clients.alertsClient.create.mockImplementation(async () => { + clients.rulesClient.create.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject(getReadBulkRequest(), context); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index 500c74e47ea7d..447da0f20a657 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -40,12 +40,12 @@ export const createRulesBulkRoute = ( }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const esClient = context.core.elasticsearch.client; const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.securitySolution?.getAppClient(); - if (!siemClient || !alertsClient) { + if (!siemClient || !rulesClient) { return siemResponse.error({ statusCode: 404 }); } @@ -65,7 +65,7 @@ export const createRulesBulkRoute = ( .map(async (payloadRule) => { if (payloadRule.rule_id != null) { const rule = await readRules({ - alertsClient, + rulesClient, ruleId: payloadRule.rule_id, id: undefined, }); @@ -99,13 +99,13 @@ export const createRulesBulkRoute = ( }); } - const createdRule = await alertsClient.create({ + const createdRule = await rulesClient.create({ data: internalRule, }); const ruleActions = await updateRulesNotifications({ ruleAlertId: createdRule.id, - alertsClient, + rulesClient, savedObjectsClient, enabled: createdRule.enabled, actions: payloadRule.actions, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts index 6b0b01a9a9de9..25bb7f2bf0d0d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts @@ -36,8 +36,8 @@ describe('create_rules', () => { ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); // no current rules - clients.alertsClient.create.mockResolvedValue(getAlertMock(getQueryRuleParams())); // creation succeeds + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no current rules + clients.rulesClient.create.mockResolvedValue(getAlertMock(getQueryRuleParams())); // creation succeeds clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); // needed to transform context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( @@ -59,7 +59,7 @@ describe('create_rules', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(getCreateRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); @@ -118,7 +118,7 @@ describe('create_rules', () => { }); test('returns a duplicate error if rule_id already exists', async () => { - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const response = await server.inject(getCreateRequest(), context); expect(response.status).toEqual(409); @@ -129,7 +129,7 @@ describe('create_rules', () => { }); test('catches error if creation throws', async () => { - clients.alertsClient.create.mockImplementation(async () => { + clients.rulesClient.create.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject(getCreateRequest(), context); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts index 4b78586ba739b..6f4cf633e5fdd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -45,18 +45,18 @@ export const createRulesRoute = ( return siemResponse.error({ statusCode: 400, body: validationErrors }); } try { - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const esClient = context.core.elasticsearch.client; const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.securitySolution?.getAppClient(); - if (!siemClient || !alertsClient) { + if (!siemClient || !rulesClient) { return siemResponse.error({ statusCode: 404 }); } if (request.body.rule_id != null) { const rule = await readRules({ - alertsClient, + rulesClient, ruleId: request.body.rule_id, id: undefined, }); @@ -92,13 +92,13 @@ export const createRulesRoute = ( // This will create the endpoint list if it does not exist yet await context.lists?.getExceptionListClient().createEndpointList(); - const createdRule = await alertsClient.create({ + const createdRule = await rulesClient.create({ data: internalRule, }); const ruleActions = await updateRulesNotifications({ ruleAlertId: createdRule.id, - alertsClient, + rulesClient, savedObjectsClient, enabled: createdRule.enabled, actions: request.body.actions, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts index 9084557962ba0..d37b0f5a685af 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts @@ -27,8 +27,8 @@ describe('delete_rules', () => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists - clients.alertsClient.delete.mockResolvedValue({}); // successful deletion + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists + clients.rulesClient.delete.mockResolvedValue({}); // successful deletion clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatusEmpty()); // rule status request deleteRulesBulkRoute(server.router); @@ -62,13 +62,13 @@ describe('delete_rules', () => { }); test('returns 200 because the error is in the payload when deleting a single rule that does not exist with a valid actionClient and alertClient', async () => { - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); const response = await server.inject(getDeleteBulkRequest(), context); expect(response.status).toEqual(200); }); test('returns 404 in the payload when deleting a single rule that does not exist with a valid actionClient and alertClient', async () => { - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); const response = await server.inject(getDeleteBulkRequest(), context); expect(response.status).toEqual(200); @@ -83,7 +83,7 @@ describe('delete_rules', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(getDeleteBulkRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts index 3068521682f8f..403565debea08 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts @@ -50,10 +50,10 @@ export const deleteRulesBulkRoute = (router: SecuritySolutionPluginRouter) => { const handler: Handler = async (context, request, response) => { const siemResponse = buildSiemResponse(response); - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const savedObjectsClient = context.core.savedObjects.client; - if (!alertsClient) { + if (!rulesClient) { return siemResponse.error({ statusCode: 404 }); } @@ -73,7 +73,7 @@ export const deleteRulesBulkRoute = (router: SecuritySolutionPluginRouter) => { } try { - const rule = await readRules({ alertsClient, id, ruleId }); + const rule = await readRules({ rulesClient, id, ruleId }); if (!rule) { return getIdBulkError({ id, ruleId }); } @@ -84,7 +84,7 @@ export const deleteRulesBulkRoute = (router: SecuritySolutionPluginRouter) => { searchFields: ['alertId'], }); await deleteRules({ - alertsClient, + rulesClient, savedObjectsClient, ruleStatusClient, ruleStatuses, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts index e820487dc0c5d..b64b14dc8cd0c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts @@ -26,7 +26,7 @@ describe('delete_rules', () => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); deleteRulesRoute(server.router); @@ -40,14 +40,14 @@ describe('delete_rules', () => { }); test('returns 200 when deleting a single rule with a valid actionClient and alertClient by id', async () => { - clients.alertsClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); + clients.rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); const response = await server.inject(getDeleteRequestById(), context); expect(response.status).toEqual(200); }); test('returns 404 when deleting a single rule that does not exist with a valid actionClient and alertClient', async () => { - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); const response = await server.inject(getDeleteRequest(), context); expect(response.status).toEqual(404); @@ -58,7 +58,7 @@ describe('delete_rules', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(getDeleteRequest(), context); expect(response.status).toEqual(404); @@ -66,7 +66,7 @@ describe('delete_rules', () => { }); test('catches error if deletion throws error', async () => { - clients.alertsClient.delete.mockImplementation(async () => { + clients.rulesClient.delete.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject(getDeleteRequest(), context); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts index 4a6b41230f799..02c22750439f0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts @@ -48,15 +48,15 @@ export const deleteRulesRoute = ( try { const { id, rule_id: ruleId } = request.query; - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const savedObjectsClient = context.core.savedObjects.client; - if (!alertsClient) { + if (!rulesClient) { return siemResponse.error({ statusCode: 404 }); } const ruleStatusClient = ruleStatusSavedObjectsClientFactory(savedObjectsClient); - const rule = await readRules({ alertsClient, id, ruleId }); + const rule = await readRules({ rulesClient, id, ruleId }); if (!rule) { const error = getIdError({ id, ruleId }); return siemResponse.error({ @@ -71,7 +71,7 @@ export const deleteRulesRoute = ( searchFields: ['alertId'], }); await deleteRules({ - alertsClient, + rulesClient, savedObjectsClient, ruleStatusClient, ruleStatuses, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts index cb1c1feba5295..022118859aa0b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts @@ -39,9 +39,9 @@ export const exportRulesRoute = (router: SecuritySolutionPluginRouter, config: C }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); - if (!alertsClient) { + if (!rulesClient) { return siemResponse.error({ statusCode: 404 }); } @@ -53,7 +53,7 @@ export const exportRulesRoute = (router: SecuritySolutionPluginRouter, config: C body: `Can't export more than ${exportSizeLimit} rules`, }); } else { - const nonPackagedRulesCount = await getNonPackagedRulesCount({ alertsClient }); + const nonPackagedRulesCount = await getNonPackagedRulesCount({ rulesClient }); if (nonPackagedRulesCount > exportSizeLimit) { return siemResponse.error({ statusCode: 400, @@ -64,8 +64,8 @@ export const exportRulesRoute = (router: SecuritySolutionPluginRouter, config: C const exported = request.body?.objects != null - ? await getExportByObjectIds(alertsClient, request.body.objects) - : await getExportAll(alertsClient); + ? await getExportByObjectIds(rulesClient, request.body.objects) + : await getExportAll(rulesClient); const responseBody = request.query.exclude_export_details ? exported.rulesNdjson diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts index 06f3ca83c4722..ed92c045ade29 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts @@ -25,8 +25,8 @@ describe('find_rules', () => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - clients.alertsClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); clients.savedObjectsClient.find.mockResolvedValue(getFindBulkResultStatus()); findRulesRoute(server.router); @@ -39,14 +39,14 @@ describe('find_rules', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(getFindRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); }); test('catches error if search throws error', async () => { - clients.alertsClient.find.mockImplementation(async () => { + clients.rulesClient.find.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject(getFindRequest(), context); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts index 428978fe1d820..32f18d816bacf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts @@ -46,16 +46,16 @@ export const findRulesRoute = ( try { const { query } = request; - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const savedObjectsClient = context.core.savedObjects.client; - if (!alertsClient) { + if (!rulesClient) { return siemResponse.error({ statusCode: 404 }); } const ruleStatusClient = ruleStatusSavedObjectsClientFactory(savedObjectsClient); const rules = await findRules({ - alertsClient, + rulesClient, perPage: query.per_page, page: query.page, sortField: query.sort_field, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts index 73f076649b72f..43e60b0eb5035 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts @@ -27,18 +27,18 @@ describe('find_statuses', () => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); clients.savedObjectsClient.find.mockResolvedValue(getFindBulkResultStatus()); // successful status search - clients.alertsClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); + clients.rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); findRulesStatusesRoute(server.router); }); describe('status codes with actionClient and alertClient', () => { - test('returns 200 when finding a single rule status with a valid alertsClient', async () => { + test('returns 200 when finding a single rule status with a valid rulesClient', async () => { const response = await server.inject(ruleStatusRequest(), context); expect(response.status).toEqual(200); }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(ruleStatusRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); @@ -70,7 +70,7 @@ describe('find_statuses', () => { }; // 1. getFailingRules api found a rule where the executionStatus was 'error' - clients.alertsClient.get.mockResolvedValue({ + clients.rulesClient.get.mockResolvedValue({ ...failingExecutionRule, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.ts index aed8b80e4f133..1760d6bf7c18b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.ts @@ -40,10 +40,10 @@ export const findRulesStatusesRoute = (router: SecuritySolutionPluginRouter) => async (context, request, response) => { const { body } = request; const siemResponse = buildSiemResponse(response); - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const savedObjectsClient = context.core.savedObjects.client; - if (!alertsClient) { + if (!rulesClient) { return siemResponse.error({ statusCode: 404 }); } @@ -52,7 +52,7 @@ export const findRulesStatusesRoute = (router: SecuritySolutionPluginRouter) => const ruleStatusClient = ruleStatusSavedObjectsClientFactory(savedObjectsClient); const [statusesById, failingRules] = await Promise.all([ ruleStatusClient.findBulk(ids, 6), - getFailingRules(ids, alertsClient), + getFailingRules(ids, rulesClient), ]); const statuses = ids.reduce((acc, id) => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts index f88da36db4491..61c618dc4d5e6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts @@ -73,7 +73,7 @@ describe('get_prepackaged_rule_status_route', () => { authz: {}, } as unknown) as SecurityPluginSetup; - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); getPrepackagedRulesStatusRoute(server.router, createMockConfig(), securitySetup); }); @@ -85,14 +85,14 @@ describe('get_prepackaged_rule_status_route', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(getPrepackagedRulesStatusRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); }); test('catch error when finding rules throws error', async () => { - clients.alertsClient.find.mockImplementation(async () => { + clients.rulesClient.find.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject(getPrepackagedRulesStatusRequest(), context); @@ -106,7 +106,7 @@ describe('get_prepackaged_rule_status_route', () => { describe('responses', () => { test('0 rules installed, 0 custom rules, 1 rules not installed, and 1 rule not updated', async () => { - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); const request = getPrepackagedRulesStatusRequest(); const response = await server.inject(request, context); @@ -123,7 +123,7 @@ describe('get_prepackaged_rule_status_route', () => { }); test('1 rule installed, 1 custom rules, 0 rules not installed, and 1 rule to not updated', async () => { - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const request = getPrepackagedRulesStatusRequest(); const response = await server.inject(request, context); @@ -140,7 +140,7 @@ describe('get_prepackaged_rule_status_route', () => { }); test('0 timelines installed, 3 timelines not installed, 0 timelines not updated', async () => { - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); (checkTimelinesStatus as jest.Mock).mockResolvedValue( mockCheckTimelinesStatusBeforeInstallResult ); @@ -160,7 +160,7 @@ describe('get_prepackaged_rule_status_route', () => { }); test('3 timelines installed, 0 timelines not installed, 0 timelines not updated', async () => { - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); (checkTimelinesStatus as jest.Mock).mockResolvedValue( mockCheckTimelinesStatusAfterInstallResult ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts index a20152a07ef15..38c315462bf55 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts @@ -45,10 +45,10 @@ export const getPrepackagedRulesStatusRoute = ( async (context, request, response) => { const savedObjectsClient = context.core.savedObjects.client; const siemResponse = buildSiemResponse(response); - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const ruleAssetsClient = ruleAssetSavedObjectsClientFactory(savedObjectsClient); - if (!alertsClient) { + if (!rulesClient) { return siemResponse.error({ statusCode: 404 }); } @@ -59,7 +59,7 @@ export const getPrepackagedRulesStatusRoute = ( config.prebuiltRulesFromSavedObjects ); const customRules = await findRules({ - alertsClient, + rulesClient, perPage: 1, page: 1, sortField: 'enabled', @@ -68,7 +68,7 @@ export const getPrepackagedRulesStatusRoute = ( fields: undefined, }); const frameworkRequest = await buildFrameworkRequest(context, security, request); - const prepackagedRules = await getExistingPrepackagedRules({ alertsClient }); + const prepackagedRules = await getExistingPrepackagedRules({ rulesClient }); const rulesToInstall = getRulesToInstall(latestPrepackagedRules, prepackagedRules); const rulesToUpdate = getRulesToUpdate(latestPrepackagedRules, prepackagedRules); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts index ab9e6983590c9..210a065012d03 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts @@ -44,7 +44,7 @@ describe('import_rules_route', () => { request = getImportRulesRequest(hapiStream); ml = mlServicesMock.createSetupContract(); - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); // no extant rules + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no extant rules context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } }) @@ -72,7 +72,7 @@ describe('import_rules_route', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(request, context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); @@ -166,7 +166,7 @@ describe('import_rules_route', () => { describe('single rule import', () => { test('returns 200 if rule imported successfully', async () => { - clients.alertsClient.create.mockResolvedValue(getAlertMock(getQueryRuleParams())); + clients.rulesClient.create.mockResolvedValue(getAlertMock(getQueryRuleParams())); const response = await server.inject(request, context); expect(response.status).toEqual(200); expect(response.body).toEqual({ @@ -199,7 +199,7 @@ describe('import_rules_route', () => { describe('rule with existing rule_id', () => { test('returns with reported conflict if `overwrite` is set to `false`', async () => { - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); // extant rule + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // extant rule const response = await server.inject(request, context); expect(response.status).toEqual(200); @@ -219,7 +219,7 @@ describe('import_rules_route', () => { }); test('returns with NO reported conflict if `overwrite` is set to `true`', async () => { - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); // extant rule + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // extant rule const overwriteRequest = getImportRulesRequestOverwriteTrue( buildHapiStream(ruleIdsToNdJsonString(['rule-1'])) ); @@ -339,7 +339,7 @@ describe('import_rules_route', () => { describe('rules with existing rule_id', () => { beforeEach(() => { - clients.alertsClient.find.mockResolvedValueOnce(getFindResultWithSingleHit()); // extant rule + clients.rulesClient.find.mockResolvedValueOnce(getFindResultWithSingleHit()); // extant rule }); test('returns with reported conflict if `overwrite` is set to `false`', async () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts index 8e322405280d3..b4a7fc4ac554f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -76,12 +76,12 @@ export const importRulesRoute = ( const siemResponse = buildSiemResponse(response); try { - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const esClient = context.core.elasticsearch.client; const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.securitySolution?.getAppClient(); - if (!siemClient || !alertsClient) { + if (!siemClient || !rulesClient) { return siemResponse.error({ statusCode: 404 }); } @@ -200,10 +200,10 @@ export const importRulesRoute = ( throwHttpError(await mlAuthz.validateRuleType(type)); - const rule = await readRules({ alertsClient, ruleId, id: undefined }); + const rule = await readRules({ rulesClient, ruleId, id: undefined }); if (rule == null) { await createRules({ - alertsClient, + rulesClient, anomalyThreshold, author, buildingBlockType, @@ -256,7 +256,7 @@ export const importRulesRoute = ( resolve({ rule_id: ruleId, status_code: 200 }); } else if (rule != null && request.query.overwrite) { await patchRules({ - alertsClient, + rulesClient, author, buildingBlockType, savedObjectsClient, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts index b6dd8a3fe0431..31f805c563f76 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts @@ -32,8 +32,8 @@ describe('patch_rules_bulk', () => { ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists - clients.alertsClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams())); // update succeeds + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists + clients.rulesClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams())); // update succeeds patchRulesBulkRoute(server.router, ml); }); @@ -45,7 +45,7 @@ describe('patch_rules_bulk', () => { }); test('returns an error in the response when updating a single rule that does not exist', async () => { - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); const response = await server.inject(getPatchBulkRequest(), context); expect(response.status).toEqual(200); expect(response.body).toEqual([ @@ -71,7 +71,7 @@ describe('patch_rules_bulk', () => { }); await server.inject(request, context); - expect(clients.alertsClient.update).toHaveBeenCalledWith( + expect(clients.rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ params: expect.objectContaining({ @@ -84,7 +84,7 @@ describe('patch_rules_bulk', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(getPatchBulkRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 7eb01e8b0d402..6def864885dcd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -46,10 +46,10 @@ export const patchRulesBulkRoute = ( async (context, request, response) => { const siemResponse = buildSiemResponse(response); - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const savedObjectsClient = context.core.savedObjects.client; - if (!alertsClient) { + if (!rulesClient) { return siemResponse.error({ statusCode: 404 }); } @@ -123,7 +123,7 @@ export const patchRulesBulkRoute = ( throwHttpError(await mlAuthz.validateRuleType(type)); } - const existingRule = await readRules({ alertsClient, ruleId, id }); + const existingRule = await readRules({ rulesClient, ruleId, id }); if (existingRule?.params.type) { // reject an unauthorized modification of an ML rule throwHttpError(await mlAuthz.validateRuleType(existingRule?.params.type)); @@ -131,7 +131,7 @@ export const patchRulesBulkRoute = ( const rule = await patchRules({ rule: existingRule, - alertsClient, + rulesClient, author, buildingBlockType, description, @@ -182,7 +182,7 @@ export const patchRulesBulkRoute = ( if (rule != null && rule.enabled != null && rule.name != null) { const ruleActions = await updateRulesNotifications({ ruleAlertId: rule.id, - alertsClient, + rulesClient, savedObjectsClient, enabled: rule.enabled, actions, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts index 9920ec5229a02..840763661f0bc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts @@ -34,9 +34,9 @@ describe('patch_rules', () => { ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); - clients.alertsClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); // existing rule - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); // existing rule - clients.alertsClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams())); // successful update + clients.rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); // existing rule + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // existing rule + clients.rulesClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams())); // successful update clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); // successful transform patchRulesRoute(server.router, ml); @@ -49,7 +49,7 @@ describe('patch_rules', () => { }); test('returns 404 when updating a single rule that does not exist', async () => { - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); const response = await server.inject(getPatchRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ @@ -59,14 +59,14 @@ describe('patch_rules', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(getPatchRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); }); test('returns error if requesting a non-rule', async () => { - clients.alertsClient.find.mockResolvedValue(nonRuleFindResult()); + clients.rulesClient.find.mockResolvedValue(nonRuleFindResult()); const response = await server.inject(getPatchRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ @@ -76,7 +76,7 @@ describe('patch_rules', () => { }); test('catches error if update throws error', async () => { - clients.alertsClient.update.mockImplementation(async () => { + clients.rulesClient.update.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject(getPatchRequest(), context); @@ -100,7 +100,7 @@ describe('patch_rules', () => { }); await server.inject(request, context); - expect(clients.alertsClient.update).toHaveBeenCalledWith( + expect(clients.rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ params: expect.objectContaining({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts index eaaa44fcf1916..c6123a5ac53c8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts @@ -107,10 +107,10 @@ export const patchRulesRoute = ( const actions: RuleAlertAction[] = actionsRest as RuleAlertAction[]; const filters: PartialFilter[] | undefined = filtersRest as PartialFilter[]; - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const savedObjectsClient = context.core.savedObjects.client; - if (!alertsClient) { + if (!rulesClient) { return siemResponse.error({ statusCode: 404 }); } @@ -125,7 +125,7 @@ export const patchRulesRoute = ( throwHttpError(await mlAuthz.validateRuleType(type)); } - const existingRule = await readRules({ alertsClient, ruleId, id }); + const existingRule = await readRules({ rulesClient, ruleId, id }); if (existingRule?.params.type) { // reject an unauthorized modification of an ML rule throwHttpError(await mlAuthz.validateRuleType(existingRule?.params.type)); @@ -133,7 +133,7 @@ export const patchRulesRoute = ( const ruleStatusClient = ruleStatusSavedObjectsClientFactory(savedObjectsClient); const rule = await patchRules({ - alertsClient, + rulesClient, author, buildingBlockType, description, @@ -185,7 +185,7 @@ export const patchRulesRoute = ( if (rule != null && rule.enabled != null && rule.name != null) { const ruleActions = await updateRulesNotifications({ ruleAlertId: rule.id, - alertsClient, + rulesClient, savedObjectsClient, enabled: rule.enabled, actions, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts index 60677fd8eda90..1facd291eede4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts @@ -31,7 +31,7 @@ describe('perform_bulk_action', () => { ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); performBulkActionRoute(server.router, ml); @@ -45,14 +45,14 @@ describe('perform_bulk_action', () => { }); it("returns 200 when provided filter query doesn't match any rules", async () => { - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); const response = await server.inject(getBulkActionRequest(), context); expect(response.status).toEqual(200); expect(response.body).toEqual({ success: true, rules_count: 0 }); }); it('returns 400 when provided filter query matches too many rules', async () => { - clients.alertsClient.find.mockResolvedValue( + clients.rulesClient.find.mockResolvedValue( getFindResultWithMultiHits({ data: [], total: Infinity }) ); const response = await server.inject(getBulkActionRequest(), context); @@ -64,14 +64,14 @@ describe('perform_bulk_action', () => { }); it('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(getBulkActionRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); }); it('catches error if disable throws error', async () => { - clients.alertsClient.disable.mockImplementation(async () => { + clients.rulesClient.disable.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject(getBulkActionRequest(), context); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts index 9d569acf3782a..ab6fc24c5fa76 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts @@ -45,7 +45,7 @@ export const performBulkActionRoute = ( const siemResponse = buildSiemResponse(response); try { - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const savedObjectsClient = context.core.savedObjects.client; const ruleStatusClient = ruleStatusSavedObjectsClientFactory(savedObjectsClient); @@ -56,12 +56,12 @@ export const performBulkActionRoute = ( savedObjectsClient, }); - if (!alertsClient) { + if (!rulesClient) { return siemResponse.error({ statusCode: 404 }); } const rules = await findRules({ - alertsClient, + rulesClient, perPage: BULK_ACTION_RULES_LIMIT, filter: body.query !== '' ? body.query : undefined, page: undefined, @@ -83,7 +83,7 @@ export const performBulkActionRoute = ( rules.data.map(async (rule) => { if (!rule.enabled) { throwHttpError(await mlAuthz.validateRuleType(rule.params.type)); - await enableRule({ rule, alertsClient, savedObjectsClient }); + await enableRule({ rule, rulesClient, savedObjectsClient }); } }) ); @@ -93,7 +93,7 @@ export const performBulkActionRoute = ( rules.data.map(async (rule) => { if (rule.enabled) { throwHttpError(await mlAuthz.validateRuleType(rule.params.type)); - await alertsClient.disable({ id: rule.id }); + await rulesClient.disable({ id: rule.id }); } }) ); @@ -107,7 +107,7 @@ export const performBulkActionRoute = ( searchFields: ['alertId'], }); await deleteRules({ - alertsClient, + rulesClient, savedObjectsClient, ruleStatusClient, ruleStatuses, @@ -121,7 +121,7 @@ export const performBulkActionRoute = ( rules.data.map(async (rule) => { throwHttpError(await mlAuthz.validateRuleType(rule.params.type)); - const createdRule = await alertsClient.create({ + const createdRule = await rulesClient.create({ data: duplicateRule(rule), }); @@ -132,7 +132,7 @@ export const performBulkActionRoute = ( await updateRulesNotifications({ ruleAlertId: createdRule.id, - alertsClient, + rulesClient, savedObjectsClient, enabled: createdRule.enabled, actions: ruleActions?.actions || [], @@ -144,7 +144,7 @@ export const performBulkActionRoute = ( break; case BulkAction.export: const exported = await getExportByObjectIds( - alertsClient, + rulesClient, rules.data.map(({ params }) => ({ rule_id: params.ruleId })) ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts index bc3c282c9cdde..11043273eaef0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts @@ -24,7 +24,7 @@ describe('read_signals', () => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatusEmpty()); // successful transform readRulesRoute(server.router); @@ -37,14 +37,14 @@ describe('read_signals', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(getReadRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); }); test('returns error if requesting a non-rule', async () => { - clients.alertsClient.find.mockResolvedValue(nonRuleFindResult()); + clients.rulesClient.find.mockResolvedValue(nonRuleFindResult()); const response = await server.inject(getReadRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ @@ -54,7 +54,7 @@ describe('read_signals', () => { }); test('catches error if search throws error', async () => { - clients.alertsClient.find.mockImplementation(async () => { + clients.rulesClient.find.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject(getReadRequest(), context); @@ -68,7 +68,7 @@ describe('read_signals', () => { describe('data validation', () => { test('returns 404 if given a non-existent id', async () => { - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); const request = requestMock.create({ method: 'get', path: DETECTION_ENGINE_RULES_URL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts index 917da6c9708d5..ab618dc2a30e8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts @@ -47,17 +47,17 @@ export const readRulesRoute = ( const { id, rule_id: ruleId } = request.query; - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const savedObjectsClient = context.core.savedObjects.client; try { - if (!alertsClient) { + if (!rulesClient) { return siemResponse.error({ statusCode: 404 }); } const ruleStatusClient = ruleStatusSavedObjectsClientFactory(savedObjectsClient); const rule = await readRules({ - alertsClient, + rulesClient, id, ruleId, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts index a57bed7a895f9..7851920adc1d9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts @@ -34,8 +34,8 @@ describe('update_rules_bulk', () => { ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - clients.alertsClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams())); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams())); clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatus()); updateRulesBulkRoute(server.router, ml); @@ -48,7 +48,7 @@ describe('update_rules_bulk', () => { }); test('returns 200 as a response when updating a single rule that does not exist', async () => { - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); const expected: BulkError[] = [ { error: { message: 'rule_id: "rule-1" not found', status_code: 404 }, @@ -62,7 +62,7 @@ describe('update_rules_bulk', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(getUpdateBulkRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); @@ -77,7 +77,7 @@ describe('update_rules_bulk', () => { }); test('returns an error if update throws', async () => { - clients.alertsClient.update.mockImplementation(() => { + clients.rulesClient.update.mockImplementation(() => { throw new Error('Test error'); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index 4c59ae2ba442e..f7604ebcdc22f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -39,11 +39,11 @@ export const updateRulesBulkRoute = ( async (context, request, response) => { const siemResponse = buildSiemResponse(response); - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.securitySolution?.getAppClient(); - if (!siemClient || !alertsClient) { + if (!siemClient || !rulesClient) { return siemResponse.error({ statusCode: 404 }); } @@ -71,7 +71,7 @@ export const updateRulesBulkRoute = ( throwHttpError(await mlAuthz.validateRuleType(payloadRule.type)); const rule = await updateRules({ - alertsClient, + rulesClient, savedObjectsClient, defaultOutputIndex: siemClient.getSignalsIndex(), ruleUpdate: payloadRule, @@ -79,7 +79,7 @@ export const updateRulesBulkRoute = ( if (rule != null) { const ruleActions = await updateRulesNotifications({ ruleAlertId: rule.id, - alertsClient, + rulesClient, savedObjectsClient, enabled: payloadRule.enabled ?? true, actions: payloadRule.actions, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts index cf121d1610d39..60a91521bc766 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts @@ -36,9 +36,9 @@ describe('update_rules', () => { ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); - clients.alertsClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); // existing rule - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists - clients.alertsClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams())); // successful update + clients.rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); // existing rule + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists + clients.rulesClient.update.mockResolvedValue(getAlertMock(getQueryRuleParams())); // successful update clients.savedObjectsClient.find.mockResolvedValue(getFindResultStatusEmpty()); // successful transform updateRulesRoute(server.router, ml); @@ -57,7 +57,7 @@ describe('update_rules', () => { }); test('returns 404 when updating a single rule that does not exist', async () => { - clients.alertsClient.find.mockResolvedValue(getEmptyFindResult()); + clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); const response = await server.inject(getUpdateRequest(), context); expect(response.status).toEqual(404); @@ -68,7 +68,7 @@ describe('update_rules', () => { }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getAlertsClient = jest.fn(); + context.alerting!.getRulesClient = jest.fn(); const response = await server.inject(getUpdateRequest(), context); expect(response.status).toEqual(404); @@ -84,7 +84,7 @@ describe('update_rules', () => { }); test('returns error when updating non-rule', async () => { - clients.alertsClient.find.mockResolvedValue(nonRuleFindResult()); + clients.rulesClient.find.mockResolvedValue(nonRuleFindResult()); const response = await server.inject(getUpdateRequest(), context); expect(response.status).toEqual(404); @@ -95,7 +95,7 @@ describe('update_rules', () => { }); test('catches error if search throws error', async () => { - clients.alertsClient.find.mockImplementation(async () => { + clients.rulesClient.find.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject(getUpdateRequest(), context); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts index 0ff6cb3cd2d0f..a6f07c2f84d16 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts @@ -45,12 +45,12 @@ export const updateRulesRoute = ( return siemResponse.error({ statusCode: 400, body: validationErrors }); } try { - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.securitySolution?.getAppClient(); const ruleStatusClient = ruleStatusSavedObjectsClientFactory(savedObjectsClient); - if (!siemClient || !alertsClient) { + if (!siemClient || !rulesClient) { return siemResponse.error({ statusCode: 404 }); } @@ -63,7 +63,7 @@ export const updateRulesRoute = ( throwHttpError(await mlAuthz.validateRuleType(request.body.type)); const rule = await updateRules({ - alertsClient, + rulesClient, savedObjectsClient, defaultOutputIndex: siemClient.getSignalsIndex(), ruleUpdate: request.body, @@ -72,7 +72,7 @@ export const updateRulesRoute = ( if (rule != null) { const ruleActions = await updateRulesNotifications({ ruleAlertId: rule.id, - alertsClient, + rulesClient, savedObjectsClient, enabled: request.body.enabled ?? true, actions: request.body.actions ?? [], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags/read_tags_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags/read_tags_route.ts index 817e4b95aabce..e22497071b2b7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags/read_tags_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags/read_tags_route.ts @@ -23,15 +23,15 @@ export const readTagsRoute = (router: SecuritySolutionPluginRouter) => { }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); - if (!alertsClient) { + if (!rulesClient) { return siemResponse.error({ statusCode: 404 }); } try { const tags = await readTags({ - alertsClient, + rulesClient, }); return response.ok({ body: tags }); } catch (err) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts index ce7d4b3173370..83091c7e1f82e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts @@ -9,7 +9,7 @@ import Boom from '@hapi/boom'; import { SavedObjectsFindResponse } from 'kibana/server'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { IRuleSavedAttributesSavedObjectAttributes, IRuleStatusSOAttributes } from '../rules/types'; import { BadRequestError } from '@kbn/securitysolution-es-utils'; import { @@ -30,7 +30,7 @@ import { getAlertMock } from './__mocks__/request_responses'; import { AlertExecutionStatusErrorReasons } from '../../../../../alerting/common'; import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; -let alertsClient: ReturnType; +let rulesClient: ReturnType; describe('utils', () => { describe('transformBulkError', () => { @@ -386,11 +386,11 @@ describe('utils', () => { describe('getFailingRules', () => { beforeEach(() => { - alertsClient = alertsClientMock.create(); + rulesClient = rulesClientMock.create(); }); it('getFailingRules finds no failing rules', async () => { - alertsClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); - const res = await getFailingRules(['my-fake-id'], alertsClient); + rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); + const res = await getFailingRules(['my-fake-id'], rulesClient); expect(res).toEqual({}); }); it('getFailingRules finds a failing rule', async () => { @@ -403,22 +403,22 @@ describe('utils', () => { message: 'oops', }, }; - alertsClient.get.mockResolvedValue(foundRule); - const res = await getFailingRules([foundRule.id], alertsClient); + rulesClient.get.mockResolvedValue(foundRule); + const res = await getFailingRules([foundRule.id], rulesClient); expect(res).toEqual({ [foundRule.id]: foundRule }); }); it('getFailingRules throws an error', async () => { - alertsClient.get.mockImplementation(() => { + rulesClient.get.mockImplementation(() => { throw new Error('my test error'); }); let error; try { - await getFailingRules(['my-fake-id'], alertsClient); + await getFailingRules(['my-fake-id'], rulesClient); } catch (exc) { error = exc; } expect(error.message).toEqual( - 'Failed to get executionStatus with AlertsClient: my test error' + 'Failed to get executionStatus with RulesClient: my test error' ); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.ts index 9ff75726322a1..8f078b01daf73 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.ts @@ -15,7 +15,7 @@ import { KibanaResponseFactory, CustomHttpResponseOptions, } from '../../../../../../../src/core/server'; -import { AlertsClient } from '../../../../../alerting/server'; +import { RulesClient } from '../../../../../alerting/server'; import { RuleStatusResponse, IRuleStatusSOAttributes } from '../rules/types'; import { RuleParams } from '../schemas/rule_schemas'; @@ -304,12 +304,12 @@ export type GetFailingRulesResult = Record>; export const getFailingRules = async ( ids: string[], - alertsClient: AlertsClient + rulesClient: RulesClient ): Promise => { try { const errorRules = await Promise.all( ids.map(async (id) => - alertsClient.get({ + rulesClient.get({ id, }) ) @@ -328,6 +328,6 @@ export const getFailingRules = async ( if (Boom.isBoom(exc)) { throw exc; } - throw new Error(`Failed to get executionStatus with AlertsClient: ${exc.message}`); + throw new Error(`Failed to get executionStatus with RulesClient: ${exc.message}`); } }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.mock.ts index 86feed1f39cef..f7aae1564bb17 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.mock.ts @@ -6,12 +6,12 @@ */ import { CreateRulesOptions } from './types'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; export const getCreateRulesOptionsMock = (): CreateRulesOptions => ({ author: ['Elastic'], buildingBlockType: undefined, - alertsClient: alertsClientMock.create(), + rulesClient: rulesClientMock.create(), anomalyThreshold: undefined, description: 'some description', enabled: true, @@ -63,7 +63,7 @@ export const getCreateRulesOptionsMock = (): CreateRulesOptions => ({ export const getCreateMlRulesOptionsMock = (): CreateRulesOptions => ({ author: ['Elastic'], buildingBlockType: undefined, - alertsClient: alertsClientMock.create(), + rulesClient: rulesClientMock.create(), anomalyThreshold: 55, description: 'some description', enabled: true, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.test.ts index c719412d27e4d..3dd29977a8f2c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.test.ts @@ -9,10 +9,10 @@ import { createRules } from './create_rules'; import { getCreateMlRulesOptionsMock } from './create_rules.mock'; describe('createRules', () => { - it('calls the alertsClient with legacy ML params', async () => { + it('calls the rulesClient with legacy ML params', async () => { const ruleOptions = getCreateMlRulesOptionsMock(); await createRules(ruleOptions); - expect(ruleOptions.alertsClient.create).toHaveBeenCalledWith( + expect(ruleOptions.rulesClient.create).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ params: expect.objectContaining({ @@ -24,13 +24,13 @@ describe('createRules', () => { ); }); - it('calls the alertsClient with ML params', async () => { + it('calls the rulesClient with ML params', async () => { const ruleOptions = { ...getCreateMlRulesOptionsMock(), machineLearningJobId: ['new_job_1', 'new_job_2'], }; await createRules(ruleOptions); - expect(ruleOptions.alertsClient.create).toHaveBeenCalledWith( + expect(ruleOptions.rulesClient.create).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ params: expect.objectContaining({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts index db039bbc31390..c94cb39572ddc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts @@ -17,7 +17,7 @@ import { addTags } from './add_tags'; import { PartialFilter, RuleTypeParams } from '../types'; export const createRules = async ({ - alertsClient, + rulesClient, anomalyThreshold, author, buildingBlockType, @@ -67,7 +67,7 @@ export const createRules = async ({ exceptionsList, actions, }: CreateRulesOptions): Promise> => { - return alertsClient.create({ + return rulesClient.create({ data: { name, tags: addTags(tags, ruleId, immutable), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.test.ts index f581be9e1f62b..cd7bbfd9fced7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.test.ts @@ -6,7 +6,7 @@ */ import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { ruleStatusSavedObjectsClientMock } from '../signals/__mocks__/rule_status_saved_objects_client.mock'; import { deleteRules } from './delete_rules'; import { deleteNotifications } from '../notifications/delete_notifications'; @@ -18,12 +18,12 @@ jest.mock('../notifications/delete_notifications'); jest.mock('../rule_actions/delete_rule_actions_saved_object'); describe('deleteRules', () => { - let alertsClient: ReturnType; + let rulesClient: ReturnType; let ruleStatusClient: ReturnType; let savedObjectsClient: ReturnType; beforeEach(() => { - alertsClient = alertsClientMock.create(); + rulesClient = rulesClientMock.create(); savedObjectsClient = savedObjectsClientMock.create(); ruleStatusClient = ruleStatusSavedObjectsClientMock.create(); }); @@ -50,7 +50,7 @@ describe('deleteRules', () => { }; const rule = { - alertsClient, + rulesClient, savedObjectsClient, ruleStatusClient, id: 'ruleId', @@ -64,10 +64,10 @@ describe('deleteRules', () => { await deleteRules(rule); - expect(alertsClient.delete).toHaveBeenCalledWith({ id: rule.id }); + expect(rulesClient.delete).toHaveBeenCalledWith({ id: rule.id }); expect(deleteNotifications).toHaveBeenCalledWith({ ruleAlertId: rule.id, - alertsClient: expect.any(Object), + rulesClient: expect.any(Object), }); expect(deleteRuleActionsSavedObject).toHaveBeenCalledWith({ ruleAlertId: rule.id, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.ts index ed5477599253b..e1385eb05a6b4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.ts @@ -10,14 +10,14 @@ import { deleteRuleActionsSavedObject } from '../rule_actions/delete_rule_action import { DeleteRuleOptions } from './types'; export const deleteRules = async ({ - alertsClient, + rulesClient, savedObjectsClient, ruleStatusClient, ruleStatuses, id, }: DeleteRuleOptions) => { - await alertsClient.delete({ id }); - await deleteNotifications({ alertsClient, ruleAlertId: id }); + await rulesClient.delete({ id }); + await deleteNotifications({ rulesClient, ruleAlertId: id }); await deleteRuleActionsSavedObject({ ruleAlertId: id, savedObjectsClient }); ruleStatuses.saved_objects.forEach(async (obj) => ruleStatusClient.delete(obj.id)); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts index dc4cca2059b3e..1a81cf0cb5ebe 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts @@ -7,13 +7,13 @@ import { SavedObjectsClientContract } from 'kibana/server'; import { SanitizedAlert } from '../../../../../alerting/common'; -import { AlertsClient } from '../../../../../alerting/server'; +import { RulesClient } from '../../../../../alerting/server'; import { RuleParams } from '../schemas/rule_schemas'; import { ruleStatusSavedObjectsClientFactory } from '../signals/rule_status_saved_objects_client'; interface EnableRuleArgs { rule: SanitizedAlert; - alertsClient: AlertsClient; + rulesClient: RulesClient; savedObjectsClient: SavedObjectsClientContract; } @@ -21,11 +21,11 @@ interface EnableRuleArgs { * Enables the rule and updates its status to 'going to run' * * @param rule - rule to enable - * @param alertsClient - Alerts client + * @param rulesClient - Alerts client * @param savedObjectsClient - Saved Objects client */ -export const enableRule = async ({ rule, alertsClient, savedObjectsClient }: EnableRuleArgs) => { - await alertsClient.enable({ id: rule.id }); +export const enableRule = async ({ rule, rulesClient, savedObjectsClient }: EnableRuleArgs) => { + await rulesClient.enable({ id: rule.id }); const ruleStatusClient = ruleStatusSavedObjectsClientFactory(savedObjectsClient); const ruleCurrentStatus = await ruleStatusClient.find({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts index eae5ccd2f6ffd..e41dac066e18a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts @@ -19,7 +19,7 @@ export const getFilter = (filter: string | null | undefined) => { }; export const findRules = ({ - alertsClient, + rulesClient, perPage, page, fields, @@ -27,7 +27,7 @@ export const findRules = ({ sortField, sortOrder, }: FindRuleOptions): Promise> => { - return alertsClient.find({ + return rulesClient.find({ options: { fields, page, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts index da67bea0ca970..19a6a4e43d877 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { getAlertMock, getFindResultWithSingleHit, @@ -27,14 +27,14 @@ describe('get_existing_prepackaged_rules', () => { describe('getExistingPrepackagedRules', () => { test('should return a single item in a single page', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const rules = await getExistingPrepackagedRules({ alertsClient }); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const rules = await getExistingPrepackagedRules({ rulesClient }); expect(rules).toEqual([getAlertMock(getQueryRuleParams())]); }); test('should return 3 items over 1 page with all on one page', async () => { - const alertsClient = alertsClientMock.create(); + const rulesClient = rulesClientMock.create(); const result1 = getAlertMock(getQueryRuleParams()); result1.params.immutable = true; @@ -49,7 +49,7 @@ describe('get_existing_prepackaged_rules', () => { result3.id = 'f3e1bf0b-b95f-43da-b1de-5d2f0af2287a'; // first result mock which is for returning the total - alertsClient.find.mockResolvedValueOnce( + rulesClient.find.mockResolvedValueOnce( getFindResultWithMultiHits({ data: [result1], perPage: 1, @@ -59,7 +59,7 @@ describe('get_existing_prepackaged_rules', () => { ); // second mock which will return all the data on a single page - alertsClient.find.mockResolvedValueOnce( + rulesClient.find.mockResolvedValueOnce( getFindResultWithMultiHits({ data: [result1, result2, result3], perPage: 3, @@ -68,21 +68,21 @@ describe('get_existing_prepackaged_rules', () => { }) ); - const rules = await getExistingPrepackagedRules({ alertsClient }); + const rules = await getExistingPrepackagedRules({ rulesClient }); expect(rules).toEqual([result1, result2, result3]); }); }); describe('getNonPackagedRules', () => { test('should return a single item in a single page', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const rules = await getNonPackagedRules({ alertsClient }); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const rules = await getNonPackagedRules({ rulesClient }); expect(rules).toEqual([getAlertMock(getQueryRuleParams())]); }); test('should return 2 items over 1 page', async () => { - const alertsClient = alertsClientMock.create(); + const rulesClient = rulesClientMock.create(); const result1 = getAlertMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; @@ -91,7 +91,7 @@ describe('get_existing_prepackaged_rules', () => { result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; // first result mock which is for returning the total - alertsClient.find.mockResolvedValueOnce( + rulesClient.find.mockResolvedValueOnce( getFindResultWithMultiHits({ data: [result1], perPage: 1, @@ -101,16 +101,16 @@ describe('get_existing_prepackaged_rules', () => { ); // second mock which will return all the data on a single page - alertsClient.find.mockResolvedValueOnce( + rulesClient.find.mockResolvedValueOnce( getFindResultWithMultiHits({ data: [result1, result2], perPage: 2, page: 1, total: 2 }) ); - const rules = await getNonPackagedRules({ alertsClient }); + const rules = await getNonPackagedRules({ rulesClient }); expect(rules).toEqual([result1, result2]); }); test('should return 3 items over 1 page with all on one page', async () => { - const alertsClient = alertsClientMock.create(); + const rulesClient = rulesClientMock.create(); const result1 = getAlertMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; @@ -122,7 +122,7 @@ describe('get_existing_prepackaged_rules', () => { result3.id = 'f3e1bf0b-b95f-43da-b1de-5d2f0af2287a'; // first result mock which is for returning the total - alertsClient.find.mockResolvedValueOnce( + rulesClient.find.mockResolvedValueOnce( getFindResultWithMultiHits({ data: [result1], perPage: 3, @@ -132,7 +132,7 @@ describe('get_existing_prepackaged_rules', () => { ); // second mock which will return all the data on a single page - alertsClient.find.mockResolvedValueOnce( + rulesClient.find.mockResolvedValueOnce( getFindResultWithMultiHits({ data: [result1, result2, result3], perPage: 3, @@ -141,21 +141,21 @@ describe('get_existing_prepackaged_rules', () => { }) ); - const rules = await getNonPackagedRules({ alertsClient }); + const rules = await getNonPackagedRules({ rulesClient }); expect(rules).toEqual([result1, result2, result3]); }); }); describe('getRules', () => { test('should return a single item in a single page', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const rules = await getRules({ alertsClient, filter: '' }); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const rules = await getRules({ rulesClient, filter: '' }); expect(rules).toEqual([getAlertMock(getQueryRuleParams())]); }); test('should return 2 items over two pages, one per page', async () => { - const alertsClient = alertsClientMock.create(); + const rulesClient = rulesClientMock.create(); const result1 = getAlertMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; @@ -164,7 +164,7 @@ describe('get_existing_prepackaged_rules', () => { result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; // first result mock which is for returning the total - alertsClient.find.mockResolvedValueOnce( + rulesClient.find.mockResolvedValueOnce( getFindResultWithMultiHits({ data: [result1], perPage: 1, @@ -174,29 +174,29 @@ describe('get_existing_prepackaged_rules', () => { ); // second mock which will return all the data on a single page - alertsClient.find.mockResolvedValueOnce( + rulesClient.find.mockResolvedValueOnce( getFindResultWithMultiHits({ data: [result1, result2], perPage: 2, page: 1, total: 2 }) ); - const rules = await getRules({ alertsClient, filter: '' }); + const rules = await getRules({ rulesClient, filter: '' }); expect(rules).toEqual([result1, result2]); }); }); describe('getRulesCount', () => { test('it returns a count', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const rules = await getRulesCount({ alertsClient, filter: '' }); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const rules = await getRulesCount({ rulesClient, filter: '' }); expect(rules).toEqual(1); }); }); describe('getNonPackagedRulesCount', () => { test('it returns a count', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); - const rules = await getNonPackagedRulesCount({ alertsClient }); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const rules = await getNonPackagedRulesCount({ rulesClient }); expect(rules).toEqual(1); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts index a8c1d7aeb8eb1..be8bf1303846d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts @@ -6,7 +6,7 @@ */ import { INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; -import { AlertsClient } from '../../../../../alerting/server'; +import { RulesClient } from '../../../../../alerting/server'; import { RuleAlertType, isAlertTypes } from './types'; import { findRules } from './find_rules'; @@ -14,22 +14,22 @@ export const FILTER_NON_PREPACKED_RULES = `alert.attributes.tags: "${INTERNAL_IM export const FILTER_PREPACKED_RULES = `alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true"`; export const getNonPackagedRulesCount = async ({ - alertsClient, + rulesClient, }: { - alertsClient: AlertsClient; + rulesClient: RulesClient; }): Promise => { - return getRulesCount({ alertsClient, filter: FILTER_NON_PREPACKED_RULES }); + return getRulesCount({ rulesClient, filter: FILTER_NON_PREPACKED_RULES }); }; export const getRulesCount = async ({ - alertsClient, + rulesClient, filter, }: { - alertsClient: AlertsClient; + rulesClient: RulesClient; filter: string; }): Promise => { const firstRule = await findRules({ - alertsClient, + rulesClient, filter, perPage: 1, page: 1, @@ -41,15 +41,15 @@ export const getRulesCount = async ({ }; export const getRules = async ({ - alertsClient, + rulesClient, filter, }: { - alertsClient: AlertsClient; + rulesClient: RulesClient; filter: string; }): Promise => { - const count = await getRulesCount({ alertsClient, filter }); + const count = await getRulesCount({ rulesClient, filter }); const rules = await findRules({ - alertsClient, + rulesClient, filter, perPage: count, page: 1, @@ -68,23 +68,23 @@ export const getRules = async ({ }; export const getNonPackagedRules = async ({ - alertsClient, + rulesClient, }: { - alertsClient: AlertsClient; + rulesClient: RulesClient; }): Promise => { return getRules({ - alertsClient, + rulesClient, filter: FILTER_NON_PREPACKED_RULES, }); }; export const getExistingPrepackagedRules = async ({ - alertsClient, + rulesClient, }: { - alertsClient: AlertsClient; + rulesClient: RulesClient; }): Promise => { return getRules({ - alertsClient, + rulesClient, filter: FILTER_PREPACKED_RULES, }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts index 4c937b2e4ca8a..2870bee99e51a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts @@ -10,7 +10,7 @@ import { getFindResultWithSingleHit, FindHit, } from '../routes/__mocks__/request_responses'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { getExportAll } from './get_export_all'; import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock'; import { getThreatMock } from '../../../../common/detection_engine/schemas/types/threat.mock'; @@ -18,7 +18,7 @@ import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; describe('getExportAll', () => { test('it exports everything from the alerts client', async () => { - const alertsClient = alertsClientMock.create(); + const rulesClient = rulesClientMock.create(); const result = getFindResultWithSingleHit(); const alert = getAlertMock(getQueryRuleParams()); alert.params = { @@ -30,9 +30,9 @@ describe('getExportAll', () => { timelineTitle: 'some-timeline-title', }; result.data = [alert]; - alertsClient.find.mockResolvedValue(result); + rulesClient.find.mockResolvedValue(result); - const exports = await getExportAll(alertsClient); + const exports = await getExportAll(rulesClient); const rulesJson = JSON.parse(exports.rulesNdjson); const detailsJson = JSON.parse(exports.exportDetails); expect(rulesJson).toEqual({ @@ -84,7 +84,7 @@ describe('getExportAll', () => { }); test('it will export empty rules', async () => { - const alertsClient = alertsClientMock.create(); + const rulesClient = rulesClientMock.create(); const findResult: FindHit = { page: 1, perPage: 1, @@ -92,9 +92,9 @@ describe('getExportAll', () => { data: [], }; - alertsClient.find.mockResolvedValue(findResult); + rulesClient.find.mockResolvedValue(findResult); - const exports = await getExportAll(alertsClient); + const exports = await getExportAll(rulesClient); expect(exports).toEqual({ rulesNdjson: '', exportDetails: '{"exported_count":0,"missing_rules":[],"missing_rules_count":0}\n', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts index c0e893b8aea96..9ec51cf18c7c7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts @@ -5,19 +5,19 @@ * 2.0. */ -import { AlertsClient } from '../../../../../alerting/server'; +import { RulesClient } from '../../../../../alerting/server'; import { getNonPackagedRules } from './get_existing_prepackaged_rules'; import { getExportDetailsNdjson } from './get_export_details_ndjson'; import { transformAlertsToRules } from '../routes/rules/utils'; import { transformDataToNdjson } from '../../../utils/read_stream/create_stream_from_ndjson'; export const getExportAll = async ( - alertsClient: AlertsClient + rulesClient: RulesClient ): Promise<{ rulesNdjson: string; exportDetails: string; }> => { - const ruleAlertTypes = await getNonPackagedRules({ alertsClient }); + const ruleAlertTypes = await getNonPackagedRules({ rulesClient }); const rules = transformAlertsToRules(ruleAlertTypes); const rulesNdjson = transformDataToNdjson(rules); const exportDetails = getExportDetailsNdjson(rules); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts index 7410f97241966..f4325086e4212 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts @@ -11,7 +11,7 @@ import { getFindResultWithSingleHit, FindHit, } from '../routes/__mocks__/request_responses'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock'; import { getThreatMock } from '../../../../common/detection_engine/schemas/types/threat.mock'; import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; @@ -24,11 +24,11 @@ describe('get_export_by_object_ids', () => { }); describe('getExportByObjectIds', () => { test('it exports object ids into an expected string with new line characters', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const objects = [{ rule_id: 'rule-1' }]; - const exports = await getExportByObjectIds(alertsClient, objects); + const exports = await getExportByObjectIds(rulesClient, objects); const exportsObj = { rulesNdjson: JSON.parse(exports.rulesNdjson), exportDetails: JSON.parse(exports.exportDetails), @@ -84,7 +84,7 @@ describe('get_export_by_object_ids', () => { }); test('it does not export immutable rules', async () => { - const alertsClient = alertsClientMock.create(); + const rulesClient = rulesClientMock.create(); const result = getAlertMock(getQueryRuleParams()); result.params.immutable = true; @@ -95,11 +95,11 @@ describe('get_export_by_object_ids', () => { data: [result], }; - alertsClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); - alertsClient.find.mockResolvedValue(findResult); + rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); + rulesClient.find.mockResolvedValue(findResult); const objects = [{ rule_id: 'rule-1' }]; - const exports = await getExportByObjectIds(alertsClient, objects); + const exports = await getExportByObjectIds(rulesClient, objects); expect(exports).toEqual({ rulesNdjson: '', exportDetails: @@ -110,11 +110,11 @@ describe('get_export_by_object_ids', () => { describe('getRulesFromObjects', () => { test('it returns transformed rules from objects sent in', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const objects = [{ rule_id: 'rule-1' }]; - const exports = await getRulesFromObjects(alertsClient, objects); + const exports = await getRulesFromObjects(rulesClient, objects); const expected: RulesErrors = { exportedCount: 1, missingRules: [], @@ -174,7 +174,7 @@ describe('get_export_by_object_ids', () => { }); test('it does not transform the rule if the rule is an immutable rule and designates it as a missing rule', async () => { - const alertsClient = alertsClientMock.create(); + const rulesClient = rulesClientMock.create(); const result = getAlertMock(getQueryRuleParams()); result.params.immutable = true; @@ -185,11 +185,11 @@ describe('get_export_by_object_ids', () => { data: [result], }; - alertsClient.get.mockResolvedValue(result); - alertsClient.find.mockResolvedValue(findResult); + rulesClient.get.mockResolvedValue(result); + rulesClient.find.mockResolvedValue(findResult); const objects = [{ rule_id: 'rule-1' }]; - const exports = await getRulesFromObjects(alertsClient, objects); + const exports = await getRulesFromObjects(rulesClient, objects); const expected: RulesErrors = { exportedCount: 0, missingRules: [{ rule_id: 'rule-1' }], @@ -199,7 +199,7 @@ describe('get_export_by_object_ids', () => { }); test('it exports missing rules', async () => { - const alertsClient = alertsClientMock.create(); + const rulesClient = rulesClientMock.create(); const findResult: FindHit = { page: 1, @@ -208,11 +208,11 @@ describe('get_export_by_object_ids', () => { data: [], }; - alertsClient.get.mockRejectedValue({ output: { statusCode: 404 } }); - alertsClient.find.mockResolvedValue(findResult); + rulesClient.get.mockRejectedValue({ output: { statusCode: 404 } }); + rulesClient.find.mockResolvedValue(findResult); const objects = [{ rule_id: 'rule-1' }]; - const exports = await getRulesFromObjects(alertsClient, objects); + const exports = await getRulesFromObjects(rulesClient, objects); const expected: RulesErrors = { exportedCount: 0, missingRules: [{ rule_id: 'rule-1' }], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts index 63b34435e8427..5d33e37c2ecf9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts @@ -8,7 +8,7 @@ import { chunk } from 'lodash'; import { RulesSchema } from '../../../../common/detection_engine/schemas/response/rules_schema'; -import { AlertsClient } from '../../../../../alerting/server'; +import { RulesClient } from '../../../../../alerting/server'; import { getExportDetailsNdjson } from './get_export_details_ndjson'; import { isAlertType } from '../rules/types'; import { transformAlertToRule } from '../routes/rules/utils'; @@ -33,20 +33,20 @@ export interface RulesErrors { } export const getExportByObjectIds = async ( - alertsClient: AlertsClient, + rulesClient: RulesClient, objects: Array<{ rule_id: string }> ): Promise<{ rulesNdjson: string; exportDetails: string; }> => { - const rulesAndErrors = await getRulesFromObjects(alertsClient, objects); + const rulesAndErrors = await getRulesFromObjects(rulesClient, objects); const rulesNdjson = transformDataToNdjson(rulesAndErrors.rules); const exportDetails = getExportDetailsNdjson(rulesAndErrors.rules, rulesAndErrors.missingRules); return { rulesNdjson, exportDetails }; }; export const getRulesFromObjects = async ( - alertsClient: AlertsClient, + rulesClient: RulesClient, objects: Array<{ rule_id: string }> ): Promise => { // If we put more than 1024 ids in one block like "alert.attributes.tags: (id1 OR id2 OR ... OR id1100)" @@ -65,7 +65,7 @@ export const getRulesFromObjects = async ( }) .join(' OR '); const rules = await findRules({ - alertsClient, + rulesClient, filter, page: 1, fields: undefined, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts index 7efd63cc67722..587ce3f002b80 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts @@ -7,12 +7,12 @@ import { AddPrepackagedRulesSchemaDecoded } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema'; import { SanitizedAlert, AlertTypeParams } from '../../../../../alerting/common'; -import { AlertsClient } from '../../../../../alerting/server'; +import { RulesClient } from '../../../../../alerting/server'; import { createRules } from './create_rules'; import { PartialFilter } from '../types'; export const installPrepackagedRules = ( - alertsClient: AlertsClient, + rulesClient: RulesClient, rules: AddPrepackagedRulesSchemaDecoded[], outputIndex: string ): Array>> => @@ -70,7 +70,7 @@ export const installPrepackagedRules = ( return [ ...acc, createRules({ - alertsClient, + rulesClient, anomalyThreshold, author, buildingBlockType, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts index d42b6c5aeefaa..826b197cfe094 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts @@ -6,7 +6,7 @@ */ import { PatchRulesOptions } from './types'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; import { getAlertMock } from '../routes/__mocks__/request_responses'; import { getMlRuleParams, getQueryRuleParams } from '../schemas/rule_schemas.mock'; @@ -14,7 +14,7 @@ import { getMlRuleParams, getQueryRuleParams } from '../schemas/rule_schemas.moc export const getPatchRulesOptionsMock = (): PatchRulesOptions => ({ author: ['Elastic'], buildingBlockType: undefined, - alertsClient: alertsClientMock.create(), + rulesClient: rulesClientMock.create(), savedObjectsClient: savedObjectsClientMock.create(), anomalyThreshold: undefined, description: 'some description', @@ -65,7 +65,7 @@ export const getPatchRulesOptionsMock = (): PatchRulesOptions => ({ export const getPatchMlRulesOptionsMock = (): PatchRulesOptions => ({ author: ['Elastic'], buildingBlockType: undefined, - alertsClient: alertsClientMock.create(), + rulesClient: rulesClientMock.create(), savedObjectsClient: savedObjectsClientMock.create(), anomalyThreshold: 55, description: 'some description', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.test.ts index e275a02b2b0c1..1bd2656e41bae 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.test.ts @@ -10,21 +10,21 @@ import { getPatchRulesOptionsMock, getPatchMlRulesOptionsMock } from './patch_ru import { PatchRulesOptions } from './types'; describe('patchRules', () => { - it('should call alertsClient.disable if the rule was enabled and enabled is false', async () => { + it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { const rulesOptionsMock = getPatchRulesOptionsMock(); const ruleOptions: PatchRulesOptions = { ...rulesOptionsMock, enabled: false, }; await patchRules(ruleOptions); - expect(ruleOptions.alertsClient.disable).toHaveBeenCalledWith( + expect(ruleOptions.rulesClient.disable).toHaveBeenCalledWith( expect.objectContaining({ id: ruleOptions.rule?.id, }) ); }); - it('should call alertsClient.enable if the rule was disabled and enabled is true', async () => { + it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { const rulesOptionsMock = getPatchRulesOptionsMock(); const ruleOptions: PatchRulesOptions = { ...rulesOptionsMock, @@ -34,14 +34,14 @@ describe('patchRules', () => { ruleOptions.rule.enabled = false; } await patchRules(ruleOptions); - expect(ruleOptions.alertsClient.enable).toHaveBeenCalledWith( + expect(ruleOptions.rulesClient.enable).toHaveBeenCalledWith( expect.objectContaining({ id: ruleOptions.rule?.id, }) ); }); - it('calls the alertsClient with legacy ML params', async () => { + it('calls the rulesClient with legacy ML params', async () => { const rulesOptionsMock = getPatchMlRulesOptionsMock(); const ruleOptions: PatchRulesOptions = { ...rulesOptionsMock, @@ -51,7 +51,7 @@ describe('patchRules', () => { ruleOptions.rule.enabled = false; } await patchRules(ruleOptions); - expect(ruleOptions.alertsClient.update).toHaveBeenCalledWith( + expect(ruleOptions.rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ params: expect.objectContaining({ @@ -63,7 +63,7 @@ describe('patchRules', () => { ); }); - it('calls the alertsClient with new ML params', async () => { + it('calls the rulesClient with new ML params', async () => { const rulesOptionsMock = getPatchMlRulesOptionsMock(); const ruleOptions: PatchRulesOptions = { ...rulesOptionsMock, @@ -74,7 +74,7 @@ describe('patchRules', () => { ruleOptions.rule.enabled = false; } await patchRules(ruleOptions); - expect(ruleOptions.alertsClient.update).toHaveBeenCalledWith( + expect(ruleOptions.rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ params: expect.objectContaining({ @@ -103,7 +103,7 @@ describe('patchRules', () => { ], }; await patchRules(ruleOptions); - expect(ruleOptions.alertsClient.update).toHaveBeenCalledWith( + expect(ruleOptions.rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ actions: [ @@ -138,7 +138,7 @@ describe('patchRules', () => { } await patchRules(ruleOptions); - expect(ruleOptions.alertsClient.update).toHaveBeenCalledWith( + expect(ruleOptions.rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ actions: [ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts index 72af7ebc340cd..60e406255494a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts @@ -28,7 +28,7 @@ class PatchError extends Error { } export const patchRules = async ({ - alertsClient, + rulesClient, author, buildingBlockType, savedObjectsClient, @@ -192,15 +192,15 @@ export const patchRules = async ({ throw new PatchError(`Applying patch would create invalid rule: ${errors}`, 400); } - const update = await alertsClient.update({ + const update = await rulesClient.update({ id: rule.id, data: validated, }); if (rule.enabled && enabled === false) { - await alertsClient.disable({ id: rule.id }); + await rulesClient.disable({ id: rule.id }); } else if (!rule.enabled && enabled === true) { - await enableRule({ rule, alertsClient, savedObjectsClient }); + await enableRule({ rule, rulesClient, savedObjectsClient }); } else { // enabled is null or undefined and we do not touch the rule } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.test.ts index ce82384291303..33bc002942497 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.test.ts @@ -6,7 +6,7 @@ */ import { readRules } from './read_rules'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { getAlertMock, getFindResultWithSingleHit } from '../routes/__mocks__/request_responses'; import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; @@ -28,26 +28,26 @@ describe('read_rules', () => { jest.clearAllMocks(); }); describe('readRules', () => { - test('should return the output from alertsClient if id is set but ruleId is undefined', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); + test('should return the output from rulesClient if id is set but ruleId is undefined', async () => { + const rulesClient = rulesClientMock.create(); + rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); const rule = await readRules({ - alertsClient, + rulesClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleId: undefined, }); expect(rule).toEqual(getAlertMock(getQueryRuleParams())); }); test('should return null if saved object found by alerts client given id is not alert type', async () => { - const alertsClient = alertsClientMock.create(); + const rulesClient = rulesClientMock.create(); const result = getAlertMock(getQueryRuleParams()); // @ts-expect-error delete result.alertTypeId; - alertsClient.get.mockResolvedValue(result); + rulesClient.get.mockResolvedValue(result); const rule = await readRules({ - alertsClient, + rulesClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleId: undefined, }); @@ -55,13 +55,13 @@ describe('read_rules', () => { }); test('should return error if alerts client throws 404 error on get', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.get.mockImplementation(() => { + const rulesClient = rulesClientMock.create(); + rulesClient.get.mockImplementation(() => { throw new TestError(); }); const rule = await readRules({ - alertsClient, + rulesClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleId: undefined, }); @@ -69,13 +69,13 @@ describe('read_rules', () => { }); test('should return error if alerts client throws error on get', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.get.mockImplementation(() => { + const rulesClient = rulesClientMock.create(); + rulesClient.get.mockImplementation(() => { throw new Error('Test error'); }); try { await readRules({ - alertsClient, + rulesClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleId: undefined, }); @@ -84,39 +84,39 @@ describe('read_rules', () => { } }); - test('should return the output from alertsClient if id is undefined but ruleId is set', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + test('should return the output from rulesClient if id is undefined but ruleId is set', async () => { + const rulesClient = rulesClientMock.create(); + rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const rule = await readRules({ - alertsClient, + rulesClient, id: undefined, ruleId: 'rule-1', }); expect(rule).toEqual(getAlertMock(getQueryRuleParams())); }); - test('should return null if the output from alertsClient with ruleId set is empty', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); - alertsClient.find.mockResolvedValue({ data: [], page: 0, perPage: 1, total: 0 }); + test('should return null if the output from rulesClient with ruleId set is empty', async () => { + const rulesClient = rulesClientMock.create(); + rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); + rulesClient.find.mockResolvedValue({ data: [], page: 0, perPage: 1, total: 0 }); const rule = await readRules({ - alertsClient, + rulesClient, id: undefined, ruleId: 'rule-1', }); expect(rule).toEqual(null); }); - test('should return the output from alertsClient if id is null but ruleId is set', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + test('should return the output from rulesClient if id is null but ruleId is set', async () => { + const rulesClient = rulesClientMock.create(); + rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const rule = await readRules({ - alertsClient, + rulesClient, id: undefined, ruleId: 'rule-1', }); @@ -124,12 +124,12 @@ describe('read_rules', () => { }); test('should return null if id and ruleId are undefined', async () => { - const alertsClient = alertsClientMock.create(); - alertsClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const rulesClient = rulesClientMock.create(); + rulesClient.get.mockResolvedValue(getAlertMock(getQueryRuleParams())); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const rule = await readRules({ - alertsClient, + rulesClient, id: undefined, ruleId: undefined, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts index 62f8e7642cc64..141977f2474e0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts @@ -14,19 +14,19 @@ import { isAlertType, ReadRuleOptions } from './types'; /** * This reads the rules through a cascade try of what is fastest to what is slowest. * @param id - This is the fastest. This is the auto-generated id through the parameter id. - * and the id will either be found through `alertsClient.get({ id })` or it will not + * and the id will either be found through `rulesClient.get({ id })` or it will not * be returned as a not-found or a thrown error that is not 404. * @param ruleId - This is a close second to being fast as long as it can find the rule_id from * a filter query against the tags using `alert.attributes.tags: "__internal:${ruleId}"]` */ export const readRules = async ({ - alertsClient, + rulesClient, id, ruleId, }: ReadRuleOptions): Promise | null> => { if (id != null) { try { - const rule = await alertsClient.get({ id }); + const rule = await rulesClient.get({ id }); if (isAlertType(rule)) { return rule; } else { @@ -42,7 +42,7 @@ export const readRules = async ({ } } else if (ruleId != null) { const ruleFromFind = await findRules({ - alertsClient, + rulesClient, filter: `alert.attributes.tags: "${INTERNAL_RULE_ID_KEY}:${ruleId}"`, page: 1, fields: undefined, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts index d029393ce781e..7274614e2c9ba 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts @@ -101,7 +101,7 @@ import { EventCategoryOverrideOrUndefined, } from '../../../../common/detection_engine/schemas/common/schemas'; -import { AlertsClient, PartialAlert } from '../../../../../alerting/server'; +import { RulesClient, PartialAlert } from '../../../../../alerting/server'; import { Alert, SanitizedAlert } from '../../../../../alerting/common'; import { SIGNALS_ID } from '../../../../common/constants'; import { PartialFilter } from '../types'; @@ -186,7 +186,7 @@ export interface HapiReadableStream extends Readable { } export interface Clients { - alertsClient: AlertsClient; + rulesClient: RulesClient; } export const isAlertTypes = ( @@ -214,7 +214,7 @@ export const isRuleStatusFindType = ( }; export interface CreateRulesOptions { - alertsClient: AlertsClient; + rulesClient: RulesClient; anomalyThreshold: AnomalyThresholdOrUndefined; author: Author; buildingBlockType: BuildingBlockTypeOrUndefined; @@ -267,14 +267,14 @@ export interface CreateRulesOptions { export interface UpdateRulesOptions { savedObjectsClient: SavedObjectsClientContract; - alertsClient: AlertsClient; + rulesClient: RulesClient; defaultOutputIndex: string; ruleUpdate: UpdateRulesSchema; } export interface PatchRulesOptions { savedObjectsClient: SavedObjectsClientContract; - alertsClient: AlertsClient; + rulesClient: RulesClient; anomalyThreshold: AnomalyThresholdOrUndefined; author: AuthorOrUndefined; buildingBlockType: BuildingBlockTypeOrUndefined; @@ -324,13 +324,13 @@ export interface PatchRulesOptions { } export interface ReadRuleOptions { - alertsClient: AlertsClient; + rulesClient: RulesClient; id: IdOrUndefined; ruleId: RuleIdOrUndefined; } export interface DeleteRuleOptions { - alertsClient: AlertsClient; + rulesClient: RulesClient; savedObjectsClient: SavedObjectsClientContract; ruleStatusClient: RuleStatusSavedObjectsClient; ruleStatuses: SavedObjectsFindResponse; @@ -338,7 +338,7 @@ export interface DeleteRuleOptions { } export interface FindRuleOptions { - alertsClient: AlertsClient; + rulesClient: RulesClient; perPage: PerPageOrUndefined; page: PageOrUndefined; sortField: SortFieldOrUndefined; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts index 1f75aa5a19a98..b88c7f0450cac 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts @@ -6,7 +6,7 @@ */ import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { getFindResultWithSingleHit } from '../routes/__mocks__/request_responses'; import { updatePrepackagedRules } from './update_prepacked_rules'; import { patchRules } from './patch_rules'; @@ -14,11 +14,11 @@ import { getAddPrepackagedRulesSchemaDecodedMock } from '../../../../common/dete jest.mock('./patch_rules'); describe('updatePrepackagedRules', () => { - let alertsClient: ReturnType; + let rulesClient: ReturnType; let savedObjectsClient: ReturnType; beforeEach(() => { - alertsClient = alertsClientMock.create(); + rulesClient = rulesClientMock.create(); savedObjectsClient = savedObjectsClientMock.create(); }); @@ -33,10 +33,10 @@ describe('updatePrepackagedRules', () => { ]; const outputIndex = 'outputIndex'; const prepackagedRule = getAddPrepackagedRulesSchemaDecodedMock(); - alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); await updatePrepackagedRules( - alertsClient, + rulesClient, savedObjectsClient, [{ ...prepackagedRule, actions }], outputIndex diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts index f3ee7e251c02d..872a3b69d27ed 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts @@ -8,7 +8,7 @@ import { SavedObjectsClientContract } from 'kibana/server'; import { chunk } from 'lodash/fp'; import { AddPrepackagedRulesSchemaDecoded } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema'; -import { AlertsClient, PartialAlert } from '../../../../../alerting/server'; +import { RulesClient, PartialAlert } from '../../../../../alerting/server'; import { patchRules } from './patch_rules'; import { readRules } from './read_rules'; import { PartialFilter } from '../types'; @@ -43,34 +43,34 @@ export const UPDATE_CHUNK_SIZE = 50; * Updates the prepackaged rules given a set of rules and output index. * This implements a chunked approach to not saturate network connections and * avoid being a "noisy neighbor". - * @param alertsClient Alerting client + * @param rulesClient Alerting client * @param savedObjectsClient Saved object client * @param rules The rules to apply the update for * @param outputIndex The output index to apply the update to. */ export const updatePrepackagedRules = async ( - alertsClient: AlertsClient, + rulesClient: RulesClient, savedObjectsClient: SavedObjectsClientContract, rules: AddPrepackagedRulesSchemaDecoded[], outputIndex: string ): Promise => { const ruleChunks = chunk(UPDATE_CHUNK_SIZE, rules); for (const ruleChunk of ruleChunks) { - const rulePromises = createPromises(alertsClient, savedObjectsClient, ruleChunk, outputIndex); + const rulePromises = createPromises(rulesClient, savedObjectsClient, ruleChunk, outputIndex); await Promise.all(rulePromises); } }; /** * Creates promises of the rules and returns them. - * @param alertsClient Alerting client + * @param rulesClient Alerting client * @param savedObjectsClient Saved object client * @param rules The rules to apply the update for * @param outputIndex The output index to apply the update to. * @returns Promise of what was updated. */ export const createPromises = ( - alertsClient: AlertsClient, + rulesClient: RulesClient, savedObjectsClient: SavedObjectsClientContract, rules: AddPrepackagedRulesSchemaDecoded[], outputIndex: string @@ -122,7 +122,7 @@ export const createPromises = ( exceptions_list: exceptionsList, } = rule; - const existingRule = await readRules({ alertsClient, ruleId, id: undefined }); + const existingRule = await readRules({ rulesClient, ruleId, id: undefined }); // TODO: Fix these either with an is conversion or by better typing them within io-ts const filters: PartialFilter[] | undefined = filtersObject as PartialFilter[]; @@ -130,7 +130,7 @@ export const createPromises = ( // Note: we do not pass down enabled as we do not want to suddenly disable // or enable rules on the user when they were not expecting it if a rule updates return patchRules({ - alertsClient, + rulesClient, author, buildingBlockType, description, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.mock.ts index fd18ac7f7b6bc..778337a3650c7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.mock.ts @@ -6,7 +6,7 @@ */ import { UpdateRulesOptions } from './types'; -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; import { getUpdateRulesSchemaMock, @@ -14,14 +14,14 @@ import { } from '../../../../common/detection_engine/schemas/request/rule_schemas.mock'; export const getUpdateRulesOptionsMock = (): UpdateRulesOptions => ({ - alertsClient: alertsClientMock.create(), + rulesClient: rulesClientMock.create(), savedObjectsClient: savedObjectsClientMock.create(), defaultOutputIndex: '.siem-signals-default', ruleUpdate: getUpdateRulesSchemaMock(), }); export const getUpdateMlRulesOptionsMock = (): UpdateRulesOptions => ({ - alertsClient: alertsClientMock.create(), + rulesClient: rulesClientMock.create(), savedObjectsClient: savedObjectsClientMock.create(), defaultOutputIndex: '.siem-signals-default', ruleUpdate: getUpdateMachineLearningSchemaMock(), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.test.ts index 48b8905384566..7d04d3412899d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.test.ts @@ -8,55 +8,55 @@ import { getAlertMock } from '../routes/__mocks__/request_responses'; import { updateRules } from './update_rules'; import { getUpdateRulesOptionsMock, getUpdateMlRulesOptionsMock } from './update_rules.mock'; -import { AlertsClientMock } from '../../../../../alerting/server/alerts_client.mock'; +import { RulesClientMock } from '../../../../../alerting/server/rules_client.mock'; import { getMlRuleParams, getQueryRuleParams } from '../schemas/rule_schemas.mock'; describe('updateRules', () => { - it('should call alertsClient.disable if the rule was enabled and enabled is false', async () => { + it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { const rulesOptionsMock = getUpdateRulesOptionsMock(); rulesOptionsMock.ruleUpdate.enabled = false; - ((rulesOptionsMock.alertsClient as unknown) as AlertsClientMock).get.mockResolvedValue( + ((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).get.mockResolvedValue( getAlertMock(getQueryRuleParams()) ); await updateRules(rulesOptionsMock); - expect(rulesOptionsMock.alertsClient.disable).toHaveBeenCalledWith( + expect(rulesOptionsMock.rulesClient.disable).toHaveBeenCalledWith( expect.objectContaining({ id: rulesOptionsMock.ruleUpdate.id, }) ); }); - it('should call alertsClient.enable if the rule was disabled and enabled is true', async () => { + it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { const rulesOptionsMock = getUpdateRulesOptionsMock(); rulesOptionsMock.ruleUpdate.enabled = true; - ((rulesOptionsMock.alertsClient as unknown) as AlertsClientMock).get.mockResolvedValue({ + ((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).get.mockResolvedValue({ ...getAlertMock(getQueryRuleParams()), enabled: false, }); await updateRules(rulesOptionsMock); - expect(rulesOptionsMock.alertsClient.enable).toHaveBeenCalledWith( + expect(rulesOptionsMock.rulesClient.enable).toHaveBeenCalledWith( expect.objectContaining({ id: rulesOptionsMock.ruleUpdate.id, }) ); }); - it('calls the alertsClient with params', async () => { + it('calls the rulesClient with params', async () => { const rulesOptionsMock = getUpdateMlRulesOptionsMock(); rulesOptionsMock.ruleUpdate.enabled = true; - ((rulesOptionsMock.alertsClient as unknown) as AlertsClientMock).get.mockResolvedValue( + ((rulesOptionsMock.rulesClient as unknown) as RulesClientMock).get.mockResolvedValue( getAlertMock(getMlRuleParams()) ); await updateRules(rulesOptionsMock); - expect(rulesOptionsMock.alertsClient.update).toHaveBeenCalledWith( + expect(rulesOptionsMock.rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ params: expect.objectContaining({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts index 0fac804163afa..e0be646dc3f39 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts @@ -18,13 +18,13 @@ import { InternalRuleUpdate, RuleParams } from '../schemas/rule_schemas'; import { enableRule } from './enable_rule'; export const updateRules = async ({ - alertsClient, + rulesClient, savedObjectsClient, defaultOutputIndex, ruleUpdate, }: UpdateRulesOptions): Promise | null> => { const existingRule = await readRules({ - alertsClient, + rulesClient, ruleId: ruleUpdate.rule_id, id: ruleUpdate.id, }); @@ -80,15 +80,15 @@ export const updateRules = async ({ notifyWhen: null, }; - const update = await alertsClient.update({ + const update = await rulesClient.update({ id: existingRule.id, data: newInternalRule, }); if (existingRule.enabled && enabled === false) { - await alertsClient.disable({ id: existingRule.id }); + await rulesClient.disable({ id: existingRule.id }); } else if (!existingRule.enabled && enabled === true) { - await enableRule({ rule: existingRule, alertsClient, savedObjectsClient }); + await enableRule({ rule: existingRule, rulesClient, savedObjectsClient }); } return { ...update, enabled }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules_notifications.ts index 60e448e3058c8..5f2729f129948 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules_notifications.ts @@ -6,13 +6,13 @@ */ import { RuleAlertAction } from '../../../../common/detection_engine/types'; -import { AlertsClient, AlertServices } from '../../../../../alerting/server'; +import { RulesClient, AlertServices } from '../../../../../alerting/server'; import { updateOrCreateRuleActionsSavedObject } from '../rule_actions/update_or_create_rule_actions_saved_object'; import { updateNotifications } from '../notifications/update_notifications'; import { RuleActions } from '../rule_actions/types'; interface UpdateRulesNotifications { - alertsClient: AlertsClient; + rulesClient: RulesClient; savedObjectsClient: AlertServices['savedObjectsClient']; ruleAlertId: string; actions: RuleAlertAction[] | undefined; @@ -22,7 +22,7 @@ interface UpdateRulesNotifications { } export const updateRulesNotifications = async ({ - alertsClient, + rulesClient, savedObjectsClient, ruleAlertId, actions, @@ -38,7 +38,7 @@ export const updateRulesNotifications = async ({ }); await updateNotifications({ - alertsClient, + rulesClient, ruleAlertId, enabled, name, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index edf6d244cfa17..052270d491cda 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -6,7 +6,7 @@ */ import type { estypes } from '@elastic/elasticsearch'; -import { DslQuery, Filter } from 'src/plugins/data/common'; +import { DslQuery, Filter } from '@kbn/es-query'; import moment, { Moment } from 'moment'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { Status } from '../../../../common/detection_engine/schemas/common/schemas'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.test.ts index b2a589dacd371..1b7bf048646ed 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { alertsClientMock } from '../../../../../alerting/server/mocks'; +import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { getAlertMock, getFindResultWithMultiHits } from '../routes/__mocks__/request_responses'; import { INTERNAL_RULE_ID_KEY, INTERNAL_IDENTIFIER } from '../../../../common/constants'; import { readRawTags, readTags, convertTagsToSet, convertToTags, isTags } from './read_tags'; @@ -28,10 +28,10 @@ describe('read_tags', () => { result2.params.ruleId = 'rule-2'; result2.tags = ['tag 1', 'tag 2', 'tag 3', 'tag 4']; - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const tags = await readRawTags({ alertsClient }); + const tags = await readRawTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); }); @@ -46,10 +46,10 @@ describe('read_tags', () => { result2.params.ruleId = 'rule-2'; result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4']; - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const tags = await readRawTags({ alertsClient }); + const tags = await readRawTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); }); @@ -64,10 +64,10 @@ describe('read_tags', () => { result2.params.ruleId = 'rule-2'; result2.tags = []; - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const tags = await readRawTags({ alertsClient }); + const tags = await readRawTags({ rulesClient }); expect(tags).toEqual([]); }); @@ -77,10 +77,10 @@ describe('read_tags', () => { result1.params.ruleId = 'rule-1'; result1.tags = ['tag 1', 'tag 1', 'tag 1', 'tag 2']; - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const tags = await readRawTags({ alertsClient }); + const tags = await readRawTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2']); }); @@ -90,10 +90,10 @@ describe('read_tags', () => { result1.params.ruleId = 'rule-1'; result1.tags = []; - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const tags = await readRawTags({ alertsClient }); + const tags = await readRawTags({ rulesClient }); expect(tags).toEqual([]); }); }); @@ -110,10 +110,10 @@ describe('read_tags', () => { result2.params.ruleId = 'rule-2'; result2.tags = ['tag 1', 'tag 2', 'tag 3', 'tag 4']; - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const tags = await readTags({ alertsClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); }); @@ -128,10 +128,10 @@ describe('read_tags', () => { result2.params.ruleId = 'rule-2'; result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4']; - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const tags = await readTags({ alertsClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); }); @@ -146,10 +146,10 @@ describe('read_tags', () => { result2.params.ruleId = 'rule-2'; result2.tags = []; - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const tags = await readTags({ alertsClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual([]); }); @@ -159,10 +159,10 @@ describe('read_tags', () => { result1.params.ruleId = 'rule-1'; result1.tags = ['tag 1', 'tag 1', 'tag 1', 'tag 2']; - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const tags = await readTags({ alertsClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2']); }); @@ -172,10 +172,10 @@ describe('read_tags', () => { result1.params.ruleId = 'rule-1'; result1.tags = []; - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const tags = await readTags({ alertsClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual([]); }); @@ -189,10 +189,10 @@ describe('read_tags', () => { 'tag 1', ]; - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const tags = await readTags({ alertsClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual(['tag 1']); }); @@ -222,10 +222,10 @@ describe('read_tags', () => { 'tag 4', ]; - const alertsClient = alertsClientMock.create(); - alertsClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); + const rulesClient = rulesClientMock.create(); + rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const tags = await readTags({ alertsClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4', 'tag 5']); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.ts index 2d1966917d287..2314a8a49f567 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.ts @@ -7,7 +7,7 @@ import { has } from 'lodash/fp'; import { INTERNAL_IDENTIFIER } from '../../../../common/constants'; -import { AlertsClient } from '../../../../../alerting/server'; +import { RulesClient } from '../../../../../alerting/server'; import { findRules } from '../rules/find_rules'; export interface TagType { @@ -40,23 +40,23 @@ export const convertTagsToSet = (tagObjects: object[]): Set => { // then this should be replaced with a an aggregation call. // Ref: https://www.elastic.co/guide/en/kibana/master/saved-objects-api.html export const readTags = async ({ - alertsClient, + rulesClient, }: { - alertsClient: AlertsClient; + rulesClient: RulesClient; }): Promise => { - const tags = await readRawTags({ alertsClient }); + const tags = await readRawTags({ rulesClient }); return tags.filter((tag) => !tag.startsWith(INTERNAL_IDENTIFIER)); }; export const readRawTags = async ({ - alertsClient, + rulesClient, }: { - alertsClient: AlertsClient; + rulesClient: RulesClient; perPage?: number; }): Promise => { // Get just one record so we can get the total count const firstTags = await findRules({ - alertsClient, + rulesClient, fields: ['tags'], perPage: 1, page: 1, @@ -66,7 +66,7 @@ export const readRawTags = async ({ }); // Get all the rules to aggregate over all the tags of the rules const rules = await findRules({ - alertsClient, + rulesClient, fields: ['tags'], perPage: firstTags.total, sortField: 'createdAt', diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/helpers.test.ts index bee39be6cbd5c..f30f80a4cf14c 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/helpers.test.ts @@ -46,7 +46,7 @@ describe('installPrepackagedTimelines', () => { authz: {}, } as unknown) as SecurityPluginSetup; - clients.alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); jest.doMock('./helpers', () => { return { diff --git a/x-pack/plugins/spaces/server/saved_objects/migrations/index.ts b/x-pack/plugins/spaces/server/saved_objects/migrations/index.ts index 01a08afdcefcc..7935aaac3cc92 100644 --- a/x-pack/plugins/spaces/server/saved_objects/migrations/index.ts +++ b/x-pack/plugins/spaces/server/saved_objects/migrations/index.ts @@ -5,4 +5,7 @@ * 2.0. */ -export { migrateToKibana660 } from './migrate_6x'; +import * as spaceMigrations from './space_migrations'; +import * as usageStatsMigrations from './usage_stats_migrations'; + +export { spaceMigrations, usageStatsMigrations }; diff --git a/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.test.ts b/x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.test.ts similarity index 61% rename from x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.test.ts rename to x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.test.ts index 71d467f6f56d0..62a3d98662939 100644 --- a/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.test.ts +++ b/x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.test.ts @@ -5,23 +5,18 @@ * 2.0. */ -import type { SavedObjectMigrationContext } from 'src/core/server'; +import type { Space } from 'src/plugins/spaces_oss/common'; -import { migrateToKibana660 } from './migrate_6x'; - -const mockContext = {} as SavedObjectMigrationContext; +import { migrateTo660 } from './space_migrations'; describe('migrateTo660', () => { it('adds a "disabledFeatures" attribute initialized as an empty array', () => { expect( - migrateToKibana660( - { - id: 'space:foo', - type: 'space', - attributes: {}, - }, - mockContext - ) + migrateTo660({ + id: 'space:foo', + type: 'space', + attributes: {} as Space, + }) ).toEqual({ id: 'space:foo', type: 'space', @@ -34,16 +29,13 @@ describe('migrateTo660', () => { it('does not initialize "disabledFeatures" if the property already exists', () => { // This scenario shouldn't happen organically. Protecting against defects in the migration. expect( - migrateToKibana660( - { - id: 'space:foo', - type: 'space', - attributes: { - disabledFeatures: ['foo', 'bar', 'baz'], - }, - }, - mockContext - ) + migrateTo660({ + id: 'space:foo', + type: 'space', + attributes: { + disabledFeatures: ['foo', 'bar', 'baz'], + } as Space, + }) ).toEqual({ id: 'space:foo', type: 'space', diff --git a/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts b/x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.ts similarity index 65% rename from x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts rename to x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.ts index 973d6ec1751a2..d88db2b0181dd 100644 --- a/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts +++ b/x-pack/plugins/spaces/server/saved_objects/migrations/space_migrations.ts @@ -5,9 +5,10 @@ * 2.0. */ -import type { SavedObjectMigrationFn } from 'src/core/server'; +import type { SavedObjectUnsanitizedDoc } from 'src/core/server'; +import type { Space } from 'src/plugins/spaces_oss/common'; -export const migrateToKibana660: SavedObjectMigrationFn = (doc) => { +export const migrateTo660 = (doc: SavedObjectUnsanitizedDoc) => { if (!doc.attributes.hasOwnProperty('disabledFeatures')) { doc.attributes.disabledFeatures = []; } diff --git a/x-pack/plugins/spaces/server/saved_objects/migrations/usage_stats_migrations.test.ts b/x-pack/plugins/spaces/server/saved_objects/migrations/usage_stats_migrations.test.ts new file mode 100644 index 0000000000000..3cece7dcbb553 --- /dev/null +++ b/x-pack/plugins/spaces/server/saved_objects/migrations/usage_stats_migrations.test.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObjectUnsanitizedDoc } from 'src/core/server'; + +import type { UsageStats } from '../../usage_stats'; +import { migrateTo7141 } from './usage_stats_migrations'; + +const type = 'obj-type'; +const id = 'obj-id'; + +describe('#migrateTo7141', () => { + it('Resets targeted counter fields and leaves others unchanged', () => { + const doc = { + type, + id, + attributes: { + foo: 'bar', + 'apiCalls.copySavedObjects.total': 10, + }, + } as SavedObjectUnsanitizedDoc; + + expect(migrateTo7141(doc)).toEqual({ + type, + id, + attributes: { + foo: 'bar', + 'apiCalls.copySavedObjects.total': 0, + 'apiCalls.copySavedObjects.kibanaRequest.yes': 0, + 'apiCalls.copySavedObjects.kibanaRequest.no': 0, + 'apiCalls.copySavedObjects.createNewCopiesEnabled.yes': 0, + 'apiCalls.copySavedObjects.createNewCopiesEnabled.no': 0, + 'apiCalls.copySavedObjects.overwriteEnabled.yes': 0, + 'apiCalls.copySavedObjects.overwriteEnabled.no': 0, + }, + }); + }); +}); diff --git a/x-pack/plugins/spaces/server/saved_objects/migrations/usage_stats_migrations.ts b/x-pack/plugins/spaces/server/saved_objects/migrations/usage_stats_migrations.ts new file mode 100644 index 0000000000000..974b9ee1078b8 --- /dev/null +++ b/x-pack/plugins/spaces/server/saved_objects/migrations/usage_stats_migrations.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { cloneDeep } from 'lodash'; + +import type { SavedObjectUnsanitizedDoc } from 'src/core/server'; + +import type { UsageStats } from '../../usage_stats'; + +export const migrateTo7141 = (doc: SavedObjectUnsanitizedDoc) => { + try { + return resetFields(doc, [ + // Prior to this, we were counting the `overwrite` option incorrectly; reset all copy API counter fields so we get clean data + 'apiCalls.copySavedObjects.total', + 'apiCalls.copySavedObjects.kibanaRequest.yes', + 'apiCalls.copySavedObjects.kibanaRequest.no', + 'apiCalls.copySavedObjects.createNewCopiesEnabled.yes', + 'apiCalls.copySavedObjects.createNewCopiesEnabled.no', + 'apiCalls.copySavedObjects.overwriteEnabled.yes', + 'apiCalls.copySavedObjects.overwriteEnabled.no', + ]); + } catch (err) { + // fail-safe + } + return doc; +}; + +function resetFields( + doc: SavedObjectUnsanitizedDoc, + fieldsToReset: Array +) { + const newDoc = cloneDeep(doc); + const { attributes = {} } = newDoc; + for (const field of fieldsToReset) { + attributes[field] = 0; + } + return { ...newDoc, attributes }; +} diff --git a/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts b/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts index b1bf7fe580a03..2e2bad9ff0994 100644 --- a/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts +++ b/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts @@ -10,7 +10,7 @@ import type { CoreSetup } from 'src/core/server'; import type { SpacesServiceStart } from '../spaces_service'; import { SPACES_USAGE_STATS_TYPE } from '../usage_stats'; import { SpacesSavedObjectMappings, UsageStatsMappings } from './mappings'; -import { migrateToKibana660 } from './migrations'; +import { spaceMigrations, usageStatsMigrations } from './migrations'; import { spacesSavedObjectsClientWrapperFactory } from './saved_objects_client_wrapper_factory'; interface SetupDeps { @@ -26,7 +26,7 @@ export class SpacesSavedObjectsService { namespaceType: 'agnostic', mappings: SpacesSavedObjectMappings, migrations: { - '6.6.0': migrateToKibana660, + '6.6.0': spaceMigrations.migrateTo660, }, }); @@ -35,6 +35,9 @@ export class SpacesSavedObjectsService { hidden: true, namespaceType: 'agnostic', mappings: UsageStatsMappings, + migrations: { + '7.14.1': usageStatsMigrations.migrateTo7141, + }, }); core.savedObjects.addClientWrapper( diff --git a/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.test.ts b/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.test.ts index c5b63a4d007b9..aa3fa29e87ce1 100644 --- a/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.test.ts +++ b/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.test.ts @@ -117,14 +117,32 @@ describe('UsageStatsClient', () => { createNewCopies: true, overwrite: true, } as IncrementCopySavedObjectsOptions); - expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1); - expect(repositoryMock.incrementCounter).toHaveBeenCalledWith( + await usageStatsClient.incrementCopySavedObjects({ + headers: firstPartyRequestHeaders, + createNewCopies: false, + overwrite: true, + } as IncrementCopySavedObjectsOptions); + expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(2); + expect(repositoryMock.incrementCounter).toHaveBeenNthCalledWith( + 1, SPACES_USAGE_STATS_TYPE, SPACES_USAGE_STATS_ID, [ `${COPY_STATS_PREFIX}.total`, `${COPY_STATS_PREFIX}.kibanaRequest.yes`, `${COPY_STATS_PREFIX}.createNewCopiesEnabled.yes`, + // excludes 'overwriteEnabled.yes' and 'overwriteEnabled.no' when createNewCopies is true + ], + incrementOptions + ); + expect(repositoryMock.incrementCounter).toHaveBeenNthCalledWith( + 2, + SPACES_USAGE_STATS_TYPE, + SPACES_USAGE_STATS_ID, + [ + `${COPY_STATS_PREFIX}.total`, + `${COPY_STATS_PREFIX}.kibanaRequest.yes`, + `${COPY_STATS_PREFIX}.createNewCopiesEnabled.no`, `${COPY_STATS_PREFIX}.overwriteEnabled.yes`, ], incrementOptions diff --git a/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.ts b/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.ts index 5dcf106d6cfb4..e73d6db948bb4 100644 --- a/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.ts +++ b/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.ts @@ -71,7 +71,7 @@ export class UsageStatsClient { 'total', `kibanaRequest.${isKibanaRequest ? 'yes' : 'no'}`, `createNewCopiesEnabled.${createNewCopies ? 'yes' : 'no'}`, - `overwriteEnabled.${overwrite ? 'yes' : 'no'}`, + ...(!createNewCopies ? [`overwriteEnabled.${overwrite ? 'yes' : 'no'}`] : []), // the overwrite option is ignored when createNewCopies is true ]; await this.updateUsageStats(counterFieldNames, COPY_STATS_PREFIX); } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/es_query_builder.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/es_query_builder.ts index 37e0a293b03a0..9a95517986bee 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/es_query_builder.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/es_query_builder.ts @@ -9,12 +9,12 @@ import { ElasticsearchClient } from 'kibana/server'; import { Logger } from 'src/core/server'; import type { ApiResponse, estypes } from '@elastic/elasticsearch'; import { - Query, - IIndexPattern, fromKueryExpression, toElasticsearchQuery, luceneStringToDsl, -} from '../../../../../../src/plugins/data/common'; + IndexPatternBase, + Query, +} from '@kbn/es-query'; export const OTHER_CATEGORY = 'other'; // Consider dynamically obtaining from config? @@ -22,7 +22,7 @@ const MAX_TOP_LEVEL_QUERY_SIZE = 0; const MAX_SHAPES_QUERY_SIZE = 10000; const MAX_BUCKETS_LIMIT = 65535; -export const getEsFormattedQuery = (query: Query, indexPattern?: IIndexPattern) => { +export const getEsFormattedQuery = (query: Query, indexPattern?: IndexPatternBase) => { let esFormattedQuery; const queryLanguage = query.language; diff --git a/x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts b/x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts index 76ab48a8243db..8fbab31b49266 100644 --- a/x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts +++ b/x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts @@ -5,12 +5,8 @@ * 2.0. */ -import { IIndexPattern } from 'src/plugins/data/public'; -import { - IEsSearchRequest, - IEsSearchResponse, - IFieldSubType, -} from '../../../../../../src/plugins/data/common'; +import { IFieldSubType, IndexPatternBase } from '@kbn/es-query'; +import { IEsSearchRequest, IEsSearchResponse } from '../../../../../../src/plugins/data/common'; import { DocValueFields, Maybe } from '../common'; export type BeatFieldsFactoryQueryType = 'beatFields'; @@ -83,7 +79,7 @@ export type BrowserFields = Readonly>>; export const EMPTY_BROWSER_FIELDS = {}; export const EMPTY_DOCVALUE_FIELD: DocValueFields[] = []; -export const EMPTY_INDEX_PATTERN: IIndexPattern = { +export const EMPTY_INDEX_PATTERN: IndexPatternBase = { fields: [], title: '', }; diff --git a/x-pack/plugins/timelines/common/typed_json.ts b/x-pack/plugins/timelines/common/typed_json.ts index 71ece54777871..c639c1c0322dc 100644 --- a/x-pack/plugins/timelines/common/typed_json.ts +++ b/x-pack/plugins/timelines/common/typed_json.ts @@ -6,7 +6,7 @@ */ import { JsonObject } from '@kbn/common-utils'; -import { DslQuery, Filter } from 'src/plugins/data/common'; +import { DslQuery, Filter } from '@kbn/es-query'; export type ESQuery = | ESRangeQuery diff --git a/x-pack/plugins/timelines/common/types/timeline/columns/index.ts b/x-pack/plugins/timelines/common/types/timeline/columns/index.ts index 61f0c6a0b8f23..9161ea3ea78ce 100644 --- a/x-pack/plugins/timelines/common/types/timeline/columns/index.ts +++ b/x-pack/plugins/timelines/common/types/timeline/columns/index.ts @@ -6,8 +6,7 @@ */ import { EuiDataGridColumn } from '@elastic/eui'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { IFieldSubType } from '../../../../../../../src/plugins/data/public'; +import { IFieldSubType } from '../../../../../../../src/plugins/data/common'; import { TimelineNonEcsData } from '../../../search_strategy/timeline'; export type ColumnHeaderType = 'not-filtered' | 'text-filter'; diff --git a/x-pack/plugins/timelines/public/components/clipboard/index.tsx b/x-pack/plugins/timelines/public/components/clipboard/index.tsx index ed4b143b2bca1..23a0a629d17a1 100644 --- a/x-pack/plugins/timelines/public/components/clipboard/index.tsx +++ b/x-pack/plugins/timelines/public/components/clipboard/index.tsx @@ -66,6 +66,7 @@ export const Clipboard = ({ aria-label={i18n.COPY_TO_THE_CLIPBOARD} className={className} data-test-subj="clipboard" + iconSize="s" iconType="copyClipboard" onClick={onClick} > diff --git a/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_for_value.tsx b/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_for_value.tsx index f62d29c8b648f..421cd0089c1b7 100644 --- a/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_for_value.tsx +++ b/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_for_value.tsx @@ -23,11 +23,16 @@ export const filterForValueFn = ({ filterManager, onFilterAdded, }: FilterValueFnArgs): void => { - const filter = value?.length === 0 ? createFilter(field, undefined) : createFilter(field, value); + const makeFilter = (currentVal: string | null | undefined) => + currentVal?.length === 0 ? createFilter(field, undefined) : createFilter(field, currentVal); + const filters = Array.isArray(value) + ? value.map((currentVal: string | null | undefined) => makeFilter(currentVal)) + : makeFilter(value); + const activeFilterManager = filterManager; if (activeFilterManager != null) { - activeFilterManager.addFilters(filter); + activeFilterManager.addFilters(filters); if (onFilterAdded != null) { onFilterAdded(); } diff --git a/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_out_value.tsx b/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_out_value.tsx index a0888ac7c6e11..bfa7848025bf4 100644 --- a/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_out_value.tsx +++ b/x-pack/plugins/timelines/public/components/hover_actions/actions/filter_out_value.tsx @@ -24,12 +24,18 @@ export const filterOutValueFn = ({ filterManager, onFilterAdded, }: FilterValueFnArgs) => { - const filter = - value?.length === 0 ? createFilter(field, null, false) : createFilter(field, value, true); + const makeFilter = (currentVal: string | null | undefined) => + currentVal?.length === 0 + ? createFilter(field, null, false) + : createFilter(field, currentVal, true); + const filters = Array.isArray(value) + ? value.map((currentVal: string | null | undefined) => makeFilter(currentVal)) + : makeFilter(value); + const activeFilterManager = filterManager; if (activeFilterManager != null) { - activeFilterManager.addFilters(filter); + activeFilterManager.addFilters(filters); if (onFilterAdded != null) { onFilterAdded(); } diff --git a/x-pack/plugins/timelines/public/components/t_grid/helpers.tsx b/x-pack/plugins/timelines/public/components/t_grid/helpers.tsx index fc040522f3e15..6cfd97c8bad55 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/helpers.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/helpers.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import type { Filter, EsQueryConfig, Query } from '@kbn/es-query'; import { isEmpty, get } from 'lodash/fp'; import memoizeOne from 'memoize-one'; import { @@ -14,12 +15,7 @@ import { handleSkipFocus, stopPropagationAndPreventDefault, } from '../../../common'; -import type { - EsQueryConfig, - Filter, - IIndexPattern, - Query, -} from '../../../../../../src/plugins/data/public'; +import type { IIndexPattern } from '../../../../../../src/plugins/data/public'; import type { BrowserFields } from '../../../common/search_strategy/index_fields'; import { DataProviderType, EXISTS_OPERATOR } from '../../../common/types/timeline'; // eslint-disable-next-line no-duplicate-imports diff --git a/x-pack/plugins/timelines/public/components/utils/keury/index.ts b/x-pack/plugins/timelines/public/components/utils/keury/index.ts index e31d682fd7021..391b15e8fdbac 100644 --- a/x-pack/plugins/timelines/public/components/utils/keury/index.ts +++ b/x-pack/plugins/timelines/public/components/utils/keury/index.ts @@ -7,15 +7,9 @@ import { isEmpty, isString, flow } from 'lodash/fp'; import { JsonObject } from '@kbn/common-utils'; +import { EsQueryConfig, Filter, Query } from '@kbn/es-query'; -import { - EsQueryConfig, - Query, - Filter, - esQuery, - esKuery, - IIndexPattern, -} from '../../../../../../../src/plugins/data/public'; +import { esQuery, esKuery, IIndexPattern } from '../../../../../../../src/plugins/data/public'; export const convertKueryToElasticSearchQuery = ( kueryExpression: string, diff --git a/x-pack/plugins/timelines/public/mock/mock_hover_actions.tsx b/x-pack/plugins/timelines/public/mock/mock_hover_actions.tsx new file mode 100644 index 0000000000000..52ea1fa827136 --- /dev/null +++ b/x-pack/plugins/timelines/public/mock/mock_hover_actions.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; + +/* eslint-disable react/display-name */ +export const mockHoverActions = { + addToTimeline: { + AddToTimelineButton: () => <>{'Add To Timeline'}, + keyboardShortcut: 'timelineAddShortcut', + useGetHandleStartDragToTimeline: () => jest.fn, + }, + columnToggle: { + ColumnToggleButton: () => <>{'Column Toggle'}, + columnToggleFn: jest.fn, + keyboardShortcut: 'columnToggleShortcut', + }, + copy: { + CopyButton: () => <>{'Copy button'}, + keyboardShortcut: 'copyShortcut', + }, + filterForValue: { + FilterForValueButton: () => <>{'Filter button'}, + filterForValueFn: jest.fn, + keyboardShortcut: 'filterForShortcut', + }, + filterOutValue: { + FilterOutValueButton: () => <>{'Filter out button'}, + filterOutValueFn: jest.fn, + keyboardShortcut: 'filterOutShortcut', + }, +}; diff --git a/x-pack/plugins/timelines/public/mock/plugin_mock.tsx b/x-pack/plugins/timelines/public/mock/plugin_mock.tsx index 64be3306e7aa6..90fb076592001 100644 --- a/x-pack/plugins/timelines/public/mock/plugin_mock.tsx +++ b/x-pack/plugins/timelines/public/mock/plugin_mock.tsx @@ -13,8 +13,10 @@ import { useDraggableKeyboardWrapper, } from '../components'; import { useAddToTimeline, useAddToTimelineSensor } from '../hooks/use_add_to_timeline'; +import { mockHoverActions } from './mock_hover_actions'; export const createTGridMocks = () => ({ + getHoverActions: () => mockHoverActions, // eslint-disable-next-line react/display-name getTGrid: () => <>{'hello grid'}, // eslint-disable-next-line react/display-name diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/index.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/index.ts index c1b567b99cfb1..b40dc728741ed 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/index.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/index.ts @@ -42,7 +42,8 @@ export const timelineEventsAll: TimelineFactory = { const hits = response.rawResponse.hits.hits; if (fieldRequested.includes('*') && hits.length > 0) { - fieldRequested = Object.keys(hits[0]?.fields ?? {}).reduce((acc, f) => { + const fieldsReturned = hits.flatMap((hit) => Object.keys(hit.fields ?? {})); + fieldRequested = fieldsReturned.reduce((acc, f) => { if (!acc.includes(f)) { return [...acc, f]; } @@ -59,6 +60,7 @@ export const timelineEventsAll: TimelineFactory = { ) ) ); + const inspect = { dsl: [inspectStringifyObject(buildTimelineEventsAllQuery(queryOptions))], }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ad3a58e0e9584..567496ee6ed26 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -771,12 +771,12 @@ "data.advancedSettings.timepicker.today": "今日", "data.aggTypes.buckets.ranges.rangesFormatMessage": "{gte} {from} と {lt} {to}", "data.aggTypes.buckets.ranges.rangesFormatMessageArrowRight": "{from} → {to}", - "data.common.kql.errors.endOfInputText": "インプットの終わり", - "data.common.kql.errors.fieldNameText": "フィールド名", - "data.common.kql.errors.literalText": "文字通り", - "data.common.kql.errors.syntaxError": "{expectedList} を期待しましたが {foundInput} が検出されました。", - "data.common.kql.errors.valueText": "値", - "data.common.kql.errors.whitespaceText": "空白類", + "esQuery.kql.errors.endOfInputText": "インプットの終わり", + "esQuery.kql.errors.fieldNameText": "フィールド名", + "esQuery.kql.errors.literalText": "文字通り", + "esQuery.kql.errors.syntaxError": "{expectedList} を期待しましたが {foundInput} が検出されました。", + "esQuery.kql.errors.valueText": "値", + "esQuery.kql.errors.whitespaceText": "空白類", "data.errors.fetchError": "ネットワークとプロキシ構成を確認してください。問題が解決しない場合は、ネットワーク管理者に問い合わせてください。", "data.fieldFormats.boolean.title": "ブール", "data.fieldFormats.bytes.title": "バイト", @@ -1697,16 +1697,8 @@ "discover.localMenu.shareTitle": "共有", "discover.noResults.expandYourTimeRangeTitle": "時間範囲を拡大", "discover.noResults.queryMayNotMatchTitle": "1つ以上の表示されているインデックスに日付フィールドが含まれています。クエリが現在の時間範囲のデータと一致しないか、現在選択された時間範囲にデータがまったく存在しない可能性があります。データが存在する時間範囲に変えることができます。", - "discover.noResults.searchExamples.400to499StatusCodeExampleTitle": "400-499のすべてのステータスコードを検索", - "discover.noResults.searchExamples.400to499StatusCodeWithPhpExtensionExampleTitle": "400-499のphp拡張子のステータスコードを検索", - "discover.noResults.searchExamples.400to499StatusCodeWithPhpOrHtmlExtensionExampleTitle": "400-499のphpまたはhtml拡張子のステータスコードを検索", - "discover.noResults.searchExamples.anyField200StatusCodeExampleTitle": "いずれかのフィールドに数字200が含まれているリクエストを検索", - "discover.noResults.searchExamples.howTosearchForWebServerLogsDescription": "画面上部の検索バーは、ElasticsearchのLucene {queryStringSyntaxLink}サポートを利用します。新規フィールドにパースされたウェブサーバーログの検索方法の例は、次のとおりです。", "discover.noResults.searchExamples.noResultsBecauseOfError": "検索結果の取得中にエラーが発生しました", "discover.noResults.searchExamples.noResultsMatchSearchCriteriaTitle": "検索条件と一致する結果がありません。", - "discover.noResults.searchExamples.queryStringSyntaxLinkText": "クエリ文字列の構文", - "discover.noResults.searchExamples.refineYourQueryTitle": "クエリの調整", - "discover.noResults.searchExamples.statusField200StatusCodeExampleTitle": "ステータスフィールドの200を検索", "discover.noResultsFound": "結果が見つかりませんでした", "discover.notifications.invalidTimeRangeText": "指定された時間範囲が無効です。 (開始:'{from}'、終了:'{to}') ", "discover.notifications.invalidTimeRangeTitle": "無効な時間範囲", @@ -1935,20 +1927,12 @@ "home.manageData.sectionTitle": "データを管理", "home.pageTitle": "ホーム", "home.recentlyAccessed.recentlyViewedTitle": "最近閲覧", - "home.sampleData.ecommerceSpec.averageSalesPriceTitle": "[e コマース] 平均販売価格", - "home.sampleData.ecommerceSpec.averageSoldQuantityTitle": "[e コマース] 平均販売数", - "home.sampleData.ecommerceSpec.controlsTitle": "[e コマース] コントロール", - "home.sampleData.ecommerceSpec.markdownTitle": "[e コマース] マークダウン", "home.sampleData.ecommerceSpec.ordersTitle": "[e コマース] 注文", "home.sampleData.ecommerceSpec.promotionTrackingTitle": "[e コマース] プロモーショントラッキング", "home.sampleData.ecommerceSpec.revenueDashboardDescription": "サンプルの e コマースの注文と収益を分析します", "home.sampleData.ecommerceSpec.revenueDashboardTitle": "[e コマース] 収益ダッシュボード", - "home.sampleData.ecommerceSpec.salesByCategoryTitle": "[e コマース] カテゴリーごとの売上", - "home.sampleData.ecommerceSpec.salesByGenderTitle": "[e コマース] 性別ごとの売上", "home.sampleData.ecommerceSpec.salesCountMapTitle": "[eコマース] 売上カウントマップ", "home.sampleData.ecommerceSpec.soldProductsPerDayTitle": "[e コマース] 1 日の販売製品", - "home.sampleData.ecommerceSpec.topSellingProductsTitle": "[e コマース] トップセラー製品", - "home.sampleData.ecommerceSpec.totalRevenueTitle": "[e コマース] 合計収益", "home.sampleData.ecommerceSpecDescription": "e コマースの注文をトラッキングするサンプルデータ、ビジュアライゼーション、ダッシュボードです。", "home.sampleData.ecommerceSpecTitle": "サンプル e コマース注文", "home.sampleData.flightsSpec.airportConnectionsTitle": "[フライト] 空港乗り継ぎ (空港にカーソルを合わせてください) ", @@ -5193,8 +5177,8 @@ "xpack.alerting.alertNavigationRegistry.get.missingNavigationError": "「{consumer}」内のアラートタイプ「{alertType}」のナビゲーションは登録されていません。", "xpack.alerting.alertNavigationRegistry.register.duplicateDefaultError": "「{consumer}」内のデフォルトナビゲーションはすでに登録されています。", "xpack.alerting.alertNavigationRegistry.register.duplicateNavigationError": "「{consumer}」内のアラートタイプ「{alertType}」のナビゲーションはすでに登録されています。", - "xpack.alerting.alertsClient.invalidDate": "パラメーター{field}の無効な日付:「{dateValue}」", - "xpack.alerting.alertsClient.validateActions.invalidGroups": "無効なアクショングループ:{groups}", + "xpack.alerting.rulesClient.invalidDate": "パラメーター{field}の無効な日付:「{dateValue}」", + "xpack.alerting.rulesClient.validateActions.invalidGroups": "無効なアクショングループ:{groups}", "xpack.alerting.alertTypeRegistry.get.missingAlertTypeError": "アラートタイプ「{id}」は登録されていません。", "xpack.alerting.alertTypeRegistry.register.customRecoveryActionGroupUsageError": "アラートタイプ [id=\"{id}\"] を登録できません。アクショングループ [{actionGroup}] は、復元とアクティブなアクショングループの両方として使用できません。", "xpack.alerting.alertTypeRegistry.register.duplicateAlertTypeError": "アラートタイプ\"{id}\"はすでに登録されています。", @@ -6355,12 +6339,12 @@ "xpack.canvas.functions.seriesStyle.args.pointsHelpText": "線上の点のサイズです。", "xpack.canvas.functions.seriesStyle.args.stackHelpText": "数列をスタックするかを指定します。数字はスタック ID です。同じスタック ID の数列は一緒にスタックされます。", "xpack.canvas.functions.seriesStyleHelpText": "チャートの数列のプロパティの説明に使用されるオブジェクトを作成します。{plotFn} や {pieFn} のように、チャート関数内で {seriesStyleFn} を使用します。", - "xpack.canvas.functions.shape.args.borderHelpText": "図形の外郭の {SVG} カラーです。", - "xpack.canvas.functions.shape.args.borderWidthHelpText": "境界の太さです。", - "xpack.canvas.functions.shape.args.fillHelpText": "図形を塗りつぶす {SVG} カラーです。", - "xpack.canvas.functions.shape.args.maintainAspectHelpText": "図形の元の横縦比を維持しますか?", - "xpack.canvas.functions.shape.args.shapeHelpText": "図形を選択します。", - "xpack.canvas.functions.shapeHelpText": "図形を作成します。", + "expressionShape.functions.shape.args.borderHelpText": "図形の外郭の {SVG} カラーです。", + "expressionShape.functions.shape.args.borderWidthHelpText": "境界の太さです。", + "expressionShape.functions.shape.args.fillHelpText": "図形を塗りつぶす {SVG} カラーです。", + "expressionShape.functions.shape.args.maintainAspectHelpText": "図形の元の横縦比を維持しますか?", + "expressionShape.functions.shape.args.shapeHelpText": "図形を選択します。", + "expressionShape.functions.shapeHelpText": "図形を作成します。", "xpack.canvas.functions.sort.args.byHelpText": "並べ替えの基準となる列です。指定されていない場合、{DATATABLE}は初めの列で並べられます。", "xpack.canvas.functions.sort.args.reverseHelpText": "並び順を反転させます。指定されていない場合、{DATATABLE}は昇順で並べられます。", "xpack.canvas.functions.sortHelpText": "{DATATABLE}を指定された列で並べ替えます。", @@ -6517,8 +6501,8 @@ "xpack.canvas.renderer.progress.helpDescription": "エレメントのパーセンテージを示す進捗インジケーターをレンダリングします", "xpack.canvas.renderer.repeatImage.displayName": "画像の繰り返し", "xpack.canvas.renderer.repeatImage.helpDescription": "画像を指定回数繰り返し表示します", - "xpack.canvas.renderer.shape.displayName": "形状", - "xpack.canvas.renderer.shape.helpDescription": "基本的な図形をレンダリングします", + "expressionShape.renderer.shape.displayName": "形状", + "expressionShape.renderer.shape.helpDescription": "基本的な図形をレンダリングします", "xpack.canvas.renderer.table.displayName": "データテーブル", "xpack.canvas.renderer.table.helpDescription": "表形式データを {HTML} としてレンダリングします", "xpack.canvas.renderer.text.displayName": "プレインテキスト", @@ -17110,20 +17094,10 @@ "xpack.observability.expView.operationType.99thPercentile": "99パーセンタイル", "xpack.observability.expView.operationType.average": "平均", "xpack.observability.expView.operationType.median": "中央", - "xpack.observability.expView.reportType.noDataType": "データ型を選択すると、系列の構築を開始します。", - "xpack.observability.expView.seriesBuilder.breakdown": "内訳", - "xpack.observability.expView.seriesBuilder.dataType": "データ型", - "xpack.observability.expView.seriesBuilder.definition": "定義", - "xpack.observability.expView.seriesBuilder.filters": "フィルター", - "xpack.observability.expView.seriesBuilder.report": "レポート", "xpack.observability.expView.seriesBuilder.selectReportType": "レポートタイプを選択すると、ビジュアライゼーションを定義します。", - "xpack.observability.expView.seriesEditor.actions": "アクション", - "xpack.observability.expView.seriesEditor.addFilter": "フィルターを追加します", - "xpack.observability.expView.seriesEditor.breakdowns": "内訳", "xpack.observability.expView.seriesEditor.clearFilter": "フィルターを消去", "xpack.observability.expView.seriesEditor.filters": "フィルター", "xpack.observability.expView.seriesEditor.name": "名前", - "xpack.observability.expView.seriesEditor.removeSeries": "クリックすると、系列を削除します", "xpack.observability.expView.seriesEditor.time": "時間", "xpack.observability.featureCatalogueDescription": "専用UIで、ログ、メトリック、アプリケーショントレース、システム可用性を連結します。", "xpack.observability.featureCatalogueDescription1": "インフラストラクチャメトリックを監視します。", @@ -17183,7 +17157,6 @@ "xpack.observability.overview.uptime.up": "アップ", "xpack.observability.overview.ux.appLink": "アプリで表示", "xpack.observability.overview.ux.title": "ユーザーエクスペリエンス", - "xpack.observability.reportTypeCol.nodata": "利用可能なデータがありません", "xpack.observability.resources.documentation": "ドキュメント", "xpack.observability.resources.forum": "ディスカッションフォーラム", "xpack.observability.resources.title": "リソース", @@ -17197,7 +17170,6 @@ "xpack.observability.section.apps.uptime.description": "サイトとサービスの可用性をアクティブに監視するアラートを受信し、問題をより迅速に解決して、ユーザーエクスペリエンスを最適化します。", "xpack.observability.section.apps.uptime.title": "アップタイム", "xpack.observability.section.errorPanel": "データの取得時にエラーが発生しました。再試行してください", - "xpack.observability.seriesEditor.edit": "系列を編集", "xpack.observability.severityBadge.unknownDescription": "不明", "xpack.observability.transactionRateLabel": "{value} tpm", "xpack.observability.ux.coreVitals.average": "平均", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f7b8e216f3a4f..1c1306281cbcb 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -774,12 +774,12 @@ "data.advancedSettings.timepicker.today": "今日", "data.aggTypes.buckets.ranges.rangesFormatMessage": "{gte} {from} 且 {lt} {to}", "data.aggTypes.buckets.ranges.rangesFormatMessageArrowRight": "{from} → {to}", - "data.common.kql.errors.endOfInputText": "输入结束", - "data.common.kql.errors.fieldNameText": "字段名称", - "data.common.kql.errors.literalText": "文本", - "data.common.kql.errors.syntaxError": "应找到 {expectedList},但却找到了 {foundInput}。", - "data.common.kql.errors.valueText": "值", - "data.common.kql.errors.whitespaceText": "空白", + "esQuery.kql.errors.endOfInputText": "输入结束", + "esQuery.kql.errors.fieldNameText": "字段名称", + "esQuery.kql.errors.literalText": "文本", + "esQuery.kql.errors.syntaxError": "应找到 {expectedList},但却找到了 {foundInput}。", + "esQuery.kql.errors.valueText": "值", + "esQuery.kql.errors.whitespaceText": "空白", "data.errors.fetchError": "请检查您的网络和代理配置。如果问题持续存在,请联系网络管理员。", "data.fieldFormats.boolean.title": "布尔型", "data.fieldFormats.bytes.title": "字节", @@ -1707,16 +1707,8 @@ "discover.localMenu.shareTitle": "共享", "discover.noResults.expandYourTimeRangeTitle": "展开时间范围", "discover.noResults.queryMayNotMatchTitle": "您正在查看的一个或多个索引包含日期字段。您的查询在当前时间范围内可能不匹配任何数据,也可能在当前选定的时间范围内没有任何数据。您可以尝试将时间范围更改为包含数据的时间范围。", - "discover.noResults.searchExamples.400to499StatusCodeExampleTitle": "查找所有介于 400-499 之间的状态代码", - "discover.noResults.searchExamples.400to499StatusCodeWithPhpExtensionExampleTitle": "查找状态代码 400-499 以及扩展名 php", - "discover.noResults.searchExamples.400to499StatusCodeWithPhpOrHtmlExtensionExampleTitle": "查找状态代码 400-499 以及扩展名 php 或 html", - "discover.noResults.searchExamples.anyField200StatusCodeExampleTitle": "查找任意字段包含数字 200 的请求", - "discover.noResults.searchExamples.howTosearchForWebServerLogsDescription": "顶部的搜索栏使用 Elasticsearch 对 Lucene {queryStringSyntaxLink} 的支持。以下是一些示例,说明如何搜索已解析成若干字段的 Web 服务器日志。", "discover.noResults.searchExamples.noResultsBecauseOfError": "检索搜索结果时遇到问题", "discover.noResults.searchExamples.noResultsMatchSearchCriteriaTitle": "没有任何结果匹配您的搜索条件", - "discover.noResults.searchExamples.queryStringSyntaxLinkText": "查询字符串语法", - "discover.noResults.searchExamples.refineYourQueryTitle": "优化您的查询", - "discover.noResults.searchExamples.statusField200StatusCodeExampleTitle": "在状态字段中查找 200", "discover.noResultsFound": "找不到结果", "discover.notifications.invalidTimeRangeText": "提供的时间范围无效。 (自:“{from}”,至:“{to}”) ", "discover.notifications.invalidTimeRangeTitle": "时间范围无效", @@ -1947,20 +1939,11 @@ "home.manageData.sectionTitle": "管理您的数据", "home.pageTitle": "主页", "home.recentlyAccessed.recentlyViewedTitle": "最近查看", - "home.sampleData.ecommerceSpec.averageSalesPriceTitle": "[电子商务] 平均销售价格", - "home.sampleData.ecommerceSpec.averageSoldQuantityTitle": "[电子商务] 平均销售数量", - "home.sampleData.ecommerceSpec.controlsTitle": "[电子商务] 控制", - "home.sampleData.ecommerceSpec.markdownTitle": "[eCommerce] Markdown", "home.sampleData.ecommerceSpec.ordersTitle": "[电子商务] 订单", "home.sampleData.ecommerceSpec.promotionTrackingTitle": "[电子商务] 促销追踪", "home.sampleData.ecommerceSpec.revenueDashboardDescription": "分析模拟的电子商务订单和收入", "home.sampleData.ecommerceSpec.revenueDashboardTitle": "[电子商务] 收入仪表板", - "home.sampleData.ecommerceSpec.salesByCategoryTitle": "[电子商务] 按类别划分的销售额", - "home.sampleData.ecommerceSpec.salesByGenderTitle": "[电子商务] 按性别划分的销售额", "home.sampleData.ecommerceSpec.salesCountMapTitle": "[电子商务] 销售计数地图", - "home.sampleData.ecommerceSpec.soldProductsPerDayTitle": "[电子商务] 每天已售产品", - "home.sampleData.ecommerceSpec.topSellingProductsTitle": "[电子商务] 热卖产品", - "home.sampleData.ecommerceSpec.totalRevenueTitle": "[电子商务] 总收入", "home.sampleData.ecommerceSpecDescription": "用于追踪电子商务订单的样例数据、可视化和仪表板。", "home.sampleData.ecommerceSpecTitle": "样例电子商务订单", "home.sampleData.flightsSpec.airportConnectionsTitle": "[航班] 机场航线 (将鼠标悬停在机场上) ", @@ -5221,8 +5204,8 @@ "xpack.alerting.alertNavigationRegistry.get.missingNavigationError": "在“{consumer}”内针对告警类型“{alertType}”的导航未注册。", "xpack.alerting.alertNavigationRegistry.register.duplicateDefaultError": "“{consumer}”内的默认导航已注册。", "xpack.alerting.alertNavigationRegistry.register.duplicateNavigationError": "在“{consumer}”内针对告警类型“{alertType}”的导航已注册。", - "xpack.alerting.alertsClient.invalidDate": "参数 {field} 的日期无效:“{dateValue}”", - "xpack.alerting.alertsClient.validateActions.invalidGroups": "无效操作组:{groups}", + "xpack.alerting.rulesClient.invalidDate": "参数 {field} 的日期无效:“{dateValue}”", + "xpack.alerting.rulesClient.validateActions.invalidGroups": "无效操作组:{groups}", "xpack.alerting.alertTypeRegistry.get.missingAlertTypeError": "未注册告警类型“{id}”。", "xpack.alerting.alertTypeRegistry.register.customRecoveryActionGroupUsageError": "无法注册告警类型 [id=\"{id}\"]。操作组 [{actionGroup}] 无法同时用作恢复和活动操作组。", "xpack.alerting.alertTypeRegistry.register.duplicateAlertTypeError": "已注册告警类型“{id}”。", @@ -6080,8 +6063,6 @@ "xpack.canvas.elements.progressWheelHelpText": "将进度显示为轮盘的一部分", "xpack.canvas.elements.repeatImageDisplayName": "图像重复", "xpack.canvas.elements.repeatImageHelpText": "使图像重复 N 次", - "xpack.canvas.elements.revealImageDisplayName": "图像显示", - "xpack.canvas.elements.revealImageHelpText": "显示图像特定百分比", "xpack.canvas.elements.shapeDisplayName": "形状", "xpack.canvas.elements.shapeHelpText": "可定制的形状", "xpack.canvas.elements.tableDisplayName": "数据表", @@ -6395,12 +6376,12 @@ "xpack.canvas.functions.seriesStyle.args.pointsHelpText": "折线图上的点大小。", "xpack.canvas.functions.seriesStyle.args.stackHelpText": "指定是否应堆叠序列。数字为堆叠 ID。具有相同堆叠 ID 的序列将堆叠在一起。", "xpack.canvas.functions.seriesStyleHelpText": "创建用于在图表上描述序列属性的对象。在绘图函数 (如 {plotFn} 或 {pieFn} ) 内使用 {seriesStyleFn}。", - "xpack.canvas.functions.shape.args.borderHelpText": "形状轮廓边框的 {SVG} 颜色。", - "xpack.canvas.functions.shape.args.borderWidthHelpText": "边框的粗细。", - "xpack.canvas.functions.shape.args.fillHelpText": "填充形状的 {SVG} 颜色。", - "xpack.canvas.functions.shape.args.maintainAspectHelpText": "维持形状的原始纵横比?", - "xpack.canvas.functions.shape.args.shapeHelpText": "选取形状。", - "xpack.canvas.functions.shapeHelpText": "创建形状。", + "expressionShape.functions.shape.args.borderHelpText": "形状轮廓边框的 {SVG} 颜色。", + "expressionShape.functions.shape.args.borderWidthHelpText": "边框的粗细。", + "expressionShape.functions.shape.args.fillHelpText": "填充形状的 {SVG} 颜色。", + "expressionShape.functions.shape.args.maintainAspectHelpText": "维持形状的原始纵横比?", + "expressionShape.functions.shape.args.shapeHelpText": "选取形状。", + "expressionShape.functions.shapeHelpText": "创建形状。", "xpack.canvas.functions.sort.args.byHelpText": "排序依据的列。如果未指定,则 {DATATABLE} 按第一列排序。", "xpack.canvas.functions.sort.args.reverseHelpText": "反转排序顺序。如果未指定,则 {DATATABLE} 按升序排序。", "xpack.canvas.functions.sortHelpText": "按指定列对 {DATATABLE} 进行排序。", @@ -6557,8 +6538,8 @@ "xpack.canvas.renderer.progress.helpDescription": "呈现显示元素百分比的进度指示", "xpack.canvas.renderer.repeatImage.displayName": "图像重复", "xpack.canvas.renderer.repeatImage.helpDescription": "重复图像给定次数", - "xpack.canvas.renderer.shape.displayName": "形状", - "xpack.canvas.renderer.shape.helpDescription": "呈现基本形状", + "expressionShape.renderer.shape.displayName": "形状", + "expressionShape.renderer.shape.helpDescription": "呈现基本形状", "xpack.canvas.renderer.table.displayName": "数据表", "xpack.canvas.renderer.table.helpDescription": "将表格数据呈现为 {HTML}", "xpack.canvas.renderer.text.displayName": "纯文本", @@ -17346,20 +17327,10 @@ "xpack.observability.expView.operationType.99thPercentile": "第 99 个百分位", "xpack.observability.expView.operationType.average": "平均值", "xpack.observability.expView.operationType.median": "中值", - "xpack.observability.expView.reportType.noDataType": "选择数据类型以开始构建序列。", - "xpack.observability.expView.seriesBuilder.breakdown": "分解", - "xpack.observability.expView.seriesBuilder.dataType": "数据类型", - "xpack.observability.expView.seriesBuilder.definition": "定义", - "xpack.observability.expView.seriesBuilder.filters": "筛选", - "xpack.observability.expView.seriesBuilder.report": "报告", "xpack.observability.expView.seriesBuilder.selectReportType": "选择报告类型以定义可视化。", - "xpack.observability.expView.seriesEditor.actions": "操作", - "xpack.observability.expView.seriesEditor.addFilter": "添加筛选", - "xpack.observability.expView.seriesEditor.breakdowns": "分解", "xpack.observability.expView.seriesEditor.clearFilter": "清除筛选", "xpack.observability.expView.seriesEditor.filters": "筛选", "xpack.observability.expView.seriesEditor.name": "名称", - "xpack.observability.expView.seriesEditor.removeSeries": "单击移除序列", "xpack.observability.expView.seriesEditor.time": "时间", "xpack.observability.featureCatalogueDescription": "通过专用 UI 整合您的日志、指标、应用程序跟踪和系统可用性。", "xpack.observability.featureCatalogueDescription1": "监测基础架构指标。", @@ -17419,7 +17390,6 @@ "xpack.observability.overview.uptime.up": "运行", "xpack.observability.overview.ux.appLink": "在应用中查看", "xpack.observability.overview.ux.title": "用户体验", - "xpack.observability.reportTypeCol.nodata": "没有可用数据", "xpack.observability.resources.documentation": "文档", "xpack.observability.resources.forum": "讨论论坛", "xpack.observability.resources.title": "资源", @@ -17433,7 +17403,6 @@ "xpack.observability.section.apps.uptime.description": "主动监测站点和服务的可用性。接收告警并更快地解决问题,从而优化用户体验。", "xpack.observability.section.apps.uptime.title": "运行时间", "xpack.observability.section.errorPanel": "尝试提取数据时发生错误。请重试", - "xpack.observability.seriesEditor.edit": "编辑序列", "xpack.observability.severityBadge.unknownDescription": "未知", "xpack.observability.transactionRateLabel": "{value} tpm", "xpack.observability.ux.coreVitals.average": "平均值", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.tsx index 2e8f1d5ef3bd7..f42b571408502 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.tsx @@ -6,8 +6,9 @@ */ import React from 'react'; +import { LogoProps } from '../types'; -const Logo = () => ( +const Logo = (props: LogoProps) => ( ( fill="none" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" + {...props} > diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/logo.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/logo.tsx index 20db34351c6b1..ab991651a8a52 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/logo.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/logo.tsx @@ -6,8 +6,9 @@ */ import React from 'react'; +import { LogoProps } from '../types'; -const Logo = () => ( +const Logo = (props: LogoProps) => ( ( xmlSpace="preserve" width="32" height="32" + {...props} > diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.tsx index 325893756e2f4..7b64a1330d401 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.tsx @@ -6,9 +6,17 @@ */ import React from 'react'; +import { LogoProps } from '../types'; -const Logo = () => ( - +const Logo = (props: LogoProps) => ( + + { +const Logo = (props: LogoProps) => { return ( { fill="none" stroke="null" vectorEffect="non-scaling-stroke" + {...props} > ( +const Logo = (props: LogoProps) => ( ( viewBox="0 0 32 32" enableBackground="new 0 0 32 32" xmlSpace="preserve" + {...props} > ; + +export type LogoProps = Omit; diff --git a/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx b/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx index 1a53a2c9b64a0..aa981071b7ee2 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx @@ -22,6 +22,7 @@ import React, { useContext } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import numeral from '@elastic/numeral'; import moment from 'moment'; +import { useSelector } from 'react-redux'; import { getChartDateLabel } from '../../../lib/helper'; import { ChartWrapper } from './chart_wrapper'; import { UptimeThemeContext } from '../../../contexts'; @@ -32,6 +33,7 @@ import { getDateRangeFromChartElement } from './utils'; import { STATUS_DOWN_LABEL, STATUS_UP_LABEL } from '../translations'; import { createExploratoryViewUrl } from '../../../../../observability/public'; import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context'; +import { monitorStatusSelector } from '../../../state/selectors'; export interface PingHistogramComponentProps { /** @@ -73,6 +75,8 @@ export const PingHistogramComponent: React.FC = ({ const monitorId = useMonitorId(); + const selectedMonitor = useSelector(monitorStatusSelector); + const { basePath } = useUptimeSettingsContext(); const [getUrlParams, updateUrlParams] = useUrlParams(); @@ -189,12 +193,21 @@ export const PingHistogramComponent: React.FC = ({ const pingHistogramExploratoryViewLink = createExploratoryViewUrl( { - 'pings-over-time': { - dataType: 'synthetics', - reportType: 'kpi-over-time', - time: { from: dateRangeStart, to: dateRangeEnd }, - ...(monitorId ? { filters: [{ field: 'monitor.id', values: [monitorId] }] } : {}), - }, + reportType: 'kpi-over-time', + allSeries: [ + { + name: `${monitorId}-pings`, + dataType: 'synthetics', + selectedMetricField: 'summary.up', + time: { from: dateRangeStart, to: dateRangeEnd }, + reportDefinitions: { + 'monitor.name': + monitorId && selectedMonitor?.monitor?.name + ? [selectedMonitor.monitor.name] + : ['ALL_VALUES'], + }, + }, + ], }, basePath ); diff --git a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx index 9f00dd2e8f061..c459fe46da975 100644 --- a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx +++ b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx @@ -10,13 +10,15 @@ import { EuiHeaderLinks, EuiToolTip, EuiHeaderLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { useHistory } from 'react-router-dom'; -import { createExploratoryViewUrl, SeriesUrl } from '../../../../../observability/public'; +import { useSelector } from 'react-redux'; +import { createExploratoryViewUrl } from '../../../../../observability/public'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context'; import { useGetUrlParams } from '../../../hooks'; import { ToggleAlertFlyoutButton } from '../../overview/alerts/alerts_containers'; import { SETTINGS_ROUTE } from '../../../../common/constants'; import { stringifyUrlParams } from '../../../lib/helper/stringify_url_params'; +import { monitorStatusSelector } from '../../../state/selectors'; const ADD_DATA_LABEL = i18n.translate('xpack.uptime.addDataButtonLabel', { defaultMessage: 'Add data', @@ -38,13 +40,28 @@ export function ActionMenuContent(): React.ReactElement { const { dateRangeStart, dateRangeEnd } = params; const history = useHistory(); + const selectedMonitor = useSelector(monitorStatusSelector); + + const monitorId = selectedMonitor?.monitor?.id; + const syntheticExploratoryViewLink = createExploratoryViewUrl( { - 'synthetics-series': ({ - dataType: 'synthetics', - isNew: true, - time: { from: dateRangeStart, to: dateRangeEnd }, - } as unknown) as SeriesUrl, + reportType: 'kpi-over-time', + allSeries: [ + { + dataType: 'synthetics', + seriesType: 'area_stacked', + selectedMetricField: 'monitor.duration.us', + time: { from: dateRangeStart, to: dateRangeEnd }, + breakdown: monitorId ? 'observer.geo.name' : 'monitor.type', + reportDefinitions: { + 'monitor.name': selectedMonitor?.monitor?.name + ? [selectedMonitor?.monitor?.name] + : ['ALL_VALUES'], + }, + name: monitorId ? `${monitorId}-response-duration` : 'All monitors response duration', + }, + ], }, basePath ); diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration_container.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration_container.tsx index 1590e225f9ca8..18aba948eaa37 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration_container.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration_container.tsx @@ -55,16 +55,19 @@ export const MonitorDuration: React.FC = ({ monitorId }) => { const exploratoryViewLink = createExploratoryViewUrl( { - [`monitor-duration`]: { - reportType: 'kpi-over-time', - time: { from: dateRangeStart, to: dateRangeEnd }, - reportDefinitions: { - 'monitor.id': [monitorId] as string[], + reportType: 'kpi-over-time', + allSeries: [ + { + name: `${monitorId}-response-duration`, + time: { from: dateRangeStart, to: dateRangeEnd }, + reportDefinitions: { + 'monitor.id': [monitorId] as string[], + }, + breakdown: 'observer.geo.name', + operationType: 'average', + dataType: 'synthetics', }, - breakdown: 'observer.geo.name', - operationType: 'average', - dataType: 'synthetics', - }, + ], }, basePath ); diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts index 03587e9ed622e..14f0404a02185 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts @@ -14,16 +14,16 @@ export interface GetMonitorDetailsParams { monitorId: string; dateStart: string; dateEnd: string; - alertsClient: any; + rulesClient: any; } const getMonitorAlerts = async ({ uptimeEsClient, - alertsClient, + rulesClient, monitorId, }: { uptimeEsClient: UptimeESClient; - alertsClient: any; + rulesClient: any; monitorId: string; }) => { const options: any = { @@ -34,7 +34,7 @@ const getMonitorAlerts = async ({ sortField: 'name.keyword', }; - const { data } = await alertsClient.find({ options }); + const { data } = await rulesClient.find({ options }); const monitorAlerts = []; for (let i = 0; i < data.length; i++) { const currAlert = data[i]; @@ -85,7 +85,7 @@ const getMonitorAlerts = async ({ export const getMonitorDetails: UMElasticsearchQueryFn< GetMonitorDetailsParams, MonitorDetails -> = async ({ uptimeEsClient, monitorId, dateStart, dateEnd, alertsClient }) => { +> = async ({ uptimeEsClient, monitorId, dateStart, dateEnd, rulesClient }) => { const queryFilters: any = [ { range: { @@ -133,7 +133,7 @@ export const getMonitorDetails: UMElasticsearchQueryFn< const errorTimestamp: string | undefined = data?.['@timestamp']; const monAlerts = await getMonitorAlerts({ uptimeEsClient, - alertsClient, + rulesClient, monitorId, }); diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts index eefde90677312..50712153a5fea 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts @@ -24,14 +24,14 @@ export const createGetMonitorDetailsRoute: UMRestApiRouteFactory = (libs: UMServ handler: async ({ uptimeEsClient, context, request }): Promise => { const { monitorId, dateStart, dateEnd } = request.query; - const alertsClient = context.alerting?.getAlertsClient(); + const rulesClient = context.alerting?.getRulesClient(); return await libs.requests.getMonitorDetails({ uptimeEsClient, monitorId, dateStart, dateEnd, - alertsClient, + rulesClient, }); }, }); diff --git a/x-pack/test/functional/apps/observability/exploratory_view.ts b/x-pack/test/functional/apps/observability/exploratory_view.ts new file mode 100644 index 0000000000000..258caea87ffda --- /dev/null +++ b/x-pack/test/functional/apps/observability/exploratory_view.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import Path from 'path'; +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['observability', 'common', 'header']); + const esArchiver = getService('esArchiver'); + const find = getService('find'); + + const testSubjects = getService('testSubjects'); + + const rangeFrom = '2021-01-17T16%3A46%3A15.338Z'; + const rangeTo = '2021-01-19T17%3A01%3A32.309Z'; + + describe('ExploratoryView', () => { + before(async () => { + await esArchiver.loadIfNeeded( + Path.join('x-pack/test/apm_api_integration/common/fixtures/es_archiver', '8.0.0') + ); + + await esArchiver.loadIfNeeded( + Path.join('x-pack/test/apm_api_integration/common/fixtures/es_archiver', 'rum_8.0.0') + ); + + await esArchiver.loadIfNeeded( + Path.join('x-pack/test/apm_api_integration/common/fixtures/es_archiver', 'rum_test_data') + ); + + await PageObjects.common.navigateToApp('ux', { + search: `?rangeFrom=${rangeFrom}&rangeTo=${rangeTo}`, + }); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + + after(async () => { + await esArchiver.unload( + Path.join('x-pack/test/apm_api_integration/common/fixtures/es_archiver', '8.0.0') + ); + + await esArchiver.unload( + Path.join('x-pack/test/apm_api_integration/common/fixtures/es_archiver', 'rum_8.0.0') + ); + }); + + it('should able to open exploratory view from ux app', async () => { + await testSubjects.exists('uxAnalyzeBtn'); + await testSubjects.click('uxAnalyzeBtn'); + expect(await find.existsByCssSelector('.euiBasicTable')).to.eql(true); + }); + + it('renders lens visualization', async () => { + expect(await testSubjects.exists('lnsVisualizationContainer')).to.eql(true); + + expect( + await find.existsByCssSelector('div[data-title="Prefilled from exploratory view app"]') + ).to.eql(true); + + expect((await find.byCssSelector('dd')).getVisibleText()).to.eql(true); + }); + + it('can do a breakdown per series', async () => { + await testSubjects.click('seriesBreakdown'); + + expect(await find.existsByCssSelector('[id="user_agent.name"]')).to.eql(true); + + await find.clickByCssSelector('[id="user_agent.name"]'); + + await PageObjects.header.waitUntilLoadingHasFinished(); + + expect(await find.existsByCssSelector('[title="Chrome Mobile iOS"]')).to.eql(true); + expect(await find.existsByCssSelector('[title="Mobile Safari"]')).to.eql(true); + }); + }); +} diff --git a/x-pack/test/functional/apps/observability/index.ts b/x-pack/test/functional/apps/observability/index.ts index b7f03b5f27bae..cce07b9ff7d86 100644 --- a/x-pack/test/functional/apps/observability/index.ts +++ b/x-pack/test/functional/apps/observability/index.ts @@ -8,8 +8,9 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { - describe('Observability specs', function () { + describe('ObservabilityApp', function () { this.tags('ciGroup6'); loadTestFile(require.resolve('./feature_controls')); + loadTestFile(require.resolve('./exploratory_view')); }); } diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/__snapshots__/download_csv_dashboard.snap b/x-pack/test/reporting_api_integration/reporting_and_security/__snapshots__/download_csv_dashboard.snap index 094d72942353d..6215ae7e11a4c 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/__snapshots__/download_csv_dashboard.snap +++ b/x-pack/test/reporting_api_integration/reporting_and_security/__snapshots__/download_csv_dashboard.snap @@ -1,48 +1,46 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Reporting APIs CSV Generation from SearchSource Exports CSV with all fields when using defaults 1`] = ` -"\\"_id\\",\\"_index\\",\\"_score\\",\\"_type\\",category,\\"category.keyword\\",currency,\\"customer_first_name\\",\\"customer_first_name.keyword\\",\\"customer_full_name\\",\\"customer_full_name.keyword\\",\\"customer_gender\\",\\"customer_id\\",\\"customer_last_name\\",\\"customer_last_name.keyword\\",\\"customer_phone\\",\\"day_of_week\\",\\"day_of_week_i\\",email,\\"geoip.city_name\\",\\"geoip.continent_name\\",\\"geoip.country_iso_code\\",\\"geoip.location\\",\\"geoip.region_name\\",manufacturer,\\"manufacturer.keyword\\",\\"order_date\\",\\"order_id\\",\\"products._id\\",\\"products._id.keyword\\",\\"products.base_price\\",\\"products.base_unit_price\\",\\"products.category\\",\\"products.category.keyword\\",\\"products.created_on\\",\\"products.discount_amount\\",\\"products.discount_percentage\\",\\"products.manufacturer\\",\\"products.manufacturer.keyword\\",\\"products.min_price\\",\\"products.price\\",\\"products.product_id\\",\\"products.product_name\\",\\"products.product_name.keyword\\",\\"products.quantity\\",\\"products.sku\\",\\"products.tax_amount\\",\\"products.taxful_price\\",\\"products.taxless_price\\",\\"products.unit_discount_amount\\",sku,\\"taxful_total_price\\",\\"taxless_total_price\\",\\"total_quantity\\",\\"total_unique_products\\",type,user -3AMtOW0BH63Xcmy432DJ,ecommerce,\\"-\\",\\"-\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,\\"Sultan Al\\",\\"Sultan Al\\",\\"Sultan Al Boone\\",\\"Sultan Al Boone\\",MALE,19,Boone,Boone,,Saturday,5,\\"sultan al@boone-family.zzz\\",\\"Abu Dhabi\\",Asia,AE,\\"{ - \\"\\"coordinates\\"\\": [ +"_id,_index,_score,_type,category,category.keyword,currency,customer_first_name,customer_first_name.keyword,customer_full_name,customer_full_name.keyword,customer_gender,customer_id,customer_last_name,customer_last_name.keyword,customer_phone,day_of_week,day_of_week_i,email,geoip.city_name,geoip.continent_name,geoip.country_iso_code,geoip.location,geoip.region_name,manufacturer,manufacturer.keyword,order_date,order_id,products._id,products._id.keyword,products.base_price,products.base_unit_price,products.category,products.category.keyword,products.created_on,products.discount_amount,products.discount_percentage,products.manufacturer,products.manufacturer.keyword,products.min_price,products.price,products.product_id,products.product_name,products.product_name.keyword,products.quantity,products.sku,products.tax_amount,products.taxful_price,products.taxless_price,products.unit_discount_amount,sku,taxful_total_price,taxless_total_price,total_quantity,total_unique_products,type,user +3AMtOW0BH63Xcmy432DJ,ecommerce,-,-,Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories,Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories,EUR,Sultan Al,Sultan Al,Sultan Al Boone,Sultan Al Boone,MALE,19,Boone,Boone,(empty),Saturday,5,sultan al@boone-family.zzz,Abu Dhabi,Asia,AE,{ + \\"coordinates\\": [ 54.4, 24.5 ], - \\"\\"type\\"\\": \\"\\"Point\\"\\" -}\\",\\"Abu Dhabi\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"80, 60, 21.984, 11.992\\",\\"80, 60, 21.984, 11.992\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0, 0, 0\\",\\"0, 0, 0, 0\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"42.375, 33, 10.344, 6.109\\",\\"80, 60, 21.984, 11.992\\",\\"23,975, 6,338, 14,116, 15,290\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"1, 1, 1, 1\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"0, 0, 0, 0\\",\\"80, 60, 21.984, 11.992\\",\\"80, 60, 21.984, 11.992\\",\\"0, 0, 0, 0\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",174,174,4,4,order,sultan -9gMtOW0BH63Xcmy432DJ,ecommerce,\\"-\\",\\"-\\",\\"Women's Shoes, Women's Clothing\\",\\"Women's Shoes, Women's Clothing\\",EUR,Pia,Pia,\\"Pia Richards\\",\\"Pia Richards\\",FEMALE,45,Richards,Richards,,Saturday,5,\\"pia@richards-family.zzz\\",Cannes,Europe,FR,\\"{ - \\"\\"coordinates\\"\\": [ + \\"type\\": \\"Point\\" +},Abu Dhabi,Angeldale, Oceanavigations, Microlutions,Angeldale, Oceanavigations, Microlutions,Jul 12, 2019 @ 00:00:00.000,716724,sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290,sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290,80, 60, 21.984, 11.992,80, 60, 21.984, 11.992,Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories,Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories,Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000,0, 0, 0, 0,0, 0, 0, 0,Angeldale, Oceanavigations, Microlutions, Oceanavigations,Angeldale, Oceanavigations, Microlutions, Oceanavigations,42.375, 33, 10.344, 6.109,80, 60, 21.984, 11.992,23,975, 6,338, 14,116, 15,290,Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor,Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor,1, 1, 1, 1,ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085,0, 0, 0, 0,80, 60, 21.984, 11.992,80, 60, 21.984, 11.992,0, 0, 0, 0,ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085,174,174,4,4,order,sultan +9gMtOW0BH63Xcmy432DJ,ecommerce,-,-,Women's Shoes, Women's Clothing,Women's Shoes, Women's Clothing,EUR,Pia,Pia,Pia Richards,Pia Richards,FEMALE,45,Richards,Richards,(empty),Saturday,5,pia@richards-family.zzz,Cannes,Europe,FR,{ + \\"coordinates\\": [ 7, 43.6 ], - \\"\\"type\\"\\": \\"\\"Point\\"\\" -}\\",\\"Alpes-Maritimes\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Jul 12, 2019 @ 00:00:00.000\\",591503,\\"sold_product_591503_14761, sold_product_591503_11632\\",\\"sold_product_591503_14761, sold_product_591503_11632\\",\\"20.984, 20.984\\",\\"20.984, 20.984\\",\\"Women's Shoes, Women's Clothing\\",\\"Women's Shoes, Women's Clothing\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0\\",\\"0, 0\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Tigress Enterprises, Pyramidustries\\",\\"10.703, 9.867\\",\\"20.984, 20.984\\",\\"14,761, 11,632\\",\\"Classic heels - blue, Summer dress - coral/pink\\",\\"Classic heels - blue, Summer dress - coral/pink\\",\\"1, 1\\",\\"ZO0006400064, ZO0150601506\\",\\"0, 0\\",\\"20.984, 20.984\\",\\"20.984, 20.984\\",\\"0, 0\\",\\"ZO0006400064, ZO0150601506\\",\\"41.969\\",\\"41.969\\",2,2,order,pia -BgMtOW0BH63Xcmy432LJ,ecommerce,\\"-\\",\\"-\\",\\"Women's Clothing\\",\\"Women's Clothing\\",EUR,Brigitte,Brigitte,\\"Brigitte Meyer\\",\\"Brigitte Meyer\\",FEMALE,12,Meyer,Meyer,,Saturday,5,\\"brigitte@meyer-family.zzz\\",\\"New York\\",\\"North America\\",US,\\"{ - \\"\\"coordinates\\"\\": [ + \\"type\\": \\"Point\\" +},Alpes-Maritimes,Tigress Enterprises, Pyramidustries,Tigress Enterprises, Pyramidustries,Jul 12, 2019 @ 00:00:00.000,591503,sold_product_591503_14761, sold_product_591503_11632,sold_product_591503_14761, sold_product_591503_11632,20.984, 20.984,20.984, 20.984,Women's Shoes, Women's Clothing,Women's Shoes, Women's Clothing,Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000,0, 0,0, 0,Tigress Enterprises, Pyramidustries,Tigress Enterprises, Pyramidustries,10.703, 9.867,20.984, 20.984,14,761, 11,632,Classic heels - blue, Summer dress - coral/pink,Classic heels - blue, Summer dress - coral/pink,1, 1,ZO0006400064, ZO0150601506,0, 0,20.984, 20.984,20.984, 20.984,0, 0,ZO0006400064, ZO0150601506,41.969,41.969,2,2,order,pia +BgMtOW0BH63Xcmy432LJ,ecommerce,-,-,Women's Clothing,Women's Clothing,EUR,Brigitte,Brigitte,Brigitte Meyer,Brigitte Meyer,FEMALE,12,Meyer,Meyer,(empty),Saturday,5,brigitte@meyer-family.zzz,New York,North America,US,{ + \\"coordinates\\": [ -74, 40.8 ], - \\"\\"type\\"\\": \\"\\"Point\\"\\" -}\\",\\"New York\\",\\"Spherecords, Tigress Enterprises\\",\\"Spherecords, Tigress Enterprises\\",\\"Jul 12, 2019 @ 00:00:00.000\\",591709,\\"sold_product_591709_20734, sold_product_591709_7539\\",\\"sold_product_591709_20734, sold_product_591709_7539\\",\\"7.988, 33\\",\\"7.988, 33\\",\\"Women's Clothing, Women's Clothing\\",\\"Women's Clothing, Women's Clothing\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0\\",\\"0, 0\\",\\"Spherecords, Tigress Enterprises\\",\\"Spherecords, Tigress Enterprises\\",\\"3.6, 17.484\\",\\"7.988, 33\\",\\"20,734, 7,539\\",\\"Basic T-shirt - dark blue, Summer dress - scarab\\",\\"Basic T-shirt - dark blue, Summer dress - scarab\\",\\"1, 1\\",\\"ZO0638206382, ZO0038800388\\",\\"0, 0\\",\\"7.988, 33\\",\\"7.988, 33\\",\\"0, 0\\",\\"ZO0638206382, ZO0038800388\\",\\"40.969\\",\\"40.969\\",2,2,order,brigitte -KQMtOW0BH63Xcmy432LJ,ecommerce,\\"-\\",\\"-\\",\\"Men's Clothing\\",\\"Men's Clothing\\",EUR,Abd,Abd,\\"Abd Mccarthy\\",\\"Abd Mccarthy\\",MALE,52,Mccarthy,Mccarthy,,Saturday,5,\\"abd@mccarthy-family.zzz\\",Cairo,Africa,EG,\\"{ - \\"\\"coordinates\\"\\": [ + \\"type\\": \\"Point\\" +},New York,Spherecords, Tigress Enterprises,Spherecords, Tigress Enterprises,Jul 12, 2019 @ 00:00:00.000,591709,sold_product_591709_20734, sold_product_591709_7539,sold_product_591709_20734, sold_product_591709_7539,7.988, 33,7.988, 33,Women's Clothing, Women's Clothing,Women's Clothing, Women's Clothing,Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000,0, 0,0, 0,Spherecords, Tigress Enterprises,Spherecords, Tigress Enterprises,3.6, 17.484,7.988, 33,20,734, 7,539,Basic T-shirt - dark blue, Summer dress - scarab,Basic T-shirt - dark blue, Summer dress - scarab,1, 1,ZO0638206382, ZO0038800388,0, 0,7.988, 33,7.988, 33,0, 0,ZO0638206382, ZO0038800388,40.969,40.969,2,2,order,brigitte +KQMtOW0BH63Xcmy432LJ,ecommerce,-,-,Men's Clothing,Men's Clothing,EUR,Abd,Abd,Abd Mccarthy,Abd Mccarthy,MALE,52,Mccarthy,Mccarthy,(empty),Saturday,5,abd@mccarthy-family.zzz,Cairo,Africa,EG,{ + \\"coordinates\\": [ 31.3, 30.1 ], - \\"\\"type\\"\\": \\"\\"Point\\"\\" -}\\",\\"Cairo Governorate\\",\\"Oceanavigations, Elitelligence\\",\\"Oceanavigations, Elitelligence\\",\\"Jul 12, 2019 @ 00:00:00.000\\",590937,\\"sold_product_590937_14438, sold_product_590937_23607\\",\\"sold_product_590937_14438, sold_product_590937_23607\\",\\"28.984, 12.992\\",\\"28.984, 12.992\\",\\"Men's Clothing, Men's Clothing\\",\\"Men's Clothing, Men's Clothing\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0\\",\\"0, 0\\",\\"Oceanavigations, Elitelligence\\",\\"Oceanavigations, Elitelligence\\",\\"13.344, 6.109\\",\\"28.984, 12.992\\",\\"14,438, 23,607\\",\\"Jumper - dark grey multicolor, Print T-shirt - black\\",\\"Jumper - dark grey multicolor, Print T-shirt - black\\",\\"1, 1\\",\\"ZO0297602976, ZO0565605656\\",\\"0, 0\\",\\"28.984, 12.992\\",\\"28.984, 12.992\\",\\"0, 0\\",\\"ZO0297602976, ZO0565605656\\",\\"41.969\\",\\"41.969\\",2,2,order,abd + \\"type\\": \\"Point\\" +},Cairo Governorate,Oceanavigations, Elitelligence,Oceanavigations, Elitelligence,Jul 12, 2019 @ 00:00:00.000,590937,sold_product_590937_14438, sold_product_590937_23607,sold_product_590937_14438, sold_product_590937_23607,28.984, 12.992,28.984, 12.992,Men's Clothing, Men's Clothing,Men's Clothing, Men's Clothing,Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000,0, 0,0, 0,Oceanavigations, Elitelligence,Oceanavigations, Elitelligence,13.344, 6.109,28.984, 12.992,14,438, 23,607,Jumper - dark grey multicolor, Print T-shirt - black,Jumper - dark grey multicolor, Print T-shirt - black,1, 1,ZO0297602976, ZO0565605656,0, 0,28.984, 12.992,28.984, 12.992,0, 0,ZO0297602976, ZO0565605656,41.969,41.969,2,2,order,abd " `; exports[`Reporting APIs CSV Generation from SearchSource Exports CSV with almost all fields when using fieldsFromSource 1`] = ` -"\\"_id\\",\\"_index\\",\\"_score\\",\\"_type\\",category,currency,\\"customer_first_name\\",\\"customer_full_name\\",\\"customer_gender\\",\\"customer_id\\",\\"customer_last_name\\",\\"customer_phone\\",\\"day_of_week\\",\\"day_of_week_i\\",email,geoip,manufacturer,\\"order_date\\",\\"order_id\\",products,\\"products.created_on\\",sku,\\"taxful_total_price\\",\\"taxless_total_price\\",\\"total_quantity\\",\\"total_unique_products\\",type,user -3AMtOW0BH63Xcmy432DJ,ecommerce,\\"-\\",\\"-\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,\\"Sultan Al\\",\\"Sultan Al Boone\\",MALE,19,Boone,\\"-\\",Saturday,5,\\"sultan al@boone-family.zzz\\",\\"{\\"\\"city_name\\"\\":\\"\\"Abu Dhabi\\"\\",\\"\\"continent_name\\"\\":\\"\\"Asia\\"\\",\\"\\"country_iso_code\\"\\":\\"\\"AE\\"\\",\\"\\"location\\"\\":{\\"\\"lat\\"\\":24.5,\\"\\"lon\\"\\":54.4},\\"\\"region_name\\"\\":\\"\\"Abu Dhabi\\"\\"}\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"{\\"\\"_id\\"\\":\\"\\"sold_product_716724_23975\\"\\",\\"\\"base_price\\"\\":79.99,\\"\\"base_unit_price\\"\\":79.99,\\"\\"category\\"\\":\\"\\"Men's Shoes\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Angeldale\\"\\",\\"\\"min_price\\"\\":42.39,\\"\\"price\\"\\":79.99,\\"\\"product_id\\"\\":23975,\\"\\"product_name\\"\\":\\"\\"Winter boots - cognac\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0687606876\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":79.99,\\"\\"taxless_price\\"\\":79.99,\\"\\"unit_discount_amount\\"\\":0}, {\\"\\"_id\\"\\":\\"\\"sold_product_716724_6338\\"\\",\\"\\"base_price\\"\\":59.99,\\"\\"base_unit_price\\"\\":59.99,\\"\\"category\\"\\":\\"\\"Men's Clothing\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Oceanavigations\\"\\",\\"\\"min_price\\"\\":32.99,\\"\\"price\\"\\":59.99,\\"\\"product_id\\"\\":6338,\\"\\"product_name\\"\\":\\"\\"Trenchcoat - black\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0290502905\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":59.99,\\"\\"taxless_price\\"\\":59.99,\\"\\"unit_discount_amount\\"\\":0}, {\\"\\"_id\\"\\":\\"\\"sold_product_716724_14116\\"\\",\\"\\"base_price\\"\\":21.99,\\"\\"base_unit_price\\"\\":21.99,\\"\\"category\\"\\":\\"\\"Women's Accessories\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Microlutions\\"\\",\\"\\"min_price\\"\\":10.34,\\"\\"price\\"\\":21.99,\\"\\"product_id\\"\\":14116,\\"\\"product_name\\"\\":\\"\\"Watch - black\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0126701267\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":21.99,\\"\\"taxless_price\\"\\":21.99,\\"\\"unit_discount_amount\\"\\":0}, {\\"\\"_id\\"\\":\\"\\"sold_product_716724_15290\\"\\",\\"\\"base_price\\"\\":11.99,\\"\\"base_unit_price\\"\\":11.99,\\"\\"category\\"\\":\\"\\"Men's Accessories\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Oceanavigations\\"\\",\\"\\"min_price\\"\\":6.11,\\"\\"price\\"\\":11.99,\\"\\"product_id\\"\\":15290,\\"\\"product_name\\"\\":\\"\\"Hat - light grey multicolor\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0308503085\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":11.99,\\"\\"taxless_price\\"\\":11.99,\\"\\"unit_discount_amount\\"\\":0}\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",173.96,173.96,4,4,order,sultan -9gMtOW0BH63Xcmy432DJ,ecommerce,\\"-\\",\\"-\\",\\"Women's Shoes, Women's Clothing\\",EUR,Pia,\\"Pia Richards\\",FEMALE,45,Richards,\\"-\\",Saturday,5,\\"pia@richards-family.zzz\\",\\"{\\"\\"city_name\\"\\":\\"\\"Cannes\\"\\",\\"\\"continent_name\\"\\":\\"\\"Europe\\"\\",\\"\\"country_iso_code\\"\\":\\"\\"FR\\"\\",\\"\\"location\\"\\":{\\"\\"lat\\"\\":43.6,\\"\\"lon\\"\\":7},\\"\\"region_name\\"\\":\\"\\"Alpes-Maritimes\\"\\"}\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Jul 12, 2019 @ 00:00:00.000\\",591503,\\"{\\"\\"_id\\"\\":\\"\\"sold_product_591503_14761\\"\\",\\"\\"base_price\\"\\":20.99,\\"\\"base_unit_price\\"\\":20.99,\\"\\"category\\"\\":\\"\\"Women's Shoes\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Tigress Enterprises\\"\\",\\"\\"min_price\\"\\":10.7,\\"\\"price\\"\\":20.99,\\"\\"product_id\\"\\":14761,\\"\\"product_name\\"\\":\\"\\"Classic heels - blue\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0006400064\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":20.99,\\"\\"taxless_price\\"\\":20.99,\\"\\"unit_discount_amount\\"\\":0}, {\\"\\"_id\\"\\":\\"\\"sold_product_591503_11632\\"\\",\\"\\"base_price\\"\\":20.99,\\"\\"base_unit_price\\"\\":20.99,\\"\\"category\\"\\":\\"\\"Women's Clothing\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Pyramidustries\\"\\",\\"\\"min_price\\"\\":9.87,\\"\\"price\\"\\":20.99,\\"\\"product_id\\"\\":11632,\\"\\"product_name\\"\\":\\"\\"Summer dress - coral/pink\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0150601506\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":20.99,\\"\\"taxless_price\\"\\":20.99,\\"\\"unit_discount_amount\\"\\":0}\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0006400064, ZO0150601506\\",41.98,41.98,2,2,order,pia -BgMtOW0BH63Xcmy432LJ,ecommerce,\\"-\\",\\"-\\",\\"Women's Clothing\\",EUR,Brigitte,\\"Brigitte Meyer\\",FEMALE,12,Meyer,\\"-\\",Saturday,5,\\"brigitte@meyer-family.zzz\\",\\"{\\"\\"city_name\\"\\":\\"\\"New York\\"\\",\\"\\"continent_name\\"\\":\\"\\"North America\\"\\",\\"\\"country_iso_code\\"\\":\\"\\"US\\"\\",\\"\\"location\\"\\":{\\"\\"lat\\"\\":40.8,\\"\\"lon\\"\\":-74},\\"\\"region_name\\"\\":\\"\\"New York\\"\\"}\\",\\"Spherecords, Tigress Enterprises\\",\\"Jul 12, 2019 @ 00:00:00.000\\",591709,\\"{\\"\\"_id\\"\\":\\"\\"sold_product_591709_20734\\"\\",\\"\\"base_price\\"\\":7.99,\\"\\"base_unit_price\\"\\":7.99,\\"\\"category\\"\\":\\"\\"Women's Clothing\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Spherecords\\"\\",\\"\\"min_price\\"\\":3.6,\\"\\"price\\"\\":7.99,\\"\\"product_id\\"\\":20734,\\"\\"product_name\\"\\":\\"\\"Basic T-shirt - dark blue\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0638206382\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":7.99,\\"\\"taxless_price\\"\\":7.99,\\"\\"unit_discount_amount\\"\\":0}, {\\"\\"_id\\"\\":\\"\\"sold_product_591709_7539\\"\\",\\"\\"base_price\\"\\":32.99,\\"\\"base_unit_price\\"\\":32.99,\\"\\"category\\"\\":\\"\\"Women's Clothing\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Tigress Enterprises\\"\\",\\"\\"min_price\\"\\":17.48,\\"\\"price\\"\\":32.99,\\"\\"product_id\\"\\":7539,\\"\\"product_name\\"\\":\\"\\"Summer dress - scarab\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0038800388\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":32.99,\\"\\"taxless_price\\"\\":32.99,\\"\\"unit_discount_amount\\"\\":0}\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638206382, ZO0038800388\\",40.98,40.98,2,2,order,brigitte +"_id,_index,_score,_type,category,currency,customer_first_name,customer_full_name,customer_gender,customer_id,customer_last_name,customer_phone,day_of_week,day_of_week_i,email,geoip,manufacturer,order_date,order_id,products,products.created_on,sku,taxful_total_price,taxless_total_price,total_quantity,total_unique_products,type,user +3AMtOW0BH63Xcmy432DJ,ecommerce,-,-,Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories,EUR,Sultan Al,Sultan Al Boone,MALE,19,Boone,-,Saturday,5,sultan al@boone-family.zzz,{\\"city_name\\":\\"Abu Dhabi\\",\\"continent_name\\":\\"Asia\\",\\"country_iso_code\\":\\"AE\\",\\"location\\":{\\"lat\\":24.5,\\"lon\\":54.4},\\"region_name\\":\\"Abu Dhabi\\"},Angeldale, Oceanavigations, Microlutions,Jul 12, 2019 @ 00:00:00.000,716724,{\\"_id\\":\\"sold_product_716724_23975\\",\\"base_price\\":79.99,\\"base_unit_price\\":79.99,\\"category\\":\\"Men's Shoes\\",\\"created_on\\":\\"2016-12-31T00:00:00+00:00\\",\\"discount_amount\\":0,\\"discount_percentage\\":0,\\"manufacturer\\":\\"Angeldale\\",\\"min_price\\":42.39,\\"price\\":79.99,\\"product_id\\":23975,\\"product_name\\":\\"Winter boots - cognac\\",\\"quantity\\":1,\\"sku\\":\\"ZO0687606876\\",\\"tax_amount\\":0,\\"taxful_price\\":79.99,\\"taxless_price\\":79.99,\\"unit_discount_amount\\":0}, {\\"_id\\":\\"sold_product_716724_6338\\",\\"base_price\\":59.99,\\"base_unit_price\\":59.99,\\"category\\":\\"Men's Clothing\\",\\"created_on\\":\\"2016-12-31T00:00:00+00:00\\",\\"discount_amount\\":0,\\"discount_percentage\\":0,\\"manufacturer\\":\\"Oceanavigations\\",\\"min_price\\":32.99,\\"price\\":59.99,\\"product_id\\":6338,\\"product_name\\":\\"Trenchcoat - black\\",\\"quantity\\":1,\\"sku\\":\\"ZO0290502905\\",\\"tax_amount\\":0,\\"taxful_price\\":59.99,\\"taxless_price\\":59.99,\\"unit_discount_amount\\":0}, {\\"_id\\":\\"sold_product_716724_14116\\",\\"base_price\\":21.99,\\"base_unit_price\\":21.99,\\"category\\":\\"Women's Accessories\\",\\"created_on\\":\\"2016-12-31T00:00:00+00:00\\",\\"discount_amount\\":0,\\"discount_percentage\\":0,\\"manufacturer\\":\\"Microlutions\\",\\"min_price\\":10.34,\\"price\\":21.99,\\"product_id\\":14116,\\"product_name\\":\\"Watch - black\\",\\"quantity\\":1,\\"sku\\":\\"ZO0126701267\\",\\"tax_amount\\":0,\\"taxful_price\\":21.99,\\"taxless_price\\":21.99,\\"unit_discount_amount\\":0}, {\\"_id\\":\\"sold_product_716724_15290\\",\\"base_price\\":11.99,\\"base_unit_price\\":11.99,\\"category\\":\\"Men's Accessories\\",\\"created_on\\":\\"2016-12-31T00:00:00+00:00\\",\\"discount_amount\\":0,\\"discount_percentage\\":0,\\"manufacturer\\":\\"Oceanavigations\\",\\"min_price\\":6.11,\\"price\\":11.99,\\"product_id\\":15290,\\"product_name\\":\\"Hat - light grey multicolor\\",\\"quantity\\":1,\\"sku\\":\\"ZO0308503085\\",\\"tax_amount\\":0,\\"taxful_price\\":11.99,\\"taxless_price\\":11.99,\\"unit_discount_amount\\":0},Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000,ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085,173.96,173.96,4,4,order,sultan +9gMtOW0BH63Xcmy432DJ,ecommerce,-,-,Women's Shoes, Women's Clothing,EUR,Pia,Pia Richards,FEMALE,45,Richards,-,Saturday,5,pia@richards-family.zzz,{\\"city_name\\":\\"Cannes\\",\\"continent_name\\":\\"Europe\\",\\"country_iso_code\\":\\"FR\\",\\"location\\":{\\"lat\\":43.6,\\"lon\\":7},\\"region_name\\":\\"Alpes-Maritimes\\"},Tigress Enterprises, Pyramidustries,Jul 12, 2019 @ 00:00:00.000,591503,{\\"_id\\":\\"sold_product_591503_14761\\",\\"base_price\\":20.99,\\"base_unit_price\\":20.99,\\"category\\":\\"Women's Shoes\\",\\"created_on\\":\\"2016-12-31T00:00:00+00:00\\",\\"discount_amount\\":0,\\"discount_percentage\\":0,\\"manufacturer\\":\\"Tigress Enterprises\\",\\"min_price\\":10.7,\\"price\\":20.99,\\"product_id\\":14761,\\"product_name\\":\\"Classic heels - blue\\",\\"quantity\\":1,\\"sku\\":\\"ZO0006400064\\",\\"tax_amount\\":0,\\"taxful_price\\":20.99,\\"taxless_price\\":20.99,\\"unit_discount_amount\\":0}, {\\"_id\\":\\"sold_product_591503_11632\\",\\"base_price\\":20.99,\\"base_unit_price\\":20.99,\\"category\\":\\"Women's Clothing\\",\\"created_on\\":\\"2016-12-31T00:00:00+00:00\\",\\"discount_amount\\":0,\\"discount_percentage\\":0,\\"manufacturer\\":\\"Pyramidustries\\",\\"min_price\\":9.87,\\"price\\":20.99,\\"product_id\\":11632,\\"product_name\\":\\"Summer dress - coral/pink\\",\\"quantity\\":1,\\"sku\\":\\"ZO0150601506\\",\\"tax_amount\\":0,\\"taxful_price\\":20.99,\\"taxless_price\\":20.99,\\"unit_discount_amount\\":0},Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000,ZO0006400064, ZO0150601506,41.98,41.98,2,2,order,pia +BgMtOW0BH63Xcmy432LJ,ecommerce,-,-,Women's Clothing,EUR,Brigitte,Brigitte Meyer,FEMALE,12,Meyer,-,Saturday,5,brigitte@meyer-family.zzz,{\\"city_name\\":\\"New York\\",\\"continent_name\\":\\"North America\\",\\"country_iso_code\\":\\"US\\",\\"location\\":{\\"lat\\":40.8,\\"lon\\":-74},\\"region_name\\":\\"New York\\"},Spherecords, Tigress Enterprises,Jul 12, 2019 @ 00:00:00.000,591709,{\\"_id\\":\\"sold_product_591709_20734\\",\\"base_price\\":7.99,\\"base_unit_price\\":7.99,\\"category\\":\\"Women's Clothing\\",\\"created_on\\":\\"2016-12-31T00:00:00+00:00\\",\\"discount_amount\\":0,\\"discount_percentage\\":0,\\"manufacturer\\":\\"Spherecords\\",\\"min_price\\":3.6,\\"price\\":7.99,\\"product_id\\":20734,\\"product_name\\":\\"Basic T-shirt - dark blue\\",\\"quantity\\":1,\\"sku\\":\\"ZO0638206382\\",\\"tax_amount\\":0,\\"taxful_price\\":7.99,\\"taxless_price\\":7.99,\\"unit_discount_amount\\":0}, {\\"_id\\":\\"sold_product_591709_7539\\",\\"base_price\\":32.99,\\"base_unit_price\\":32.99,\\"category\\":\\"Women's Clothing\\",\\"created_on\\":\\"2016-12-31T00:00:00+00:00\\",\\"discount_amount\\":0,\\"discount_percentage\\":0,\\"manufacturer\\":\\"Tigress Enterprises\\",\\"min_price\\":17.48,\\"price\\":32.99,\\"product_id\\":7539,\\"product_name\\":\\"Summer dress - scarab\\",\\"quantity\\":1,\\"sku\\":\\"ZO0038800388\\",\\"tax_amount\\":0,\\"taxful_price\\":32.99,\\"taxless_price\\":32.99,\\"unit_discount_amount\\":0},Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000,ZO0638206382, ZO0038800388,40.98,40.98,2,2,order,brigitte " `; -exports[`Reporting APIs CSV Generation from SearchSource Logs the error explanation if the search query returns an error 1`] = `"{\\"statusCode\\":500,\\"error\\":\\"Internal Server Error\\",\\"message\\":\\"An internal server error occurred.\\"}"`; - exports[`Reporting APIs CSV Generation from SearchSource date formatting Formatted date_nanos data, UTC timezone 1`] = ` "date,message \\"Jan 1, 2015 @ 12:10:30.123456789\\",\\"Hello 2\\" diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/download_csv_dashboard.ts b/x-pack/test/reporting_api_integration/reporting_and_security/download_csv_dashboard.ts index 2a29d6c45dbe1..220fe29d5a6e7 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/download_csv_dashboard.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/download_csv_dashboard.ts @@ -31,8 +31,7 @@ export default function ({ getService }: FtrProviderContext) { }, }; - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/99915 - describe.skip('CSV Generation from SearchSource', () => { + describe('CSV Generation from SearchSource', () => { before(async () => { await kibanaServer.uiSettings.update({ 'csv:quoteValues': false, @@ -188,42 +187,6 @@ export default function ({ getService }: FtrProviderContext) { expectSnapshot(resText).toMatch(); }); - it('Logs the error explanation if the search query returns an error', async () => { - const { status: resStatus, text: resText } = (await generateAPI.getCSVFromSearchSource( - getMockJobParams({ - searchSource: { - query: { query: '', language: 'kuery' }, - index: '5193f870-d861-11e9-a311-0fa548c5f953', - sort: [{ order_date: 'desc' }], - fields: ['order_date', 'products'], // products is a non-leaf field - filter: [], - parent: { - query: { language: 'kuery', query: '' }, - filter: [], - parent: { - filter: [ - { - meta: { index: '5193f870-d861-11e9-a311-0fa548c5f953', params: {} }, - range: { - order_date: { - gte: '2019-03-23T03:06:17.785Z', - lte: '2019-10-04T02:33:16.708Z', - format: 'strict_date_optional_time', - }, - }, - }, - ], - }, - }, - }, - browserTimezone: 'UTC', - title: 'testfooyu78yt90-', - }) - )) as supertest.Response; - expect(resStatus).to.eql(500); - expectSnapshot(resText).toMatch(); - }); - describe('date formatting', () => { before(async () => { // load test data that contains a saved search and documents diff --git a/yarn.lock b/yarn.lock index d9088ef7e6240..0fc2e306c4229 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2783,6 +2783,10 @@ version "0.0.0" uid "" +"@kbn/es-query@link:bazel-bin/packages/kbn-es-query": + version "0.0.0" + uid "" + "@kbn/es@link:bazel-bin/packages/kbn-es": version "0.0.0" uid "" @@ -7095,7 +7099,7 @@ any-observable@^0.3.0: resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== -any-promise@^1.0.0: +any-promise@^1.0.0, any-promise@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= @@ -8959,7 +8963,7 @@ bytes@3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= -bytes@3.1.0: +bytes@3.1.0, bytes@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== @@ -10617,21 +10621,6 @@ css-loader@^3.4.2, css-loader@^3.5.3: schema-utils "^2.7.0" semver "^6.3.0" -css-minimizer-webpack-plugin@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-1.3.0.tgz#d867b4a54ca9920125b30263505e8cca72bc8cf1" - integrity sha512-jFa0Siplmfef4ndKglpVaduY47oHQwioAOEGK0f0vAX0s+vc+SmP6cCMoc+8Adau5600RnOEld5VVdC8CQau7w== - dependencies: - cacache "^15.0.5" - cssnano "^4.1.10" - find-cache-dir "^3.3.1" - jest-worker "^26.3.0" - p-limit "^3.0.2" - schema-utils "^3.0.0" - serialize-javascript "^5.0.1" - source-map "^0.6.1" - webpack-sources "^1.4.3" - css-select-base-adapter@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" @@ -10747,10 +10736,10 @@ cssfontparser@^1.2.1: resolved "https://registry.yarnpkg.com/cssfontparser/-/cssfontparser-1.2.1.tgz#f4022fc8f9700c68029d542084afbaf425a3f3e3" integrity sha1-9AIvyPlwDGgCnVQghK+69CWj8+M= -cssnano-preset-default@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" - integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== +cssnano-preset-default@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz#920622b1fc1e95a34e8838203f1397a504f2d3ff" + integrity sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ== dependencies: css-declaration-sorter "^4.0.1" cssnano-util-raw-cache "^4.0.1" @@ -10780,7 +10769,7 @@ cssnano-preset-default@^4.0.7: postcss-ordered-values "^4.1.2" postcss-reduce-initial "^4.0.3" postcss-reduce-transforms "^4.0.2" - postcss-svgo "^4.0.2" + postcss-svgo "^4.0.3" postcss-unique-selectors "^4.0.1" cssnano-util-get-arguments@^4.0.0: @@ -10805,13 +10794,13 @@ cssnano-util-same-parent@^4.0.0: resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== -cssnano@^4.1.10: - version "4.1.10" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" - integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== +cssnano@^4.1.11: + version "4.1.11" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.11.tgz#c7b5f5b81da269cb1fd982cb960c1200910c9a99" + integrity sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g== dependencies: cosmiconfig "^5.0.0" - cssnano-preset-default "^4.0.7" + cssnano-preset-default "^4.0.8" is-resolvable "^1.0.0" postcss "^7.0.0" @@ -15124,6 +15113,14 @@ gulp-babel@^8.0.0: through2 "^2.0.0" vinyl-sourcemaps-apply "^0.2.0" +gulp-brotli@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/gulp-brotli/-/gulp-brotli-3.0.0.tgz#7f5a1d8a6d43cab28056f9e56f29ae071dcfe4b4" + integrity sha512-AkGR+FRn4Nrf9Ocx8/WXgNNX+owk4FN4xU4kRrso41DWWd9v1KXO8EyG1cGcwt4364Ax68b7ANcP9Em+/RM2OQ== + dependencies: + plugin-error "^1.0.1" + through2 "^3.0.1" + gulp-cli@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.2.0.tgz#5533126eeb7fe415a7e3e84a297d334d5cf70ebc" @@ -15148,6 +15145,29 @@ gulp-cli@^2.2.0: v8flags "^3.0.1" yargs "^7.1.0" +gulp-gzip@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/gulp-gzip/-/gulp-gzip-1.4.2.tgz#0422a94014248655b5b1a9eea1c2abee1d4f4337" + integrity sha512-ZIxfkUwk2XmZPTT9pPHrHUQlZMyp9nPhg2sfoeN27mBGpi7OaHnOD+WCN41NXjfJQ69lV1nQ9LLm1hYxx4h3UQ== + dependencies: + ansi-colors "^1.0.1" + bytes "^3.0.0" + fancy-log "^1.3.2" + plugin-error "^1.0.0" + stream-to-array "^2.3.0" + through2 "^2.0.3" + +gulp-postcss@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/gulp-postcss/-/gulp-postcss-8.0.0.tgz#8d3772cd4d27bca55ec8cb4c8e576e3bde4dc550" + integrity sha512-Wtl6vH7a+8IS/fU5W9IbOpcaLqKxd5L1DUOzaPmlnCbX1CrG0aWdwVnC3Spn8th0m8D59YbysV5zPUe1n/GJYg== + dependencies: + fancy-log "^1.3.2" + plugin-error "^1.0.1" + postcss "^7.0.2" + postcss-load-config "^2.0.0" + vinyl-sourcemaps-apply "^0.2.1" + gulp-sourcemaps@2.6.5: version "2.6.5" resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz#a3f002d87346d2c0f3aec36af7eb873f23de8ae6" @@ -15165,6 +15185,16 @@ gulp-sourcemaps@2.6.5: strip-bom-string "1.X" through2 "2.X" +gulp-terser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/gulp-terser/-/gulp-terser-2.0.1.tgz#5f8f4fc54588b79519243809cc8eef4936286d0d" + integrity sha512-XCrnCXP8ovNpgLK9McJIXlgm0j3W2TsiWu7K9y3m+Sn5XZgUzi6U8MPHtS3NdLMic9poCj695N0ARJ2B6atypw== + dependencies: + plugin-error "^1.0.1" + terser "5.4.0" + through2 "^4.0.2" + vinyl-sourcemaps-apply "^0.2.1" + gulp-zip@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/gulp-zip/-/gulp-zip-5.0.2.tgz#2edf797ec842e770f4dfde8bef97d139015b1972" @@ -17629,7 +17659,7 @@ jest-worker@^25.4.0: merge-stream "^2.0.0" supports-color "^7.0.0" -jest-worker@^26.2.1, jest-worker@^26.3.0, jest-worker@^26.6.2: +jest-worker@^26.2.1, jest-worker@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== @@ -21530,10 +21560,10 @@ pdfmake@^0.1.65: pdfkit "^0.11.0" svg-to-pdfkit "^0.1.8" -peggy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/peggy/-/peggy-1.0.0.tgz#df6c7816c9df0ef35e071aaf96836cb866fe7eb4" - integrity sha512-lH12sxAXj4Aug+vH6IGoByIQOREIlhH+x4Uzb9kce6DD8wcGeidkC0JYEOwHormKrLt5BFLTbR4PuD/tiMOirQ== +peggy@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/peggy/-/peggy-1.2.0.tgz#657ba45900cbef1dc9f52356704bdbb193c2021c" + integrity sha512-PQ+NKpAobImfMprYQtc4Egmyi29bidRGEX0kKjCU5uuW09s0Cthwqhfy7mLkwcB4VcgacE5L/ZjruD/kOPCUUw== pegjs@0.10.0: version "0.10.0" @@ -21690,7 +21720,7 @@ platform@^1.3.0: resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.5.tgz#fb6958c696e07e2918d2eeda0f0bc9448d733444" integrity sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q== -plugin-error@^1.0.1: +plugin-error@^1.0.0, plugin-error@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" integrity sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA== @@ -22144,7 +22174,7 @@ postcss-selector-parser@^6.0.4: uniq "^1.0.1" util-deprecate "^1.0.2" -postcss-svgo@^4.0.2: +postcss-svgo@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.3.tgz#343a2cdbac9505d416243d496f724f38894c941e" integrity sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw== @@ -23680,7 +23710,7 @@ readable-stream@1.0, "readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0 isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@3, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -25018,7 +25048,7 @@ serialize-error@^2.1.0: resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go= -serialize-javascript@5.0.1, serialize-javascript@^5.0.1: +serialize-javascript@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== @@ -25459,7 +25489,7 @@ source-map-support@^0.3.2: dependencies: source-map "0.1.32" -source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@~0.5.12: +source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@~0.5.12, source-map-support@~0.5.19: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -25501,7 +25531,7 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.2, source-map@^0.7.3: +source-map@^0.7.2, source-map@^0.7.3, source-map@~0.7.2: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== @@ -25941,6 +25971,13 @@ stream-splicer@^2.0.0: inherits "^2.0.1" readable-stream "^2.0.2" +stream-to-array@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353" + integrity sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M= + dependencies: + any-promise "^1.1.0" + stream-to-async-iterator@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/stream-to-async-iterator/-/stream-to-async-iterator-0.2.0.tgz#bef5c885e9524f98b2fa5effecc357bd58483780" @@ -26819,6 +26856,15 @@ terser-webpack-plugin@^3.0.0: terser "^4.8.0" webpack-sources "^1.4.3" +terser@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.4.0.tgz#9815c0839072d5c894e22c6fc508fbe9f5e7d7e8" + integrity sha512-3dZunFLbCJis9TAF2VnX+VrQLctRUmt1p3W2kCsJuZE4ZgWqh//+1MZ62EanewrqKoUf4zIaDGZAvml4UDc0OQ== + dependencies: + commander "^2.20.0" + source-map "~0.7.2" + source-map-support "~0.5.19" + terser@^4.1.2, terser@^4.6.12, terser@^4.6.3, terser@^4.8.0: version "4.8.0" resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" @@ -26828,6 +26874,15 @@ terser@^4.1.2, terser@^4.6.12, terser@^4.6.3, terser@^4.8.0: source-map "~0.6.1" source-map-support "~0.5.12" +terser@^5.7.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.1.tgz#2dc7a61009b66bb638305cb2a824763b116bf784" + integrity sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg== + dependencies: + commander "^2.20.0" + source-map "~0.7.2" + source-map-support "~0.5.19" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -26912,6 +26967,13 @@ through2@^3.0.1: dependencies: readable-stream "2 || 3" +through2@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" + integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== + dependencies: + readable-stream "3" + "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8, through@~2.3.4, through@~2.3.6, through@~2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -28774,7 +28836,7 @@ vinyl-sourcemap@^1.1.0: remove-bom-buffer "^3.0.0" vinyl "^2.0.0" -vinyl-sourcemaps-apply@^0.2.0: +vinyl-sourcemaps-apply@^0.2.0, vinyl-sourcemaps-apply@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" integrity sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=