From ab026f8926f6b1ae8c600914e5a352e8b1f217a7 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:33:58 +1100 Subject: [PATCH] [8.x] [ES|QL] Supports custom formatters in charts (#201540) (#203717) # Backport This will backport the following commits from `main` to `8.x`: - [[ES|QL] Supports custom formatters in charts (#201540)](https://github.com/elastic/kibana/pull/201540) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Peter Pisljar --- .../src/fetch_fields_from_esql.ts | 12 ++- .../datatable_utilities_service.ts | 5 +- ...query_state_to_ast_with_validation.test.ts | 19 +--- ...ased_query_state_to_ast_with_validation.ts | 8 +- .../data/common/search/expressions/esql.ts | 12 ++- .../main/data_fetching/fetch_esql.ts | 2 +- .../map_to_columns/map_to_columns.test.ts | 4 +- .../map_to_columns_fn_textbased.ts | 11 ++- .../expressions/map_to_columns/types.ts | 3 +- .../dimension_panel/format_selector.tsx | 3 +- .../operations/definitions/helpers.tsx | 5 +- .../components/dimension_editor.tsx | 98 ++++++++++++++++++- .../fetch_data_from_aggregate_query.ts | 2 +- .../datasources/text_based/to_expression.ts | 48 ++++++++- .../public/datasources/text_based/types.ts | 6 ++ .../datasources/text_based/utils.test.ts | 2 + .../public/datasources/text_based/utils.ts | 82 +++++++++++++++- .../expression/esql_query_expression.tsx | 9 +- 18 files changed, 275 insertions(+), 56 deletions(-) diff --git a/src/platform/packages/private/kbn-esql-editor/src/fetch_fields_from_esql.ts b/src/platform/packages/private/kbn-esql-editor/src/fetch_fields_from_esql.ts index 7df17498b803f..9098ef2bd9edd 100644 --- a/src/platform/packages/private/kbn-esql-editor/src/fetch_fields_from_esql.ts +++ b/src/platform/packages/private/kbn-esql-editor/src/fetch_fields_from_esql.ts @@ -12,7 +12,7 @@ import { lastValueFrom } from 'rxjs'; import { Query, AggregateQuery, TimeRange } from '@kbn/es-query'; import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; import type { Datatable } from '@kbn/expressions-plugin/public'; -import { type DataView, textBasedQueryStateToAstWithValidation } from '@kbn/data-plugin/common'; +import { textBasedQueryStateToAstWithValidation } from '@kbn/data-plugin/common'; interface TextBasedLanguagesErrorResponse { error: { @@ -26,16 +26,20 @@ export function fetchFieldsFromESQL( expressions: ExpressionsStart, time?: TimeRange, abortController?: AbortController, - dataView?: DataView + timeFieldName?: string ) { return textBasedQueryStateToAstWithValidation({ query, time, - dataView, + timeFieldName, }) .then((ast) => { if (ast) { - const executionContract = expressions.execute(ast, null); + const executionContract = expressions.execute(ast, null, { + searchContext: { + timeRange: time, + }, + }); if (abortController) { abortController.signal.onabort = () => { diff --git a/src/plugins/data/common/datatable_utilities/datatable_utilities_service.ts b/src/plugins/data/common/datatable_utilities/datatable_utilities_service.ts index 3d80fb4fd3f91..30720bcd30316 100644 --- a/src/plugins/data/common/datatable_utilities/datatable_utilities_service.ts +++ b/src/plugins/data/common/datatable_utilities/datatable_utilities_service.ts @@ -74,10 +74,7 @@ export class DatatableUtilitiesService { timeZone: string; }> = {} ): DateHistogramMeta | undefined { - if (column.meta.source !== 'esaggs') { - return; - } - if (column.meta.sourceParams?.type !== BUCKET_TYPES.DATE_HISTOGRAM) { + if (!column.meta.sourceParams || !column.meta.sourceParams.params) { return; } diff --git a/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.test.ts b/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.test.ts index c8aafc01e2bce..068ffa599fa92 100644 --- a/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.test.ts +++ b/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.test.ts @@ -7,7 +7,6 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { createStubDataView } from '@kbn/data-views-plugin/common/mocks'; import { textBasedQueryStateToAstWithValidation } from './text_based_query_state_to_ast_with_validation'; describe('textBasedQueryStateToAstWithValidation', () => { @@ -25,13 +24,6 @@ describe('textBasedQueryStateToAstWithValidation', () => { }); it('returns an object with the correct structure for an SQL query with existing dataview', async () => { - const dataView = createStubDataView({ - spec: { - id: 'foo', - title: 'foo', - timeFieldName: '@timestamp', - }, - }); const actual = await textBasedQueryStateToAstWithValidation({ filters: [], query: { esql: 'FROM foo' }, @@ -39,7 +31,7 @@ describe('textBasedQueryStateToAstWithValidation', () => { from: 'now', to: 'now+7d', }, - dataView, + timeFieldName: '@timestamp', }); expect(actual).toHaveProperty( @@ -76,13 +68,6 @@ describe('textBasedQueryStateToAstWithValidation', () => { }); it('returns an object with the correct structure for ES|QL', async () => { - const dataView = createStubDataView({ - spec: { - id: 'foo', - title: 'foo', - timeFieldName: '@timestamp', - }, - }); const actual = await textBasedQueryStateToAstWithValidation({ filters: [], query: { esql: 'from logs*' }, @@ -90,7 +75,7 @@ describe('textBasedQueryStateToAstWithValidation', () => { from: 'now', to: 'now+7d', }, - dataView, + timeFieldName: '@timestamp', titleForInspector: 'Custom title', descriptionForInspector: 'Custom desc', }); diff --git a/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.ts b/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.ts index 170bc4c55a926..8bcc377754171 100644 --- a/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.ts +++ b/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.ts @@ -8,12 +8,10 @@ */ import { isOfAggregateQueryType, Query } from '@kbn/es-query'; -import type { DataView } from '@kbn/data-views-plugin/common'; import type { QueryState } from '..'; import { textBasedQueryStateToExpressionAst } from './text_based_query_state_to_ast'; interface Args extends QueryState { - dataView?: DataView; inputQuery?: Query; timeFieldName?: string; titleForInspector?: string; @@ -26,7 +24,7 @@ interface Args extends QueryState { * @param query kibana query or aggregate query * @param inputQuery * @param time kibana time range - * @param dataView + * @param timeFieldName * @param titleForInspector * @param descriptionForInspector */ @@ -35,7 +33,7 @@ export async function textBasedQueryStateToAstWithValidation({ query, inputQuery, time, - dataView, + timeFieldName, titleForInspector, descriptionForInspector, }: Args) { @@ -46,7 +44,7 @@ export async function textBasedQueryStateToAstWithValidation({ query, inputQuery, time, - timeFieldName: dataView?.timeFieldName, + timeFieldName, titleForInspector, descriptionForInspector, }); diff --git a/src/plugins/data/common/search/expressions/esql.ts b/src/plugins/data/common/search/expressions/esql.ts index a93996f163962..cc1180ff57e49 100644 --- a/src/plugins/data/common/search/expressions/esql.ts +++ b/src/plugins/data/common/search/expressions/esql.ts @@ -315,7 +315,17 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => { (body.all_columns ?? body.columns)?.map(({ name, type }) => ({ id: name, name, - meta: { type: esFieldTypeToKibanaFieldType(type), esType: type }, + meta: { + type: esFieldTypeToKibanaFieldType(type), + esType: type, + sourceParams: + type === 'date' + ? { + appliedTimeRange: input?.timeRange, + params: {}, + } + : {}, + }, isNull: hasEmptyColumns ? !lookup.has(name) : false, })) ?? []; diff --git a/src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts b/src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts index dca01247296a4..d7cad10b177f6 100644 --- a/src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts +++ b/src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts @@ -56,7 +56,7 @@ export function fetchEsql({ filters, query, time: timeRange, - dataView, + timeFieldName: dataView.timeFieldName, inputQuery, titleForInspector: i18n.translate('discover.inspectorEsqlRequestTitle', { defaultMessage: 'Table', diff --git a/x-pack/plugins/lens/common/expressions/map_to_columns/map_to_columns.test.ts b/x-pack/plugins/lens/common/expressions/map_to_columns/map_to_columns.test.ts index 5a70c4385784c..530d3bf3121a1 100644 --- a/x-pack/plugins/lens/common/expressions/map_to_columns/map_to_columns.test.ts +++ b/x-pack/plugins/lens/common/expressions/map_to_columns/map_to_columns.test.ts @@ -319,8 +319,8 @@ describe('map_to_columns', () => { ); expect(result.columns).toStrictEqual([ - { id: 'a', name: 'A', meta: { type: 'number' } }, - { id: 'b', name: 'B', meta: { type: 'number' } }, + { id: 'a', name: 'A', meta: { type: 'number', field: undefined, params: undefined } }, + { id: 'b', name: 'B', meta: { type: 'number', field: undefined, params: undefined } }, ]); expect(result.rows).toStrictEqual([ diff --git a/x-pack/plugins/lens/common/expressions/map_to_columns/map_to_columns_fn_textbased.ts b/x-pack/plugins/lens/common/expressions/map_to_columns/map_to_columns_fn_textbased.ts index cbaa8b8888dfe..1e6a869626ffd 100644 --- a/x-pack/plugins/lens/common/expressions/map_to_columns/map_to_columns_fn_textbased.ts +++ b/x-pack/plugins/lens/common/expressions/map_to_columns/map_to_columns_fn_textbased.ts @@ -32,7 +32,16 @@ export const mapToOriginalColumnsTextBased: MapToColumnsExpressionFunction['fn'] if (!(column.id in idMap)) { return []; } - return idMap[column.id].map((originalColumn) => ({ ...column, id: originalColumn.id })); + return idMap[column.id].map((originalColumn) => ({ + ...column, + id: originalColumn.id, + name: originalColumn.label, + meta: { + ...column.meta, + field: originalColumn.sourceField, + params: originalColumn.format, + }, + })); }), }; }; diff --git a/x-pack/plugins/lens/common/expressions/map_to_columns/types.ts b/x-pack/plugins/lens/common/expressions/map_to_columns/types.ts index e6617c38863bf..f7559a539910b 100644 --- a/x-pack/plugins/lens/common/expressions/map_to_columns/types.ts +++ b/x-pack/plugins/lens/common/expressions/map_to_columns/types.ts @@ -6,8 +6,9 @@ */ import { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import { SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; -export type OriginalColumn = { id: string; label: string } & ( +export type OriginalColumn = { id: string; label: string; format?: SerializedFieldFormat } & ( | { operationType: 'date_histogram'; sourceField: string } | { operationType: string; sourceField: never } ); diff --git a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/format_selector.tsx b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/format_selector.tsx index d3982881a8705..2b838ac8036e3 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/format_selector.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/format_selector.tsx @@ -28,6 +28,7 @@ import { } from '@kbn/field-formats-plugin/common'; import { css } from '@emotion/react'; import type { DocLinksStart } from '@kbn/core/public'; +import { TextBasedLayerColumn } from '../../text_based/types'; import { LensAppServices } from '../../../app_plugin/types'; import { GenericIndexPatternColumn } from '../form_based'; import { isColumnFormatted } from '../operations/definitions/helpers'; @@ -127,7 +128,7 @@ type FormatParams = NonNullable; type FormatParamsKeys = keyof FormatParams; export interface FormatSelectorProps { - selectedColumn: GenericIndexPatternColumn; + selectedColumn: GenericIndexPatternColumn | TextBasedLayerColumn; onChange: (newFormat?: { id: string; params?: FormatParams }) => void; docLinks: DocLinksStart; } diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/helpers.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/helpers.tsx index b65deebec963a..59e0d43e491b2 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/helpers.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/helpers.tsx @@ -10,6 +10,7 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { isEqual } from 'lodash'; import { Query } from '@kbn/es-query'; +import { TextBasedLayerColumn } from '../../../text_based/types'; import type { IndexPattern, IndexPatternField } from '../../../../types'; import { type FieldBasedOperationErrorMessage, @@ -178,8 +179,8 @@ export const isColumn = ( }; export function isColumnFormatted( - column: GenericIndexPatternColumn -): column is FormattedIndexPatternColumn { + column: GenericIndexPatternColumn | TextBasedLayerColumn +): column is FormattedIndexPatternColumn | TextBasedLayerColumn { return Boolean( 'params' in column && (column as FormattedIndexPatternColumn).params && diff --git a/x-pack/plugins/lens/public/datasources/text_based/components/dimension_editor.tsx b/x-pack/plugins/lens/public/datasources/text_based/components/dimension_editor.tsx index 5b0032caff0d2..bfaf056f6089d 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/components/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/components/dimension_editor.tsx @@ -5,16 +5,24 @@ * 2.0. */ -import React, { useEffect, useState, useMemo } from 'react'; +import React, { useEffect, useState, useMemo, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiFormRow } from '@elastic/eui'; +import { EuiFormRow, useEuiTheme, EuiText } from '@elastic/eui'; import { euiThemeVars } from '@kbn/ui-theme'; import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; import { fetchFieldsFromESQL } from '@kbn/esql-editor'; +import { NameInput } from '@kbn/visualization-ui-components'; +import { css } from '@emotion/react'; +import { mergeLayer, updateColumnFormat, updateColumnLabel } from '../utils'; +import { + FormatSelector, + FormatSelectorProps, +} from '../../form_based/dimension_panel/format_selector'; import type { DatasourceDimensionEditorProps, DataType } from '../../../types'; import { FieldSelect, type FieldOptionCompatible } from './field_select'; import type { TextBasedPrivateState } from '../types'; import { isNotNumeric, isNumeric } from '../utils'; +import { TextBasedLayer } from '../types'; export type TextBasedDimensionEditorProps = DatasourceDimensionEditorProps & { @@ -24,6 +32,17 @@ export type TextBasedDimensionEditorProps = export function TextBasedDimensionEditor(props: TextBasedDimensionEditorProps) { const [allColumns, setAllColumns] = useState([]); const query = props.state.layers[props.layerId]?.query; + const { euiTheme } = useEuiTheme(); + const { + isFullscreen, + columnId, + layerId, + state, + setState, + indexPatterns, + dateRange, + expressions, + } = props; useEffect(() => { // in case the columns are not in the cache, I refetch them @@ -31,7 +50,12 @@ export function TextBasedDimensionEditor(props: TextBasedDimensionEditorProps) { if (query) { const table = await fetchFieldsFromESQL( { esql: `${query.esql} | limit 0` }, - props.expressions + expressions, + { from: dateRange.fromDate, to: dateRange.toDate }, + undefined, + Object.values(indexPatterns).length + ? Object.values(indexPatterns)[0].timeFieldName + : undefined ); if (table) { const hasNumberTypeColumns = table.columns?.some(isNumeric); @@ -55,13 +79,40 @@ export function TextBasedDimensionEditor(props: TextBasedDimensionEditorProps) { } } fetchColumns(); - }, [props, props.expressions, query]); + }, [ + dateRange.fromDate, + dateRange.toDate, + expressions, + indexPatterns, + props, + props.expressions, + query, + ]); const selectedField = useMemo(() => { const layerColumns = props.state.layers[props.layerId].columns; return layerColumns?.find((column) => column.columnId === props.columnId); }, [props.columnId, props.layerId, props.state.layers]); + const updateLayer = useCallback( + (newLayer: Partial) => + setState((prevState) => mergeLayer({ state: prevState, layerId, newLayer })), + [layerId, setState] + ); + + const onFormatChange = useCallback( + (newFormat) => { + updateLayer( + updateColumnFormat({ + layer: state.layers[layerId], + columnId, + value: newFormat, + }) + ); + }, + [columnId, layerId, state.layers, updateLayer] + ); + return ( <> )} + {!isFullscreen && selectedField && ( +
+ +

+ {i18n.translate('xpack.lens.indexPattern.dimensionEditor.headingAppearance', { + defaultMessage: 'Appearance', + })} +

+
+ + { + updateLayer( + updateColumnLabel({ + layer: state.layers[layerId], + columnId, + value, + }) + ); + }} + /> + + {selectedField.meta?.type === 'number' ? ( + + ) : null} +
+ )} ); } diff --git a/x-pack/plugins/lens/public/datasources/text_based/fetch_data_from_aggregate_query.ts b/x-pack/plugins/lens/public/datasources/text_based/fetch_data_from_aggregate_query.ts index 84fc5bbd950d1..779541f95a9c4 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/fetch_data_from_aggregate_query.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/fetch_data_from_aggregate_query.ts @@ -33,7 +33,7 @@ export function fetchDataFromAggregateQuery( filters, query, time: timeRange, - dataView, + timeFieldName: dataView.timeFieldName, inputQuery, }) .then((ast) => { diff --git a/x-pack/plugins/lens/public/datasources/text_based/to_expression.ts b/x-pack/plugins/lens/public/datasources/text_based/to_expression.ts index a175f191d5916..d1ef206ad5b57 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/to_expression.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/to_expression.ts @@ -8,8 +8,9 @@ import { i18n } from '@kbn/i18n'; import { Ast } from '@kbn/interpreter'; import { textBasedQueryStateToExpressionAst } from '@kbn/data-plugin/common'; -import type { OriginalColumn } from '../../../common/types'; +import { ExpressionAstFunction } from '@kbn/expressions-plugin/common'; import { TextBasedPrivateState, TextBasedLayer, IndexPatternRef } from './types'; +import type { OriginalColumn } from '../../../common/types'; function getExpressionForLayer( layer: TextBasedLayer, @@ -25,7 +26,7 @@ function getExpressionForLayer( if (idMapper[col.fieldName]) { idMapper[col.fieldName].push({ id: col.columnId, - label: col.fieldName, + label: col.customLabel ? col.label : col.fieldName, } as OriginalColumn); } else { idMapper = { @@ -33,7 +34,7 @@ function getExpressionForLayer( [col.fieldName]: [ { id: col.columnId, - label: col.fieldName, + label: col.customLabel ? col.label : col.fieldName, } as OriginalColumn, ], }; @@ -41,6 +42,45 @@ function getExpressionForLayer( }); const timeFieldName = layer.timeField ?? undefined; + const formatterOverrides: ExpressionAstFunction[] = layer.columns + .filter((col) => col.params?.format) + .map((col) => { + const format = col.params!.format!; + + const base: ExpressionAstFunction = { + type: 'function', + function: 'lens_format_column', + arguments: { + format: format ? [format.id] : [''], + columnId: [col.columnId], + decimals: typeof format?.params?.decimals === 'number' ? [format.params.decimals] : [], + suffix: + format?.params && 'suffix' in format.params && format.params.suffix + ? [format.params.suffix] + : [], + compact: + format?.params && 'compact' in format.params && format.params.compact + ? [format.params.compact] + : [], + pattern: + format?.params && 'pattern' in format.params && format.params.pattern + ? [format.params.pattern] + : [], + fromUnit: + format?.params && 'fromUnit' in format.params && format.params.fromUnit + ? [format.params.fromUnit] + : [], + toUnit: + format?.params && 'toUnit' in format.params && format.params.toUnit + ? [format.params.toUnit] + : [], + parentFormat: [], + }, + }; + + return base; + }); + if (!layer.table) { const textBasedQueryToAst = textBasedQueryStateToExpressionAst({ query: layer.query, @@ -62,6 +102,7 @@ function getExpressionForLayer( isTextBased: [true], }, }); + textBasedQueryToAst.chain.push(...formatterOverrides); return textBasedQueryToAst; } else { return { @@ -81,6 +122,7 @@ function getExpressionForLayer( idMap: [JSON.stringify(idMapper)], }, }, + ...formatterOverrides, ], }; } diff --git a/x-pack/plugins/lens/public/datasources/text_based/types.ts b/x-pack/plugins/lens/public/datasources/text_based/types.ts index cd4568101a603..4516a4e46a412 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/types.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/types.ts @@ -7,11 +7,17 @@ import type { Datatable, DatatableColumn } from '@kbn/expressions-plugin/public'; import type { AggregateQuery } from '@kbn/es-query'; import type { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; +import { ValueFormatConfig } from '../form_based/operations/definitions/column_types'; import type { VisualizeEditorContext } from '../../types'; export interface TextBasedLayerColumn { columnId: string; fieldName: string; + label?: string; + customLabel?: boolean; + params?: { + format?: ValueFormatConfig; + }; meta?: DatatableColumn['meta']; inMetricDimension?: boolean; } diff --git a/x-pack/plugins/lens/public/datasources/text_based/utils.test.ts b/x-pack/plugins/lens/public/datasources/text_based/utils.test.ts index 77853e3052b24..72ee6869e811a 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/utils.test.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/utils.test.ts @@ -129,6 +129,7 @@ describe('Text based languages utils', () => { { fieldName: 'timestamp', columnId: 'timestamp', + label: 'timestamp', meta: { type: 'date', }, @@ -136,6 +137,7 @@ describe('Text based languages utils', () => { { fieldName: 'memory', columnId: 'memory', + label: 'memory', meta: { type: 'number', }, diff --git a/x-pack/plugins/lens/public/datasources/text_based/utils.ts b/x-pack/plugins/lens/public/datasources/text_based/utils.ts index 79e3a15e78a50..ec8b1cc7645e9 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/utils.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/utils.ts @@ -11,9 +11,15 @@ import { getESQLAdHocDataview } from '@kbn/esql-utils'; import type { AggregateQuery } from '@kbn/es-query'; import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; import type { DatatableColumn } from '@kbn/expressions-plugin/public'; +import { ValueFormatConfig } from '../form_based/operations/definitions/column_types'; import { generateId } from '../../id_generator'; import { fetchDataFromAggregateQuery } from './fetch_data_from_aggregate_query'; -import type { IndexPatternRef, TextBasedPrivateState, TextBasedLayerColumn } from './types'; +import type { + IndexPatternRef, + TextBasedPrivateState, + TextBasedLayerColumn, + TextBasedLayer, +} from './types'; import type { DataViewsState } from '../../state_management'; import { addColumnsToCache } from './fieldlist_cache'; @@ -46,7 +52,12 @@ export const getAllColumns = ( }); const allCols = [ ...columns, - ...columnsFromQuery.map((c) => ({ columnId: c.id, fieldName: c.id, meta: c.meta })), + ...columnsFromQuery.map((c) => ({ + columnId: c.id, + fieldName: c.id, + label: c.name, + meta: c.meta, + })), ]; const uniqueIds: string[] = []; @@ -154,3 +165,70 @@ export function canColumnBeUsedBeInMetricDimension( (hasNumberTypeColumns && selectedColumnType === 'number') ); } + +export function mergeLayer({ + state, + layerId, + newLayer, +}: { + state: TextBasedPrivateState; + layerId: string; + newLayer: Partial; +}) { + return { + ...state, + layers: { + ...state.layers, + [layerId]: { ...state.layers[layerId], ...newLayer }, + }, + }; +} + +export function updateColumnLabel({ + layer, + columnId, + value, +}: { + layer: TextBasedLayer; + columnId: string; + value: string; +}): TextBasedLayer { + const currentColumnIndex = layer.columns.findIndex((c) => c.columnId === columnId); + const currentColumn = layer.columns[currentColumnIndex]; + return { + ...layer, + columns: [ + ...layer.columns.slice(0, currentColumnIndex), + { + ...currentColumn, + label: value, + customLabel: !!value, + }, + ...layer.columns.slice(currentColumnIndex + 1), + ], + }; +} + +export function updateColumnFormat({ + layer, + columnId, + value, +}: { + layer: TextBasedLayer; + columnId: string; + value: ValueFormatConfig | undefined; +}): TextBasedLayer { + const currentColumnIndex = layer.columns.findIndex((c) => c.columnId === columnId); + const currentColumn = layer.columns[currentColumnIndex]; + return { + ...layer, + columns: [ + ...layer.columns.slice(0, currentColumnIndex), + { + ...currentColumn, + params: { format: value }, + }, + ...layer.columns.slice(currentColumnIndex + 1), + ], + }; +} diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/esql_query_expression.tsx b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/esql_query_expression.tsx index cfa6cde15ee50..adee5b4bc2bc1 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/esql_query_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/expression/esql_query_expression.tsx @@ -27,7 +27,6 @@ import { getTimeOptions, parseAggregationResults, } from '@kbn/triggers-actions-ui-plugin/public/common'; -import { DataView } from '@kbn/data-views-plugin/common'; import { EsQueryRuleParams, EsQueryRuleMetaData, SearchType } from '../types'; import { DEFAULT_VALUES, SERVERLESS_DEFAULT_VALUES } from '../constants'; import { useTriggerUiActionServices } from '../util'; @@ -38,7 +37,7 @@ import { rowToDocument, toEsQueryHits, transformDatatableToEsqlTable } from '../ export const EsqlQueryExpression: React.FC< RuleTypeParamsExpressionProps, EsQueryRuleMetaData> > = ({ ruleParams, setRuleParams, setRuleProperty, errors }) => { - const { expressions, http, fieldFormats, isServerless, dataViews } = useTriggerUiActionServices(); + const { expressions, http, isServerless, dataViews } = useTriggerUiActionServices(); const { esqlQuery, timeWindowSize, timeWindowUnit, timeField } = ruleParams; const [currentRuleParams, setCurrentRuleParams] = useState< @@ -116,10 +115,7 @@ export const EsqlQueryExpression: React.FC< }, undefined, // create a data view with the timefield to pass into the query - new DataView({ - spec: { timeFieldName: timeField }, - fieldFormats, - }) + timeField ); if (table) { const esqlTable = transformDatatableToEsqlTable(table); @@ -154,7 +150,6 @@ export const EsqlQueryExpression: React.FC< currentRuleParams, esqlQuery, expressions, - fieldFormats, timeField, isServerless, ]);