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 c25cd70e99b4f..e16db4415f248 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 @@ -15,6 +15,6 @@ esKuery: { nodeTypes: import("@kbn/es-query/target_types/kuery/node_types").NodeTypes; fromKueryExpression: (expression: string | import("@elastic/elasticsearch/api/types").QueryDslQueryContainer, 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/utility-types").JsonObject; + toElasticsearchQuery: (node: import("@kbn/es-query").KueryNode, indexPattern?: import("@kbn/es-query").IndexPatternBase | undefined, config?: import("@kbn/es-query").KueryQueryOptions | undefined, context?: Record | undefined) => import("@elastic/elasticsearch/api/types").QueryDslQueryContainer; } ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.md index c7046902dac72..73261cd49d6d2 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.md @@ -21,4 +21,5 @@ export interface IKibanaSearchResponse | [loaded](./kibana-plugin-plugins-data-public.ikibanasearchresponse.loaded.md) | number | If relevant to the search strategy, return a loaded number that represents how progress is indicated. | | [rawResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.rawresponse.md) | RawResponse | The raw response returned by the internal search method (usually the raw ES response) | | [total](./kibana-plugin-plugins-data-public.ikibanasearchresponse.total.md) | number | If relevant to the search strategy, return a total number that represents how progress is indicated. | +| [warning](./kibana-plugin-plugins-data-public.ikibanasearchresponse.warning.md) | string | Optional warnings that should be surfaced to the end user | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.warning.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.warning.md new file mode 100644 index 0000000000000..cc0b8e2bea56e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.warning.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IKibanaSearchResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.md) > [warning](./kibana-plugin-plugins-data-public.ikibanasearchresponse.warning.md) + +## IKibanaSearchResponse.warning property + +Optional warnings that should be surfaced to the end user + +Signature: + +```typescript +warning?: string; +``` 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 620a547d30245..d4365550c2a38 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 @@ -10,6 +10,6 @@ esKuery: { nodeTypes: import("@kbn/es-query/target_types/kuery/node_types").NodeTypes; fromKueryExpression: (expression: string | import("@elastic/elasticsearch/api/types").QueryDslQueryContainer, 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/utility-types").JsonObject; + toElasticsearchQuery: (node: import("@kbn/es-query").KueryNode, indexPattern?: import("@kbn/es-query").IndexPatternBase | undefined, config?: import("@kbn/es-query").KueryQueryOptions | undefined, context?: Record | undefined) => import("@elastic/elasticsearch/api/types").QueryDslQueryContainer; } ``` diff --git a/examples/search_examples/public/search/app.tsx b/examples/search_examples/public/search/app.tsx index 06f9426b4965c..bfb41160ae963 100644 --- a/examples/search_examples/public/search/app.tsx +++ b/examples/search_examples/public/search/app.tsx @@ -131,12 +131,46 @@ export const SearchExamplesApp = ({ setSelectedNumericField(fields?.length ? getNumeric(fields)[0] : null); }, [fields]); - const doAsyncSearch = async (strategy?: string, sessionId?: string) => { + const doAsyncSearch = async ( + strategy?: string, + sessionId?: string, + addWarning: boolean = false, + addError: boolean = false + ) => { if (!indexPattern || !selectedNumericField) return; // Construct the query portion of the search request const query = data.query.getEsQuery(indexPattern); + if (addWarning) { + query.bool.must.push({ + // @ts-ignore + error_query: { + indices: [ + { + name: indexPattern.title, + error_type: 'warning', + message: 'Watch out!', + }, + ], + }, + }); + } + if (addError) { + query.bool.must.push({ + // @ts-ignore + error_query: { + indices: [ + { + name: indexPattern.title, + error_type: 'exception', + message: 'Watch out!', + }, + ], + }, + }); + } + // Construct the aggregations portion of the search request by using the `data.search.aggs` service. const aggs = [{ type: 'avg', params: { field: selectedNumericField!.name } }]; const aggsDsl = data.search.aggs.createAggConfigs(indexPattern, aggs).toDsl(); @@ -193,14 +227,23 @@ export const SearchExamplesApp = ({ } ); searchSubscription$.unsubscribe(); + if (res.warning) { + notifications.toasts.addWarning({ + title: 'Warning', + text: mountReactNode(res.warning), + }); + } } else if (isErrorResponse(res)) { // TODO: Make response error status clearer - notifications.toasts.addWarning('An error has occurred'); + notifications.toasts.addDanger('An error has occurred'); searchSubscription$.unsubscribe(); } }, - error: () => { - notifications.toasts.addDanger('Failed to run search'); + error: (e) => { + notifications.toasts.addDanger({ + title: 'Failed to run search', + text: e.message, + }); }, }); }; @@ -270,6 +313,14 @@ export const SearchExamplesApp = ({ doAsyncSearch('myStrategy'); }; + const onWarningSearchClickHandler = () => { + doAsyncSearch(undefined, undefined, true); + }; + + const onErrorSearchClickHandler = () => { + doAsyncSearch(undefined, undefined, false, true); + }; + const onPartialResultsClickHandler = () => { setSelectedTab(1); const req = { @@ -299,8 +350,11 @@ export const SearchExamplesApp = ({ searchSubscription$.unsubscribe(); } }, - error: () => { - notifications.toasts.addDanger('Failed to run search'); + error: (e) => { + notifications.toasts.addDanger({ + title: 'Failed to run search', + text: e.message, + }); }, }); }; @@ -530,6 +584,38 @@ export const SearchExamplesApp = ({ + +

Handling errors & warnings

+
+ + When fetching data from Elasticsearch, there are several different ways warnings and + errors may be returned. In general, it is recommended to surface these in the UX. + + + + + + + + + +

Handling partial results

diff --git a/package.json b/package.json index 950a916b2419f..4fdb713e37a12 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "@elastic/charts": "33.2.2", "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.14", - "@elastic/ems-client": "7.14.0", + "@elastic/ems-client": "7.15.0", "@elastic/eui": "37.1.1", "@elastic/filesaver": "1.1.2", "@elastic/good": "^9.0.1-kibana3", @@ -632,7 +632,6 @@ "@types/strong-log-transformer": "^1.0.0", "@types/styled-components": "^5.1.0", "@types/supertest": "^2.0.5", - "@types/supertest-as-promised": "^2.0.38", "@types/tapable": "^1.0.6", "@types/tar": "^4.0.3", "@types/tar-fs": "^1.16.1", @@ -824,7 +823,6 @@ "stylelint-scss": "^3.18.0", "superagent": "^3.8.2", "supertest": "^3.1.0", - "supertest-as-promised": "^4.0.2", "supports-color": "^7.0.0", "tape": "^5.0.1", "tar-fs": "^2.1.0", diff --git a/packages/kbn-es-query/src/es_query/from_kuery.ts b/packages/kbn-es-query/src/es_query/from_kuery.ts index 949f9691e9e6d..151ea6a89bb5e 100644 --- a/packages/kbn-es-query/src/es_query/from_kuery.ts +++ b/packages/kbn-es-query/src/es_query/from_kuery.ts @@ -30,7 +30,7 @@ function buildQuery( indexPattern: IndexPatternBase | undefined, queryASTs: KueryNode[], config: SerializableRecord = {} -) { +): BoolQuery { const compoundQueryAST = nodeTypes.function.buildNode('and', queryASTs); const kueryQuery = toElasticsearchQuery(compoundQueryAST, indexPattern, config); diff --git a/packages/kbn-es-query/src/kuery/ast/ast.ts b/packages/kbn-es-query/src/kuery/ast/ast.ts index 826fa194f1b30..7145eb5043c60 100644 --- a/packages/kbn-es-query/src/kuery/ast/ast.ts +++ b/packages/kbn-es-query/src/kuery/ast/ast.ts @@ -10,7 +10,7 @@ import { JsonObject } from '@kbn/utility-types'; import { estypes } from '@elastic/elasticsearch'; import { nodeTypes } from '../node_types/index'; import { KQLSyntaxError } from '../kuery_syntax_error'; -import { KueryNode, KueryParseOptions } from '../types'; +import { KueryNode, KueryParseOptions, KueryQueryOptions } from '../types'; import { parse as parseKuery } from '../grammar'; import { IndexPatternBase } from '../..'; @@ -62,18 +62,19 @@ export const fromKueryExpression = ( * * IndexPattern isn't required, but if you pass one in, we can be more intelligent * about how we craft the queries (e.g. scripted fields) + * */ export const toElasticsearchQuery = ( node: KueryNode, indexPattern?: IndexPatternBase, - config?: Record, + config: KueryQueryOptions = {}, context?: Record ): JsonObject => { if (!node || !node.type || !nodeTypes[node.type]) { return toElasticsearchQuery(nodeTypes.function.buildNode('and', []), indexPattern); } + // TODO: the return type of this function might be incorrect and it works only because of this casting const nodeType = (nodeTypes[node.type] as unknown) as any; - return nodeType.toElasticsearchQuery(node, indexPattern, config, context); }; diff --git a/packages/kbn-es-query/src/kuery/functions/exists.ts b/packages/kbn-es-query/src/kuery/functions/exists.ts index 0e05ade5181db..d1d0cb7835bca 100644 --- a/packages/kbn-es-query/src/kuery/functions/exists.ts +++ b/packages/kbn-es-query/src/kuery/functions/exists.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import { IndexPatternFieldBase, IndexPatternBase, KueryNode } from '../..'; +import { estypes } from '@elastic/elasticsearch'; +import { IndexPatternFieldBase, IndexPatternBase, KueryNode, KueryQueryOptions } from '../..'; import * as literal from '../node_types/literal'; export function buildNodeParams(fieldName: string) { @@ -18,9 +19,9 @@ export function buildNodeParams(fieldName: string) { export function toElasticsearchQuery( node: KueryNode, indexPattern?: IndexPatternBase, - config: Record = {}, + config: KueryQueryOptions = {}, context: Record = {} -) { +): estypes.QueryDslQueryContainer { const { arguments: [fieldNameArg], } = node; @@ -28,7 +29,7 @@ export function toElasticsearchQuery( ...fieldNameArg, value: context?.nested ? `${context.nested.path}.${fieldNameArg.value}` : fieldNameArg.value, }; - const fieldName = literal.toElasticsearchQuery(fullFieldNameArg); + const fieldName = literal.toElasticsearchQuery(fullFieldNameArg) as string; const field = indexPattern?.fields?.find((fld: IndexPatternFieldBase) => fld.name === fieldName); if (field?.scripted) { diff --git a/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts b/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts index 028c5e39bf5da..cfa496fb5a2a0 100644 --- a/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts @@ -12,6 +12,7 @@ import { fields } from '../../filters/stubs'; import { IndexPatternBase } from '../..'; import * as geoBoundingBox from './geo_bounding_box'; +import { JsonObject } from '@kbn/utility-types'; jest.mock('../grammar'); @@ -78,8 +79,14 @@ describe('kuery functions', () => { const result = geoBoundingBox.toElasticsearchQuery(node, indexPattern); expect(result).toHaveProperty('geo_bounding_box'); - expect(result.geo_bounding_box.geo).toHaveProperty('top_left', '73.12, -174.37'); - expect(result.geo_bounding_box.geo).toHaveProperty('bottom_right', '50.73, -135.35'); + expect((result.geo_bounding_box as JsonObject).geo).toHaveProperty( + 'top_left', + '73.12, -174.37' + ); + expect((result.geo_bounding_box as JsonObject).geo).toHaveProperty( + 'bottom_right', + '50.73, -135.35' + ); }); test('should return an ES geo_bounding_box query without an index pattern', () => { @@ -87,15 +94,21 @@ describe('kuery functions', () => { const result = geoBoundingBox.toElasticsearchQuery(node); expect(result).toHaveProperty('geo_bounding_box'); - expect(result.geo_bounding_box.geo).toHaveProperty('top_left', '73.12, -174.37'); - expect(result.geo_bounding_box.geo).toHaveProperty('bottom_right', '50.73, -135.35'); + expect((result.geo_bounding_box as JsonObject).geo).toHaveProperty( + 'top_left', + '73.12, -174.37' + ); + expect((result.geo_bounding_box as JsonObject).geo).toHaveProperty( + 'bottom_right', + '50.73, -135.35' + ); }); test('should use the ignore_unmapped parameter', () => { const node = nodeTypes.function.buildNode('geoBoundingBox', 'geo', params); const result = geoBoundingBox.toElasticsearchQuery(node, indexPattern); - expect(result.geo_bounding_box.ignore_unmapped).toBe(true); + expect(result.geo_bounding_box!.ignore_unmapped).toBe(true); }); test('should throw an error for scripted fields', () => { @@ -116,7 +129,7 @@ describe('kuery functions', () => { ); expect(result).toHaveProperty('geo_bounding_box'); - expect(result.geo_bounding_box['nestedField.geo']).toBeDefined(); + expect((result.geo_bounding_box as JsonObject)['nestedField.geo']).toBeDefined(); }); }); }); diff --git a/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.ts b/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.ts index b1fd8680af604..6a44eed1d7ec9 100644 --- a/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.ts +++ b/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.ts @@ -7,9 +7,10 @@ */ import _ from 'lodash'; +import { estypes } from '@elastic/elasticsearch'; import { nodeTypes } from '../node_types'; import * as ast from '../ast'; -import { IndexPatternBase, KueryNode, LatLon } from '../..'; +import { IndexPatternBase, KueryNode, KueryQueryOptions, LatLon } from '../..'; export function buildNodeParams(fieldName: string, params: any) { params = _.pick(params, 'topLeft', 'bottomRight'); @@ -27,9 +28,9 @@ export function buildNodeParams(fieldName: string, params: any) { export function toElasticsearchQuery( node: KueryNode, indexPattern?: IndexPatternBase, - config: Record = {}, + config: KueryQueryOptions = {}, context: Record = {} -) { +): estypes.QueryDslQueryContainer { const [fieldNameArg, ...args] = node.arguments; const fullFieldNameArg = { ...fieldNameArg, diff --git a/packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts b/packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts index f16ca378866f0..2d1d6ed6cd6de 100644 --- a/packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts @@ -79,9 +79,9 @@ describe('kuery functions', () => { const result = geoPolygon.toElasticsearchQuery(node, indexPattern); expect(result).toHaveProperty('geo_polygon'); - expect(result.geo_polygon.geo).toHaveProperty('points'); + expect((result.geo_polygon as any).geo).toHaveProperty('points'); - (result.geo_polygon.geo as any).points.forEach((point: any, index: number) => { + (result.geo_polygon as any).geo.points.forEach((point: any, index: number) => { const expectedLatLon = `${points[index].lat}, ${points[index].lon}`; expect(point).toBe(expectedLatLon); @@ -93,9 +93,9 @@ describe('kuery functions', () => { const result = geoPolygon.toElasticsearchQuery(node); expect(result).toHaveProperty('geo_polygon'); - expect(result.geo_polygon.geo).toHaveProperty('points'); + expect((result.geo_polygon as any).geo).toHaveProperty('points'); - (result.geo_polygon.geo as any).points.forEach((point: any, index: number) => { + (result.geo_polygon as any).geo.points.forEach((point: any, index: number) => { const expectedLatLon = `${points[index].lat}, ${points[index].lon}`; expect(point).toBe(expectedLatLon); @@ -106,7 +106,7 @@ describe('kuery functions', () => { const node = nodeTypes.function.buildNode('geoPolygon', 'geo', points); const result = geoPolygon.toElasticsearchQuery(node, indexPattern); - expect(result.geo_polygon.ignore_unmapped).toBe(true); + expect((result.geo_polygon as any).ignore_unmapped).toBe(true); }); test('should throw an error for scripted fields', () => { @@ -126,7 +126,7 @@ describe('kuery functions', () => { ); expect(result).toHaveProperty('geo_polygon'); - expect(result.geo_polygon['nestedField.geo']).toBeDefined(); + expect((result.geo_polygon as any)['nestedField.geo']).toBeDefined(); }); }); }); diff --git a/packages/kbn-es-query/src/kuery/functions/geo_polygon.ts b/packages/kbn-es-query/src/kuery/functions/geo_polygon.ts index d02ba84237c7f..713124e1c4e93 100644 --- a/packages/kbn-es-query/src/kuery/functions/geo_polygon.ts +++ b/packages/kbn-es-query/src/kuery/functions/geo_polygon.ts @@ -6,9 +6,10 @@ * Side Public License, v 1. */ +import { estypes } from '@elastic/elasticsearch'; import { nodeTypes } from '../node_types'; import * as ast from '../ast'; -import { IndexPatternBase, KueryNode, LatLon } from '../..'; +import { IndexPatternBase, KueryNode, KueryQueryOptions, LatLon } from '../..'; import { LiteralTypeBuildNode } from '../node_types/types'; export function buildNodeParams(fieldName: string, points: LatLon[]) { @@ -26,9 +27,9 @@ export function buildNodeParams(fieldName: string, points: LatLon[]) { export function toElasticsearchQuery( node: KueryNode, indexPattern?: IndexPatternBase, - config: Record = {}, + config: KueryQueryOptions = {}, context: Record = {} -) { +): estypes.QueryDslQueryContainer { const [fieldNameArg, ...points] = node.arguments; const fullFieldNameArg = { ...fieldNameArg, diff --git a/packages/kbn-es-query/src/kuery/functions/is.test.ts b/packages/kbn-es-query/src/kuery/functions/is.test.ts index 292159e727a92..bc1bbb2508438 100644 --- a/packages/kbn-es-query/src/kuery/functions/is.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/is.test.ts @@ -11,6 +11,7 @@ import { fields } from '../../filters/stubs'; import * as is from './is'; import { IndexPatternBase } from '../..'; +import { estypes } from '@elastic/elasticsearch'; jest.mock('../grammar'); @@ -125,7 +126,9 @@ describe('kuery functions', () => { const result = is.toElasticsearchQuery(node, indexPattern); expect(result).toHaveProperty('bool'); - expect(result.bool!.should!.length).toBe(indexPattern.fields.length); + expect((result.bool!.should! as estypes.QueryDslQueryContainer[]).length).toBe( + indexPattern.fields.length + ); }); test('should return an ES exists query when value is "*"', () => { @@ -204,7 +207,9 @@ describe('kuery functions', () => { const node = nodeTypes.function.buildNode('is', 'script string', 'foo'); const result = is.toElasticsearchQuery(node, indexPattern); - expect(result.bool!.should![0]).toHaveProperty('script'); + expect((result.bool!.should as estypes.QueryDslQueryContainer[])[0]).toHaveProperty( + 'script' + ); }); test('should support date fields without a dateFormat provided', () => { diff --git a/packages/kbn-es-query/src/kuery/functions/is.ts b/packages/kbn-es-query/src/kuery/functions/is.ts index c8d33921b084a..4dc0a66d3d9fa 100644 --- a/packages/kbn-es-query/src/kuery/functions/is.ts +++ b/packages/kbn-es-query/src/kuery/functions/is.ts @@ -7,11 +7,12 @@ */ import { get, isUndefined } from 'lodash'; +import { estypes } from '@elastic/elasticsearch'; 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, KueryQueryOptions } from '../..'; import * as ast from '../ast'; @@ -40,9 +41,9 @@ export function buildNodeParams(fieldName: string, value: any, isPhrase: boolean export function toElasticsearchQuery( node: KueryNode, indexPattern?: IndexPatternBase, - config: Record = {}, + config: KueryQueryOptions = {}, context: Record = {} -) { +): estypes.QueryDslQueryContainer { const { arguments: [fieldNameArg, valueArg, isPhraseArg], } = node; @@ -75,7 +76,7 @@ export function toElasticsearchQuery( return { multi_match: { type, - query: value, + query: (value as unknown) as string, lenient: true, }, }; diff --git a/packages/kbn-es-query/src/kuery/functions/nested.test.ts b/packages/kbn-es-query/src/kuery/functions/nested.test.ts index 47e515b9bd47f..7b6b7b695db2d 100644 --- a/packages/kbn-es-query/src/kuery/functions/nested.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/nested.test.ts @@ -48,8 +48,8 @@ describe('kuery functions', () => { expect(result).toHaveProperty('nested'); expect(Object.keys(result).length).toBe(1); - expect(result.nested.path).toBe('nestedField'); - expect(result.nested.score_mode).toBe('none'); + expect(result.nested?.path).toBe('nestedField'); + expect(result.nested?.score_mode).toBe('none'); }); test('should pass the nested path to subqueries so the full field name can be used', () => { @@ -59,7 +59,7 @@ describe('kuery functions', () => { nodeTypes.function.buildNode('is', 'nestedField.child', 'foo') ); - expect(result.nested.query).toEqual(expectedSubQuery); + expect(result.nested!.query).toEqual(expectedSubQuery); }); }); }); diff --git a/packages/kbn-es-query/src/kuery/functions/nested.ts b/packages/kbn-es-query/src/kuery/functions/nested.ts index 248de1c40d62a..3a3be4c10eade 100644 --- a/packages/kbn-es-query/src/kuery/functions/nested.ts +++ b/packages/kbn-es-query/src/kuery/functions/nested.ts @@ -6,9 +6,10 @@ * Side Public License, v 1. */ +import { estypes } from '@elastic/elasticsearch'; import * as ast from '../ast'; import * as literal from '../node_types/literal'; -import { IndexPatternBase, KueryNode } from '../..'; +import { IndexPatternBase, KueryNode, KueryQueryOptions } from '../..'; export function buildNodeParams(path: any, child: any) { const pathNode = @@ -21,11 +22,11 @@ export function buildNodeParams(path: any, child: any) { export function toElasticsearchQuery( node: KueryNode, indexPattern?: IndexPatternBase, - config: Record = {}, + config: KueryQueryOptions = {}, context: Record = {} -) { +): estypes.QueryDslQueryContainer { const [path, child] = node.arguments; - const stringPath = ast.toElasticsearchQuery(path); + const stringPath = (ast.toElasticsearchQuery(path) as unknown) as string; const fullPath = context?.nested?.path ? `${context.nested.path}.${stringPath}` : stringPath; return { @@ -34,7 +35,7 @@ export function toElasticsearchQuery( query: ast.toElasticsearchQuery(child, indexPattern, config, { ...context, nested: { path: fullPath }, - }), + }) as estypes.QueryDslQueryContainer, score_mode: 'none', }, }; diff --git a/packages/kbn-es-query/src/kuery/functions/not.test.ts b/packages/kbn-es-query/src/kuery/functions/not.test.ts index a44f3e9c5dda8..af1e8108b40f5 100644 --- a/packages/kbn-es-query/src/kuery/functions/not.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/not.test.ts @@ -46,9 +46,9 @@ describe('kuery functions', () => { expect(Object.keys(result).length).toBe(1); expect(result.bool).toHaveProperty('must_not'); - expect(Object.keys(result.bool).length).toBe(1); + expect(Object.keys(result.bool!).length).toBe(1); - expect(result.bool.must_not).toEqual(ast.toElasticsearchQuery(childNode, indexPattern)); + expect(result.bool!.must_not).toEqual(ast.toElasticsearchQuery(childNode, indexPattern)); }); }); }); diff --git a/packages/kbn-es-query/src/kuery/functions/not.ts b/packages/kbn-es-query/src/kuery/functions/not.ts index 96404ee800010..01ec89e9b499d 100644 --- a/packages/kbn-es-query/src/kuery/functions/not.ts +++ b/packages/kbn-es-query/src/kuery/functions/not.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ +import { estypes } from '@elastic/elasticsearch'; import * as ast from '../ast'; -import { IndexPatternBase, KueryNode } from '../..'; +import { IndexPatternBase, KueryNode, KueryQueryOptions } from '../..'; export function buildNodeParams(child: KueryNode) { return { @@ -18,14 +19,19 @@ export function buildNodeParams(child: KueryNode) { export function toElasticsearchQuery( node: KueryNode, indexPattern?: IndexPatternBase, - config: Record = {}, + config: KueryQueryOptions = {}, context: Record = {} -) { +): estypes.QueryDslQueryContainer { const [argument] = node.arguments; return { bool: { - must_not: ast.toElasticsearchQuery(argument, indexPattern, config, context), + must_not: ast.toElasticsearchQuery( + argument, + indexPattern, + config, + context + ) as estypes.QueryDslQueryContainer, }, }; } diff --git a/packages/kbn-es-query/src/kuery/functions/or.test.ts b/packages/kbn-es-query/src/kuery/functions/or.test.ts index 15faa7e1753d3..eb7db62b22162 100644 --- a/packages/kbn-es-query/src/kuery/functions/or.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/or.test.ts @@ -48,7 +48,7 @@ describe('kuery functions', () => { expect(result).toHaveProperty('bool'); expect(Object.keys(result).length).toBe(1); expect(result.bool).toHaveProperty('should'); - expect(result.bool.should).toEqual( + expect(result.bool!.should).toEqual( [childNode1, childNode2].map((childNode) => ast.toElasticsearchQuery(childNode, indexPattern) ) diff --git a/packages/kbn-es-query/src/kuery/functions/or.ts b/packages/kbn-es-query/src/kuery/functions/or.ts index b94814e1f7c05..d48ddb4c32d73 100644 --- a/packages/kbn-es-query/src/kuery/functions/or.ts +++ b/packages/kbn-es-query/src/kuery/functions/or.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ +import { estypes } from '@elastic/elasticsearch'; import * as ast from '../ast'; -import { IndexPatternBase, KueryNode } from '../..'; +import { IndexPatternBase, KueryNode, KueryQueryOptions } from '../..'; export function buildNodeParams(children: KueryNode[]) { return { @@ -18,9 +19,9 @@ export function buildNodeParams(children: KueryNode[]) { export function toElasticsearchQuery( node: KueryNode, indexPattern?: IndexPatternBase, - config: Record = {}, + config: KueryQueryOptions = {}, context: Record = {} -) { +): estypes.QueryDslQueryContainer { const children = node.arguments || []; return { diff --git a/packages/kbn-es-query/src/kuery/functions/range.test.ts b/packages/kbn-es-query/src/kuery/functions/range.test.ts index fa1805e64887c..42005a3fe702d 100644 --- a/packages/kbn-es-query/src/kuery/functions/range.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/range.test.ts @@ -13,6 +13,7 @@ import { IndexPatternBase } from '../..'; import { RangeFilterParams } from '../../filters'; import * as range from './range'; +import { estypes } from '@elastic/elasticsearch'; jest.mock('../grammar'); describe('kuery functions', () => { @@ -128,7 +129,9 @@ describe('kuery functions', () => { const node = nodeTypes.function.buildNode('range', 'script number', { gt: 1000, lt: 8000 }); const result = range.toElasticsearchQuery(node, indexPattern); - expect(result.bool.should[0]).toHaveProperty('script'); + expect((result.bool!.should as estypes.QueryDslQueryContainer[])[0]).toHaveProperty( + 'script' + ); }); test('should support date fields without a dateFormat provided', () => { diff --git a/packages/kbn-es-query/src/kuery/functions/range.ts b/packages/kbn-es-query/src/kuery/functions/range.ts index e80a365441c43..6b5810bb7518f 100644 --- a/packages/kbn-es-query/src/kuery/functions/range.ts +++ b/packages/kbn-es-query/src/kuery/functions/range.ts @@ -7,13 +7,14 @@ */ import _ from 'lodash'; +import { estypes } from '@elastic/elasticsearch'; import { nodeTypes } from '../node_types'; import * as ast from '../ast'; 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, KueryQueryOptions } from '../..'; export function buildNodeParams(fieldName: string, params: RangeFilterParams) { const paramsToMap = _.pick(params, 'gt', 'lt', 'gte', 'lte', 'format'); @@ -34,9 +35,9 @@ export function buildNodeParams(fieldName: string, params: RangeFilterParams) { export function toElasticsearchQuery( node: KueryNode, indexPattern?: IndexPatternBase, - config: Record = {}, + config: KueryQueryOptions = {}, context: Record = {} -) { +): estypes.QueryDslQueryContainer { const [fieldNameArg, ...args] = node.arguments; const fullFieldNameArg = getFullFieldNameNode( fieldNameArg, diff --git a/packages/kbn-es-query/src/kuery/index.ts b/packages/kbn-es-query/src/kuery/index.ts index dd1e39307b27e..15f3a768ebbd3 100644 --- a/packages/kbn-es-query/src/kuery/index.ts +++ b/packages/kbn-es-query/src/kuery/index.ts @@ -6,7 +6,21 @@ * Side Public License, v 1. */ +import { estypes } from '@elastic/elasticsearch'; +import { toElasticsearchQuery as astToElasticsearchQuery } from './ast'; + +/** + * @params {String} indexPattern + * @params {Object} config - contains the dateFormatTZ + * + * IndexPattern isn't required, but if you pass one in, we can be more intelligent + * about how we craft the queries (e.g. scripted fields) + */ +export const toElasticsearchQuery = (...params: Parameters) => { + return astToElasticsearchQuery(...params) as estypes.QueryDslQueryContainer; +}; + export { KQLSyntaxError } from './kuery_syntax_error'; export { nodeTypes, nodeBuilder } from './node_types'; -export { fromKueryExpression, toElasticsearchQuery } from './ast'; +export { fromKueryExpression } from './ast'; export { DslQuery, KueryNode, KueryQueryOptions } from './types'; diff --git a/packages/kbn-es-query/src/kuery/node_types/function.ts b/packages/kbn-es-query/src/kuery/node_types/function.ts index e72f8a6b1e77a..57954948b48c6 100644 --- a/packages/kbn-es-query/src/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, KueryQueryOptions } from '../..'; import { FunctionName, FunctionTypeBuildNode } from './types'; export function buildNode(functionName: FunctionName, ...args: any[]) { @@ -46,7 +46,7 @@ export function buildNodeWithArgumentNodes( export function toElasticsearchQuery( node: KueryNode, indexPattern?: IndexPatternBase, - config?: Record, + config?: KueryQueryOptions, context?: Record ) { const kueryFunction = functions[node.function as FunctionName]; diff --git a/packages/kbn-es-query/src/kuery/node_types/types.ts b/packages/kbn-es-query/src/kuery/node_types/types.ts index 7e6f454418555..8ee83971c833b 100644 --- a/packages/kbn-es-query/src/kuery/node_types/types.ts +++ b/packages/kbn-es-query/src/kuery/node_types/types.ts @@ -11,7 +11,7 @@ */ import { JsonValue } from '@kbn/utility-types'; -import { KueryNode } from '..'; +import { KueryNode, KueryQueryOptions } from '..'; import { IndexPatternBase } from '../..'; export type FunctionName = @@ -31,7 +31,7 @@ interface FunctionType { toElasticsearchQuery: ( node: any, indexPattern?: IndexPatternBase, - config?: Record, + config?: KueryQueryOptions, context?: Record ) => JsonValue; } diff --git a/packages/kbn-es-query/src/kuery/types.ts b/packages/kbn-es-query/src/kuery/types.ts index 656e06e712079..1ab2d07a60a11 100644 --- a/packages/kbn-es-query/src/kuery/types.ts +++ b/packages/kbn-es-query/src/kuery/types.ts @@ -7,6 +7,7 @@ */ import { estypes } from '@elastic/elasticsearch'; +import { SerializableRecord } from '@kbn/utility-types'; import { NodeTypes } from './node_types'; /** @public */ @@ -22,9 +23,7 @@ export type DslQuery = estypes.QueryDslQueryContainer; /** @internal */ export interface KueryParseOptions { - helpers: { - [key: string]: any; - }; + helpers: SerializableRecord; startRule: string; allowLeadingWildcards: boolean; cursorSymbol?: string; diff --git a/packages/kbn-test/src/functional_test_runner/lib/docker_servers/README.md b/packages/kbn-test/src/functional_test_runner/lib/docker_servers/README.md index d75faf4c854aa..11315834f4f60 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/docker_servers/README.md +++ b/packages/kbn-test/src/functional_test_runner/lib/docker_servers/README.md @@ -53,7 +53,7 @@ export default function () { To consume the test server, use can use something like supertest to send request. Just make sure that you disable your test suite if the user doesn't choose to enable your docker server: ```ts -import makeSupertest from 'supertest-as-promised'; +import supertest from 'supertest'; import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { @@ -61,7 +61,7 @@ export default function ({ getService }: FtrProviderContext) { const log = getService('log'); const server = dockerServers.get('helloWorld'); - const supertest = makeSupertest(server.url); + const request = supertest(server.url); describe('test suite name', function () { if (!server.enabled) { @@ -72,7 +72,7 @@ export default function ({ getService }: FtrProviderContext) { } it('test name', async () => { - await supertest.get('/foo/bar').expect(200); + await request.get('/foo/bar').expect(200); }); }); } diff --git a/src/cli_keystore/utils/prompt.js b/src/cli_keystore/utils/prompt.js index 195f794db3e6e..06fa151ec2ef4 100644 --- a/src/cli_keystore/utils/prompt.js +++ b/src/cli_keystore/utils/prompt.js @@ -66,6 +66,8 @@ export function question(question, options = {}) { break; default: if (options.mask) { + const cursorPos = rl.getCursorPos(); + output.moveCursor(0, -cursorPos.rows); output.cursorTo(questionPrompt.length); output.write(Array(rl.line.length + 1).join(options.mask || '*')); } diff --git a/src/core/server/execution_context/execution_context_service.mock.ts b/src/core/server/execution_context/execution_context_service.mock.ts index 2e31145f6c0ee..68aab7a5b84f8 100644 --- a/src/core/server/execution_context/execution_context_service.mock.ts +++ b/src/core/server/execution_context/execution_context_service.mock.ts @@ -6,12 +6,18 @@ * Side Public License, v 1. */ +import type { KibanaExecutionContext } from '../../types'; import type { IExecutionContext, InternalExecutionContextSetup, ExecutionContextSetup, } from './execution_context_service'; +// attempted removal of any: unsuccessful! In theory, replaceable with /R +function withContextMock(context: KibanaExecutionContext | undefined, fn: () => any): any { + return fn(); +} + const createExecutionContextMock = () => { const mock: jest.Mocked = { set: jest.fn(), @@ -21,6 +27,7 @@ const createExecutionContextMock = () => { getParentContextFrom: jest.fn(), getAsHeader: jest.fn(), }; + mock.withContext.mockImplementation(withContextMock); return mock; }; const createInternalSetupContractMock = () => { @@ -32,6 +39,7 @@ const createSetupContractMock = () => { const mock: jest.Mocked = { withContext: jest.fn(), }; + mock.withContext.mockImplementation(withContextMock); return mock; }; diff --git a/src/core/server/execution_context/execution_context_service.test.ts b/src/core/server/execution_context/execution_context_service.test.ts index 3abaa13d11103..3fa4de34ebda0 100644 --- a/src/core/server/execution_context/execution_context_service.test.ts +++ b/src/core/server/execution_context/execution_context_service.test.ts @@ -346,7 +346,7 @@ describe('ExecutionContextService', () => { id: 'id-a', description: 'description-a', }, - (i) => i + () => null ); expect(loggingSystemMock.collect(core.logger).debug).toMatchInlineSnapshot(` Array [ @@ -378,6 +378,26 @@ describe('ExecutionContextService', () => { expect(result).toBeUndefined(); }); + it('executes provided function when disabled', async () => { + const coreWithDisabledService = mockCoreContext.create(); + coreWithDisabledService.configService.atPath.mockReturnValue( + new BehaviorSubject({ enabled: false }) + ); + const disabledService = new ExecutionContextService(coreWithDisabledService).setup(); + const fn = jest.fn(); + + disabledService.withContext( + { + type: 'type-b', + name: 'name-b', + id: 'id-b', + description: 'description-b', + }, + fn + ); + + expect(fn).toHaveBeenCalledTimes(1); + }); }); describe('getAsHeader', () => { @@ -387,6 +407,21 @@ describe('ExecutionContextService', () => { expect(service.getAsHeader()).toBe('1234'); }); + it('falls back to "unknownId" if no id provided', async () => { + expect(service.getAsHeader()).toBe('unknownId'); + }); + + it('falls back to "unknownId" and context if no id provided', async () => { + service.set({ + type: 'type-a', + name: 'name-a', + id: 'id-a', + description: 'description-a', + }); + + expect(service.getAsHeader()).toBe('unknownId;kibana:type-a:name-a:id-a'); + }); + it('returns request id and registered context', async () => { service.setRequestId('1234'); service.set({ diff --git a/src/core/server/execution_context/execution_context_service.ts b/src/core/server/execution_context/execution_context_service.ts index 5a8d104cebcd9..41b225cf1d0f3 100644 --- a/src/core/server/execution_context/execution_context_service.ts +++ b/src/core/server/execution_context/execution_context_service.ts @@ -34,7 +34,7 @@ export interface IExecutionContext { * https://nodejs.org/api/async_context.html#async_context_asynclocalstorage_enterwith_store */ get(): IExecutionContextContainer | undefined; - withContext(context: KibanaExecutionContext | undefined, fn: (...args: any[]) => R): R; + withContext(context: KibanaExecutionContext | undefined, fn: () => R): R; /** * returns serialized representation to send as a header **/ @@ -153,8 +153,11 @@ export class ExecutionContextService private getAsHeader(): string | undefined { if (!this.enabled) return; - const stringifiedCtx = this.contextStore.getStore()?.toString(); - const requestId = this.requestIdStore.getStore()?.requestId; - return stringifiedCtx ? `${requestId};kibana:${stringifiedCtx}` : requestId; + // requestId may not be present in the case of FakeRequest + const requestId = this.requestIdStore.getStore()?.requestId ?? 'unknownId'; + const executionContext = this.contextStore.getStore()?.toString(); + const executionContextStr = executionContext ? `;kibana:${executionContext}` : ''; + + return `${requestId}${executionContextStr}`; } } diff --git a/src/dev/build/tasks/copy_source_task.ts b/src/dev/build/tasks/copy_source_task.ts index 06ec40e8fcfa0..65edd6b61720e 100644 --- a/src/dev/build/tasks/copy_source_task.ts +++ b/src/dev/build/tasks/copy_source_task.ts @@ -20,7 +20,7 @@ export const CopySource: Task = { 'src/**', '!src/**/*.{test,test.mocks,mock}.{js,ts,tsx}', '!src/**/mocks.ts', // special file who imports .mock files - '!src/**/{target,__tests__,__snapshots__,__mocks__}/**', + '!src/**/{target,__tests__,__snapshots__,__mocks__,integration_tests}/**', '!src/core/server/core_app/assets/favicons/favicon.distribution.png', '!src/core/server/core_app/assets/favicons/favicon.distribution.svg', '!src/test_utils/**', diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 46326fcc3b52e..9c81d077b5b1b 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -74,7 +74,7 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint 'node-sql-parser@3.6.1': ['(GPL-2.0 OR MIT)'], // GPL-2.0* https://github.com/taozhi8833998/node-sql-parser - '@elastic/ems-client@7.14.0': ['Elastic License 2.0'], + '@elastic/ems-client@7.15.0': ['Elastic License 2.0'], '@elastic/eui@37.1.1': ['SSPL-1.0 OR Elastic License 2.0'], // TODO can be removed if the https://github.com/jindw/xmldom/issues/239 is released diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts index 419d4f0854ecc..0244cb2cd9115 100644 --- a/src/dev/typescript/projects.ts +++ b/src/dev/typescript/projects.ts @@ -40,6 +40,22 @@ export const PROJECTS = [ createProject('x-pack/plugins/security_solution/cypress/tsconfig.json', { name: 'security_solution/cypress', }), + createProject( + 'x-pack/plugins/enterprise_search/public/applications/shared/cypress/tsconfig.json', + { name: 'enterprise_search/shared/cypress' } + ), + createProject( + 'x-pack/plugins/enterprise_search/public/applications/enterprise_search/cypress/tsconfig.json', + { name: 'enterprise_search/overview/cypress' } + ), + createProject( + 'x-pack/plugins/enterprise_search/public/applications/app_search/cypress/tsconfig.json', + { name: 'enterprise_search/app_search/cypress' } + ), + createProject( + 'x-pack/plugins/enterprise_search/public/applications/workplace_search/cypress/tsconfig.json', + { name: 'enterprise_search/workplace_search/cypress' } + ), createProject('x-pack/plugins/osquery/cypress/tsconfig.json', { name: 'osquery/cypress', }), diff --git a/src/plugins/data/common/es_query/stubs/phrases_filter.ts b/src/plugins/data/common/es_query/stubs/phrases_filter.ts index 56c3af56175da..112066ab3ae8e 100644 --- a/src/plugins/data/common/es_query/stubs/phrases_filter.ts +++ b/src/plugins/data/common/es_query/stubs/phrases_filter.ts @@ -22,4 +22,5 @@ export const phrasesFilter: PhrasesFilter = { $state: { store: FilterStateStore.APP_STATE, }, + query: {}, }; diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts index 4e62b49938ade..90a13c076839b 100644 --- a/src/plugins/data/common/search/search_source/search_source.test.ts +++ b/src/plugins/data/common/search/search_source/search_source.test.ts @@ -627,7 +627,7 @@ describe('SearchSource', () => { expect(request.fields).toEqual(['@timestamp']); expect(request.script_fields).toEqual({ hello: {} }); expect(request.stored_fields).toEqual(['@timestamp', 'bar']); - expect(request.runtime_mappings).toEqual({ runtime_field: runtimeFieldDef }); + expect(request.runtime_mappings).toEqual(runtimeFields); }); test('filters request when a specific list of fields is provided with fieldsFromSource or fields', async () => { 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 f2b801ebac29f..16ec3d5c6c6e0 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -757,10 +757,6 @@ export class SearchSource { body.script_fields, Object.keys(body.script_fields).filter((f) => uniqFieldNames.includes(f)) ); - body.runtime_mappings = pick( - body.runtime_mappings, - Object.keys(body.runtime_mappings).filter((f) => uniqFieldNames.includes(f)) - ); } // request the remaining fields from stored_fields just in case, since the diff --git a/src/plugins/data/common/search/types.ts b/src/plugins/data/common/search/types.ts index cffba6203dfe2..68a25d4c4d69d 100644 --- a/src/plugins/data/common/search/types.ts +++ b/src/plugins/data/common/search/types.ts @@ -70,6 +70,11 @@ export interface IKibanaSearchResponse { */ isRestored?: boolean; + /** + * Optional warnings that should be surfaced to the end user + */ + warning?: string; + /** * The raw response returned by the internal search method (usually the raw ES response) */ diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/index.ts b/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/index.ts index c5c1626ae74f6..d76c71182b8fa 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/index.ts +++ b/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/index.ts @@ -9,12 +9,12 @@ import { CoreSetup } from 'kibana/public'; import { $Keys } from 'utility-types'; import { flatten, uniqBy } from 'lodash'; +import { fromKueryExpression } from '@kbn/es-query'; import { setupGetFieldSuggestions } from './field'; import { setupGetValueSuggestions } from './value'; import { setupGetOperatorSuggestions } from './operator'; import { setupGetConjunctionSuggestions } from './conjunction'; import { - esKuery, QuerySuggestion, QuerySuggestionGetFnArgs, QuerySuggestionGetFn, @@ -43,7 +43,7 @@ export const setupKqlQuerySuggestionProvider = ( querySuggestionsArgs: QuerySuggestionGetFnArgs ): Array> | [] => { try { - const cursorNode = esKuery.fromKueryExpression(cursoredQuery, { + const cursorNode = fromKueryExpression(cursoredQuery, { cursorSymbol, parseCursor: true, }); diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 995a1e7a908c5..09e6cb329c24a 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -792,7 +792,7 @@ export const esFilters: { export const esKuery: { nodeTypes: import("@kbn/es-query/target_types/kuery/node_types").NodeTypes; fromKueryExpression: (expression: string | import("@elastic/elasticsearch/api/types").QueryDslQueryContainer, 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/utility-types").JsonObject; + toElasticsearchQuery: (node: import("@kbn/es-query").KueryNode, indexPattern?: import("@kbn/es-query").IndexPatternBase | undefined, config?: import("@kbn/es-query").KueryQueryOptions | undefined, context?: Record | undefined) => import("@elastic/elasticsearch/api/types").QueryDslQueryContainer; }; // Warning: (ae-missing-release-tag) "esQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1147,6 +1147,7 @@ export interface IKibanaSearchResponse { loaded?: number; rawResponse: RawResponse; total?: number; + warning?: string; } // Warning: (ae-forgotten-export) The symbol "MetricAggType" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/query/filter_manager/lib/generate_filter.test.ts b/src/plugins/data/public/query/filter_manager/lib/generate_filter.test.ts index 0daaf804e7b40..397754d2bd6d3 100644 --- a/src/plugins/data/public/query/filter_manager/lib/generate_filter.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/generate_filter.test.ts @@ -11,21 +11,21 @@ import { FilterManager } from '../filter_manager'; import { Filter, - IFieldType, - IIndexPattern, + IndexPatternFieldBase, + IndexPatternBase, isExistsFilter, buildExistsFilter, PhraseFilter, isPhraseFilter, RangeFilter, isRangeFilter, -} from '../../../../common'; +} from '@kbn/es-query'; const INDEX_NAME = 'my-index'; const EXISTS_FIELD_NAME = '_exists_'; const FIELD = { name: 'my-field', -} as IFieldType; +} as IndexPatternFieldBase; const PHRASE_VALUE = 'my-value'; describe('Generate filters', () => { @@ -70,7 +70,7 @@ describe('Generate filters', () => { }); it('should update and re-enable EXISTING exists filter', () => { - const filter = buildExistsFilter(FIELD, { id: INDEX_NAME } as IIndexPattern); + const filter = buildExistsFilter(FIELD, { id: INDEX_NAME } as IndexPatternBase); filter.meta.disabled = true; filtersArray.push(filter); @@ -110,7 +110,7 @@ describe('Generate filters', () => { { name: 'my-field', type: 'ip_range', - } as IFieldType, + } as IndexPatternFieldBase, { gt: '192.168.0.0', lte: '192.168.255.255', @@ -136,7 +136,7 @@ describe('Generate filters', () => { { name: 'my-field', type: 'number_range', - } as IFieldType, + } as IndexPatternFieldBase, 10000, '+', INDEX_NAME diff --git a/src/plugins/data/public/search/fetch/handle_response.tsx b/src/plugins/data/public/search/fetch/handle_response.tsx index 58e4da6b95fae..f4acebfb36060 100644 --- a/src/plugins/data/public/search/fetch/handle_response.tsx +++ b/src/plugins/data/public/search/fetch/handle_response.tsx @@ -16,7 +16,18 @@ import { getNotifications } from '../../services'; import { SearchRequest } from '..'; export function handleResponse(request: SearchRequest, response: IKibanaSearchResponse) { - const { rawResponse } = response; + const { rawResponse, warning } = response; + if (warning) { + getNotifications().toasts.addWarning({ + title: i18n.translate('data.search.searchSource.fetch.warningMessage', { + defaultMessage: 'Warning: {warning}', + values: { + warning, + }, + }), + }); + } + if (rawResponse.timed_out) { getNotifications().toasts.addWarning({ title: i18n.translate('data.search.searchSource.fetch.requestTimedOutNotificationMessage', { diff --git a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts index eb39417ac535f..75a4ddf051418 100644 --- a/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts +++ b/src/plugins/data/server/search/strategies/ese_search/ese_search_strategy.ts @@ -71,12 +71,14 @@ export const enhancedEsSearchStrategyProvider = ( const promise = id ? client.asyncSearch.get({ ...params, id }) : client.asyncSearch.submit(params); - const { body } = await shimAbortSignal(promise, options.abortSignal); + const { body, headers } = await shimAbortSignal(promise, options.abortSignal); + const response = shimHitsTotal(body.response, options); return toAsyncKibanaSearchResponse( // @ts-expect-error @elastic/elasticsearch start_time_in_millis expected to be number - { ...body, response } + { ...body, response }, + headers?.warning ); }; diff --git a/src/plugins/data/server/search/strategies/ese_search/response_utils.ts b/src/plugins/data/server/search/strategies/ese_search/response_utils.ts index ae3d258e2205d..0a92c95dac615 100644 --- a/src/plugins/data/server/search/strategies/ese_search/response_utils.ts +++ b/src/plugins/data/server/search/strategies/ese_search/response_utils.ts @@ -12,12 +12,13 @@ import { getTotalLoaded } from '../es_search'; /** * Get the Kibana representation of an async search response (see `IKibanaSearchResponse`). */ -export function toAsyncKibanaSearchResponse(response: AsyncSearchResponse) { +export function toAsyncKibanaSearchResponse(response: AsyncSearchResponse, warning?: string) { return { id: response.id, rawResponse: response.response, isPartial: response.is_partial, isRunning: response.is_running, + ...(warning ? { warning } : {}), ...getTotalLoaded(response.response), }; } diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index c1f22d1be1d01..71863ecb61341 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -125,7 +125,7 @@ export const esFilters: { export const esKuery: { nodeTypes: import("@kbn/es-query/target_types/kuery/node_types").NodeTypes; fromKueryExpression: (expression: string | import("@elastic/elasticsearch/api/types").QueryDslQueryContainer, 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/utility-types").JsonObject; + toElasticsearchQuery: (node: import("@kbn/es-query").KueryNode, indexPattern?: import("@kbn/es-query").IndexPatternBase | undefined, config?: import("@kbn/es-query").KueryQueryOptions | undefined, context?: Record | undefined) => import("@elastic/elasticsearch/api/types").QueryDslQueryContainer; }; // Warning: (ae-missing-release-tag) "esQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss index e845ba7238303..83b5ce2849c90 100644 --- a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss +++ b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss @@ -44,6 +44,11 @@ padding: $euiSizeXS; } +.kbnDocViewer__tableValueCell { + flex-direction: column; + align-items: flex-start; +} + .kbnDocViewer__value { word-break: break-all; word-wrap: break-word; diff --git a/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap b/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap index 284c77c96db78..42d6271cdb39c 100644 --- a/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap +++ b/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap @@ -98,3 +98,36 @@ Array [ , ] `; + +exports[`FieldName renders unknown field 1`] = ` +Array [ +
+ + + +
, +
+
+ + + test.test.test + + +
+
, +] +`; diff --git a/src/plugins/discover/public/application/components/field_name/field_name.test.tsx b/src/plugins/discover/public/application/components/field_name/field_name.test.tsx index 6570c1b352fed..d2f961392c43c 100644 --- a/src/plugins/discover/public/application/components/field_name/field_name.test.tsx +++ b/src/plugins/discover/public/application/components/field_name/field_name.test.tsx @@ -26,3 +26,8 @@ test('FieldName renders a geo field', () => { const component = render(); expect(component).toMatchSnapshot(); }); + +test('FieldName renders unknown field', () => { + const component = render(); + expect(component).toMatchSnapshot(); +}); diff --git a/src/plugins/discover/public/application/components/field_name/field_name.tsx b/src/plugins/discover/public/application/components/field_name/field_name.tsx index e8b6765b738a5..a180c35fb2653 100644 --- a/src/plugins/discover/public/application/components/field_name/field_name.tsx +++ b/src/plugins/discover/public/application/components/field_name/field_name.tsx @@ -19,7 +19,7 @@ import { IndexPatternField } from '../../../../../data/public'; // this should be changed when both components are deangularized interface Props { fieldName: string; - fieldType: string; + fieldType?: string; fieldMapping?: IndexPatternField; fieldIconProps?: Omit; scripted?: boolean; @@ -41,7 +41,7 @@ export function FieldName({ return ( - + diff --git a/src/plugins/discover/public/application/components/field_name/field_type_name.ts b/src/plugins/discover/public/application/components/field_name/field_type_name.ts index e2d4c2f7ddcf2..3d3e97aba624b 100644 --- a/src/plugins/discover/public/application/components/field_name/field_type_name.ts +++ b/src/plugins/discover/public/application/components/field_name/field_type_name.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; -export function getFieldTypeName(type: string) { +export function getFieldTypeName(type?: string) { switch (type) { case 'boolean': return i18n.translate('discover.fieldNameIcons.booleanAriaLabel', { diff --git a/src/plugins/discover/public/application/components/table/table.tsx b/src/plugins/discover/public/application/components/table/table.tsx index 2d261805d8eb8..d1b2d27245616 100644 --- a/src/plugins/discover/public/application/components/table/table.tsx +++ b/src/plugins/discover/public/application/components/table/table.tsx @@ -36,13 +36,14 @@ export interface FieldRecord { flattenedField: unknown; }; field: { - fieldName: string; - fieldType: string; - fieldMapping: IndexPatternField | undefined; + displayName: string; + field: string; scripted: boolean; + fieldType?: string; + fieldMapping?: IndexPatternField; }; value: { - formattedField: unknown; + formattedValue: string; }; } @@ -56,7 +57,7 @@ export const DocViewerTable = ({ }: DocViewRenderProps) => { const showMultiFields = getServices().uiSettings.get(SHOW_MULTIFIELDS); - const mapping = useCallback((name) => indexPattern?.fields.getByName(name), [ + const mapping = useCallback((name: string) => indexPattern?.fields.getByName(name), [ indexPattern?.fields, ]); @@ -82,10 +83,11 @@ export const DocViewerTable = ({ [onRemoveColumn, onAddColumn, columns] ); - const onSetRowProps = useCallback(({ field: { fieldName } }: FieldRecord) => { + const onSetRowProps = useCallback(({ field: { field } }: FieldRecord) => { return { + key: field, className: 'kbnDocViewer__tableRow', - 'data-test-subj': `tableDocViewRow-${fieldName}`, + 'data-test-subj': `tableDocViewRow-${field}`, }; }, []); @@ -117,31 +119,34 @@ export const DocViewerTable = ({ const nameB = !mappingB || !mappingB.displayName ? fieldB : mappingB.displayName; return nameA.localeCompare(nameB); }) - .map((fieldName) => { - const fieldMapping = mapping(fieldName); - const fieldType = isNestedFieldParent(fieldName, indexPattern) - ? 'nested' - : fieldMapping?.type; + .map((field) => { + const fieldMapping = mapping(field); + const displayName = fieldMapping?.displayName ?? field; + const fieldType = isNestedFieldParent(field, indexPattern) ? 'nested' : fieldMapping?.type; return { action: { onToggleColumn, onFilter: filter, - isActive: !!columns?.includes(fieldName), - flattenedField: flattened[fieldName], + isActive: !!columns?.includes(field), + flattenedField: flattened[field], }, field: { - fieldName, - fieldType: fieldType!, - scripted: Boolean(fieldMapping?.scripted), + field, + displayName, fieldMapping, + fieldType, + scripted: Boolean(fieldMapping?.scripted), + }, + value: { + formattedValue: formattedHit[field], }, - value: { formattedField: formattedHit[fieldName] }, }; }); return ( void; } export const TableActions = ({ isActive, - fieldName, + field, fieldMapping, flattenedField, onToggleColumn, onFilter, }: TableActionsProps) => { - const toggleColumn = () => { - onToggleColumn(fieldName); - }; - return (
onToggleColumn(field)} /> onFilter('_exists_', fieldName, '+')} + onClick={() => onFilter('_exists_', field, '+')} scripted={fieldMapping && fieldMapping.scripted} />
diff --git a/src/plugins/discover/public/application/components/table/table_cell_value.tsx b/src/plugins/discover/public/application/components/table/table_cell_value.tsx index de29068a82718..ba2fb707940bf 100644 --- a/src/plugins/discover/public/application/components/table/table_cell_value.tsx +++ b/src/plugins/discover/public/application/components/table/table_cell_value.tsx @@ -7,31 +7,21 @@ */ import classNames from 'classnames'; -import React, { useCallback, useState } from 'react'; -import { IndexPatternField } from '../../../../../data/public'; +import React, { Fragment, useState } from 'react'; import { FieldRecord } from './table'; import { trimAngularSpan } from './table_helper'; import { DocViewTableRowBtnCollapse } from './table_row_btn_collapse'; -import { DocViewTableRowIconUnderscore } from './table_row_icon_underscore'; const COLLAPSE_LINE_LENGTH = 350; -type TableFieldValueProps = FieldRecord['value'] & { - fieldName: string; - fieldMapping: IndexPatternField | undefined; -}; +type TableFieldValueProps = FieldRecord['value'] & Pick; -export const TableFieldValue = ({ - formattedField, - fieldName, - fieldMapping, -}: TableFieldValueProps) => { +export const TableFieldValue = ({ formattedValue, field }: TableFieldValueProps) => { const [fieldOpen, setFieldOpen] = useState(false); - const value = trimAngularSpan(String(formattedField)); + const value = trimAngularSpan(String(formattedValue)); const isCollapsible = value.length > COLLAPSE_LINE_LENGTH; const isCollapsed = isCollapsible && !fieldOpen; - const displayUnderscoreWarning = !fieldMapping && fieldName.indexOf('_') === 0; const valueClassName = classNames({ // eslint-disable-next-line @typescript-eslint/naming-convention @@ -39,28 +29,23 @@ export const TableFieldValue = ({ 'truncate-by-height': isCollapsible && isCollapsed, }); - const onToggleCollapse = useCallback( - () => setFieldOpen((fieldOpenPrev: boolean) => !fieldOpenPrev), - [] - ); + const onToggleCollapse = () => setFieldOpen((fieldOpenPrev) => !fieldOpenPrev); return ( -
+ {isCollapsible && ( )} - {displayUnderscoreWarning && } - {fieldName ? null :
{fieldName}: 
}
-
+
); }; diff --git a/src/plugins/discover/public/application/components/table/table_columns.tsx b/src/plugins/discover/public/application/components/table/table_columns.tsx index 462362d543e7a..5bd92fe9166e9 100644 --- a/src/plugins/discover/public/application/components/table/table_columns.tsx +++ b/src/plugins/discover/public/application/components/table/table_columns.tsx @@ -18,6 +18,7 @@ export const ACTIONS_COLUMN: EuiBasicTableColumn = { field: 'action', className: 'kbnDocViewer__tableActionsCell', width: '108px', + mobileOptions: { header: false }, name: ( @@ -30,12 +31,12 @@ export const ACTIONS_COLUMN: EuiBasicTableColumn = { ), render: ( { flattenedField, isActive, onFilter, onToggleColumn }: FieldRecord['action'], - { field: { fieldName, fieldMapping } }: FieldRecord + { field: { field, fieldMapping } }: FieldRecord ) => { return ( > = [ { field: 'field', className: 'kbnDocViewer__tableFieldNameCell', + mobileOptions: { header: false }, name: ( @@ -56,19 +58,23 @@ export const MAIN_COLUMNS: Array> = [ ), - render: ({ fieldName, fieldType, fieldMapping, scripted }: FieldRecord['field']) => { - return ( + render: ({ field, fieldType, displayName, fieldMapping, scripted }: FieldRecord['field']) => { + return field ? ( + ) : ( +   ); }, }, { field: 'value', + className: 'kbnDocViewer__tableValueCell', + mobileOptions: { header: false }, name: ( @@ -76,17 +82,8 @@ export const MAIN_COLUMNS: Array> = [ ), - render: ( - { formattedField }: FieldRecord['value'], - { field: { fieldName, fieldMapping } }: FieldRecord - ) => { - return ( - - ); + render: ({ formattedValue }: FieldRecord['value'], { field: { field } }: FieldRecord) => { + return ; }, }, ]; diff --git a/src/plugins/discover/public/application/components/table/table_row_icon_underscore.tsx b/src/plugins/discover/public/application/components/table/table_row_icon_underscore.tsx deleted file mode 100644 index aa0ccbcadfb73..0000000000000 --- a/src/plugins/discover/public/application/components/table/table_row_icon_underscore.tsx +++ /dev/null @@ -1,41 +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 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 React from 'react'; -import { EuiIconTip } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -export function DocViewTableRowIconUnderscore() { - const ariaLabel = i18n.translate( - 'discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedAriaLabel', - { - defaultMessage: 'Warning', - } - ); - const tooltipContent = i18n.translate( - 'discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedTooltip', - { - defaultMessage: 'Field names beginning with {underscoreSign} are not supported', - values: { underscoreSign: '_' }, - } - ); - - return ( - - ); -} diff --git a/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap b/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap index 153438b34eb47..9b3bc3e354005 100644 --- a/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap +++ b/src/plugins/home/public/application/components/__snapshots__/home.test.js.snap @@ -1,39 +1,31 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`home change home route should render a link to change the default route in advanced settings if advanced settings is enabled 1`] = ` -, - "rightSideItems": Array [], } } template="empty" > - - - - - - - - - + `; exports[`home directories should not render directory entry when showOnHomePage is false 1`] = ` -, - "rightSideItems": Array [], } } template="empty" > - - - - - - - - - + `; exports[`home directories should render ADMIN directory entry in "Manage your data" panel 1`] = ` -, - "rightSideItems": Array [], } } template="empty" > - - - - - - - - - -`; - -exports[`home directories should render DATA directory entry in "Ingest your data" panel 1`] = ` -, - "rightSideItems": Array [], + - - - - - - - - - + `; exports[`home directories should render solutions in the "solution section" 1`] = ` -, - "rightSideItems": Array [], } } template="empty" > - - - - - - - - - -`; - -exports[`home header render 1`] = ` -, - "rightSideItems": Array [], - } - } - template="empty" -> - - - - - - - - - + `; -exports[`home header should show "Dev tools" link if console is available 1`] = ` -, - "rightSideItems": Array [], } } template="empty" > - - - - - - - - - -`; - -exports[`home header should show "Manage" link if stack management is available 1`] = ` -, - "rightSideItems": Array [], - } - } - template="empty" -> - - - - - - - - - + `; -exports[`home isNewKibanaInstance should safely handle execeptions 1`] = ` -, - "rightSideItems": Array [], } } template="empty" > - - - - - - - - - -`; - -exports[`home isNewKibanaInstance should set isNewKibanaInstance to false when there are index patterns 1`] = ` -, - "rightSideItems": Array [], - } - } - template="empty" -> - - - - - - - - - + `; exports[`home isNewKibanaInstance should set isNewKibanaInstance to true when there are no index patterns 1`] = ` -, - "rightSideItems": Array [], } } template="empty" > - - - - - - - - - + `; exports[`home should render home component 1`] = ` -, - "rightSideItems": Array [], } } template="empty" > - - - - - - - - - + `; exports[`home welcome should show the normal home page if loading fails 1`] = ` -, - "rightSideItems": Array [], } } template="empty" > - - - - - - - - - + `; exports[`home welcome should show the normal home page if welcome screen is disabled locally 1`] = ` -, - "rightSideItems": Array [], } } template="empty" > - - - - - - - - - + `; exports[`home welcome should show the welcome screen if enabled, and there are no index patterns defined 1`] = ` @@ -712,39 +411,31 @@ exports[`home welcome should show the welcome screen if enabled, and there are n `; exports[`home welcome stores skip welcome setting if skipped 1`] = ` -, - "rightSideItems": Array [], } } template="empty" > - - - - - - - - - + `; diff --git a/src/plugins/home/public/application/components/__snapshots__/synopsis.test.js.snap b/src/plugins/home/public/application/components/__snapshots__/synopsis.test.js.snap index ec192ce1eb32f..8e04e70ed92b6 100644 --- a/src/plugins/home/public/application/components/__snapshots__/synopsis.test.js.snap +++ b/src/plugins/home/public/application/components/__snapshots__/synopsis.test.js.snap @@ -3,7 +3,7 @@ exports[`props iconType 1`] = ` * { - margin-bottom: 0 !important; - margin-top: 0 !important; - } -} diff --git a/src/plugins/home/public/application/components/_index.scss b/src/plugins/home/public/application/components/_index.scss index 5e2f58c7ebefa..8d7139d895714 100644 --- a/src/plugins/home/public/application/components/_index.scss +++ b/src/plugins/home/public/application/components/_index.scss @@ -5,11 +5,9 @@ // homChart__legend--small // homChart__legend-isLoading -@import 'home'; +@import 'welcome'; +@import 'solutions_section'; @import 'add_data'; +@import 'manage_data'; @import 'sample_data_set_cards'; -@import 'solutions_section'; -@import 'synopsis'; -@import 'welcome'; - @import 'tutorial/tutorial'; diff --git a/src/plugins/home/public/application/components/_manage_data.scss b/src/plugins/home/public/application/components/_manage_data.scss new file mode 100644 index 0000000000000..df0219636e17c --- /dev/null +++ b/src/plugins/home/public/application/components/_manage_data.scss @@ -0,0 +1,5 @@ +.homDataManage__item { + @include euiBreakpoint('l', 'xl') { + max-width: calc(50% - #{$euiSizeM * 2}); + } +} \ No newline at end of file diff --git a/src/plugins/home/public/application/components/_solutions_section.scss b/src/plugins/home/public/application/components/_solutions_section.scss index 965447f689f98..d563b13ac7e07 100644 --- a/src/plugins/home/public/application/components/_solutions_section.scss +++ b/src/plugins/home/public/application/components/_solutions_section.scss @@ -1,98 +1,23 @@ -.homSolutions__content { - min-height: $euiSize * 16; - - @include euiBreakpoint('xs', 's') { - flex-direction: column; - } -} - -.homSolutions__group { - @include euiBreakpoint('l') { - max-width: calc(75% - #{$euiSizeM * 2}); - } - - @include euiBreakpoint('xl') { +.homSolutions__item { + @include euiBreakpoint('l', 'xl') { max-width: calc(50% - #{$euiSizeM * 2}); } } -.homSolutionPanel { - border-radius: $euiBorderRadius; - color: inherit; - flex: 1; - transition: all $euiAnimSpeedFast $euiAnimSlightResistance; - - &:hover, - &:focus { - @include euiSlightShadowHover; - transform: translateY(-2px); - - .homSolutionPanel__title { - text-decoration: underline; - } +.euiCard__image { + .homSolutionPanel & { + background-color: $euiColorPrimary; } -} - -.homSolutionPanel, -.homSolutionPanel__inner { - display: flex; - flex-direction: column; -} - -.homSolutionPanel__inner { - overflow: hidden; -} - -.homSolutionPanel__icon { - background-color: $euiColorEmptyShade !important; - box-shadow: none !important; - margin: 0 auto $euiSizeS; - padding: $euiSizeS; -} - -.homSolutionPanel__subtitle { - margin-top: $euiSizeXS; -} - -.homSolutionPanel__content { - flex-direction: column; - justify-content: center; - padding: $euiSize; - - @include euiBreakpoint('xs', 's') { - text-align: center; - } -} - -.homSolutionPanel__header { - color: $euiColorEmptyShade; - padding: $euiSize; - background-color: $euiColorPrimary; - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQYAAAFjCAYAAADfDKXVAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAfiSURBVHgB7d1JblRZGobh45SrhJiWGOQ6EENLSKyABTBmOcwRq2EnTKtUdInBuEln/OkMdB3HTTjiNqd5HinA3YBUSq++24UPUkq/J+jE+/fv09HRURrD27dv0+vXr1OLfksAG4QByAgDkBEGICMMQEYYgIwwABlhADLCAGSEAcgIA5ARBiAjDEBGGICMMAAZYQAywgBkhAHICAOQEQZg05/CAGy6FAYgIwzApnNhADLCAGy6EAZgkzAAGVclgIyTj0DGYgAyZ8IADP2ZLAZgw1n8IQzA0Hn8IQzA0EX8IQzAkEMJICMMwDURhcv44DDBlp4+fZrevHmTxvLq1av04cOHRDHO1h8IA1t7/PhxOjo6SjTrdP2BQwlg7ddiEAYgxGXK8/UnwgCEs+EnwgCEk+EnwgCE0+EnwgDEYcTF8AvCAJxufkEYgO+bXxAG6Nu1y5RrwgB9O7npi8IAfTu+6YvCAP3KrkasCQP06/i2bwgD9Ov0tm8IA/QpLlFe3PZNYYA+/bjrm8IA/YmTjqd3/YAwQH+O7/sBYYC+xHmFH/f9kDBAX/7Y5oeEAfqx1VoIwgD9+L7tDwoD9GHrtRCEAfoQ5xYutv1hYYD2PWgtBGGA9m11JWJIGKBtD14LQRigbf9POxAGaNedT1DeRRigTRGEb2lHwgBtetDlyU3CAO35mXY44TgkDNCWy9XrS9qTMEBb9jqEWBMGaEdchbj3TVi2IQzQhr2uQmwSBmjD5zTCIcSaMED94rzCaRqRMEDd4tLkaIcQa8IA9YpDh70vTd5EGKBOcb9CPCA12nmFIWGAOo16snGTMEB94mTjSZqQMEBdIgqjn2zcJAxQj7ircfIoBGGAOsTtzl/TTIQByhe/nXqSy5K3EQYoW0Rhp/dt3MdhAkoVdzV+Slf3LMxKGKBMcU5h1sOHIYcSUJ5FoxAsBijLLPcp3EcYoBxxOXKUd2DalzDA8uLk4sc08nsq7EMYYFnxINRkT0nuShhgOfEgVDwlOfvlyPsIAyyjmPMJNxEGmFccMsRKKOZ8wk2EAeZT7KHDJmGA6UUI4v6EYg8dNgkDTCued4i7GIu66nAfYYBpVLcShoQBxlflShgSBhhP1SthSBhgHNWvhCFhgP00sxKGhAF29OTJkyKfcxiDN2qBHb18+TIOH5qLQhAGICMMQEYYgIwwABlhADLCAGSEAcgIA5ARBiAjDEBGGICMMNCTy0ePHhX/RqwlEAZ6EQ88/e/Zs2dniXt57JrWNfl+CVMTBloW6+BTavTR6CkJA62KlfAtsRNhoDWxEr788zc7EgZaYiWMRBhogZUwMmGgdlbCBISBWlkJExIGamQlTEwYqImVMBNhoBZx5+LXxCyEgdLFXYufV6/TxGyEgZLFSojzCZ6InJkwUCIrYWHCQGmshAIIA6WwEgoiDJTASiiMMLCYw8NDK6FQ3tqNxbx79+5jEoUiCQOLef78uUOHQgkDkBEGICMMQEYYgIwwABlhADLCAGSEAcgIA5ARBiAjDEBGGNjWxYsXL/5IdOFg9fo9wd3+fr+Ey8vLf63+/k8az38PDg5m/RX1q/+G+Pf/O43j++rf/yU1yPsxcBfvl9ApYeA23lWpY8LAJisBYeAaK4G/CQMhQhAr4SRBEgauYhBRsBL4RRj6ZSVwK2Hok5XAnYShL1YCWxGGflgJbE0Y2mcl8GDC0DYrgZ0IQ5usBPYiDO35uXrFE3+zPrVIW4ShHbES4nbm4wR7EoY2WAmMShjqZiUwCWGol5XAZIShPlYCkxOGulgJzEIY6mAlMCthKJ+VwOyEoVxWAosRhjJZCSxKGMpiJVAEYSiHlUAxhGF5VgLFEYZlWQkUSRiWYSVQNGGYn5VA8YRhPlYC1RCGeVgJVEUYpmUlUCVhmI6VQLWEYXxWAtUThnFZCTRBGMZhJdAUYdiflUBzhGF3VgLNEobdnK1en5KVQKOE4eFiJXxL0DBh2F6shC///A1NE4btWAl0RRjuZiXQJWG4nZVAt4QhZyXQPWG4zkqAJAxrVgIMCMPVnYtfE/BLz2GIuxY/r16nCbim1zDESojzCZeJ3sTh4lj/389To3oLg5XQuYODA4eNW+gpDFYCbKmHMFgJ8ECth8FKgB20GgYrAfbQYhisBNhTS2GwEmAkrYTBSoAR1R4GKwEmUHMYrASYSI1hsBJgYrWFwUqAGdQSBisBZlRDGKwEmFnJYbASYCGlhsFKgAWVFgYrAQpQUhisBChECWGwEqAwS4fBSoACLRUGKwEKtkQYrAQo3JxhsBKgEnOFwUqAikwdhghBrISTBFRjyjBEDCIKVgJUZoowOJcAlRs7DM4lQAPGCsPP1etbshKgCfuGIQ4bYiH8SEAzdg1DHCrEQjhODhugOQ8NgyBAB7YNgyBAR+4LgyBAh24Lg6sM0LFhGGIRfE9XdywKAnQswrBeB2fJ4QKQrsLwMQEM/JYANggDkBEGICMMQEYYgIwwABlhADLCAGSEAcgIA5ARBiAjDEBGGICMMAAZYQAywgBkhAHICAOQEQYgIwxARhiAjDAAGWEAMg/9bdf0LX4h0XkCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq8hdouPfUCk+KHQAAAABJRU5ErkJggg=='), - url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWgAAAFKCAYAAAAwgcXoAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAjGSURBVHgB7d27ctRaFoDh1WBzsQ9wLkVsngHHfglSIOUdgJQiJSfmIUjhJUwGjiGaU7hmzoB7vLA1GPCt21vSlvR9Vao+5lAFbbf+EktbUgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUaxYAEzCfz/NldX9biTK+zGaz/0aLSv1FAYbg6v52I8r4e39rNdCXAoAqCTRApQQaoFICDVApgQaolEADVEqgASol0ACVEmiASgk0QKUEGqBSAg1QKYEGqJRAA1RKoAEqJdAAlRJogEp5ogowJV/3t/9EGV8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFjELABOMZ/Po6TZrL/sDO29rATA6Vb3t/UoY29/+1f04DDO1w63Ev59uLVGoIGz5GHi9Sjja/QU6EPZvFLv5Uu07FIAUCWBBqiUQANUSqABKiXQAJUSaIBKCTRApQQaoFICDVApgQaolEADVEqgASol0ACVEmiASgl0ANRJoAEqJdAAlfJEFeAs+RSUv6OMsg8FXNw/Ue69/BMAAAAAAAAAAAAAAAAAAAAAQCdmAcBpspNn3flzL1q4EZRAA2PRhLR5vXz4683ryk+/7+j/ixO+vqiM9t6Rr78e899ffvq9za9/FWigdhnNbNVqfA/wyuHr5fgxxqMi0EDfMq4Z3ya4K4fbaMN7XgINdKGJ8OUjW/M1JxBooKSj4c3X5mhYa5bgmwYsoxlHXA0hbo1vJnCW5gRds10Jo4lOCDTws4xvRjiPjvOoeDXohUDDtB09Or4W31dTUAE/CJiWJshNjK8E1RJoGDdBHjCBhvHJCBtZjIAfHAxf7scZ46uHr/brkfCDhGFqVllcCWOL0RJoGIajs+S16Hjfnc3K/XHzefG7cp7b0N6HQEO9qhhdbG5uxosXL6KET58+xb1796IPGef79+/Ho0ePooSXL1/Gq1evok0rAdSkiXIeJVdxgm9tbS22traihJ2dnejTxsZGsffy+vXraJtAQ/+qizJ1EGjoRzNTvhGizAkEGrqVKy56OdHH8Ag0tC9vPpRBXg9RZgECDe3IEGeU82jZOmWWItBQlhEGxQg0XFyG+Lc4CLN9imJ8mGB5ebScc+W8kMTRMsUJNCzGbJnOCDScj5UYdE6g4XR5lHwjHC3TA4GGXx299FqY6Y1Aw3fNagxjDKog0CDMVEqgmbIcX+QY43pAhQSaKXLij0EQaKZEmJfU52OqShvSexFopkCYL2B7ezsePnwYJezu7kZfMsz5FJQPHz5ECe/evYu2OSHCmAkzgybQjJEwMwoCzZgIM6Mi0IxB3ifjZhxc/QejIdAMWX5+84h5PWCEBJohcuUfk+DDzdBklPOo2WeX0fMhZyjyxN/vcTBvhkkQaGq3GgcnAK3MYHIEmlo5AcjkCTQ1MmeGsANQlxxj5DhjNQCBpgr5ObwV7ssMPxBo+macASewU9AX982AMwg0XbM6A85JoOmSi01gAQJNF9xtDpYg0LTNSUBYkp2GtuRRc44znASEJQk0bXDUDAXYgSjJUTMUJNCU4qgZCrMzcVGOmqElAs1F5LK5jLPPEbTAjsUyXA0IHRBoFuVqQOiIQLOIPGr+LZic2axcKubzefSl5PtIbb+XSwFny6Plv0KcJ2lrayv29vaKbO/fv4++ZJyfPHlS7L08fvw42rYScDonAqEnAs1JnAiEngk0x8mRxh/h2YDQK4HmZ2txcGtQIw3omUBzVIbZSAMqIdAkIw2okEDjwhOolEBPW44zbgZQJYGepjwBeGt/ux5AtQR6epqrAo00oHICPS05b/4zLKGDQRDo6TBvhoER6GmwvhkGSKDHzfpmGDCBHi8nA2HgBHqcnAyEERDo8XEyEEZCoMfFI6loRZ+PqSptSO/FP4HHI++n4cpAWrGxsRGl7OzsRF/W1tbi9u3bUcLHjx9jd3c32iTQw5c/wzwZaKUGjIxAD5uVGjBiAj1c4gwjJ9DDlOOMjLOfH4yYHXx4PDMQJsJOPiwZ51sBTIJAD4c1zjAxAj0M4gwTJND1E2eYKIGum/s4w4QJdL1cug0TJ9B1EmdAoCskzsA3Al0XcQb+T6DrIc7ADwS6DuIM/EKg+yfOwLEEul/iDJxIoPsjzsCpLgV9yCsExRk4lUB3L++t4fJt4ExGHN1y4yPg3AS6O+IMLESguyHOwMLMoNuXj6kSZ2BhAt2ua+EZgsCSBLo9q3Gw1hlgKStBGy7vb3+EGT8jsL6+Hpubm1HK27dvoy8bGxvfthJ2dna+bW0S6PIyzn8dvsLg3b17N968eRMlZNDu3LkTfZjNZvHgwYN49uxZlPD06dN4/vx5tMmIo6w8YhZnoAiBLuvPEGegEIEuJ++vcSUAChHoMtxfAyhOoC8uw+xCFKA4gb6YXOt8MwBaINDLa9Y6A7RCoJdjOR3QOoFeTl7CLc5AqwR6cbli41oAtEygF2PFBtAZgT4/KzaATgn0+VixAXROoM8n4+ykINApgT5bjjVWA6BjAn269XCPDaAnAn2yHGk4KQj8Yj6fRxc8UeV4zVNRYPJ2d3dje3s7Smj7EVFn+fz5c5H3koHO70vbPDPveHlS0MUoQK+MOH7lSkGgCgL9o3wiiisFgSoI9Hc5d/49ACoh0N+5Qx1QFYE+kHNnD3wFqiLQ5s5ApaYeaHNnoFpTD3SONsydgSpNOdB5j43rAVCpqQbafTaA6k010O6zAVRvioE2dwYGYWqBtqQOGIwpBdqSOmBQphRoow1gUKYS6OthSR0wMFMIdB413wiAgZlCoI02gEEae6CNNoDBGnOgjTaAQRtzoI02gEEba6CNNoDBG2OgZ2G0AYzA/wBP5hsF50HhogAAAABJRU5ErkJggg=='); - background-repeat: no-repeat; - background-position: top 0 left 0, bottom 0 right 0; - background-size: $euiSizeXL * 4, $euiSizeXL * 6; .homSolutionPanel--enterpriseSearch & { - background-color: $euiColorSecondary; - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAABKCAYAAADJwhY8AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAANMSURBVHgBzZp7buIwEMYHKC1QuqsF7Up7Hs4Al+Ff4DCcAonrtPuAPjcL1B/YyA0p2JkZK580SuQm6Y+xx4+xiSqumrG+d7/z/rYx9t+WZdZ2lFhXxq4jngfkP2Ov9qquq8jnm9Zu6eBNgD6TImwsoC80ibY1NIe1sRcSVsPYHfFVN9Yy1jG2pUPbFZEUoJMP+kYHWJakAZ0AemuvrPZZJ10B8jsdHFFKWh70BSe07X1GkUoBCCHib+w1qsq1qzivLkU6JDUgTSaT7m63C4ZMCmjgaDwe4zYYMhmgB+cEyPal95IAFsA5fTGQZ4dbdcAzcO7/9wxk7bMH8AfMTvJDUsO+fG2tSfJwvp5qtdqKPgEMEYABekeBo0IEnNODgTzpI0MBfaFhnwUtAQdlBvA+X1gG0Kmw0y0J57QykE9+ASdIHo3hF29cARMO6uYDhhvFGPwfjG0E4BxPxy/gVPFR8/m8MRwOsTosPa3y9KEtigBCpmoAJwV5jGixjtp8EG3xD8noOASKjiT2V6+Jr5YLFo2hDuvkDfEErqa7EZXx4naxWKyIr9b+e6QkU0U/iBcw+2jWnM08E09NtMMqA0JNNUC0ReInlRraE9bodXBOzaoD1rQBuVku9SpmZ7eSL9wjVa864LbqgOpVzJ4bagNyv7/RBozZgymSOuAN8bRTA7RrFM4+DJRpepBbvZAqIDf3jQmrThXbxCS3i5FdduYksXOAjUp5QJt75npvK75whwwclopd4uvV3YgB2m7lG8nouK0rAojkEcnlZTZ+plUCsDEajfom9SYBB31InXA/is64h+8sl0vKsowGgwExBO/99QvKDkXISHQpFxCz2Wx/nU6nVFIniacyHkTOpGevJ2J48sR7UKgHnceCRoiSnvxVVOj2P6C6ZyjHVKljwb7a54KDKtKT2MgpPDECD/ZJSYGedEdaCqW+437BkzgcdG/zOIVKciTgDOTawL2dezfVmYUiyHV+V6lIyQAhDxJwjyHvoPv4SWmFgAiCg1JnFqLgIO6qK1SIVowS0afjUgCin/tNJZOZ2oCIUlRr6aOlWoDwFjZz2CczpQHhKXjsYv8WKilAeAwBgL0R0ZPCHECAvFirzCFb/5hyknPV7zL4DLH0CVGgAAAAAElFTkSuQmCC'), - url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAA+CAYAAACbQR1vAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACBSURBVHgB7dpBDcAwDMVQdyzGH9xojEQjtbIfBEu5fGUx62WPjyEPcgVArgDIFQC5AiBXAOQKgFwBkCsAcgVATh9gsW+4vFIngFwBkCsAcgVArgDIFQC5AiBXAORahJjVm9zpCoBcAZArAHIFQK4AyBUAuQIgVwDkCoBcAZDTB/gBLrgCk8OhuvYAAAAASUVORK5CYII='); - background-position: top $euiSizeS left 0, bottom $euiSizeS right $euiSizeS; - background-size: $euiSize * 1.25, $euiSizeXL; + background-color: $euiColorWarning; } .homSolutionPanel--observability & { background-color: $euiColorAccent; - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADNSURBVHgB7duxCcIAEEDR08IFrGyz/0pZwVYEIcYF/FWKwHtwXP/hyrvMQbZtu+3rPid3Hf4SKAgUBAoCBYGCQEGgIFAQKAgUBAoCBYGCQEGgIFAQKAgUBAoCBYGCQEGgIFAQKAgUBAoCBYGCQEGgIFAQKAgUBAoCBYGCQEGgIFAQKAgUBAoCBYGCQEGgcJmDnt6WZfms6/qak/sFeswx3vs85+ScWBAoCBQECgIFgYJAQaAgUBAoCBQECgIFgYJAQaAgUBAoCBQECgKFLx4KCqcIFEJnAAAAAElFTkSuQmCC'); - background-position: top $euiSizeS right $euiSizeS; - background-size: $euiSizeL * 1.5; } .homSolutionPanel--securitySolution & { - background-color: $euiColorDarkestShade; - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGIAAABiCAYAAACrpQYOAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAKRSURBVHgB7d3tSgMxEIXhMeqPQoWC9f4vte5Z3dLvTbKZzCQ5LyxqKwg+tNjdUxQR+RJmXpiOvRDDvPD/kRjGhYvPgbETZlK4+fogxDApPLiNGAaFJ7cD40NYtcKL+76FGNUKK/cRo1Ih4n5gvAtTbQ1i+R5iKBcDgYBADMViIRAxFEuBQMRQKhUCEUOhHAi0YLwJK1IuBCJGwbZAoE8hRpG2QqAFg22oBAQCxkFYdqUgEE6dEyOzkhCIGJmVhkDA4PXvxDQgEMcIiWlBIGIkpAmBiBGZNgQiRkQ1IBAxVqoFgThge1FNCMTN1JNqQyBiPMgCAnHAdpMVBOJm6iJLCA7YLrKEWH4+r3+LPYQIMeY8QKDhlyFeINDQGJ4g0LAY3iDQkBgeIdBwmymvEGgoDM8QaJgBm3cINMSArQUI1P2ArRUI1PVmqiUI1C1GaxCoywFbixCouzFCqxCIyxBPnU6nLjBafkQs7YExHdJyPUCg+WmqZYxeIBAwmv3TticItJseFYcWHxm9QaD5RV9rGD1CIGAcJ4xmztr2CoHms7atYPQMgc4Y3p+qeodAwPiZjnfPGCNAoPMgwSvGKBDINcZIEMgtxmgQyCXGiBDIHcaoEAgYx+n48IAxMgQ6v1nGGmN0COQCgxB/4feAF307KwxCXDe/9dgCgxD3mWAQ4nHAqHrplRDPq3odnBCvq4ZBiPWqYBAiLnUMQsSnikGItObtlAYGIdJTmesQIq/iGITIr+h2ihDbKradIsT2imynCFGmzdspQpRr03VwQpQtG4MQ5cvCIIROyRiE0CsJgxC6RW+nCKFf1FyHEHVaxSBEvV5upwhRv4dzHULYdIdBCLuutlOEsO18HZz/u8E+YMgvrbKfmp8y7IEAAAAASUVORK5CYII='); - background-position: top $euiSizeS left $euiSizeS; - background-size: $euiSizeL * 2; + background-color: $euiColorSecondary; } -} +} \ No newline at end of file diff --git a/src/plugins/home/public/application/components/_synopsis.scss b/src/plugins/home/public/application/components/_synopsis.scss deleted file mode 100644 index 3eac2bc9705e0..0000000000000 --- a/src/plugins/home/public/application/components/_synopsis.scss +++ /dev/null @@ -1,16 +0,0 @@ -.homSynopsis__card { - &.homSynopsis__card--noPanel { - // SASSTODO: Add a prop to EuiCard to allow for no "panel" style - border: none; - box-shadow: none; - } - - .homSynopsis__cardTitle { - display: flex; - } - - // SASSTODO: Fix in EUI - .euiCard__content { - padding-top: 0 !important; - } -} diff --git a/src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap b/src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap index 5724d46fca10c..26b5697f008b6 100644 --- a/src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap +++ b/src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap @@ -1,100 +1,93 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`AddData render 1`] = ` -
- +
- - -

+ - -

-
-
- -
- + + + + + +

+ +

+
+ + - -
-
-
- - + + + + + + + + + + + + + + + + + +
+ - - - - - - - - - - - -
+ `; diff --git a/src/plugins/home/public/application/components/add_data/add_data.test.tsx b/src/plugins/home/public/application/components/add_data/add_data.test.tsx index 10119632c01b7..3e324685c8402 100644 --- a/src/plugins/home/public/application/components/add_data/add_data.test.tsx +++ b/src/plugins/home/public/application/components/add_data/add_data.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { AddData } from './add_data'; import { shallowWithIntl } from '@kbn/test/jest'; -import { FeatureCatalogueEntry, FeatureCatalogueCategory } from '../../../services'; +import { ApplicationStart } from 'kibana/public'; jest.mock('../app_navigation_handler', () => { return { @@ -27,45 +27,18 @@ beforeEach(() => { jest.clearAllMocks(); }); -const addBasePathMock = jest.fn((path: string) => (path ? path : 'path')); +const applicationStartMock = ({} as unknown) as ApplicationStart; -const mockFeatures: FeatureCatalogueEntry[] = [ - { - category: FeatureCatalogueCategory.DATA, - description: 'Ingest data from popular apps and services.', - showOnHomePage: true, - icon: 'indexOpen', - id: 'home_tutorial_directory', - order: 500, - path: '/app/home#/tutorial_directory', - title: 'Ingest data', - }, - { - category: FeatureCatalogueCategory.ADMIN, - description: 'Add and manage your fleet of Elastic Agents and integrations.', - showOnHomePage: true, - icon: 'indexManagementApp', - id: 'ingestManager', - order: 510, - path: '/app/ingestManager', - title: 'Add Elastic Agent', - }, - { - category: FeatureCatalogueCategory.DATA, - description: 'Import your own CSV, NDJSON, or log file', - showOnHomePage: true, - icon: 'document', - id: 'ml_file_data_visualizer', - order: 520, - path: '/app/ml#/filedatavisualizer', - title: 'Upload a file', - }, -]; +const addBasePathMock = jest.fn((path: string) => (path ? path : 'path')); describe('AddData', () => { test('render', () => { const component = shallowWithIntl( - + ); expect(component).toMatchSnapshot(); }); diff --git a/src/plugins/home/public/application/components/add_data/add_data.tsx b/src/plugins/home/public/application/components/add_data/add_data.tsx index e20abab271f29..97ba28a04a07e 100644 --- a/src/plugins/home/public/application/components/add_data/add_data.tsx +++ b/src/plugins/home/public/application/components/add_data/add_data.tsx @@ -7,90 +7,115 @@ */ import React, { FC, MouseEvent } from 'react'; -import PropTypes from 'prop-types'; -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiImage, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { METRIC_TYPE } from '@kbn/analytics'; -import type { FeatureCatalogueEntry } from '../../../services'; +import { ApplicationStart } from 'kibana/public'; import { createAppNavigationHandler } from '../app_navigation_handler'; // @ts-expect-error untyped component import { Synopsis } from '../synopsis'; import { getServices } from '../../kibana_services'; +import { RedirectAppLinks } from '../../../../../kibana_react/public'; interface Props { addBasePath: (path: string) => string; - features: FeatureCatalogueEntry[]; + application: ApplicationStart; + isDarkMode: boolean; } -export const AddData: FC = ({ addBasePath, features }) => { +export const AddData: FC = ({ addBasePath, application, isDarkMode }) => { const { trackUiMetric } = getServices(); return ( -
- - - -

- -

-
-
+ <> +
+ + + +

+ +

+
- -
- - - -
-
-
+ - + +

+ +

+
- - {features.map((feature) => ( - - { - trackUiMetric(METRIC_TYPE.CLICK, `ingest_data_card_${feature.id}`); - createAppNavigationHandler(feature.path)(event); - }} - description={feature.description} - iconType={feature.icon} - title={feature.title} - url={addBasePath(feature.path)} - wrapInPanel + + + + + + {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} + { + trackUiMetric(METRIC_TYPE.CLICK, 'home_tutorial_directory'); + createAppNavigationHandler('/app/home#/tutorial_directory')(event); + }} + > + + + + + + + + + + + + + + + - ))} - -
- ); -}; +
+
-AddData.propTypes = { - addBasePath: PropTypes.func.isRequired, - features: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.string.isRequired, - title: PropTypes.string.isRequired, - description: PropTypes.string.isRequired, - icon: PropTypes.string.isRequired, - path: PropTypes.string.isRequired, - showOnHomePage: PropTypes.bool.isRequired, - category: PropTypes.string.isRequired, - order: PropTypes.number, - }) - ), + + + ); }; diff --git a/src/plugins/home/public/application/components/home.js b/src/plugins/home/public/application/components/home.js index fdaaeef01f47a..e9e996cac39ef 100644 --- a/src/plugins/home/public/application/components/home.js +++ b/src/plugins/home/public/application/components/home.js @@ -9,14 +9,9 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; -import { - KibanaPageTemplate, - overviewPageActions, - OverviewPageFooter, -} from '../../../../../../src/plugins/kibana_react/public'; +import { KibanaPageTemplate, OverviewPageFooter } from '../../../../kibana_react/public'; import { HOME_APP_BASE_PATH } from '../../../common/constants'; import { FeatureCatalogueCategory } from '../../services'; import { getServices } from '../kibana_services'; @@ -113,10 +108,10 @@ export class Home extends Component { .sort((directoryA, directoryB) => directoryA.order - directoryB.order); renderNormal() { - const { addBasePath, solutions, directories } = this.props; + const { addBasePath, solutions } = this.props; const { application, trackUiMetric } = getServices(); + const isDarkMode = getServices().uiSettings?.get('theme:darkMode') || false; const devTools = this.findDirectoryById('console'); - const addDataFeatures = this.getFeaturesByCategory(FeatureCatalogueCategory.DATA); const manageDataFeatures = this.getFeaturesByCategory(FeatureCatalogueCategory.ADMIN); // Show card for console if none of the manage data plugins are available, most likely in OSS @@ -128,41 +123,20 @@ export class Home extends Component { , - rightSideItems: overviewPageActions({ - addBasePath, - application, - showDevToolsLink: true, - showManagementLink: true, - }), + bottomBorder: false, + pageTitle: , }} template="empty" > - {solutions.length ? ( - - ) : null} - - - - - - - - - - - -
<> - {timelineId !== TimelineId.active && eventType === 'signal' && ( = ({ /> )} - {[ - TimelineId.detectionsPage, - TimelineId.detectionsRulesDetailsPage, - TimelineId.active, - ].includes(timelineId as TimelineId) && - timelinesUi.getAddToCasePopover(addToCaseActionProps)} = ({ onRuleChange={onRuleChange} /> - {timelinesUi.getAddToCaseAction(addToCaseActionProps)} ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap index b008f95285f23..6050263fff638 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap @@ -2,7 +2,7 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = ` { expect(wrapper.find('[data-test-subj="pin"]').exists()).toBe(false); }); - test('it render AddToCaseAction if timelineId === TimelineId.detectionsPage', () => { - const wrapper = mount(, { - wrappingComponent: TestProviders, - }); - - expect(wrapper.find('[data-test-subj="add-to-case-action"]').exists()).toBeTruthy(); - }); - - test('it render AddToCaseAction if timelineId === TimelineId.detectionsRulesDetailsPage', () => { - const wrapper = mount( - , - { - wrappingComponent: TestProviders, - } - ); - - expect(wrapper.find('[data-test-subj="add-to-case-action"]').exists()).toBeTruthy(); - }); - - test('it render AddToCaseAction if timelineId === TimelineId.active', () => { - const wrapper = mount(, { - wrappingComponent: TestProviders, - }); - - expect(wrapper.find('[data-test-subj="add-to-case-action"]').exists()).toBeTruthy(); - }); - - test('it does NOT render AddToCaseAction when timelineId is not in the allowed list', () => { - const wrapper = mount(, { - wrappingComponent: TestProviders, - }); - - expect(wrapper.find('[data-test-subj="add-to-case-action"]').exists()).toBeFalsy(); - }); - test('it renders a custom control column in addition to the default control column', () => { const wrapper = mount( { }); }); - describe('isInvestigateInResolverActionEnabled', () => { - it('returns false if agent.type does not equal endpoint', () => { - const data: Ecs = { _id: '1', agent: { type: ['blah'] } }; - - expect(isInvestigateInResolverActionEnabled(data)).toBeFalsy(); - }); - - it('returns false if agent.type does not have endpoint in first array index', () => { - const data: Ecs = { _id: '1', agent: { type: ['blah', 'endpoint'] } }; - - expect(isInvestigateInResolverActionEnabled(data)).toBeFalsy(); - }); - - it('returns false if process.entity_id is not defined', () => { - const data: Ecs = { _id: '1', agent: { type: ['endpoint'] } }; - - expect(isInvestigateInResolverActionEnabled(data)).toBeFalsy(); - }); - - it('returns true if agent.type has endpoint in first array index', () => { - const data: Ecs = { - _id: '1', - agent: { type: ['endpoint', 'blah'] }, - process: { entity_id: ['5'] }, - }; - - expect(isInvestigateInResolverActionEnabled(data)).toBeTruthy(); - }); - - it('returns false if multiple entity_ids', () => { - const data: Ecs = { - _id: '1', - agent: { type: ['endpoint', 'blah'] }, - process: { entity_id: ['5', '10'] }, - }; - - expect(isInvestigateInResolverActionEnabled(data)).toBeFalsy(); - }); - - it('returns false if entity_id is an empty string', () => { - const data: Ecs = { - _id: '1', - agent: { type: ['endpoint', 'blah'] }, - process: { entity_id: [''] }, - }; - - expect(isInvestigateInResolverActionEnabled(data)).toBeFalsy(); - }); - }); - describe('getPinOnClick', () => { const eventId = 'abcd'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx index dd701aa284997..8ddea99cddaa0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx @@ -5,22 +5,16 @@ * 2.0. */ -import React, { useCallback, useMemo } from 'react'; -import { get, isEmpty } from 'lodash/fp'; -import { useDispatch } from 'react-redux'; +import { isEmpty } from 'lodash/fp'; import { Ecs } from '../../../../../common/ecs'; import { TimelineItem, TimelineNonEcsData } from '../../../../../common/search_strategy'; -import { setActiveTabTimeline, updateTimelineGraphEventId } from '../../../store/timeline/actions'; import { TimelineEventsType, TimelineTypeLiteral, TimelineType, - TimelineId, - TimelineTabs, } from '../../../../../common/types/timeline'; import { OnPinEvent, OnUnPinEvent } from '../events'; -import { ActionIconItem } from './actions/action_icon_item'; import * as i18n from './translations'; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -129,51 +123,6 @@ export const getEventType = (event: Ecs): Omit => { return 'raw'; }; -export const isInvestigateInResolverActionEnabled = (ecsData?: Ecs) => - (get(['agent', 'type', 0], ecsData) === 'endpoint' || - (get(['agent', 'type', 0], ecsData) === 'winlogbeat' && - get(['event', 'module', 0], ecsData) === 'sysmon')) && - get(['process', 'entity_id'], ecsData)?.length === 1 && - get(['process', 'entity_id', 0], ecsData) !== ''; - -interface InvestigateInResolverActionProps { - ariaLabel?: string; - timelineId: string; - ecsData: Ecs; -} - -const InvestigateInResolverActionComponent: React.FC = ({ - ariaLabel = i18n.ACTION_INVESTIGATE_IN_RESOLVER, - timelineId, - ecsData, -}) => { - const dispatch = useDispatch(); - const isDisabled = useMemo(() => !isInvestigateInResolverActionEnabled(ecsData), [ecsData]); - const handleClick = useCallback(() => { - dispatch(updateTimelineGraphEventId({ id: timelineId, graphEventId: ecsData._id })); - if (timelineId === TimelineId.active) { - dispatch(setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.graph })); - } - }, [dispatch, ecsData._id, timelineId]); - - return ( - - ); -}; - -InvestigateInResolverActionComponent.displayName = 'InvestigateInResolverActionComponent'; - -export const InvestigateInResolverAction = React.memo(InvestigateInResolverActionComponent); - export const ROW_RENDERER_CLASS_NAME = 'row-renderer'; export const NOTES_CONTAINER_CLASS_NAME = 'notes-container'; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.ts index 32ab80fb67684..10db3372e0891 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.ts @@ -6,9 +6,9 @@ */ import type { estypes } from '@elastic/elasticsearch'; +import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; import { metadataCurrentIndexPattern } from '../../../../common/endpoint/constants'; import { KibanaRequest } from '../../../../../../../src/core/server'; -import { esKuery } from '../../../../../../../src/plugins/data/server'; import { EndpointAppContext } from '../../types'; export interface QueryBuilderOptions { @@ -123,9 +123,7 @@ function buildQueryBody( }; if (request?.body?.filters?.kql) { - const kqlQuery = esKuery.toElasticsearchQuery( - esKuery.fromKueryExpression(request.body.filters.kql) - ); + const kqlQuery = toElasticsearchQuery(fromKueryExpression(request.body.filters.kql)); const q = []; if (filterUnenrolledAgents || filterStatusAgents) { q.push(idFilter); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts index e8ef18ec798ae..6c0b9f4b8236e 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts @@ -464,6 +464,7 @@ const allowlistProcessFields: AllowlistFields = { args: true, name: true, executable: true, + code_signature: true, command_line: true, hash: true, pid: true, @@ -555,8 +556,10 @@ const allowlistEventFields: AllowlistFields = { data_stream: true, ecs: true, elastic: true, - // behavioral protection re-nests some field sets under events.* + // behavioral protection re-nests some field sets under events.* (< 7.15) events: allowlistBaseEventFields, + // behavioral protection re-nests some field sets under Events.* (>=7.15) + Events: allowlistBaseEventFields, rule: { id: true, name: true, diff --git a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts index 182e7cd5bcabf..5dbea8e2f4dee 100644 --- a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts +++ b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts @@ -21,6 +21,9 @@ import { asTaskPollingCycleEvent, asTaskRunEvent, TaskPersistence } from './task import { TaskRunResult } from './task_running'; import { TaskPoolRunResult } from './task_pool'; import { TaskPoolMock } from './task_pool.mock'; +import { executionContextServiceMock } from '../../../../src/core/server/mocks'; + +const executionContext = executionContextServiceMock.createSetupContract(); describe('EphemeralTaskLifecycle', () => { function initTaskLifecycleParams({ @@ -37,6 +40,7 @@ describe('EphemeralTaskLifecycle', () => { const opts: EphemeralTaskLifecycleOpts = { logger: taskManagerLogger, definitions: new TaskTypeDictionary(taskManagerLogger), + executionContext, config: { enabled: true, max_workers: 10, diff --git a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.ts b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.ts index ce719ebed36e4..ded9091b4f226 100644 --- a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.ts +++ b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.ts @@ -7,7 +7,7 @@ import { Subject, Observable, Subscription } from 'rxjs'; import { filter } from 'rxjs/operators'; -import { Logger } from '../../../../src/core/server'; +import { Logger, ExecutionContextStart } from '../../../../src/core/server'; import { Result, asErr, asOk } from './lib/result_type'; import { TaskManagerConfig } from './config'; @@ -28,6 +28,7 @@ export interface EphemeralTaskLifecycleOpts { elasticsearchAndSOAvailability$: Observable; pool: TaskPool; lifecycleEvent: Observable; + executionContext: ExecutionContextStart; } export type EphemeralTaskInstanceRequest = Omit; @@ -46,6 +47,7 @@ export class EphemeralTaskLifecycle { private config: TaskManagerConfig; private middleware: Middleware; private lifecycleSubscription: Subscription = Subscription.EMPTY; + private readonly executionContext: ExecutionContextStart; constructor({ logger, @@ -54,6 +56,7 @@ export class EphemeralTaskLifecycle { pool, lifecycleEvent, config, + executionContext, }: EphemeralTaskLifecycleOpts) { this.logger = logger; this.middleware = middleware; @@ -61,6 +64,7 @@ export class EphemeralTaskLifecycle { this.pool = pool; this.lifecycleEvent = lifecycleEvent; this.config = config; + this.executionContext = executionContext; if (this.enabled) { this.lifecycleSubscription = this.lifecycleEvent @@ -179,6 +183,7 @@ export class EphemeralTaskLifecycle { beforeRun: this.middleware.beforeRun, beforeMarkRunning: this.middleware.beforeMarkRunning, onTaskEvent: this.emitEvent, + executionContext: this.executionContext, }); }; } diff --git a/x-pack/plugins/task_manager/server/plugin.ts b/x-pack/plugins/task_manager/server/plugin.ts index 3d3d180fc0665..11746e2da2847 100644 --- a/x-pack/plugins/task_manager/server/plugin.ts +++ b/x-pack/plugins/task_manager/server/plugin.ts @@ -126,7 +126,11 @@ export class TaskManagerPlugin }; } - public start({ savedObjects, elasticsearch }: CoreStart): TaskManagerStartContract { + public start({ + savedObjects, + elasticsearch, + executionContext, + }: CoreStart): TaskManagerStartContract { const savedObjectsRepository = savedObjects.createInternalRepository(['task']); const serializer = savedObjects.createSerializer(); @@ -150,6 +154,7 @@ export class TaskManagerPlugin config: this.config!, definitions: this.definitions, logger: this.logger, + executionContext, taskStore, middleware: this.middleware, elasticsearchAndSOAvailability$: this.elasticsearchAndSOAvailability$!, @@ -160,6 +165,7 @@ export class TaskManagerPlugin config: this.config!, definitions: this.definitions, logger: this.logger, + executionContext, middleware: this.middleware, elasticsearchAndSOAvailability$: this.elasticsearchAndSOAvailability$!, pool: this.taskPollingLifecycle.pool, diff --git a/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts b/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts index aad03951bbb9b..42bccbcbaba89 100644 --- a/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts +++ b/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts @@ -20,7 +20,9 @@ import type { TaskClaiming as TaskClaimingClass } from './queries/task_claiming' import { asOk, Err, isErr, isOk, Result } from './lib/result_type'; import { FillPoolResult } from './lib/fill_pool'; import { ElasticsearchResponseError } from './lib/identify_es_error'; +import { executionContextServiceMock } from '../../../../src/core/server/mocks'; +const executionContext = executionContextServiceMock.createSetupContract(); let mockTaskClaiming = taskClaimingMock.create({}); jest.mock('./queries/task_claiming', () => { return { @@ -69,6 +71,7 @@ describe('TaskPollingLifecycle', () => { middleware: createInitialMiddleware(), maxWorkersConfiguration$: of(100), pollIntervalConfiguration$: of(100), + executionContext, }; beforeEach(() => { diff --git a/x-pack/plugins/task_manager/server/polling_lifecycle.ts b/x-pack/plugins/task_manager/server/polling_lifecycle.ts index 16b15d0c46e3e..847e1f32e8f2a 100644 --- a/x-pack/plugins/task_manager/server/polling_lifecycle.ts +++ b/x-pack/plugins/task_manager/server/polling_lifecycle.ts @@ -9,7 +9,7 @@ import { Subject, Observable, Subscription } from 'rxjs'; import { pipe } from 'fp-ts/lib/pipeable'; import { Option, some, map as mapOptional } from 'fp-ts/lib/Option'; import { tap } from 'rxjs/operators'; -import { Logger } from '../../../../src/core/server'; +import type { Logger, ExecutionContextStart } from '../../../../src/core/server'; import { Result, asErr, mapErr, asOk, map, mapOk } from './lib/result_type'; import { ManagedConfiguration } from './lib/create_managed_configuration'; @@ -53,6 +53,7 @@ export type TaskPollingLifecycleOpts = { config: TaskManagerConfig; middleware: Middleware; elasticsearchAndSOAvailability$: Observable; + executionContext: ExecutionContextStart; } & ManagedConfiguration; export type TaskLifecycleEvent = @@ -73,6 +74,7 @@ export class TaskPollingLifecycle { private store: TaskStore; private taskClaiming: TaskClaiming; private bufferedStore: BufferedTaskStore; + private readonly executionContext: ExecutionContextStart; private logger: Logger; public pool: TaskPool; @@ -100,11 +102,13 @@ export class TaskPollingLifecycle { config, taskStore, definitions, + executionContext, }: TaskPollingLifecycleOpts) { this.logger = logger; this.middleware = middleware; this.definitions = definitions; this.store = taskStore; + this.executionContext = executionContext; const emitEvent = (event: TaskLifecycleEvent) => this.events$.next(event); @@ -227,6 +231,7 @@ export class TaskPollingLifecycle { beforeMarkRunning: this.middleware.beforeMarkRunning, onTaskEvent: this.emitEvent, defaultMaxAttempts: this.taskClaiming.maxAttempts, + executionContext: this.executionContext, }); }; diff --git a/x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts b/x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts index bc1ff0541fdff..358a8003382b0 100644 --- a/x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts +++ b/x-pack/plugins/task_manager/server/task_running/ephemeral_task_runner.ts @@ -14,7 +14,7 @@ import apm from 'elastic-apm-node'; import { withSpan } from '@kbn/apm-utils'; import { identity } from 'lodash'; -import { Logger } from '../../../../../src/core/server'; +import { Logger, ExecutionContextStart } from '../../../../../src/core/server'; import { Middleware } from '../lib/middleware'; import { asOk, asErr, eitherAsync, Result } from '../lib/result_type'; @@ -54,6 +54,7 @@ type Opts = { definitions: TaskTypeDictionary; instance: EphemeralTaskInstance; onTaskEvent?: (event: TaskRun | TaskMarkRunning) => void; + executionContext: ExecutionContextStart; } & Pick; // ephemeral tasks cannot be rescheduled or scheduled to run again in the future @@ -74,6 +75,7 @@ export class EphemeralTaskManagerRunner implements TaskRunner { private beforeRun: Middleware['beforeRun']; private beforeMarkRunning: Middleware['beforeMarkRunning']; private onTaskEvent: (event: TaskRun | TaskMarkRunning) => void; + private readonly executionContext: ExecutionContextStart; /** * Creates an instance of EphemeralTaskManagerRunner. @@ -91,6 +93,7 @@ export class EphemeralTaskManagerRunner implements TaskRunner { beforeRun, beforeMarkRunning, onTaskEvent = identity, + executionContext, }: Opts) { this.instance = asPending(asConcreteInstance(sanitizeInstance(instance))); this.definitions = definitions; @@ -98,6 +101,7 @@ export class EphemeralTaskManagerRunner implements TaskRunner { this.beforeRun = beforeRun; this.beforeMarkRunning = beforeMarkRunning; this.onTaskEvent = onTaskEvent; + this.executionContext = executionContext; } /** @@ -193,8 +197,14 @@ export class EphemeralTaskManagerRunner implements TaskRunner { const stopTaskTimer = startTaskTimer(); try { this.task = this.definition.createTaskRunner(modifiedContext); - const result = await withSpan({ name: 'ephemeral run', type: 'task manager' }, () => - this.task!.run() + const ctx = { + type: 'task manager', + name: `run ephemeral ${this.instance.task.taskType}`, + id: this.instance.task.id, + description: 'run ephemeral task', + }; + const result = await this.executionContext.withContext(ctx, () => + withSpan({ name: 'ephemeral run', type: 'task manager' }, () => this.task!.run()) ); const validatedResult = this.validateResult(result); const processedResult = await withSpan( diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts index e54962c7c8857..b78232d59803e 100644 --- a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts +++ b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts @@ -25,7 +25,9 @@ import { mockLogger } from '../test_utils'; import { throwUnrecoverableError } from './errors'; import { taskStoreMock } from '../task_store.mock'; import apm from 'elastic-apm-node'; +import { executionContextServiceMock } from '../../../../../src/core/server/mocks'; +const executionContext = executionContextServiceMock.createSetupContract(); const minutesFromNow = (mins: number): Date => secondsFromNow(mins * 60); let fakeTimer: sinon.SinonFakeTimers; @@ -103,6 +105,31 @@ describe('TaskManagerRunner', () => { ); expect(mockApmTrans.end).toHaveBeenCalledWith('failure'); }); + test('provides execution context on run', async () => { + const { runner } = await readyToRunStageSetup({ + definitions: { + bar: { + title: 'Bar!', + createTaskRunner: () => ({ + async run() { + return { state: {} }; + }, + }), + }, + }, + }); + await runner.run(); + expect(executionContext.withContext).toHaveBeenCalledTimes(1); + expect(executionContext.withContext).toHaveBeenCalledWith( + { + description: 'run task', + id: 'foo', + name: 'run bar', + type: 'task manager', + }, + expect.any(Function) + ); + }); test('provides details about the task that is running', async () => { const { runner } = await pendingStageSetup({ instance: { @@ -690,6 +717,31 @@ describe('TaskManagerRunner', () => { }); expect(mockApmTrans.end).toHaveBeenCalledWith('failure'); }); + test('provides execution context on run', async () => { + const { runner } = await readyToRunStageSetup({ + definitions: { + bar: { + title: 'Bar!', + createTaskRunner: () => ({ + async run() { + return { state: {} }; + }, + }), + }, + }, + }); + await runner.run(); + expect(executionContext.withContext).toHaveBeenCalledTimes(1); + expect(executionContext.withContext).toHaveBeenCalledWith( + { + description: 'run task', + id: 'foo', + name: 'run bar', + type: 'task manager', + }, + expect.any(Function) + ); + }); test('queues a reattempt if the task fails', async () => { const initialAttempts = _.random(0, 2); const id = Date.now().toString(); @@ -1465,6 +1517,7 @@ describe('TaskManagerRunner', () => { instance, definitions, onTaskEvent: opts.onTaskEvent, + executionContext, }); if (stage === TaskRunningStage.READY_TO_RUN) { diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.ts index 97b40a75a59c4..30aa0cd0c522e 100644 --- a/x-pack/plugins/task_manager/server/task_running/task_runner.ts +++ b/x-pack/plugins/task_manager/server/task_running/task_runner.ts @@ -15,7 +15,11 @@ import apm from 'elastic-apm-node'; import { withSpan } from '@kbn/apm-utils'; import { performance } from 'perf_hooks'; import { identity, defaults, flow } from 'lodash'; -import { Logger, SavedObjectsErrorHelpers } from '../../../../../src/core/server'; +import { + Logger, + SavedObjectsErrorHelpers, + ExecutionContextStart, +} from '../../../../../src/core/server'; import { Middleware } from '../lib/middleware'; import { @@ -93,6 +97,7 @@ type Opts = { store: Updatable; onTaskEvent?: (event: TaskRun | TaskMarkRunning) => void; defaultMaxAttempts: number; + executionContext: ExecutionContextStart; } & Pick; export enum TaskRunResult { @@ -137,6 +142,7 @@ export class TaskManagerRunner implements TaskRunner { private beforeMarkRunning: Middleware['beforeMarkRunning']; private onTaskEvent: (event: TaskRun | TaskMarkRunning) => void; private defaultMaxAttempts: number; + private readonly executionContext: ExecutionContextStart; /** * Creates an instance of TaskManagerRunner. @@ -157,6 +163,7 @@ export class TaskManagerRunner implements TaskRunner { beforeMarkRunning, defaultMaxAttempts, onTaskEvent = identity, + executionContext, }: Opts) { this.instance = asPending(sanitizeInstance(instance)); this.definitions = definitions; @@ -166,6 +173,7 @@ export class TaskManagerRunner implements TaskRunner { this.beforeMarkRunning = beforeMarkRunning; this.onTaskEvent = onTaskEvent; this.defaultMaxAttempts = defaultMaxAttempts; + this.executionContext = executionContext; } /** @@ -261,7 +269,15 @@ export class TaskManagerRunner implements TaskRunner { try { this.task = this.definition.createTaskRunner(modifiedContext); - const result = await withSpan({ name: 'run', type: 'task manager' }, () => this.task!.run()); + const ctx = { + type: 'task manager', + name: `run ${this.instance.task.taskType}`, + id: this.instance.task.id, + description: 'run task', + }; + const result = await this.executionContext.withContext(ctx, () => + withSpan({ name: 'run', type: 'task manager' }, () => this.task!.run()) + ); const validatedResult = this.validateResult(result); const processedResult = await withSpan({ name: 'process result', type: 'task manager' }, () => this.processResult(validatedResult, stopTaskTimer()) diff --git a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx index deb239ba306ea..8bcacc72f4cdd 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx @@ -125,6 +125,7 @@ export interface TGridIntegratedProps { entityType: EntityType; filters: Filter[]; globalFullScreen: boolean; + graphOverlay?: React.ReactNode; headerFilterGroup?: React.ReactNode; filterStatus?: AlertStatus; height?: number; @@ -180,6 +181,7 @@ const TGridIntegratedComponent: React.FC = ({ start, sort, additionalFilters, + graphOverlay = null, graphEventId, leadingControlColumns, trailingControlColumns, @@ -332,13 +334,17 @@ const TGridIntegratedComponent: React.FC = ({ data-timeline-id={id} data-test-subj={`events-container-loading-${loading}`} > + {graphOverlay} {!resolverIsShowing(graphEventId) && additionalFilters} - + {nonDeletedEvents.length === 0 && loading === false ? ( { try { return kueryExpression - ? JSON.stringify( - esKuery.toElasticsearchQuery(esKuery.fromKueryExpression(kueryExpression), indexPattern) - ) + ? JSON.stringify(toElasticsearchQuery(fromKueryExpression(kueryExpression), indexPattern)) : ''; } catch (err) { return ''; @@ -28,11 +31,11 @@ export const convertKueryToElasticSearchQuery = ( export const convertKueryToDslFilter = ( kueryExpression: string, - indexPattern: IIndexPattern -): JsonObject => { + indexPattern: IndexPatternBase +) => { try { return kueryExpression - ? esKuery.toElasticsearchQuery(esKuery.fromKueryExpression(kueryExpression), indexPattern) + ? toElasticsearchQuery(fromKueryExpression(kueryExpression), indexPattern) : {}; } catch (err) { return {}; @@ -71,13 +74,13 @@ export const convertToBuildEsQuery = ({ filters, }: { config: EsQueryConfig; - indexPattern: IIndexPattern; + indexPattern: IndexPatternBase; queries: Query[]; filters: Filter[]; }) => { try { return JSON.stringify( - esQuery.buildEsQuery( + buildEsQuery( indexPattern, queries, filters.filter((f) => f.meta.disabled === false), diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index b6471c07db0db..8829bc66d0fc7 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1543,8 +1543,6 @@ "discover.docTable.tableRow.viewSurroundingDocumentsLinkText": "周りのドキュメントを表示", "discover.documentsAriaLabel": "ドキュメント", "discover.docViews.json.jsonTitle": "JSON", - "discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedAriaLabel": "警告", - "discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedTooltip": "{underscoreSign} で始まるフィールド名はサポートされません", "discover.docViews.table.filterForFieldPresentButtonAriaLabel": "フィールド表示のフィルター", "discover.docViews.table.filterForFieldPresentButtonTooltip": "フィールド表示のフィルター", "discover.docViews.table.filterForValueButtonAriaLabel": "値でフィルター", @@ -3173,17 +3171,7 @@ "kibanaOverview.addData.sampleDataButtonLabel": "サンプルデータを試す", "kibanaOverview.addData.sectionTitle": "データを取り込む", "kibanaOverview.apps.title": "これらのアプリを検索", - "kibanaOverview.gettingStarted.addDataButtonLabel": "データを追加", - "kibanaOverview.gettingStarted.description": "Kibanaを使用すると、データや方法を可視化する能力が高まります。 1つの質問から始めて、解答から知見を得ることができます。", - "kibanaOverview.gettingStarted.title": "Kibanaの基本操作", "kibanaOverview.header.title": "Kibana", - "kibanaOverview.kibana.appDescription1": "ダッシュボードでデータを分析します。", - "kibanaOverview.kibana.appDescription2": "インサイトを検索して見つけます。", - "kibanaOverview.kibana.appDescription3": "詳細まで正確な表示を設計します。", - "kibanaOverview.kibana.appDescription4": "地理的なデータをプロットします。", - "kibanaOverview.kibana.appDescription5": "モデリング、予測、検出を行います。", - "kibanaOverview.kibana.appDescription6": "パターンと関係を明らかにします。", - "kibanaOverview.kibana.solution.subtitle": "可視化&分析", "kibanaOverview.kibana.solution.title": "Kibana", "kibanaOverview.manageData.sectionTitle": "データを管理", "kibanaOverview.more.title": "Elasticではさまざまなことが可能です", @@ -8677,10 +8665,6 @@ "xpack.enterpriseSearch.errorConnectingState.troubleshootAuthNative": "Elasticsearchネイティブ認証またはSSO/SAMLを使用して認証する必要があります。", "xpack.enterpriseSearch.errorConnectingState.troubleshootAuthSAML": "SSO/SAMLを使用している場合は、エンタープライズ サーチでSAMLレルムも設定する必要があります。", "xpack.enterpriseSearch.FeatureCatalogue.description": "厳選されたAPIとツールを使用して検索エクスペリエンスを作成します。", - "xpack.enterpriseSearch.featureCatalogue.subtitle": "すべて検索", - "xpack.enterpriseSearch.featureCatalogueDescription1": "強力な検索エクスペリエンスを構築します。", - "xpack.enterpriseSearch.featureCatalogueDescription2": "ユーザーを関連するデータにつなげます。", - "xpack.enterpriseSearch.featureCatalogueDescription3": "チームの内容を統合します。", "xpack.enterpriseSearch.hiddenText": "非表示のテキスト", "xpack.enterpriseSearch.licenseCalloutBody": "SAML経由のエンタープライズ認証、ドキュメントレベルのアクセス権と許可サポート、カスタム検索経験などは有効なPlatinumライセンスで提供されます。", "xpack.enterpriseSearch.licenseDocumentationLink": "ライセンス機能の詳細", @@ -8975,7 +8959,6 @@ "xpack.enterpriseSearch.workplaceSearch.groups.description": "共有コンテンツソースとユーザーをグループに割り当て、さまざまな内部チーム向けに関連する検索エクスペリエンスを作成します。", "xpack.enterpriseSearch.workplaceSearch.groups.filterGroups.placeholder": "名前でグループをフィルター...", "xpack.enterpriseSearch.workplaceSearch.groups.filterSources.buttonText": "ソース", - "xpack.enterpriseSearch.workplaceSearch.groups.filterUsers.placeholder": "ユーザーをフィルター...", "xpack.enterpriseSearch.workplaceSearch.groups.groupDeleted": "グループ「{groupName}」が正常に削除されました。", "xpack.enterpriseSearch.workplaceSearch.groups.groupManagerHeaderTitle": "{label}を管理", "xpack.enterpriseSearch.workplaceSearch.groups.groupManagerSelectAllToggle": "{action}すべて", @@ -8994,8 +8977,6 @@ "xpack.enterpriseSearch.workplaceSearch.groups.newGroup.action": "グループを管理", "xpack.enterpriseSearch.workplaceSearch.groups.newGroupSavedSuccess": "{groupName}が正常に作成されました", "xpack.enterpriseSearch.workplaceSearch.groups.noSourcesMessage": "共有コンテンツソースがありません", - "xpack.enterpriseSearch.workplaceSearch.groups.noUsersFound": "ユーザーが見つかりません", - "xpack.enterpriseSearch.workplaceSearch.groups.noUsersMessage": "ユーザーがありません", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmRemoveButtonText": "{name}を削除", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmRemoveDescription": "グループはWorkplace Searchから削除されます。{name}を削除してよろしいですか?", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmTitleText": "確認", @@ -9021,7 +9002,6 @@ "xpack.enterpriseSearch.workplaceSearch.groups.sourceProioritization.zeroStateTitle": "ソースはこのグループと共有されていません", "xpack.enterpriseSearch.workplaceSearch.groups.sourcesModalLabel": "共有コンテンツソース", "xpack.enterpriseSearch.workplaceSearch.groups.sourcesModalTitle": "{groupName}と共有するコンテンツソースを選択", - "xpack.enterpriseSearch.workplaceSearch.groups.userListCount": "{maxVisibleUsers}/{numUsers}ユーザーを表示しています。", "xpack.enterpriseSearch.workplaceSearch.keepEditing.button": "編集を続行", "xpack.enterpriseSearch.workplaceSearch.name.label": "名前", "xpack.enterpriseSearch.workplaceSearch.nav.addSource": "ソースの追加", @@ -9140,8 +9120,6 @@ "xpack.enterpriseSearch.workplaceSearch.sources.config.link": "コンテンツソースコネクター設定を編集", "xpack.enterpriseSearch.workplaceSearch.sources.config.title": "コンテンツソース構成", "xpack.enterpriseSearch.workplaceSearch.sources.configuration.title": "構成", - "xpack.enterpriseSearch.workplaceSearch.sources.connectStepDescription.attachments": "添付ファイル (PDF、Microsoft Officeファイル、別の一般的なテキストファイル形式) 内で検出されたコンテンツは、自動的にインデックスが作成され、検索可能になります。", - "xpack.enterpriseSearch.workplaceSearch.sources.connectStepDescription.files": "PDF、Microsoft Officeファイル、別の一般的なテキストファイル形式内で検出されたコンテンツは、自動的にインデックスが作成され、検索可能になります。", "xpack.enterpriseSearch.workplaceSearch.sources.contentLoading.text": "コンテンツを読み込んでいます...", "xpack.enterpriseSearch.workplaceSearch.sources.contentSummary.title": "コンテンツ概要", "xpack.enterpriseSearch.workplaceSearch.sources.contentType.header": "コンテンツタイプ", @@ -9233,23 +9211,6 @@ "xpack.enterpriseSearch.workplaceSearch.sources.shared.empty.title": "コンテンツソースがありません", "xpack.enterpriseSearch.workplaceSearch.sources.sourceContent.searchBar.placeholder": "{prefix}コンテンツ...", "xpack.enterpriseSearch.workplaceSearch.sources.sourceContent.title": "ソースコンテンツ", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.box": "{sourceName}は、あらゆる規模の組織にあったクラウドストレージサービスです。デスクトップとWeb全体でドキュメントを作成、共有し、自動的に同期します。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.confluence": "{sourceName}はチームワークスペースであり、知識やコラボレーションが結集しています。一般的に、組織wikiやイントラネットで使用されます。通常は、ビジネスの複数の領域で従業員にとって価値のある情報が格納されます。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.confluenceServer": "{sourceName}はチームワークスペースであり、知識やコラボレーションが結集しています。一般的に、組織wikiやイントラネットで使用されます。通常は、ビジネスの複数の領域で従業員にとって価値のある情報が格納されます。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.dropbox": "{sourceName}は、あらゆる規模の組織にあったクラウドストレージサービスです。デスクトップとWeb全体でドキュメントを作成、共有し、自動的に同期します。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.github": "{sourceName}は、あらゆる規模のサイズに合った開発プラットフォームであり、バージョン管理・コラボレーションプラットフォームです。オープンソースからビジネスまで、部署や国全体でコードのホスティングとレビュー、プロジェクトの管理、ソフトウェアの構築が可能です。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.githubEnterprise": "{sourceName}は、あらゆる規模のサイズに合った開発プラットフォームであり、バージョン管理・コラボレーションプラットフォームです。オープンソースからビジネスまで、部署や国全体でコードのホスティングとレビュー、プロジェクトの管理、ソフトウェアの構築が可能です。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.gmail": "{sourceName}はGoogleが開発した無料の電子メールサービスです。世界中で億単位のユーザーや組織が信頼している、高速で信頼できるサービスです。Workplace SearchはすべてのGmailコンテンツを1つの関連する使いやすい検索エクスペリエンスに統合します。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.googleDrive": "{sourceName}はあらゆる規模の組織に合ったクラウドストレージ・コラボレーションサービスです。G Suiteドキュメント (Googleドキュメント、Googleスプレッドシート、Googleスライドなど) の保存とコラボレーションに特化しています。デスクトップとWeb全体でドキュメントを作成、共有し、自動的に同期します。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.jira": "{sourceName}は問題追跡製品であり、あらゆる規模のチームに合った不具合追跡、ワークフロー自動化、アジャイルプロジェクト管理ツールを提供します。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.jiraServer": "{sourceName}は問題追跡製品であり、あらゆる規模のチームに合った不具合追跡、ワークフロー自動化、アジャイルプロジェクト管理ツールを提供します。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.oneDrive": "{sourceName}はあらゆる規模の組織に合ったクラウドストレージサービスです。Office 365ドキュメントの保存とコラボレーションに特化しています。組織全体でドキュメントを作成、共有し、自動的に同期します。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.salesforce": "{sourceName}はクラウドベースの顧客関係管理 (CRM) プラットフォームであり、カスタマーサービス、マーケティング自動化、分析、営業活動ツールに特化しています。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.salesforceSandbox": "{sourceName}はクラウドベースの顧客関係管理 (CRM) プラットフォームであり、カスタマーサービス、マーケティング自動化、分析、営業活動ツールに特化しています。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.serviceNow": "{sourceName}はクラウドITサービス管理 (ITSM) プラットフォームで、ワークフロー自動化と内部組織サポートに特化しています。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.sharePoint": "{sourceName}はあらゆる規模の組織に合ったクラウドコラボレーション、ナレッジ管理、ストレージプラットフォームです。SharePoint Onlineは、多くの場合、集中管理されたコンテンツ管理システム (CMS) として使用され、さまざまな部署やチーム全体における豊富な情報を格納します。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.slack": "{sourceName}はコミュニケーションツールであり、リアルタイムのコラボレーションと意思決定を実現します。{sourceName}では、複数のチーム全体の作業を追跡したり、継続中のプロジェクトで同僚と直接連携したり、他の組織とコミュニケーションを取ったりすることができます。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.zendesk": "{sourceName}はクラウドベースの顧客関係管理・カスタマーサポートプラットフォームであり、カスタマーサポートチケットの追跡、優先度決定、解決のためのツールを提供します。", "xpack.enterpriseSearch.workplaceSearch.sources.sourceDisabled.button": "プラチナライセンスの詳細", "xpack.enterpriseSearch.workplaceSearch.sources.sourceDisabled.description": "組織のライセンスレベルが変更されました。データは安全ですが、ドキュメントレベルのアクセス権はサポートされなくなり、このソースの検索は無効になっています。このソースを再有効化するには、プラチナライセンスにアップグレードしてください。", "xpack.enterpriseSearch.workplaceSearch.sources.sourceDisabled.title": "コンテンツソースが無効です", @@ -17388,13 +17349,10 @@ "xpack.monitoring.elasticsearch.shardAllocation.unassignedDisplayName": "割り当てなし", "xpack.monitoring.elasticsearch.shardAllocation.unassignedPrimaryLabel": "未割り当てプライマリ", "xpack.monitoring.elasticsearch.shardAllocation.unassignedReplicaLabel": "未割り当てレプリカ", - "xpack.monitoring.errors.connectionFaultErrorMessage": "Elasticsearch 監視クラスターのネットワーク接続を確認し、詳細は Kibana のログをご覧ください。", "xpack.monitoring.errors.insufficientUserErrorMessage": "データの監視に必要なユーザーパーミッションがありません", "xpack.monitoring.errors.invalidAuthErrorMessage": "クラスターの監視に無効な認証です", "xpack.monitoring.errors.monitoringLicenseErrorDescription": "クラスター = 「{clusterId}」のライセンス情報が見つかりませんでした。クラスターのマスターノードサーバーログにエラーや警告がないか確認してください。", "xpack.monitoring.errors.monitoringLicenseErrorTitle": "監視ライセンスエラー", - "xpack.monitoring.errors.noConnectionsErrorMessage": "Elasticsearch 監視クラスターのネットワーク接続を確認し、詳細は Kibana のログをご覧ください。", - "xpack.monitoring.errors.statusCodeErrorMessage": "Elasticsearch 監視クラスターのネットワーク接続、またはノードの負荷レベルを確認してください。", "xpack.monitoring.es.indices.deletedClosedStatusLabel": "削除済み / クローズ済み", "xpack.monitoring.es.indices.notAvailableStatusLabel": "利用不可", "xpack.monitoring.es.indices.unknownStatusLabel": "不明", @@ -18477,10 +18435,6 @@ "xpack.observability.expView.seriesEditor.removeSeries": "クリックすると、系列を削除します", "xpack.observability.expView.seriesEditor.time": "時間", "xpack.observability.featureCatalogueDescription": "専用UIで、ログ、メトリック、アプリケーショントレース、システム可用性を連結します。", - "xpack.observability.featureCatalogueDescription1": "インフラストラクチャメトリックを監視します。", - "xpack.observability.featureCatalogueDescription2": "アプリケーションリクエストをトレースします。", - "xpack.observability.featureCatalogueDescription3": "SLAを計測し、問題に対応します。", - "xpack.observability.featureCatalogueSubtitle": "一元化と監視", "xpack.observability.featureCatalogueTitle": "オブザーバビリティ", "xpack.observability.featureRegistry.linkObservabilityTitle": "ケース", "xpack.observability.feedbackMenu.appName": "オブザーバビリティ", @@ -19455,7 +19409,6 @@ "xpack.security.loggedOutAppTitle": "ログアウト", "xpack.security.login.basicLoginForm.logInButtonLabel": "ログイン", "xpack.security.login.basicLoginForm.passwordFormRowLabel": "パスワード", - "xpack.security.login.basicLoginForm.unknownErrorMessage": "おっと!エラー。再試行してください。", "xpack.security.login.basicLoginForm.usernameFormRowLabel": "ユーザー名", "xpack.security.login.basicLoginForm.usernameOrPasswordIsIncorrectErrorMessage": "ユーザー名またはパスワードが正しくありません。再試行してください。", "xpack.security.login.loggedOutDescription": "Elasticからログアウトしました。", @@ -21771,11 +21724,7 @@ "xpack.securitySolution.exceptions.viewer.noSearchResultsPromptBody": "検索結果が見つかりません。", "xpack.securitySolution.exceptions.viewer.searchDefaultPlaceholder": "検索フィールド (例:host.name) ", "xpack.securitySolution.exitFullScreenButton": "全画面を終了", - "xpack.securitySolution.featureCatalogue.subtitle": "SIEM & Endpoint Security", "xpack.securitySolution.featureCatalogueDescription": "インフラストラクチャ全体の統合保護のため、脅威を防止、収集、検出し、それに対応します。", - "xpack.securitySolution.featureCatalogueDescription1": "自律的に脅威を防止します。", - "xpack.securitySolution.featureCatalogueDescription2": "検出して対応します。", - "xpack.securitySolution.featureCatalogueDescription3": "インシデントを調査します。", "xpack.securitySolution.featureRegistry.linkSecuritySolutionTitle": "セキュリティ", "xpack.securitySolution.fieldRenderers.moreLabel": "詳細", "xpack.securitySolution.firstLastSeenHost.errorSearchDescription": "最初の前回確認されたホスト検索でエラーが発生しました", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 9c9fe3e001faa..95683e7e38885 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1552,8 +1552,6 @@ "discover.docTable.tableRow.viewSurroundingDocumentsLinkText": "查看周围文档", "discover.documentsAriaLabel": "文档", "discover.docViews.json.jsonTitle": "JSON", - "discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedAriaLabel": "警告", - "discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedTooltip": "不支持以 {underscoreSign} 开头的字段名称", "discover.docViews.table.filterForFieldPresentButtonAriaLabel": "字段是否存在筛选", "discover.docViews.table.filterForFieldPresentButtonTooltip": "字段是否存在筛选", "discover.docViews.table.filterForValueButtonAriaLabel": "筛留值", @@ -3187,17 +3185,7 @@ "kibanaOverview.addData.sampleDataButtonLabel": "试用我们的样例数据", "kibanaOverview.addData.sectionTitle": "采集您的数据", "kibanaOverview.apps.title": "浏览这些应用", - "kibanaOverview.gettingStarted.addDataButtonLabel": "添加您的数据", - "kibanaOverview.gettingStarted.description": "Kibana 使您能够以自己的方式可视化数据。 首先提个问题,看看答案将您带往何处。", - "kibanaOverview.gettingStarted.title": "Kibana 入门", "kibanaOverview.header.title": "Kibana", - "kibanaOverview.kibana.appDescription1": "在仪表板中分析数据。", - "kibanaOverview.kibana.appDescription2": "搜索和查找数据分析结果。", - "kibanaOverview.kibana.appDescription3": "设计像素级完美的演示文稿。", - "kibanaOverview.kibana.appDescription4": "绘制地理数据。", - "kibanaOverview.kibana.appDescription5": "建模、预测和检测。", - "kibanaOverview.kibana.appDescription6": "显示模式和关系。", - "kibanaOverview.kibana.solution.subtitle": "可视化和分析", "kibanaOverview.kibana.solution.title": "Kibana", "kibanaOverview.manageData.sectionTitle": "管理您的数据", "kibanaOverview.more.title": "Elastic 让您事半功倍", @@ -8924,10 +8912,6 @@ "xpack.enterpriseSearch.errorConnectingState.troubleshootAuthNative": "必须使用 Elasticsearch 本机身份验证或 SSO/SAML 执行身份验证。", "xpack.enterpriseSearch.errorConnectingState.troubleshootAuthSAML": "如果使用的是 SSO/SAML,则还必须在“企业搜索”中设置 SAML 领域。", "xpack.enterpriseSearch.FeatureCatalogue.description": "使用一组优化的 API 和工具打造搜索体验。", - "xpack.enterpriseSearch.featureCatalogue.subtitle": "全面搜索", - "xpack.enterpriseSearch.featureCatalogueDescription1": "打造强大的搜索体验。", - "xpack.enterpriseSearch.featureCatalogueDescription2": "将您的用户连接到相关数据。", - "xpack.enterpriseSearch.featureCatalogueDescription3": "统一您的团队内容。", "xpack.enterpriseSearch.hiddenText": "隐藏文本", "xpack.enterpriseSearch.licenseCalloutBody": "使用有效的白金级许可证,可获得通过 SAML 实现的企业验证、文档级别权限和授权支持、定制搜索体验等等。", "xpack.enterpriseSearch.licenseDocumentationLink": "详细了解许可证功能", @@ -9223,7 +9207,6 @@ "xpack.enterpriseSearch.workplaceSearch.groups.description": "将共享内容源和用户分配到组,以便为各种内部团队打造相关搜索体验。", "xpack.enterpriseSearch.workplaceSearch.groups.filterGroups.placeholder": "按名称筛选组......", "xpack.enterpriseSearch.workplaceSearch.groups.filterSources.buttonText": "源", - "xpack.enterpriseSearch.workplaceSearch.groups.filterUsers.placeholder": "筛选用户......", "xpack.enterpriseSearch.workplaceSearch.groups.groupDeleted": "组“{groupName}”已成功删除。", "xpack.enterpriseSearch.workplaceSearch.groups.groupManagerHeaderTitle": "管理 {label}", "xpack.enterpriseSearch.workplaceSearch.groups.groupManagerSelectAllToggle": "全部{action}", @@ -9242,8 +9225,6 @@ "xpack.enterpriseSearch.workplaceSearch.groups.newGroup.action": "管理组", "xpack.enterpriseSearch.workplaceSearch.groups.newGroupSavedSuccess": "已成功创建 {groupName}", "xpack.enterpriseSearch.workplaceSearch.groups.noSourcesMessage": "无共享内容源", - "xpack.enterpriseSearch.workplaceSearch.groups.noUsersFound": "找不到用户", - "xpack.enterpriseSearch.workplaceSearch.groups.noUsersMessage": "无用户", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmRemoveButtonText": "删除 {name}", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmRemoveDescription": "您的组将从 Workplace Search 中删除。确定要移除 {name}?", "xpack.enterpriseSearch.workplaceSearch.groups.overview.confirmTitleText": "确认", @@ -9269,7 +9250,6 @@ "xpack.enterpriseSearch.workplaceSearch.groups.sourceProioritization.zeroStateTitle": "未与此组共享任何源", "xpack.enterpriseSearch.workplaceSearch.groups.sourcesModalLabel": "共享内容源", "xpack.enterpriseSearch.workplaceSearch.groups.sourcesModalTitle": "选择要与 {groupName} 共享的内容源", - "xpack.enterpriseSearch.workplaceSearch.groups.userListCount": "正在显示 {numUsers} 个用户中的 {maxVisibleUsers} 个。", "xpack.enterpriseSearch.workplaceSearch.keepEditing.button": "继续编辑", "xpack.enterpriseSearch.workplaceSearch.name.label": "名称", "xpack.enterpriseSearch.workplaceSearch.nav.addSource": "添加源", @@ -9388,8 +9368,6 @@ "xpack.enterpriseSearch.workplaceSearch.sources.config.link": "编辑内容源连接器设置", "xpack.enterpriseSearch.workplaceSearch.sources.config.title": "内容源配置", "xpack.enterpriseSearch.workplaceSearch.sources.configuration.title": "配置", - "xpack.enterpriseSearch.workplaceSearch.sources.connectStepDescription.attachments": "附件内找到的内容(PDF、Microsoft Office 文件和其他常见文本文件格式)将自动索引并可搜索。", - "xpack.enterpriseSearch.workplaceSearch.sources.connectStepDescription.files": "PDF、Microsoft Office 文件和其他常见文本文件格式内找到的内容将自动索引并可搜索。", "xpack.enterpriseSearch.workplaceSearch.sources.contentLoading.text": "正在加载内容......", "xpack.enterpriseSearch.workplaceSearch.sources.contentSummary.title": "内容摘要", "xpack.enterpriseSearch.workplaceSearch.sources.contentType.header": "内容类型", @@ -9482,23 +9460,6 @@ "xpack.enterpriseSearch.workplaceSearch.sources.shared.empty.title": "没有可用的内容源", "xpack.enterpriseSearch.workplaceSearch.sources.sourceContent.searchBar.placeholder": "{prefix} 内容......", "xpack.enterpriseSearch.workplaceSearch.sources.sourceContent.title": "源内容", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.box": "{sourceName} 是基于云的存储服务,适合所有规模的组织。创建、存储、共享文档并在您的桌面和 Web 之间同步文档。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.confluence": "{sourceName} 是团队工作区,用于知识分享和协作。常用作组织 wiki 和 Intranet,通常存放有价值的信息,供企业多个领域的员工使用。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.confluenceServer": "{sourceName} 是团队工作区,用于知识分享和协作。常用作组织 wiki 和 Intranet,通常存放有价值的信息,供企业多个领域的员工使用。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.dropbox": "{sourceName} 是基于云的存储服务,适合所有规模的组织。创建、存储、共享文档并在您的桌面和 Web 之间同步文档。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.github": "{sourceName} 是适用于各种团队规模的开发平台以及版本控制和协作平台。无论是开源还是公司,您都可以跨部门跨洲托管并查看代码、管理项目以及构建软件。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.githubEnterprise": "{sourceName} 是适用于各种团队规模的开发平台以及版本控制和协作平台。无论是开源还是公司,您都可以跨部门跨洲托管并查看代码、管理项目以及构建软件。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.gmail": "{sourceName} 是 Google 开发的免费电子邮件服务。其快速、可靠并为全球数以百万计的人和组织所信任。Workplace Search 使您的所有 Gmail 内容变成一种相关的且易用的搜索体验。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.googleDrive": "{sourceName} 是基于云的存储和协作服务,适用于所有规模的组织,专注于 G Suite 文档(Google 文档、表格和幻灯片等)存储和协作。创建、存储、共享文档并在您的桌面和 Web 之间同步文档。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.jira": "{sourceName} 是问题跟踪产品,为各种规模的团队提供错误跟踪、工作流自动化以及敏捷项目管理工具。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.jiraServer": "{sourceName} 是问题跟踪产品,为各种规模的团队提供错误跟踪、工作流自动化以及敏捷项目管理工具。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.oneDrive": "{sourceName} 是基于云的存储服务,适用于所有规模的组织,专注于Office 365 文档存储和协作。在您的组织中创建、存储、共享和同步文档。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.salesforce": "{sourceName} 是基于云的客户关系管理 (CRM) 平台,专注于提供适用于客户服务、营销自动化、分析和销售的操作工具。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.salesforceSandbox": "{sourceName} 是基于云的客户关系管理 (CRM) 平台,专注于提供适用于客户服务、营销自动化、分析和销售的操作工具。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.serviceNow": "{sourceName} 是基于云的 IT 服务管理 (ITSM) 平台,专注于工作流自动化和内部组织支持。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.sharePoint": "{sourceName} 是基于云的协作、知识管理和存储平台,适用于各种规模的组织。经常用作集中化内容管理系统 (CMS),SharePoint Online 存储部门和团队的丰富信息。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.slack": "{sourceName} 是通讯工具,用于实时协作和决策。使用 {sourceName},跟踪跨团队进行的工作,与您的同事直接交流正在进行的项目以及与其他组织通讯。", - "xpack.enterpriseSearch.workplaceSearch.sources.sourceDescriptions.zendesk": "{sourceName} 是基于云的客户关系管理和客户支持平台,提供用于跟踪、优先级和解决客户支持工单的工具。", "xpack.enterpriseSearch.workplaceSearch.sources.sourceDisabled.button": "了解白金级许可证", "xpack.enterpriseSearch.workplaceSearch.sources.sourceDisabled.description": "您的组织的许可证级别已更改。您的数据是安全的,但不再支持文档级别权限,且已禁止搜索此源。升级到白金级许可证,以重新启用此源。", "xpack.enterpriseSearch.workplaceSearch.sources.sourceDisabled.title": "内容源已禁用", @@ -17799,13 +17760,10 @@ "xpack.monitoring.elasticsearch.shardAllocation.unassignedDisplayName": "未分配", "xpack.monitoring.elasticsearch.shardAllocation.unassignedPrimaryLabel": "未分配主分片", "xpack.monitoring.elasticsearch.shardAllocation.unassignedReplicaLabel": "未分配副本分片", - "xpack.monitoring.errors.connectionFaultErrorMessage": "检查 Elasticsearch Monitoring 集群网络连接,并参考 Kibana 日志以了解详情。", "xpack.monitoring.errors.insufficientUserErrorMessage": "对监测数据没有足够的用户权限", "xpack.monitoring.errors.invalidAuthErrorMessage": "监测集群的身份验证无效", "xpack.monitoring.errors.monitoringLicenseErrorDescription": "无法找到集群“{clusterId}”的许可信息。请在集群的主节点服务器日志中查看相关错误或警告。", "xpack.monitoring.errors.monitoringLicenseErrorTitle": "监测许可错误", - "xpack.monitoring.errors.noConnectionsErrorMessage": "检查 Elasticsearch Monitoring 集群网络连接,并参考 Kibana 日志以了解详情。", - "xpack.monitoring.errors.statusCodeErrorMessage": "检查 Elasticsearch Monitoring 集群网络连接或节点的负载水平。", "xpack.monitoring.es.indices.deletedClosedStatusLabel": "已删除 / 已关闭", "xpack.monitoring.es.indices.notAvailableStatusLabel": "不可用", "xpack.monitoring.es.indices.unknownStatusLabel": "未知", @@ -18888,10 +18846,6 @@ "xpack.observability.expView.seriesEditor.removeSeries": "单击移除序列", "xpack.observability.expView.seriesEditor.time": "时间", "xpack.observability.featureCatalogueDescription": "通过专用 UI 整合您的日志、指标、应用程序跟踪和系统可用性。", - "xpack.observability.featureCatalogueDescription1": "监测基础架构指标。", - "xpack.observability.featureCatalogueDescription2": "跟踪应用程序请求。", - "xpack.observability.featureCatalogueDescription3": "衡量 SLA 并响应问题。", - "xpack.observability.featureCatalogueSubtitle": "集中管理和监测", "xpack.observability.featureCatalogueTitle": "可观测性", "xpack.observability.featureRegistry.linkObservabilityTitle": "案例", "xpack.observability.feedbackMenu.appName": "可观测性", @@ -19884,7 +19838,6 @@ "xpack.security.loggedOutAppTitle": "已注销", "xpack.security.login.basicLoginForm.logInButtonLabel": "登录", "xpack.security.login.basicLoginForm.passwordFormRowLabel": "密码", - "xpack.security.login.basicLoginForm.unknownErrorMessage": "糟糕!错误。请重试。", "xpack.security.login.basicLoginForm.usernameFormRowLabel": "用户名", "xpack.security.login.basicLoginForm.usernameOrPasswordIsIncorrectErrorMessage": "用户或密码不正确。请重试。", "xpack.security.login.loggedOutDescription": "您已注销 Elastic。", @@ -22259,11 +22212,7 @@ "xpack.securitySolution.exceptions.viewer.noSearchResultsPromptBody": "找不到搜索结果。", "xpack.securitySolution.exceptions.viewer.searchDefaultPlaceholder": "搜索字段(例如:host.name)", "xpack.securitySolution.exitFullScreenButton": "退出全屏", - "xpack.securitySolution.featureCatalogue.subtitle": "SIEM 和 Endpoint Security", "xpack.securitySolution.featureCatalogueDescription": "预防、收集、检测和响应威胁,以对整个基础架构提供统一的保护。", - "xpack.securitySolution.featureCatalogueDescription1": "自主预防威胁。", - "xpack.securitySolution.featureCatalogueDescription2": "检测和响应。", - "xpack.securitySolution.featureCatalogueDescription3": "调查事件。", "xpack.securitySolution.featureRegistry.linkSecuritySolutionTitle": "安全", "xpack.securitySolution.fieldRenderers.moreLabel": "更多", "xpack.securitySolution.firstLastSeenHost.errorSearchDescription": "搜索上次看到的首个主机时发生错误", diff --git a/x-pack/plugins/uptime/common/rules/uptime_rule_field_map.ts b/x-pack/plugins/uptime/common/rules/uptime_rule_field_map.ts index 2a4e6ddcf58f6..ff69d3a5e6e7f 100644 --- a/x-pack/plugins/uptime/common/rules/uptime_rule_field_map.ts +++ b/x-pack/plugins/uptime/common/rules/uptime_rule_field_map.ts @@ -16,9 +16,6 @@ export const uptimeRuleFieldMap = { 'observer.geo.name': { type: 'keyword', }, - reason: { - type: 'text', - }, // monitor status alert fields 'error.message': { type: 'text', diff --git a/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts b/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts index 6ed4add217ae7..f6604513d1f8f 100644 --- a/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts +++ b/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { esKuery } from '../../../../../src/plugins/data/public'; +import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; import type { IndexPattern } from '../../../../../src/plugins/data/public'; import { combineFiltersAndUserSearch, stringifyKueries } from '../../common/lib'; @@ -39,9 +39,9 @@ export const useUpdateKueryString = ( // this error will be actually shown in UI for user to see try { if ((filterQueryString || urlFilters) && indexPattern) { - const ast = esKuery.fromKueryExpression(combinedFilterString); + const ast = fromKueryExpression(combinedFilterString); - const elasticsearchQuery = esKuery.toElasticsearchQuery(ast, indexPattern); + const elasticsearchQuery = toElasticsearchQuery(ast, indexPattern); esFilters = JSON.stringify(elasticsearchQuery); } diff --git a/x-pack/plugins/uptime/public/lib/alert_types/duration_anomaly.tsx b/x-pack/plugins/uptime/public/lib/alert_types/duration_anomaly.tsx index 68036ea047866..030c506868194 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/duration_anomaly.tsx +++ b/x-pack/plugins/uptime/public/lib/alert_types/duration_anomaly.tsx @@ -8,7 +8,7 @@ import React from 'react'; import moment from 'moment'; -import { ALERT_END, ALERT_STATUS } from '@kbn/rule-data-utils'; +import { ALERT_END, ALERT_STATUS, ALERT_REASON } from '@kbn/rule-data-utils'; import { AlertTypeInitializer } from '.'; import { getMonitorRouteFromMonitorId } from './common'; @@ -36,7 +36,7 @@ export const initDurationAnomalyAlertType: AlertTypeInitializer = ({ defaultActionMessage, requiresAppContext: true, format: ({ fields }) => ({ - reason: fields.reason, + reason: fields[ALERT_REASON] || '', link: getMonitorRouteFromMonitorId({ monitorId: fields['monitor.id']!, dateRangeEnd: fields[ALERT_STATUS] === 'open' ? 'now' : fields[ALERT_END]!, diff --git a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx index 7bd50fce52f3a..e766b377f8f7e 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx +++ b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx @@ -8,7 +8,7 @@ import React from 'react'; import moment from 'moment'; -import { ALERT_END, ALERT_START, ALERT_STATUS } from '@kbn/rule-data-utils'; +import { ALERT_END, ALERT_START, ALERT_STATUS, ALERT_REASON } from '@kbn/rule-data-utils'; import { AlertTypeInitializer } from '.'; import { getMonitorRouteFromMonitorId } from './common'; @@ -50,7 +50,7 @@ export const initMonitorStatusAlertType: AlertTypeInitializer = ({ defaultActionMessage, requiresAppContext: false, format: ({ fields }) => ({ - reason: fields.reason, + reason: fields[ALERT_REASON] || '', link: getMonitorRouteFromMonitorId({ monitorId: fields['monitor.id']!, dateRangeEnd: fields[ALERT_STATUS] === 'open' ? 'now' : fields[ALERT_END]!, diff --git a/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx b/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx index 6632a0c04396b..f8abfb6b6dd00 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx +++ b/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { ALERT_REASON } from '@kbn/rule-data-utils'; import { ObservabilityRuleTypeModel } from '../../../../observability/public'; import { CLIENT_ALERT_TYPES } from '../../../common/constants/alerts'; import { TlsTranslations } from '../../../common/translations'; @@ -32,7 +33,7 @@ export const initTlsAlertType: AlertTypeInitializer = ({ defaultActionMessage, requiresAppContext: false, format: ({ fields }) => ({ - reason: fields.reason, + reason: fields[ALERT_REASON] || '', link: `/app/uptime${CERTIFICATES_ROUTE}`, }), }); diff --git a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.test.ts b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.test.ts index a51bbc49123dc..d69453fd76b91 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.test.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.test.ts @@ -4,7 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { + ALERT_SEVERITY_LEVEL, + ALERT_SEVERITY_VALUE, + ALERT_EVALUATION_VALUE, + ALERT_EVALUATION_THRESHOLD, + ALERT_REASON, +} from '@kbn/rule-data-utils'; import { durationAnomalyAlertFactory } from './duration_anomaly'; import { DURATION_ANOMALY } from '../../../common/constants/alerts'; import { AnomaliesTableRecord, AnomalyRecordDoc } from '../../../../ml/common/types/anomalies'; @@ -12,12 +18,6 @@ import { DynamicSettings } from '../../../common/runtime_types'; import { createRuleTypeMocks, bootstrapDependencies } from './test_utils'; import { getSeverityType } from '../../../../ml/common/util/anomaly_utils'; import { Ping } from '../../../common/runtime_types/ping'; -import { - ALERT_SEVERITY_LEVEL, - ALERT_SEVERITY_VALUE, - ALERT_EVALUATION_VALUE, - ALERT_EVALUATION_THRESHOLD, -} from '@kbn/rule-data-utils'; interface MockAnomaly { severity: AnomaliesTableRecord['severity']; @@ -173,7 +173,7 @@ describe('duration anomaly alert', () => { [ALERT_EVALUATION_THRESHOLD]: anomaly.typicalSort, [ALERT_SEVERITY_LEVEL]: getSeverityType(anomaly.severity), [ALERT_SEVERITY_VALUE]: anomaly.severity, - reason: `Abnormal (${getSeverityType( + [ALERT_REASON]: `Abnormal (${getSeverityType( anomaly.severity )} level) response time detected on uptime-monitor with url ${ mockPing.url?.full diff --git a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts index 100d992f5f863..3da0fcf65cbe4 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import { KibanaRequest, SavedObjectsClientContract } from 'kibana/server'; import moment from 'moment'; import { schema } from '@kbn/config-schema'; @@ -13,6 +12,7 @@ import { ALERT_SEVERITY_VALUE, ALERT_EVALUATION_VALUE, ALERT_EVALUATION_THRESHOLD, + ALERT_REASON, } from '@kbn/rule-data-utils'; import { ActionGroupIdsOf } from '../../../../alerting/common'; import { updateState, generateAlertMessage } from './common'; @@ -137,7 +137,7 @@ export const durationAnomalyAlertFactory: UptimeAlertTypeFactory [ALERT_EVALUATION_THRESHOLD]: anomaly.typicalSort, [ALERT_SEVERITY_LEVEL]: summary.severity, [ALERT_SEVERITY_VALUE]: summary.severityScore, - reason: generateAlertMessage( + [ALERT_REASON]: generateAlertMessage( CommonDurationAnomalyTranslations.defaultActionMessage, summary ), diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.test.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.test.ts index 13c0c48eb28f8..73f4501ace591 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.test.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.test.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { ALERT_REASON } from '@kbn/rule-data-utils'; import { generateFilterDSL, hasFilters, @@ -71,7 +72,7 @@ const mockStatusAlertDocument = ( return { fields: { ...mockCommonAlertDocumentFields(monitor.monitorInfo), - reason: `Monitor first with url ${monitorInfo?.url?.full} is down from ${ + [ALERT_REASON]: `Monitor first with url ${monitorInfo?.url?.full} is down from ${ monitorInfo.observer?.geo?.name }. The latest error message is ${monitorInfo.error?.message || ''}`, }, @@ -87,7 +88,7 @@ const mockAvailabilityAlertDocument = (monitor: GetMonitorAvailabilityResult) => return { fields: { ...mockCommonAlertDocumentFields(monitor.monitorInfo), - reason: `Monitor ${monitorInfo.monitor.name || monitorInfo.monitor.id} with url ${ + [ALERT_REASON]: `Monitor ${monitorInfo.monitor.name || monitorInfo.monitor.id} with url ${ monitorInfo?.url?.full } is below threshold with ${(monitor.availabilityRatio! * 100).toFixed( 2 diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts index 8fe11000725a7..bf8c0176122f0 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts @@ -9,8 +9,9 @@ import datemath from '@elastic/datemath'; import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import { JsonObject } from '@kbn/utility-types'; +import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { ALERT_REASON } from '@kbn/rule-data-utils'; import { UptimeAlertTypeFactory } from './types'; -import { esKuery } from '../../../../../../src/plugins/data/server'; import { StatusCheckFilters, Ping, @@ -98,7 +99,7 @@ export const generateFilterDSL = async ( getIndexPattern: () => Promise, filters: StatusCheckFilters, search: string -): Promise => { +) => { const filtersExist = hasFilters(filters); if (!filtersExist && !search) return undefined; @@ -109,10 +110,7 @@ export const generateFilterDSL = async ( const combinedString = combineFiltersAndUserSearch(filterString, search); - return esKuery.toElasticsearchQuery( - esKuery.fromKueryExpression(combinedString ?? ''), - await getIndexPattern() - ); + return toElasticsearchQuery(fromKueryExpression(combinedString ?? ''), await getIndexPattern()); }; export const formatFilterString = async ( @@ -160,7 +158,7 @@ export const getMonitorAlertDocument = (monitorSummary: Record = ( timestampRange, numTimes, locations: [], - filters: filterString, + filters: filterString as JsonObject, }); } diff --git a/x-pack/plugins/uptime/server/lib/alerts/tls.test.ts b/x-pack/plugins/uptime/server/lib/alerts/tls.test.ts index 2536056363ddf..d0af8984d3a46 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/tls.test.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/tls.test.ts @@ -5,7 +5,6 @@ * 2.0. */ import moment from 'moment'; - import { tlsAlertFactory, getCertSummary, DEFAULT_SIZE } from './tls'; import { TLS } from '../../../common/constants/alerts'; import { CertResult, DynamicSettings } from '../../../common/runtime_types'; diff --git a/x-pack/plugins/uptime/server/lib/alerts/tls.ts b/x-pack/plugins/uptime/server/lib/alerts/tls.ts index 8056fe210bf50..5bb1e5ee3d903 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/tls.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/tls.ts @@ -4,9 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import moment from 'moment'; import { schema } from '@kbn/config-schema'; +import { ALERT_REASON } from '@kbn/rule-data-utils'; import { UptimeAlertTypeFactory } from './types'; import { updateState, generateAlertMessage } from './common'; import { TLS } from '../../../common/constants/alerts'; @@ -172,7 +172,7 @@ export const tlsAlertFactory: UptimeAlertTypeFactory = (_server, 'tls.server.x509.not_after': cert.not_after, 'tls.server.x509.not_before': cert.not_before, 'tls.server.hash.sha256': cert.sha256, - reason: generateAlertMessage(TlsTranslations.defaultActionMessage, summary), + [ALERT_REASON]: generateAlertMessage(TlsTranslations.defaultActionMessage, summary), }, }); alertInstance.replaceState({ diff --git a/x-pack/test/api_integration/apis/maps/index.js b/x-pack/test/api_integration/apis/maps/index.js index 6d4122163d66a..bd2505905c395 100644 --- a/x-pack/test/api_integration/apis/maps/index.js +++ b/x-pack/test/api_integration/apis/maps/index.js @@ -22,6 +22,7 @@ export default function ({ loadTestFile, getService }) { describe('', () => { loadTestFile(require.resolve('./get_indexes_matching_pattern')); loadTestFile(require.resolve('./create_doc_source')); + loadTestFile(require.resolve('./validate_drawing_index')); loadTestFile(require.resolve('./delete_feature')); loadTestFile(require.resolve('./index_data')); loadTestFile(require.resolve('./fonts_api')); diff --git a/x-pack/test/api_integration/apis/maps/validate_drawing_index.js b/x-pack/test/api_integration/apis/maps/validate_drawing_index.js new file mode 100644 index 0000000000000..491847c37be6d --- /dev/null +++ b/x-pack/test/api_integration/apis/maps/validate_drawing_index.js @@ -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 expect from '@kbn/expect'; + +export default function ({ getService }) { + const supertest = getService('supertest'); + + describe('validate drawing index', () => { + it('confirm valid drawing index', async () => { + await supertest + .post(`/api/maps/docSource`) + .set('kbn-xsrf', 'kibana') + .send({ + index: 'valid-drawing-index', + mappings: { properties: { coordinates: { type: 'geo_point' } } }, + }); + + const resp = await supertest + .get(`/api/maps/checkIsDrawingIndex?index=valid-drawing-index`) + .set('kbn-xsrf', 'kibana') + .expect(200); + + expect(resp.body.success).to.be(true); + expect(resp.body.isDrawingIndex).to.be(true); + }); + + it('confirm valid index that is not a drawing index', async () => { + const resp = await supertest + .get(`/api/maps/checkIsDrawingIndex?index=geo_shapes`) + .set('kbn-xsrf', 'kibana') + .expect(200); + + expect(resp.body.success).to.be(true); + expect(resp.body.isDrawingIndex).to.be(false); + }); + + it('confirm invalid index', async () => { + const resp = await supertest + .get(`/api/maps/checkIsDrawingIndex?index=not-an-index`) + .set('kbn-xsrf', 'kibana') + .expect(200); + + expect(resp.body.success).to.be(false); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/security_solution/saved_objects/helpers.ts b/x-pack/test/api_integration/apis/security_solution/saved_objects/helpers.ts index 7265a2caf7dd8..d3217148ad8bb 100644 --- a/x-pack/test/api_integration/apis/security_solution/saved_objects/helpers.ts +++ b/x-pack/test/api_integration/apis/security_solution/saved_objects/helpers.ts @@ -5,13 +5,12 @@ * 2.0. */ -import Supertest from 'supertest'; -import supertestAsPromised from 'supertest-as-promised'; +import type SuperTest from 'supertest'; import uuid from 'uuid'; import { TimelineType } from '../../../../../plugins/security_solution/common/types/timeline'; export const createBasicTimeline = async ( - supertest: Supertest.SuperTest, + supertest: SuperTest.SuperTest, titleToSaved: string ) => await supertest @@ -26,7 +25,7 @@ export const createBasicTimeline = async ( }); export const createBasicTimelineTemplate = async ( - supertest: Supertest.SuperTest, + supertest: SuperTest.SuperTest, titleToSaved: string ) => await supertest diff --git a/x-pack/test/api_integration/apis/telemetry/telemetry.ts b/x-pack/test/api_integration/apis/telemetry/telemetry.ts index 484eb3683a307..c5b8b40368302 100644 --- a/x-pack/test/api_integration/apis/telemetry/telemetry.ts +++ b/x-pack/test/api_integration/apis/telemetry/telemetry.ts @@ -7,8 +7,7 @@ import expect from '@kbn/expect'; import moment from 'moment'; -import type { SuperTest } from 'supertest'; -import type supertestAsPromised from 'supertest-as-promised'; +import type SuperTest from 'supertest'; import deepmerge from 'deepmerge'; import type { FtrProviderContext } from '../../ftr_provider_context'; @@ -29,7 +28,7 @@ import { assertTelemetryPayload } from '../../../../../test/api_integration/apis * @param timestamp The new timestamp to be set */ function updateMonitoringDates( - esSupertest: SuperTest, + esSupertest: SuperTest.SuperTest, fromTimestamp: string, toTimestamp: string, timestamp: string diff --git a/x-pack/test/api_integration/services/es_supertest_without_auth.js b/x-pack/test/api_integration/services/es_supertest_without_auth.js index ec52b5e487c4c..71ec058be46ab 100644 --- a/x-pack/test/api_integration/services/es_supertest_without_auth.js +++ b/x-pack/test/api_integration/services/es_supertest_without_auth.js @@ -7,7 +7,7 @@ import { format as formatUrl } from 'url'; -import supertestAsPromised from 'supertest-as-promised'; +import supertest from 'supertest'; /** * Supertest provider that doesn't include user credentials into base URL that is passed @@ -17,7 +17,7 @@ export function EsSupertestWithoutAuthProvider({ getService }) { const config = getService('config'); const elasticsearchServerConfig = config.get('servers.elasticsearch'); - return supertestAsPromised( + return supertest( formatUrl({ ...elasticsearchServerConfig, auth: false, diff --git a/x-pack/test/api_integration/services/supertest_without_auth.js b/x-pack/test/api_integration/services/supertest_without_auth.js index fec33d0420acc..ea4a5bdf08a94 100644 --- a/x-pack/test/api_integration/services/supertest_without_auth.js +++ b/x-pack/test/api_integration/services/supertest_without_auth.js @@ -7,17 +7,17 @@ import { format as formatUrl } from 'url'; -import supertestAsPromised from 'supertest-as-promised'; +import supertest from 'supertest'; /** - * Supertest provider that doesn't include user credentials into base URL that is passed + * supertest provider that doesn't include user credentials into base URL that is passed * to the supertest. It's used to test API behaviour for not yet authenticated user. */ export function SupertestWithoutAuthProvider({ getService }) { const config = getService('config'); const kibanaServerConfig = config.get('servers.kibana'); - return supertestAsPromised( + return supertest( formatUrl({ ...kibanaServerConfig, auth: false, diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index e6b21ea96a266..fe848994c884d 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -10,8 +10,7 @@ import expect from '@kbn/expect'; import type { ApiResponse, estypes } from '@elastic/elasticsearch'; import type { KibanaClient } from '@elastic/elasticsearch/api/kibana'; -import * as st from 'supertest'; -import supertestAsPromised from 'supertest-as-promised'; +import type SuperTest from 'supertest'; import { ObjectRemover as ActionsRemover } from '../../../alerting_api_integration/common/lib'; import { CASES_URL, @@ -122,7 +121,7 @@ export const setStatus = async ({ cases, type, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; cases: SetStatusCasesParams[]; type: 'case' | 'sub_case'; }): Promise => { @@ -160,7 +159,7 @@ export interface CreateSubCaseResp { * generated alert style comment which can be overridden. */ export const createSubCase = async (args: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; comment?: ContextTypeGeneratedAlertType; caseID?: string; caseInfo?: CasePostRequest; @@ -172,7 +171,7 @@ export const createSubCase = async (args: { /** * Add case as a connector */ -export const createCaseAction = async (supertest: st.SuperTest) => { +export const createCaseAction = async (supertest: SuperTest.SuperTest) => { const { body: createdAction } = await supertest .post('/api/actions/connector') .set('kbn-xsrf', 'foo') @@ -189,7 +188,7 @@ export const createCaseAction = async (supertest: st.SuperTest, + supertest: SuperTest.SuperTest, id: string ) => { await supertest.delete(`/api/actions/connector/${id}`).set('kbn-xsrf', 'foo'); @@ -208,7 +207,7 @@ export const createSubCaseComment = async ({ forceNewSubCase = false, actionID, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; comment?: ContextTypeGeneratedAlertType; caseID?: string; caseInfo?: CasePostRequest; @@ -658,7 +657,7 @@ export const createCaseWithConnector = async ({ auth = { user: superUser, space: null }, createCaseReq = getPostCaseRequest(), }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; servicenowSimulatorURL: string; actionsRemover: ActionsRemover; configureReq?: Record; @@ -717,7 +716,7 @@ export const createCaseWithConnector = async ({ }; export const createCase = async ( - supertest: st.SuperTest, + supertest: SuperTest.SuperTest, params: CasePostRequest, expectedHttpCode: number = 200, auth: { user: User; space: string | null } = { user: superUser, space: null } @@ -741,7 +740,7 @@ export const deleteCases = async ({ expectedHttpCode = 204, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; caseIDs: string[]; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -766,7 +765,7 @@ export const createComment = async ({ auth = { user: superUser, space: null }, expectedHttpCode = 200, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; caseId: string; params: CommentRequest; auth?: { user: User; space: string | null }; @@ -788,7 +787,7 @@ export const updateCase = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; params: CasesPatchRequest; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -809,7 +808,7 @@ export const getCaseUserActions = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; caseID: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -828,7 +827,7 @@ export const deleteComment = async ({ expectedHttpCode = 204, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; caseId: string; commentId: string; expectedHttpCode?: number; @@ -850,7 +849,7 @@ export const deleteAllComments = async ({ expectedHttpCode = 204, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; caseId: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -871,7 +870,7 @@ export const getAllComments = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; caseId: string; auth?: { user: User; space: string | null }; expectedHttpCode?: number; @@ -891,7 +890,7 @@ export const getComment = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; caseId: string; commentId: string; expectedHttpCode?: number; @@ -912,7 +911,7 @@ export const updateComment = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; caseId: string; req: CommentPatchRequest; expectedHttpCode?: number; @@ -934,7 +933,7 @@ export const getConfiguration = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -950,7 +949,7 @@ export const getConfiguration = async ({ }; export const createConfiguration = async ( - supertest: st.SuperTest, + supertest: SuperTest.SuperTest, req: CasesConfigureRequest = getConfigurationRequest(), expectedHttpCode: number = 200, auth: { user: User; space: string | null } = { user: superUser, space: null } @@ -975,7 +974,7 @@ export const createConnector = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; req: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -995,7 +994,7 @@ export const getCaseConnectors = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; expectedHttpCode?: number; auth?: { user: User; space: string | null }; }): Promise => { @@ -1008,7 +1007,7 @@ export const getCaseConnectors = async ({ }; export const updateConfiguration = async ( - supertest: st.SuperTest, + supertest: SuperTest.SuperTest, id: string, req: CasesConfigurePatch, expectedHttpCode: number = 200, @@ -1030,7 +1029,7 @@ export const getAllCasesStatuses = async ({ auth = { user: superUser, space: null }, query = {}, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; expectedHttpCode?: number; auth?: { user: User; space: string | null }; query?: Record; @@ -1051,7 +1050,7 @@ export const getCase = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; caseId: string; includeComments?: boolean; expectedHttpCode?: number; @@ -1074,7 +1073,7 @@ export const findCases = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -1097,7 +1096,7 @@ export const getCasesByAlert = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; alertID: string; query?: Record; expectedHttpCode?: number; @@ -1118,7 +1117,7 @@ export const getTags = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -1139,7 +1138,7 @@ export const getReporters = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -1161,7 +1160,7 @@ export const pushCase = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; caseId: string; connectorId: string; expectedHttpCode?: number; @@ -1183,7 +1182,7 @@ export const getAlertsAttachedToCase = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: st.SuperTest; + supertest: SuperTest.SuperTest; caseId: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index 28c9cdad401af..a067ac5974ac6 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -10,8 +10,7 @@ import type { ApiResponse } from '@elastic/elasticsearch'; import { Context } from '@elastic/elasticsearch/lib/Transport'; import type { estypes } from '@elastic/elasticsearch'; import type { KibanaClient } from '@elastic/elasticsearch/api/kibana'; -import { SuperTest } from 'supertest'; -import supertestAsPromised from 'supertest-as-promised'; +import type SuperTest from 'supertest'; import type { ListArray, NonEmptyEntriesArray, @@ -400,7 +399,7 @@ export const getSimpleMlRuleOutput = (ruleId = 'rule-1'): Partial = * @param supertest The supertest agent. */ export const deleteAllAlerts = async ( - supertest: SuperTest + supertest: SuperTest.SuperTest ): Promise => { await countDownTest( async () => { @@ -488,7 +487,7 @@ export const deleteAllRulesStatuses = async (es: KibanaClient): Promise => * @param supertest The supertest client library */ export const createSignalsIndex = async ( - supertest: SuperTest + supertest: SuperTest.SuperTest ): Promise => { await countDownTest(async () => { await supertest.post(DETECTION_ENGINE_INDEX_URL).set('kbn-xsrf', 'true').send(); @@ -501,7 +500,7 @@ export const createSignalsIndex = async ( * @param supertest The supertest client library */ export const deleteSignalsIndex = async ( - supertest: SuperTest + supertest: SuperTest.SuperTest ): Promise => { await countDownTest(async () => { await supertest.delete(DETECTION_ENGINE_INDEX_URL).set('kbn-xsrf', 'true').send(); @@ -887,7 +886,7 @@ export const countDownTest = async ( * @param rule The rule to create */ export const createRule = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, rule: CreateRulesSchema ): Promise => { const { body } = await supertest @@ -905,7 +904,7 @@ export const createRule = async ( * @param rule The rule to create */ export const updateRule = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, updatedRule: UpdateRulesSchema ): Promise => { const { body } = await supertest @@ -921,7 +920,7 @@ export const updateRule = async ( * creates a new action and expects a 200 and does not do any retries. * @param supertest The supertest deps */ -export const createNewAction = async (supertest: SuperTest) => { +export const createNewAction = async (supertest: SuperTest.SuperTest) => { const { body } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'true') @@ -936,7 +935,7 @@ export const createNewAction = async (supertest: SuperTest, + supertest: SuperTest.SuperTest, ruleId: string ): Promise<{ page: number; @@ -960,7 +959,7 @@ export const findImmutableRuleById = async ( * @param supertest The supertest deps */ export const getPrePackagedRulesStatus = async ( - supertest: SuperTest + supertest: SuperTest.SuperTest ): Promise => { const { body } = await supertest .get(`${DETECTION_ENGINE_PREPACKAGED_URL}/_status`) @@ -977,7 +976,7 @@ export const getPrePackagedRulesStatus = async ( * @param rule The rule to create */ export const createExceptionList = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, exceptionList: CreateExceptionListSchema ): Promise => { const { body } = await supertest @@ -995,7 +994,7 @@ export const createExceptionList = async ( * @param rule The rule to create */ export const createExceptionListItem = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, exceptionListItem: CreateExceptionListItemSchema ): Promise => { const { body } = await supertest @@ -1013,7 +1012,7 @@ export const createExceptionListItem = async ( * @param rule The rule to create */ export const getRule = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, ruleId: string ): Promise => { const { body } = await supertest @@ -1024,7 +1023,7 @@ export const getRule = async ( }; export const waitForAlertToComplete = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, id: string ): Promise => { await waitFor(async () => { @@ -1042,7 +1041,7 @@ export const waitForAlertToComplete = async ( * @param supertest Deps */ export const waitForRuleSuccessOrStatus = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, id: string, status: 'succeeded' | 'failed' | 'partial failure' | 'warning' = 'succeeded' ): Promise => { @@ -1063,7 +1062,7 @@ export const waitForRuleSuccessOrStatus = async ( * @param numberOfSignals The number of signals to wait for, default is 1 */ export const waitForSignalsToBePresent = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, numberOfSignals = 1, signalIds: string[] ): Promise => { @@ -1078,7 +1077,7 @@ export const waitForSignalsToBePresent = async ( * @param supertest Deps */ export const getSignalsByRuleIds = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, ruleIds: string[] ): Promise< estypes.SearchResponse<{ @@ -1103,7 +1102,7 @@ export const getSignalsByRuleIds = async ( * @param ids Array of the rule ids */ export const getSignalsByIds = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, ids: string[], size?: number ): Promise< @@ -1128,7 +1127,7 @@ export const getSignalsByIds = async ( * @param ids Rule id */ export const getSignalsById = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, id: string ): Promise< estypes.SearchResponse<{ @@ -1147,7 +1146,7 @@ export const getSignalsById = async ( }; export const installPrePackagedRules = async ( - supertest: SuperTest + supertest: SuperTest.SuperTest ): Promise => { await countDownTest(async () => { const { status } = await supertest @@ -1166,7 +1165,7 @@ export const installPrePackagedRules = async ( * @param osTypes The os types to optionally add or not to add to the container */ export const createContainerWithEndpointEntries = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, endpointEntries: Array<{ entries: NonEmptyEntriesArray; osTypes: OsTypeArray | undefined; @@ -1226,7 +1225,7 @@ export const createContainerWithEndpointEntries = async ( * @param osTypes The os types to optionally add or not to add to the container */ export const createContainerWithEntries = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, entries: NonEmptyEntriesArray[] ): Promise => { // If not given any endpoint entries, return without any @@ -1284,7 +1283,7 @@ export const createContainerWithEntries = async ( * @param osTypes The os types to optionally add or not to add to the container */ export const createRuleWithExceptionEntries = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, rule: CreateRulesSchema, entries: NonEmptyEntriesArray[], endpointEntries?: Array<{ @@ -1367,7 +1366,7 @@ export const startSignalsMigration = async ({ indices, supertest, }: { - supertest: SuperTest; + supertest: SuperTest.SuperTest; indices: string[]; }): Promise => { const { @@ -1391,7 +1390,7 @@ export const finalizeSignalsMigration = async ({ migrationIds, supertest, }: { - supertest: SuperTest; + supertest: SuperTest.SuperTest; migrationIds: string[]; }): Promise => { const { @@ -1406,7 +1405,7 @@ export const finalizeSignalsMigration = async ({ }; export const getOpenSignals = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, es: KibanaClient, rule: FullResponseSchema ) => { diff --git a/x-pack/test/examples/search_examples/search_example.ts b/x-pack/test/examples/search_examples/search_example.ts index c841b595ed119..fb3cef4055e33 100644 --- a/x-pack/test/examples/search_examples/search_example.ts +++ b/x-pack/test/examples/search_examples/search_example.ts @@ -5,6 +5,7 @@ * 2.0. */ +import expect from '@kbn/expect'; import { FtrProviderContext } from '../../functional/ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -13,6 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'timePicker']); const retry = getService('retry'); const comboBox = getService('comboBox'); + const toasts = getService('toasts'); describe('Search session example', () => { const appId = 'searchExamples'; @@ -28,6 +30,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); + beforeEach(async () => { + await toasts.dismissAllToasts(); + await retry.waitFor('toasts gone', async () => { + return (await toasts.getToastCount()) === 0; + }); + }); + it('should have an other bucket', async () => { await testSubjects.click('searchSourceWithOther'); await testSubjects.click('responseTab'); @@ -53,5 +62,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { return buckets.length === 2; }); }); + + it('should handle warnings', async () => { + await testSubjects.click('searchWithWarning'); + await retry.waitFor('', async () => { + const toastCount = await toasts.getToastCount(); + return toastCount > 1; + }); + const warningToast = await toasts.getToastElement(2); + const textEl = await warningToast.findByClassName('euiToastBody'); + const text: string = await textEl.getVisibleText(); + expect(text).to.contain('Watch out!'); + }); }); } diff --git a/x-pack/test/fleet_api_integration/apis/agents/services.ts b/x-pack/test/fleet_api_integration/apis/agents/services.ts index b89b0c4aa1532..be5d2d438f76f 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/services.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/services.ts @@ -5,7 +5,7 @@ * 2.0. */ -import supertestAsPromised from 'supertest-as-promised'; +import supertest from 'supertest'; import { Client } from '@elastic/elasticsearch'; import { format as formatUrl } from 'url'; @@ -17,7 +17,7 @@ export function getSupertestWithoutAuth({ getService }: FtrProviderContext) { kibanaUrl.auth = null; kibanaUrl.password = null; - return supertestAsPromised(formatUrl(kibanaUrl)); + return supertest(formatUrl(kibanaUrl)); } export function getEsClientForAPIKey({ getService }: FtrProviderContext, esApiKey: string) { diff --git a/x-pack/test/functional/apps/grok_debugger/grok_debugger.js b/x-pack/test/functional/apps/grok_debugger/grok_debugger.js deleted file mode 100644 index 68cd5820e2a32..0000000000000 --- a/x-pack/test/functional/apps/grok_debugger/grok_debugger.js +++ /dev/null @@ -1,89 +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 default function ({ getService, getPageObjects }) { - const browser = getService('browser'); - const grokDebugger = getService('grokDebugger'); - const esArchiver = getService('esArchiver'); - - const PageObjects = getPageObjects(['grokDebugger']); - // FLAKY: https://github.com/elastic/kibana/issues/84440 - describe.skip('grok debugger app', function () { - this.tags('includeFirefox'); - before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana'); - // Increase window height to ensure "Simulate" button is shown above the - // fold. Otherwise it can't be clicked by the browser driver. - await browser.setWindowSize(1600, 1000); - - await PageObjects.grokDebugger.gotoGrokDebugger(); - }); - - describe('input with built-in grok patterns', () => { - it('accepts and parses the input', async () => { - await grokDebugger.setEventInput('SegerCommaBob'); - await grokDebugger.setPatternInput('%{USERNAME:u}'); - await grokDebugger.clickSimulate(); - - await grokDebugger.assertEventOutput({ u: 'SegerCommaBob' }); - }); - }); - - describe('input with custom grok patterns', () => { - it('accepts and parses the input', async () => { - await grokDebugger.setEventInput('Seger Comma Bob'); - await grokDebugger.setPatternInput('%{FIRSTNAME:f} %{MIDDLENAME:m} %{LASTNAME:l}'); - - await grokDebugger.toggleCustomPatternsInput(); - await grokDebugger.setCustomPatternsInput( - 'FIRSTNAME %{WORD}\nMIDDLENAME %{WORD}\nLASTNAME %{WORD}' - ); - - await grokDebugger.clickSimulate(); - - await grokDebugger.assertEventOutput({ f: 'Seger', m: 'Comma', l: 'Bob' }); - }); - }); - - describe('syntax highlighting', () => { - it.skip('applies the correct CSS classes', async () => { - const grokPattern = '\\[(?:-|%{NUMBER:bytes:int})\\]'; - - await grokDebugger.setPatternInput(grokPattern); - - const GROK_START = 'grokStart'; - const GROK_PATTERN_NAME = 'grokPatternName'; - const GROK_SEPARATOR = 'grokSeparator'; - const GROK_FIELD_NAME = 'grokFieldName'; - const GROK_FIELD_TYPE = 'grokFieldType'; - const GROK_END = 'grokEnd'; - const GROK_ESCAPE = 'grokEscape'; - const GROK_ESCAPED = 'grokEscaped'; - const GROK_REGEX = 'grokRegex'; - - await grokDebugger.assertPatternInputSyntaxHighlighting([ - { token: GROK_ESCAPE, content: '\\' }, - { token: GROK_ESCAPED, content: '[' }, - { token: GROK_REGEX, content: '(' }, - { token: GROK_REGEX, content: '?' }, - { token: GROK_REGEX, content: ':' }, - { token: GROK_REGEX, content: '|' }, - { token: GROK_START, content: '%{' }, - { token: GROK_PATTERN_NAME, content: 'NUMBER' }, - { token: GROK_SEPARATOR, content: ':' }, - { token: GROK_FIELD_NAME, content: 'bytes' }, - { token: GROK_SEPARATOR, content: ':' }, - { token: GROK_FIELD_TYPE, content: 'int' }, - { token: GROK_END, content: '}' }, - { token: GROK_REGEX, content: ')' }, - { token: GROK_ESCAPE, content: '\\' }, - { token: GROK_ESCAPED, content: ']' }, - ]); - }); - }); - }); -} diff --git a/x-pack/test/functional/apps/grok_debugger/home_page.ts b/x-pack/test/functional/apps/grok_debugger/home_page.ts new file mode 100644 index 0000000000000..da76a80abbd61 --- /dev/null +++ b/x-pack/test/functional/apps/grok_debugger/home_page.ts @@ -0,0 +1,110 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const PageObjects = getPageObjects(['common', 'grokDebugger']); + const browser = getService('browser'); + const security = getService('security'); + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + + describe('Grok Debugger', function () { + before(async () => { + // Increase window height to ensure "Simulate" button is shown above the + // fold. Otherwise it can't be clicked by the browser driver. + await browser.setWindowSize(1600, 1000); + await security.testUser.setRoles(['global_devtools_read', 'ingest_pipelines_user']); + await PageObjects.common.navigateToApp('grokDebugger'); + await retry.waitFor('Grok Debugger Header to be visible', async () => { + return testSubjects.isDisplayed('grokDebuggerContainer'); + }); + }); + + it('Loads the app', async () => { + await retry.waitForWithTimeout('Grok Debugger to be visible', 15000, async () => { + return await (await PageObjects.grokDebugger.simulateButton()).isDisplayed(); + }); + }); + + it('Accept and parse input with built-in grok pattern', async () => { + const eventInput = 'SegerCommaBob'; + const patternInput = '%{USERNAME:u}'; + const response = await PageObjects.grokDebugger.executeGrokSimulation( + eventInput, + patternInput, + null + ); + expect(response).to.eql({ u: 'SegerCommaBob' }); + }); + + it('Accept and parse input with custom grok pattern', async () => { + await PageObjects.common.navigateToApp('grokDebugger'); + const eventInput = + 'Jan 1 06:25:43 mailserver14 postfix/cleanup[21403]: BEF25A72965: message-id=<20130101142543.5828399CCAF@mailserver14.example.com>'; + const patternInput = '%{SYSLOGBASE} %{POSTFIX_QUEUEID:queue_id}: %{MSG:syslog_message}'; + const customPatternInput = 'POSTFIX_QUEUEID [0-9A-F]{10,11}\nMSG message-id=<%{GREEDYDATA}>'; + const testData = { + pid: '21403', + program: 'postfix/cleanup', + logsource: 'mailserver14', + syslog_message: 'message-id=<20130101142543.5828399CCAF@mailserver14.example.com>', + queue_id: 'BEF25A72965', + timestamp: 'Jan 1 06:25:43', + }; + + const response = await PageObjects.grokDebugger.executeGrokSimulation( + eventInput, + patternInput, + customPatternInput + ); + expect(response).to.eql(testData); + }); + + // This test will need to be fixed. + it.skip('applies the correct CSS classes', async () => { + const grokPattern = '\\[(?:-|%{NUMBER:bytes:int})\\]'; + + await PageObjects.grokDebugger.setPatternInput(grokPattern); + + const GROK_START = 'grokStart'; + const GROK_PATTERN_NAME = 'grokPatternName'; + const GROK_SEPARATOR = 'grokSeparator'; + const GROK_FIELD_NAME = 'grokFieldName'; + const GROK_FIELD_TYPE = 'grokFieldType'; + const GROK_END = 'grokEnd'; + const GROK_ESCAPE = 'grokEscape'; + const GROK_ESCAPED = 'grokEscaped'; + const GROK_REGEX = 'grokRegex'; + + await PageObjects.grokDebugger.assertPatternInputSyntaxHighlighting([ + { token: GROK_ESCAPE, content: '\\' }, + { token: GROK_ESCAPED, content: '[' }, + { token: GROK_REGEX, content: '(' }, + { token: GROK_REGEX, content: '?' }, + { token: GROK_REGEX, content: ':' }, + { token: GROK_REGEX, content: '|' }, + { token: GROK_START, content: '%{' }, + { token: GROK_PATTERN_NAME, content: 'NUMBER' }, + { token: GROK_SEPARATOR, content: ':' }, + { token: GROK_FIELD_NAME, content: 'bytes' }, + { token: GROK_SEPARATOR, content: ':' }, + { token: GROK_FIELD_TYPE, content: 'int' }, + { token: GROK_END, content: '}' }, + { token: GROK_REGEX, content: ')' }, + { token: GROK_ESCAPE, content: '\\' }, + { token: GROK_ESCAPED, content: ']' }, + ]); + }); + + after(async () => { + await security.testUser.restoreDefaults(); + }); + }); +}; diff --git a/x-pack/test/functional/apps/grok_debugger/index.ts b/x-pack/test/functional/apps/grok_debugger/index.ts new file mode 100644 index 0000000000000..1fed41f6e3e36 --- /dev/null +++ b/x-pack/test/functional/apps/grok_debugger/index.ts @@ -0,0 +1,15 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ loadTestFile }: FtrProviderContext) => { + describe('Grok Debugger App', function () { + this.tags('ciGroup13'); + loadTestFile(require.resolve('./home_page')); + }); +}; diff --git a/x-pack/test/functional/apps/lens/runtime_fields.ts b/x-pack/test/functional/apps/lens/runtime_fields.ts index da5634b0ae838..2ea0a05aece26 100644 --- a/x-pack/test/functional/apps/lens/runtime_fields.ts +++ b/x-pack/test/functional/apps/lens/runtime_fields.ts @@ -14,8 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const fieldEditor = getService('fieldEditor'); const retry = getService('retry'); - // FLAKY: https://github.com/elastic/kibana/issues/95614 - describe.skip('lens runtime fields', () => { + describe('lens runtime fields', () => { it('should be able to add runtime field and use it', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); @@ -49,7 +48,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should able to edit field', async () => { await PageObjects.lens.clickField('runtimefield'); await PageObjects.lens.editField(); - await fieldEditor.setName('runtimefield2'); + await fieldEditor.setName('runtimefield2', true, true); await fieldEditor.save(); await fieldEditor.confirmSave(); await PageObjects.lens.searchField('runtime'); diff --git a/x-pack/test/functional/page_objects/grok_debugger_page.ts b/x-pack/test/functional/page_objects/grok_debugger_page.ts index 89017683f632f..06848c6b9ed3b 100644 --- a/x-pack/test/functional/page_objects/grok_debugger_page.ts +++ b/x-pack/test/functional/page_objects/grok_debugger_page.ts @@ -4,15 +4,87 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import expect from '@kbn/expect'; import { FtrService } from '../ftr_provider_context'; export class GrokDebuggerPageObject extends FtrService { - private readonly common = this.ctx.getPageObject('common'); - private readonly grokDebugger = this.ctx.getService('grokDebugger'); + private readonly testSubjects = this.ctx.getService('testSubjects'); + private readonly aceEditor = this.ctx.getService('aceEditor'); + private readonly retry = this.ctx.getService('retry'); + + async simulateButton() { + return await this.testSubjects.find('btnSimulate'); + } + + async getEventOutput() { + return await this.aceEditor.getValue( + 'grokDebuggerContainer > aceEventOutput > codeEditorContainer' + ); + } + + async setEventInput(value: string) { + await this.aceEditor.setValue( + 'grokDebuggerContainer > aceEventInput > codeEditorContainer', + value + ); + } + + async setPatternInput(pattern: string) { + await this.aceEditor.setValue( + 'grokDebuggerContainer > acePatternInput > codeEditorContainer', + pattern + ); + } + + async setCustomPatternInput(customPattern: string) { + await this.aceEditor.setValue( + 'grokDebuggerContainer > aceCustomPatternsInput > codeEditorContainer', + customPattern + ); + } + + async toggleSetCustomPattern() { + await this.testSubjects.click('grokDebuggerContainer > btnToggleCustomPatternsInput'); + } + + async executeGrokSimulation(input: string, pattern: string, customPattern: string | null) { + let value; + await this.setEventInput(input); + await this.setPatternInput(pattern); + if (customPattern) { + await this.toggleSetCustomPattern(); + await this.setCustomPatternInput(customPattern); + } + await (await this.simulateButton()).click(); + await this.retry.try(async () => { + value = JSON.parse(await this.getEventOutput()); + expect(Object.keys(value).length).to.be.greaterThan(0); + }); + return value; + } + + // This needs to be fixed to work with the new test functionality. This method is skipped currently. + async assertPatternInputSyntaxHighlighting(expectedHighlights: any[]) { + const patternInputElement = await this.testSubjects.find( + 'grokDebuggerContainer > acePatternInput > codeEditorContainer' + ); + const highlightedElements = await patternInputElement.findAllByXpath( + './/div[@class="ace_line"]/*' + ); + + expect(highlightedElements.length).to.be(expectedHighlights.length); + await Promise.all( + highlightedElements.map(async (element: any, index) => { + const highlightClass = await element.getAttribute('class'); + const highlightedContent = await element.getVisibleText(); + + const expectedHighlight = expectedHighlights[index]; + const expectedHighlightClass = `ace_${expectedHighlight.token}`; + const expectedHighlightedContent = expectedHighlight.content; - async gotoGrokDebugger() { - await this.common.navigateToApp('grokDebugger'); - await this.grokDebugger.assertExists(); + expect(highlightClass).to.be(expectedHighlightClass); + expect(highlightedContent).to.be(expectedHighlightedContent); + }) + ); } } diff --git a/x-pack/test/functional/page_objects/reporting_page.ts b/x-pack/test/functional/page_objects/reporting_page.ts index 302e71304869b..ca48cec092ecb 100644 --- a/x-pack/test/functional/page_objects/reporting_page.ts +++ b/x-pack/test/functional/page_objects/reporting_page.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { format as formatUrl } from 'url'; -import supertestAsPromised from 'supertest-as-promised'; +import type SuperTest from 'supertest'; import { FtrService } from '../ftr_provider_context'; import { REPORT_TABLE_ID, REPORT_TABLE_ROW_ID } from '../../../plugins/reporting/common/constants'; @@ -52,7 +52,7 @@ export class ReportingPageObject extends FtrService { `); } - async getResponse(fullUrl: string): Promise { + async getResponse(fullUrl: string): Promise { this.log.debug(`getResponse for ${fullUrl}`); const kibanaServerConfig = this.config.get('servers.kibana'); const baseURL = formatUrl({ diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index ab3692be9288f..45ba4c5c34833 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -277,9 +277,9 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( async assertRuntimeMappingsEditorContent(expectedContent: string[]) { await this.assertRuntimeMappingEditorExists(); - const runtimeMappingsEditorString = await aceEditor.getValue( - 'mlDataFrameAnalyticsAdvancedRuntimeMappingsEditor' - ); + const wrapper = await testSubjects.find('mlDataFrameAnalyticsAdvancedRuntimeMappingsEditor'); + const editor = await wrapper.findByCssSelector('.monaco-editor .view-lines'); + const runtimeMappingsEditorString = await editor.getVisibleText(); // Not all lines may be visible in the editor and thus aceEditor may not return all lines. // This means we might not get back valid JSON so we only test against the first few lines // and see if the string matches. diff --git a/x-pack/test/functional_enterprise_search/cypress.config.ts b/x-pack/test/functional_enterprise_search/cypress.config.ts new file mode 100644 index 0000000000000..9a6918ab0557d --- /dev/null +++ b/x-pack/test/functional_enterprise_search/cypress.config.ts @@ -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 { FtrConfigProviderContext } from '@kbn/test'; + +// TODO: If Kibana CI doesn't end up using this (e.g., uses Dockerized containers +// instead of the functional test server), we can opt to delete this file later. + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const baseConfig = await readConfigFile(require.resolve('./base_config')); + + return { + // default to the xpack functional config + ...baseConfig.getAll(), + + esTestCluster: { + ...baseConfig.get('esTestCluster'), + serverArgs: [ + ...baseConfig.get('esTestCluster.serverArgs'), + 'xpack.security.enabled=true', + 'xpack.security.authc.api_key.enabled=true', + ], + }, + + kbnTestServer: { + ...baseConfig.get('kbnTestServer'), + serverArgs: [ + ...baseConfig.get('kbnTestServer.serverArgs'), + '--csp.strict=false', + '--csp.warnLegacyBrowsers=false', + '--enterpriseSearch.host=http://localhost:3002', + ], + }, + }; +} diff --git a/x-pack/test/lists_api_integration/utils.ts b/x-pack/test/lists_api_integration/utils.ts index 90103ec8b856c..8975feb6fbe05 100644 --- a/x-pack/test/lists_api_integration/utils.ts +++ b/x-pack/test/lists_api_integration/utils.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { SuperTest } from 'supertest'; -import supertestAsPromised from 'supertest-as-promised'; +import type SuperTest from 'supertest'; import type { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import type { @@ -26,7 +25,7 @@ import { countDownES, countDownTest } from '../detection_engine_api_integration/ * @param supertest The supertest client library */ export const createListsIndex = async ( - supertest: SuperTest + supertest: SuperTest.SuperTest ): Promise => { return countDownTest(async () => { await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true').send(); @@ -39,7 +38,7 @@ export const createListsIndex = async ( * @param supertest The supertest client library */ export const deleteListsIndex = async ( - supertest: SuperTest + supertest: SuperTest.SuperTest ): Promise => { return countDownTest(async () => { await supertest.delete(LIST_INDEX).set('kbn-xsrf', 'true').send(); @@ -53,7 +52,7 @@ export const deleteListsIndex = async ( * @param supertest The supertest client library */ export const createExceptionListsIndex = async ( - supertest: SuperTest + supertest: SuperTest.SuperTest ): Promise => { return countDownTest(async () => { await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true').send(); @@ -179,7 +178,7 @@ export const deleteAllExceptions = async (es: KibanaClient): Promise => { * @param testValues Optional test values in case you're using CIDR or range based lists */ export const importFile = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, type: Type, contents: string[], fileName: string, @@ -208,7 +207,7 @@ export const importFile = async ( * @param fileName filename to import as */ export const importTextFile = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, type: Type, contents: string[], fileName: string @@ -233,7 +232,7 @@ export const importTextFile = async ( * @param itemValue The item value to wait for */ export const waitForListItem = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, itemValue: string, fileName: string ): Promise => { @@ -254,7 +253,7 @@ export const waitForListItem = async ( * @param itemValue The item value to wait for */ export const waitForListItems = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, itemValues: string[], fileName: string ): Promise => { @@ -269,7 +268,7 @@ export const waitForListItems = async ( * @param itemValue The item value to wait for */ export const waitForTextListItem = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, itemValue: string, fileName: string ): Promise => { @@ -296,7 +295,7 @@ export const waitForTextListItem = async ( * @param itemValue The item value to wait for */ export const waitForTextListItems = async ( - supertest: SuperTest, + supertest: SuperTest.SuperTest, itemValues: string[], fileName: string ): Promise => { diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/health_route.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/health_route.ts index fd3a5abc0e4bf..fd40f3de97dc4 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/health_route.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/health_route.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import url from 'url'; import { keyBy, mapValues } from 'lodash'; -import supertestAsPromised from 'supertest-as-promised'; +import supertest from 'supertest'; import { FtrProviderContext } from '../../ftr_provider_context'; import { ConcreteTaskInstance } from '../../../../plugins/task_manager/server'; @@ -84,10 +84,10 @@ interface MonitoringStats { export default function ({ getService }: FtrProviderContext) { const config = getService('config'); const retry = getService('retry'); - const supertest = supertestAsPromised(url.format(config.get('servers.kibana'))); + const request = supertest(url.format(config.get('servers.kibana'))); function getHealthRequest() { - return supertest.get('/api/task_manager/_health').set('kbn-xsrf', 'foo'); + return request.get('/api/task_manager/_health').set('kbn-xsrf', 'foo'); } function getHealth(): Promise { @@ -97,7 +97,7 @@ export default function ({ getService }: FtrProviderContext) { } function scheduleTask(task: Partial): Promise { - return supertest + return request .post('/api/sample_tasks/schedule') .set('kbn-xsrf', 'xxx') .send({ task }) diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts index 3c460c2a6d8c7..400ad93e47995 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts @@ -8,8 +8,6 @@ import { random, times } from 'lodash'; import expect from '@kbn/expect'; import type { estypes } from '@elastic/elasticsearch'; -import url from 'url'; -import supertestAsPromised from 'supertest-as-promised'; import { FtrProviderContext } from '../../ftr_provider_context'; import TaskManagerMapping from '../../../../plugins/task_manager/server/saved_objects/mappings.json'; import { @@ -55,9 +53,8 @@ export default function ({ getService }: FtrProviderContext) { const es = getService('es'); const log = getService('log'); const retry = getService('retry'); - const config = getService('config'); + const supertest = getService('supertest'); const testHistoryIndex = '.kibana_task_manager_test_result'; - const supertest = supertestAsPromised(url.format(config.get('servers.kibana'))); describe('scheduling and running tasks', () => { beforeEach(async () => { diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts index 4941bddbda53c..61223b8b67e64 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts @@ -8,7 +8,7 @@ import _ from 'lodash'; import expect from '@kbn/expect'; import url from 'url'; -import supertestAsPromised from 'supertest-as-promised'; +import supertest from 'supertest'; import { FtrProviderContext } from '../../ftr_provider_context'; import { ConcreteTaskInstance } from '../../../../plugins/task_manager/server'; @@ -43,7 +43,7 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const retry = getService('retry'); const config = getService('config'); - const supertest = supertestAsPromised(url.format(config.get('servers.kibana'))); + const request = supertest(url.format(config.get('servers.kibana'))); const REMOVED_TASK_TYPE_ID = 'be7e1250-3322-11eb-94c1-db6995e83f6a'; @@ -59,7 +59,7 @@ export default function ({ getService }: FtrProviderContext) { function scheduleTask( task: Partial ): Promise { - return supertest + return request .post('/api/sample_tasks/schedule') .set('kbn-xsrf', 'xxx') .send({ task }) @@ -70,7 +70,7 @@ export default function ({ getService }: FtrProviderContext) { function currentTasks(): Promise<{ docs: Array>; }> { - return supertest + return request .get('/api/sample_tasks') .expect(200) .then((response) => response.body); diff --git a/yarn.lock b/yarn.lock index 121ba05364a81..8f22540dcefa7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1443,10 +1443,10 @@ ms "^2.1.3" secure-json-parse "^2.4.0" -"@elastic/ems-client@7.14.0": - version "7.14.0" - resolved "https://registry.yarnpkg.com/@elastic/ems-client/-/ems-client-7.14.0.tgz#7c8095086bd9a637f72d6d810d494a460c68e0fc" - integrity sha512-axXTyBrC1I2TMmcxGC04SgODwb5Cp6svcW64RoTr8X2XrSSuH0gh+X5qMsC9FgGGnmbVNCEYIs3JK4AJ7X4bxA== +"@elastic/ems-client@7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@elastic/ems-client/-/ems-client-7.15.0.tgz#c101d7f83aa56463bcc385fd4eb883c6ea3ae9fc" + integrity sha512-BAAAVPhoaH6SGrfuO6U0MVRg4lvblhJ9VqYlMf3dZN9uDBB+12CUtb6t6Kavn5Tr3nS6X3tU/KKsuomo5RrEeQ== dependencies: "@types/geojson" "^7946.0.7" "@types/lru-cache" "^5.1.0" @@ -4858,7 +4858,7 @@ resolved "https://registry.yarnpkg.com/@types/base64-js/-/base64-js-1.2.5.tgz#582b2476169a6cba460a214d476c744441d873d5" integrity sha1-WCskdhaabLpGCiFNR2x0REHYc9U= -"@types/bluebird@*", "@types/bluebird@^3.1.1": +"@types/bluebird@^3.1.1": version "3.5.30" resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.30.tgz#ee034a0eeea8b84ed868b1aa60d690b08a6cfbc5" integrity sha512-8LhzvcjIoqoi1TghEkRMkbbmM+jhHnBokPGkJWjclMK+Ks0MxEBow3/p2/iFTZ+OIbJHQDSfpgdZEb+af3gfVw== @@ -6117,16 +6117,7 @@ "@types/cookiejar" "*" "@types/node" "*" -"@types/supertest-as-promised@^2.0.38": - version "2.0.38" - resolved "https://registry.yarnpkg.com/@types/supertest-as-promised/-/supertest-as-promised-2.0.38.tgz#5077adf2a31429e06ba8de6799ebdb796ad50fc7" - integrity sha512-2vdlnsZBIgaX0DFNOACK4xFDqvoA1sAR78QD3LiDWGmzSfrRCNt1WFyUYe2Vf0QS03tZf6XC8bNlLaLYXhZbGA== - dependencies: - "@types/bluebird" "*" - "@types/superagent" "*" - "@types/supertest" "*" - -"@types/supertest@*", "@types/supertest@^2.0.5": +"@types/supertest@^2.0.5": version "2.0.8" resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.8.tgz#23801236e2b85204ed771a8e7c40febba7da2bda" integrity sha512-wcax7/ip4XSSJRLbNzEIUVy2xjcBIZZAuSd2vtltQfRK7kxhx5WMHbLHkYdxN3wuQCrwpYrg86/9byDjPXoGMA== @@ -8420,7 +8411,7 @@ bluebird@3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== -bluebird@3.7.2, bluebird@^3.3.1, bluebird@^3.3.5, bluebird@^3.4.1, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.5, bluebird@^3.7.1, bluebird@^3.7.2: +bluebird@3.7.2, bluebird@^3.3.5, bluebird@^3.4.1, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.5, bluebird@^3.7.1, bluebird@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -26429,14 +26420,6 @@ supercluster@^7.1.0: dependencies: kdbush "^3.0.0" -supertest-as-promised@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/supertest-as-promised/-/supertest-as-promised-4.0.2.tgz#0464f2bd256568d4a59bce84269c0548f6879f1a" - integrity sha1-BGTyvSVlaNSlm86EJpwFSPaHnxo= - dependencies: - bluebird "^3.3.1" - methods "^1.1.1" - supertest@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/supertest/-/supertest-3.1.0.tgz#f9ebaf488e60f2176021ec580bdd23ad269e7bc6"