mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[ES|QL] Supports custom formatters in charts (#201540)
This commit is contained in:
parent
e3ec4771d1
commit
168e67d50d
18 changed files with 275 additions and 56 deletions
|
@ -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 = () => {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
})) ?? [];
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ export function fetchEsql({
|
|||
filters,
|
||||
query,
|
||||
time: timeRange,
|
||||
dataView,
|
||||
timeFieldName: dataView.timeFieldName,
|
||||
inputQuery,
|
||||
titleForInspector: i18n.translate('discover.inspectorEsqlRequestTitle', {
|
||||
defaultMessage: 'Table',
|
||||
|
|
|
@ -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([
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}));
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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 }
|
||||
);
|
||||
|
|
|
@ -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<ValueFormatConfig['params']>;
|
|||
type FormatParamsKeys = keyof FormatParams;
|
||||
|
||||
export interface FormatSelectorProps {
|
||||
selectedColumn: GenericIndexPatternColumn;
|
||||
selectedColumn: GenericIndexPatternColumn | TextBasedLayerColumn;
|
||||
onChange: (newFormat?: { id: string; params?: FormatParams }) => void;
|
||||
docLinks: DocLinksStart;
|
||||
}
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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<TextBasedPrivateState> & {
|
||||
|
@ -24,6 +32,17 @@ export type TextBasedDimensionEditorProps =
|
|||
export function TextBasedDimensionEditor(props: TextBasedDimensionEditorProps) {
|
||||
const [allColumns, setAllColumns] = useState<FieldOptionCompatible[]>([]);
|
||||
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<TextBasedLayer>) =>
|
||||
setState((prevState) => mergeLayer({ state: prevState, layerId, newLayer })),
|
||||
[layerId, setState]
|
||||
);
|
||||
|
||||
const onFormatChange = useCallback<FormatSelectorProps['onChange']>(
|
||||
(newFormat) => {
|
||||
updateLayer(
|
||||
updateColumnFormat({
|
||||
layer: state.layers[layerId],
|
||||
columnId,
|
||||
value: newFormat,
|
||||
})
|
||||
);
|
||||
},
|
||||
[columnId, layerId, state.layers, updateLayer]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFormRow
|
||||
|
@ -80,6 +131,7 @@ export function TextBasedDimensionEditor(props: TextBasedDimensionEditorProps) {
|
|||
const newColumn = {
|
||||
columnId: props.columnId,
|
||||
fieldName: choice.field,
|
||||
label: choice.field,
|
||||
meta,
|
||||
};
|
||||
return props.setState(
|
||||
|
@ -122,6 +174,44 @@ export function TextBasedDimensionEditor(props: TextBasedDimensionEditorProps) {
|
|||
{props.dataSectionExtra}
|
||||
</div>
|
||||
)}
|
||||
{!isFullscreen && selectedField && (
|
||||
<div className="lnsIndexPatternDimensionEditor--padded lnsIndexPatternDimensionEditor--collapseNext">
|
||||
<EuiText
|
||||
size="s"
|
||||
css={css`
|
||||
margin-bottom: ${euiTheme.size.base};
|
||||
`}
|
||||
>
|
||||
<h4>
|
||||
{i18n.translate('xpack.lens.indexPattern.dimensionEditor.headingAppearance', {
|
||||
defaultMessage: 'Appearance',
|
||||
})}
|
||||
</h4>
|
||||
</EuiText>
|
||||
|
||||
<NameInput
|
||||
value={selectedField.label || ''}
|
||||
defaultValue={''}
|
||||
onChange={(value) => {
|
||||
updateLayer(
|
||||
updateColumnLabel({
|
||||
layer: state.layers[layerId],
|
||||
columnId,
|
||||
value,
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
{selectedField.meta?.type === 'number' ? (
|
||||
<FormatSelector
|
||||
selectedColumn={selectedField}
|
||||
onChange={onFormatChange}
|
||||
docLinks={props.core.docLinks}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ export function fetchDataFromAggregateQuery(
|
|||
filters,
|
||||
query,
|
||||
time: timeRange,
|
||||
dataView,
|
||||
timeFieldName: dataView.timeFieldName,
|
||||
inputQuery,
|
||||
})
|
||||
.then((ast) => {
|
||||
|
|
|
@ -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,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
|
|
|
@ -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<TextBasedLayer>;
|
||||
}) {
|
||||
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),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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<EsQueryRuleParams<SearchType.esqlQuery>, 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,
|
||||
]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue