mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Data] Move the rest of the datatable
utility functions to the related service (#134562)
* Move date histogram column meta utility function to the datatable utilities service * Move number histogram interval utility function to the datatable utilities service * Move precision error check to the datatable utility service
This commit is contained in:
parent
161e9df8c3
commit
f13d321c9f
53 changed files with 453 additions and 293 deletions
|
@ -8,6 +8,7 @@
|
|||
|
||||
import type { PaletteRegistry } from '@kbn/coloring';
|
||||
import type { ChartsPluginSetup } from '@kbn/charts-plugin/public';
|
||||
import type { DatatableUtilitiesService } from '@kbn/data-plugin/common';
|
||||
import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common';
|
||||
import type { RangeSelectContext, ValueClickContext } from '@kbn/embeddable-plugin/public';
|
||||
import type { PersistedState } from '@kbn/visualizations-plugin/public';
|
||||
|
@ -29,6 +30,7 @@ export type HeatmapRenderProps = HeatmapExpressionProps & {
|
|||
timeZone?: string;
|
||||
formatFactory: FormatFactory;
|
||||
chartsThemeService: ChartsPluginSetup['theme'];
|
||||
datatableUtilities: DatatableUtilitiesService;
|
||||
onClickValue: (data: FilterEvent['data']) => void;
|
||||
onSelectRange: (data: BrushEvent['data']) => void;
|
||||
paletteService: PaletteRegistry;
|
||||
|
|
|
@ -10,6 +10,7 @@ import React from 'react';
|
|||
import { Settings, TooltipType, Heatmap } from '@elastic/charts';
|
||||
import { chartPluginMock } from '@kbn/charts-plugin/public/mocks';
|
||||
import { EmptyPlaceholder } from '@kbn/charts-plugin/public';
|
||||
import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks';
|
||||
import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks';
|
||||
import type { Datatable } from '@kbn/expressions-plugin/public';
|
||||
import { mountWithIntl, shallowWithIntl } from '@kbn/test-jest-helpers';
|
||||
|
@ -110,6 +111,7 @@ describe('HeatmapComponent', function () {
|
|||
uiState,
|
||||
onClickValue: jest.fn(),
|
||||
onSelectRange: jest.fn(),
|
||||
datatableUtilities: createDatatableUtilitiesMock(),
|
||||
paletteService: palettesRegistry,
|
||||
formatFactory: formatService.deserialize,
|
||||
interactive: true,
|
||||
|
|
|
@ -131,6 +131,7 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
|
|||
timeZone,
|
||||
formatFactory,
|
||||
chartsThemeService,
|
||||
datatableUtilities,
|
||||
onClickValue,
|
||||
onSelectRange,
|
||||
paletteService,
|
||||
|
@ -315,7 +316,7 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
|
|||
const xValuesFormatter = formatFactory(xAxisMeta?.params);
|
||||
const metricFormatter = formatFactory(getFormatByAccessor(args.valueAccessor!, table.columns));
|
||||
const dateHistogramMeta = xAxisColumn
|
||||
? search.aggs.getDateHistogramMetaDataByDatatableColumn(xAxisColumn)
|
||||
? datatableUtilities.getDateHistogramMeta(xAxisColumn)
|
||||
: undefined;
|
||||
|
||||
// Fallback to the ordinal scale type when a single row of data is provided.
|
||||
|
|
|
@ -18,7 +18,13 @@ import {
|
|||
FilterEvent,
|
||||
BrushEvent,
|
||||
} from '../../common';
|
||||
import { getFormatService, getPaletteService, getUISettings, getThemeService } from '../services';
|
||||
import {
|
||||
getDatatableUtilities,
|
||||
getFormatService,
|
||||
getPaletteService,
|
||||
getUISettings,
|
||||
getThemeService,
|
||||
} from '../services';
|
||||
import { getTimeZone } from '../utils/get_timezone';
|
||||
|
||||
interface ExpressioHeatmapRendererDependencies {
|
||||
|
@ -55,6 +61,7 @@ export const heatmapRenderer: (
|
|||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
timeZone={timeZone}
|
||||
datatableUtilities={getDatatableUtilities()}
|
||||
formatFactory={getFormatService().deserialize}
|
||||
chartsThemeService={getThemeService()}
|
||||
paletteService={getPaletteService()}
|
||||
|
|
|
@ -7,10 +7,17 @@
|
|||
*/
|
||||
import { ChartsPluginSetup } from '@kbn/charts-plugin/public';
|
||||
import { CoreSetup, CoreStart } from '@kbn/core/public';
|
||||
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { Plugin as ExpressionsPublicPlugin } from '@kbn/expressions-plugin/public';
|
||||
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
||||
import { heatmapFunction, heatmapLegendConfig, heatmapGridConfig } from '../common';
|
||||
import { setFormatService, setPaletteService, setUISettings, setThemeService } from './services';
|
||||
import {
|
||||
setDatatableUtilities,
|
||||
setFormatService,
|
||||
setPaletteService,
|
||||
setUISettings,
|
||||
setThemeService,
|
||||
} from './services';
|
||||
import { heatmapRenderer } from './expression_renderers';
|
||||
|
||||
/** @internal */
|
||||
|
@ -21,6 +28,7 @@ export interface ExpressionHeatmapPluginSetup {
|
|||
|
||||
/** @internal */
|
||||
export interface ExpressionHeatmapPluginStart {
|
||||
data: DataPublicPluginStart;
|
||||
fieldFormats: FieldFormatsStart;
|
||||
}
|
||||
|
||||
|
@ -38,7 +46,8 @@ export class ExpressionHeatmapPlugin {
|
|||
expressions.registerRenderer(heatmapRenderer({ theme: core.theme }));
|
||||
}
|
||||
|
||||
public start(core: CoreStart, { fieldFormats }: ExpressionHeatmapPluginStart) {
|
||||
public start(core: CoreStart, { data, fieldFormats }: ExpressionHeatmapPluginStart) {
|
||||
setFormatService(fieldFormats);
|
||||
setDatatableUtilities(data.datatableUtilities);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { DatatableColumn } from '@kbn/expressions-plugin';
|
||||
import { createGetterSetter } from '@kbn/kibana-utils-plugin/public';
|
||||
import type { DatatableUtilitiesService } from '@kbn/data-plugin/common';
|
||||
|
||||
/** @public **/
|
||||
export const checkColumnForPrecisionError = (column: DatatableColumn) =>
|
||||
column.meta.sourceParams?.hasPrecisionError;
|
||||
export const [getDatatableUtilities, setDatatableUtilities] =
|
||||
createGetterSetter<DatatableUtilitiesService>('data.datatableUtilities');
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { getDatatableUtilities, setDatatableUtilities } from './datatable_utilities';
|
||||
export { getFormatService, setFormatService } from './format_service';
|
||||
export {
|
||||
getPaletteService,
|
||||
|
|
|
@ -10,7 +10,7 @@ import { isUndefined, uniq } from 'lodash';
|
|||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import { Endzones } from '@kbn/charts-plugin/public';
|
||||
import { search } from '@kbn/data-plugin/public';
|
||||
import type { DatatableUtilitiesService } from '@kbn/data-plugin/common';
|
||||
import {
|
||||
getAccessorByDimension,
|
||||
getColumnByAccessor,
|
||||
|
@ -23,12 +23,14 @@ export interface XDomain {
|
|||
minInterval?: number;
|
||||
}
|
||||
|
||||
export const getAppliedTimeRange = (layers: CommonXYDataLayerConfig[]) => {
|
||||
export const getAppliedTimeRange = (
|
||||
datatableUtilitites: DatatableUtilitiesService,
|
||||
layers: CommonXYDataLayerConfig[]
|
||||
) => {
|
||||
return layers
|
||||
.map(({ xAccessor, table }) => {
|
||||
const xColumn = xAccessor ? getColumnByAccessor(xAccessor, table.columns) : null;
|
||||
const timeRange =
|
||||
xColumn && search.aggs.getDateHistogramMetaDataByDatatableColumn(xColumn)?.timeRange;
|
||||
const timeRange = xColumn && datatableUtilitites.getDateHistogramMeta(xColumn)?.timeRange;
|
||||
if (timeRange) {
|
||||
return {
|
||||
timeRange,
|
||||
|
@ -40,13 +42,14 @@ export const getAppliedTimeRange = (layers: CommonXYDataLayerConfig[]) => {
|
|||
};
|
||||
|
||||
export const getXDomain = (
|
||||
datatableUtilitites: DatatableUtilitiesService,
|
||||
layers: CommonXYDataLayerConfig[],
|
||||
minInterval: number | undefined,
|
||||
isTimeViz: boolean,
|
||||
isHistogram: boolean,
|
||||
xExtent?: AxisExtentConfigResult
|
||||
) => {
|
||||
const appliedTimeRange = getAppliedTimeRange(layers)?.timeRange;
|
||||
const appliedTimeRange = getAppliedTimeRange(datatableUtilitites, layers)?.timeRange;
|
||||
const from = appliedTimeRange?.from;
|
||||
const to = appliedTimeRange?.to;
|
||||
const baseDomain = isTimeViz
|
||||
|
|
|
@ -32,6 +32,7 @@ import {
|
|||
} from '@elastic/charts';
|
||||
import { Datatable } from '@kbn/expressions-plugin/common';
|
||||
import { EmptyPlaceholder } from '@kbn/charts-plugin/public';
|
||||
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
|
||||
import { eventAnnotationServiceMock } from '@kbn/event-annotation-plugin/public/mocks';
|
||||
import { EventAnnotationOutput } from '@kbn/event-annotation-plugin/common';
|
||||
import { DataLayerConfig } from '../../common';
|
||||
|
@ -69,7 +70,7 @@ const onSelectRange = jest.fn();
|
|||
describe('XYChart component', () => {
|
||||
let getFormatSpy: jest.Mock;
|
||||
let convertSpy: jest.Mock;
|
||||
let defaultProps: Omit<XYChartRenderProps, 'data' | 'args'>;
|
||||
let defaultProps: Omit<XYChartRenderProps, 'args'>;
|
||||
|
||||
const dataWithoutFormats: Datatable = {
|
||||
type: 'datatable',
|
||||
|
@ -109,6 +110,7 @@ describe('XYChart component', () => {
|
|||
getFormatSpy.mockReturnValue({ convert: convertSpy });
|
||||
|
||||
defaultProps = {
|
||||
data: dataPluginMock.createStartContract(),
|
||||
formatFactory: getFormatSpy,
|
||||
timeZone: 'UTC',
|
||||
renderMode: 'view',
|
||||
|
|
|
@ -30,6 +30,7 @@ import {
|
|||
import { IconType } from '@elastic/eui';
|
||||
import { PaletteRegistry } from '@kbn/coloring';
|
||||
import { RenderMode } from '@kbn/expressions-plugin/common';
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { EmptyPlaceholder } from '@kbn/charts-plugin/public';
|
||||
import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public';
|
||||
import { ChartsPluginSetup, ChartsPluginStart, useActiveCursor } from '@kbn/charts-plugin/public';
|
||||
|
@ -101,6 +102,7 @@ declare global {
|
|||
export type XYChartRenderProps = XYChartProps & {
|
||||
chartsThemeService: ChartsPluginSetup['theme'];
|
||||
chartsActiveCursorService: ChartsPluginStart['activeCursor'];
|
||||
data: DataPublicPluginStart;
|
||||
paletteService: PaletteRegistry;
|
||||
formatFactory: FormatFactory;
|
||||
timeZone: string;
|
||||
|
@ -144,6 +146,7 @@ export const XYChartReportable = React.memo(XYChart);
|
|||
|
||||
export function XYChart({
|
||||
args,
|
||||
data,
|
||||
formatFactory,
|
||||
timeZone,
|
||||
chartsThemeService,
|
||||
|
@ -277,6 +280,7 @@ export function XYChart({
|
|||
const isHistogramViz = dataLayers.every((l) => l.isHistogram);
|
||||
|
||||
const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain(
|
||||
data.datatableUtilities,
|
||||
dataLayers,
|
||||
minInterval,
|
||||
isTimeViz,
|
||||
|
|
|
@ -13,6 +13,7 @@ import React from 'react';
|
|||
import ReactDOM from 'react-dom';
|
||||
import type { PaletteRegistry } from '@kbn/coloring';
|
||||
import type { ChartsPluginStart } from '@kbn/charts-plugin/public';
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public';
|
||||
import { ExpressionRenderDefinition } from '@kbn/expressions-plugin';
|
||||
import { FormatFactory } from '@kbn/field-formats-plugin/common';
|
||||
|
@ -21,6 +22,7 @@ import type { XYChartProps } from '../../common';
|
|||
import type { BrushEvent, FilterEvent } from '../types';
|
||||
|
||||
export type GetStartDepsFn = () => Promise<{
|
||||
data: DataPublicPluginStart;
|
||||
formatFactory: FormatFactory;
|
||||
theme: ChartsPluginStart['theme'];
|
||||
activeCursor: ChartsPluginStart['activeCursor'];
|
||||
|
@ -69,6 +71,7 @@ export const getXyChartRenderer = ({
|
|||
>
|
||||
<XYChartReportable
|
||||
{...config}
|
||||
data={deps.data}
|
||||
formatFactory={deps.formatFactory}
|
||||
chartsActiveCursorService={deps.activeCursor}
|
||||
chartsThemeService={deps.theme}
|
||||
|
@ -76,7 +79,7 @@ export const getXyChartRenderer = ({
|
|||
timeZone={deps.timeZone}
|
||||
eventAnnotationService={deps.eventAnnotationService}
|
||||
useLegacyTimeAxis={deps.useLegacyTimeAxis}
|
||||
minInterval={calculateMinInterval(config)}
|
||||
minInterval={calculateMinInterval(deps.data.datatableUtilities, config)}
|
||||
interactive={handlers.isInteractive()}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks';
|
||||
import { DataLayerConfig, XYChartProps } from '../../common';
|
||||
import { sampleArgs } from '../../common/__mocks__';
|
||||
import { calculateMinInterval } from './interval';
|
||||
|
||||
describe('calculateMinInterval', () => {
|
||||
const datatableUtilities = createDatatableUtilitiesMock();
|
||||
let xyProps: XYChartProps;
|
||||
let layer: DataLayerConfig;
|
||||
beforeEach(() => {
|
||||
|
@ -29,7 +31,7 @@ describe('calculateMinInterval', () => {
|
|||
},
|
||||
};
|
||||
xyProps.args.layers[0] = layer;
|
||||
const result = await calculateMinInterval(xyProps);
|
||||
const result = await calculateMinInterval(datatableUtilities, xyProps);
|
||||
expect(result).toEqual(5 * 60 * 1000);
|
||||
});
|
||||
|
||||
|
@ -48,7 +50,7 @@ describe('calculateMinInterval', () => {
|
|||
},
|
||||
};
|
||||
xyProps.args.layers[0] = layer;
|
||||
const result = await calculateMinInterval(xyProps);
|
||||
const result = await calculateMinInterval(datatableUtilities, xyProps);
|
||||
expect(result).toEqual(5);
|
||||
});
|
||||
|
||||
|
@ -63,19 +65,19 @@ describe('calculateMinInterval', () => {
|
|||
};
|
||||
|
||||
xyProps.args.layers[0] = layer;
|
||||
const result = await calculateMinInterval(xyProps);
|
||||
const result = await calculateMinInterval(datatableUtilities, xyProps);
|
||||
expect(result).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should return undefined if interval can not be checked', async () => {
|
||||
const result = await calculateMinInterval(xyProps);
|
||||
const result = await calculateMinInterval(datatableUtilities, xyProps);
|
||||
expect(result).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should return undefined if date column is not found', async () => {
|
||||
layer.table.columns.splice(2, 1);
|
||||
xyProps.args.layers[0] = layer;
|
||||
const result = await calculateMinInterval(xyProps);
|
||||
const result = await calculateMinInterval(datatableUtilities, xyProps);
|
||||
expect(result).toEqual(undefined);
|
||||
});
|
||||
|
||||
|
@ -83,7 +85,7 @@ describe('calculateMinInterval', () => {
|
|||
layer.xScaleType = 'ordinal';
|
||||
xyProps.args.layers[0] = layer;
|
||||
xyProps.args.layers[0].table.columns.splice(2, 1);
|
||||
const result = await calculateMinInterval(xyProps);
|
||||
const result = await calculateMinInterval(datatableUtilities, xyProps);
|
||||
expect(result).toEqual(undefined);
|
||||
});
|
||||
|
||||
|
@ -97,7 +99,7 @@ describe('calculateMinInterval', () => {
|
|||
};
|
||||
xyProps.args.layers[0] = layer;
|
||||
xyProps.args.minTimeBarInterval = '1h';
|
||||
const result = await calculateMinInterval(xyProps);
|
||||
const result = await calculateMinInterval(datatableUtilities, xyProps);
|
||||
expect(result).toEqual(60 * 60 * 1000);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { DatatableUtilitiesService } from '@kbn/data-plugin/common';
|
||||
import { search } from '@kbn/data-plugin/public';
|
||||
import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils';
|
||||
import { XYChartProps } from '../../common';
|
||||
|
@ -13,7 +14,10 @@ import { isTimeChart } from '../../common/helpers';
|
|||
import { getFilteredLayers } from './layers';
|
||||
import { isDataLayer, getDataLayers } from './visualization';
|
||||
|
||||
export function calculateMinInterval({ args: { layers, minTimeBarInterval } }: XYChartProps) {
|
||||
export function calculateMinInterval(
|
||||
datatableUtilities: DatatableUtilitiesService,
|
||||
{ args: { layers, minTimeBarInterval } }: XYChartProps
|
||||
) {
|
||||
const filteredLayers = getFilteredLayers(layers);
|
||||
if (filteredLayers.length === 0) return;
|
||||
const isTimeViz = isTimeChart(getDataLayers(filteredLayers));
|
||||
|
@ -27,14 +31,14 @@ export function calculateMinInterval({ args: { layers, minTimeBarInterval } }: X
|
|||
return search.aggs.parseInterval(minTimeBarInterval)?.as('milliseconds');
|
||||
}
|
||||
if (!isTimeViz) {
|
||||
const histogramInterval = search.aggs.getNumberHistogramIntervalByDatatableColumn(xColumn);
|
||||
const histogramInterval = datatableUtilities.getNumberHistogramInterval(xColumn);
|
||||
if (typeof histogramInterval === 'number') {
|
||||
return histogramInterval;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
const dateInterval = search.aggs.getDateHistogramMetaDataByDatatableColumn(xColumn)?.interval;
|
||||
const dateInterval = datatableUtilities.getDateHistogramMeta(xColumn)?.interval;
|
||||
if (!dateInterval) return;
|
||||
const intervalDuration = search.aggs.parseInterval(dateInterval);
|
||||
if (!intervalDuration) return;
|
||||
|
|
|
@ -12,6 +12,7 @@ import type { Datatable, DatatableColumn } from '@kbn/expressions-plugin/common'
|
|||
import { FieldFormat } from '@kbn/field-formats-plugin/common';
|
||||
import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks';
|
||||
import type { AggsCommonStart } from '../search';
|
||||
import { BUCKET_TYPES } from '../search/aggs/buckets/bucket_agg_types';
|
||||
import { DatatableUtilitiesService } from './datatable_utilities_service';
|
||||
|
||||
describe('DatatableUtilitiesService', () => {
|
||||
|
@ -106,6 +107,94 @@ describe('DatatableUtilitiesService', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getNumberHistogramInterval', () => {
|
||||
it('should return nothing on column from other data source', () => {
|
||||
expect(
|
||||
datatableUtilitiesService.getNumberHistogramInterval({
|
||||
id: 'test',
|
||||
name: 'test',
|
||||
meta: {
|
||||
type: 'date',
|
||||
source: 'essql',
|
||||
},
|
||||
})
|
||||
).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should return nothing on non histogram column', () => {
|
||||
expect(
|
||||
datatableUtilitiesService.getNumberHistogramInterval({
|
||||
id: 'test',
|
||||
name: 'test',
|
||||
meta: {
|
||||
type: 'date',
|
||||
source: 'esaggs',
|
||||
sourceParams: {
|
||||
type: BUCKET_TYPES.TERMS,
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should return interval on resolved auto interval', () => {
|
||||
expect(
|
||||
datatableUtilitiesService.getNumberHistogramInterval({
|
||||
id: 'test',
|
||||
name: 'test',
|
||||
meta: {
|
||||
type: 'date',
|
||||
source: 'esaggs',
|
||||
sourceParams: {
|
||||
type: BUCKET_TYPES.HISTOGRAM,
|
||||
params: {
|
||||
interval: 'auto',
|
||||
used_interval: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual(20);
|
||||
});
|
||||
|
||||
it('should return interval on fixed interval', () => {
|
||||
expect(
|
||||
datatableUtilitiesService.getNumberHistogramInterval({
|
||||
id: 'test',
|
||||
name: 'test',
|
||||
meta: {
|
||||
type: 'date',
|
||||
source: 'esaggs',
|
||||
sourceParams: {
|
||||
type: BUCKET_TYPES.HISTOGRAM,
|
||||
params: {
|
||||
interval: 7,
|
||||
used_interval: 7,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual(7);
|
||||
});
|
||||
|
||||
it('should return `undefined` if information is not available', () => {
|
||||
expect(
|
||||
datatableUtilitiesService.getNumberHistogramInterval({
|
||||
id: 'test',
|
||||
name: 'test',
|
||||
meta: {
|
||||
type: 'date',
|
||||
source: 'esaggs',
|
||||
sourceParams: {
|
||||
type: BUCKET_TYPES.HISTOGRAM,
|
||||
params: {},
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTotalCount', () => {
|
||||
it('should return a total hits count', () => {
|
||||
const table = {
|
||||
|
@ -116,6 +205,40 @@ describe('DatatableUtilitiesService', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('hasPrecisionError', () => {
|
||||
test('should return true if there is a precision error in the column', () => {
|
||||
expect(
|
||||
datatableUtilitiesService.hasPrecisionError({
|
||||
meta: {
|
||||
sourceParams: {
|
||||
hasPrecisionError: true,
|
||||
},
|
||||
},
|
||||
} as unknown as DatatableColumn)
|
||||
).toBeTruthy();
|
||||
});
|
||||
test('should return false if there is no precision error in the column', () => {
|
||||
expect(
|
||||
datatableUtilitiesService.hasPrecisionError({
|
||||
meta: {
|
||||
sourceParams: {
|
||||
hasPrecisionError: false,
|
||||
},
|
||||
},
|
||||
} as unknown as DatatableColumn)
|
||||
).toBeFalsy();
|
||||
});
|
||||
test('should return false if precision error is not defined', () => {
|
||||
expect(
|
||||
datatableUtilitiesService.hasPrecisionError({
|
||||
meta: {
|
||||
sourceParams: {},
|
||||
},
|
||||
} as unknown as DatatableColumn)
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setFieldFormat', () => {
|
||||
it('should set new field format', () => {
|
||||
const column = { meta: {} } as DatatableColumn;
|
||||
|
|
|
@ -9,7 +9,22 @@
|
|||
import type { DataView, DataViewsContract, DataViewField } from '@kbn/data-views-plugin/common';
|
||||
import type { Datatable, DatatableColumn } from '@kbn/expressions-plugin/common';
|
||||
import type { FieldFormatsStartCommon, FieldFormat } from '@kbn/field-formats-plugin/common';
|
||||
import type { AggsCommonStart, AggConfig, CreateAggConfigParams, IAggType } from '../search';
|
||||
import type {
|
||||
AggsCommonStart,
|
||||
AggConfig,
|
||||
AggParamsDateHistogram,
|
||||
AggParamsHistogram,
|
||||
CreateAggConfigParams,
|
||||
IAggType,
|
||||
} from '../search';
|
||||
import { BUCKET_TYPES } from '../search/aggs/buckets/bucket_agg_types';
|
||||
import type { TimeRange } from '../types';
|
||||
|
||||
interface DateHistogramMeta {
|
||||
interval?: string;
|
||||
timeZone?: string;
|
||||
timeRange?: TimeRange;
|
||||
}
|
||||
|
||||
export class DatatableUtilitiesService {
|
||||
constructor(
|
||||
|
@ -46,6 +61,39 @@ export class DatatableUtilitiesService {
|
|||
return aggs[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function returning the used interval, used time zone and applied time filters for data table column created by the date_histogramm agg type.
|
||||
* "auto" will get expanded to the actually used interval.
|
||||
* If the column is not a column created by a date_histogram aggregation of the esaggs data source,
|
||||
* this function will return undefined.
|
||||
*/
|
||||
getDateHistogramMeta(
|
||||
column: DatatableColumn,
|
||||
defaults: Partial<{
|
||||
timeZone: string;
|
||||
}> = {}
|
||||
): DateHistogramMeta | undefined {
|
||||
if (column.meta.source !== 'esaggs') {
|
||||
return;
|
||||
}
|
||||
if (column.meta.sourceParams?.type !== BUCKET_TYPES.DATE_HISTOGRAM) {
|
||||
return;
|
||||
}
|
||||
|
||||
const params = column.meta.sourceParams.params as AggParamsDateHistogram;
|
||||
|
||||
let interval: string | undefined;
|
||||
if (params.used_interval && params.used_interval !== 'auto') {
|
||||
interval = params.used_interval;
|
||||
}
|
||||
|
||||
return {
|
||||
interval,
|
||||
timeZone: params.used_time_zone || defaults.timeZone,
|
||||
timeRange: column.meta.sourceParams.appliedTimeRange as TimeRange | undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async getDataView(column: DatatableColumn): Promise<DataView | undefined> {
|
||||
if (!column.meta.index) {
|
||||
return;
|
||||
|
@ -77,10 +125,37 @@ export class DatatableUtilitiesService {
|
|||
return params?.interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function returning the used interval for data table column created by the histogramm agg type.
|
||||
* "auto" will get expanded to the actually used interval.
|
||||
* If the column is not a column created by a histogram aggregation of the esaggs data source,
|
||||
* this function will return undefined.
|
||||
*/
|
||||
getNumberHistogramInterval(column: DatatableColumn): number | undefined {
|
||||
if (column.meta.source !== 'esaggs') {
|
||||
return;
|
||||
}
|
||||
if (column.meta.sourceParams?.type !== BUCKET_TYPES.HISTOGRAM) {
|
||||
return;
|
||||
}
|
||||
|
||||
const params = column.meta.sourceParams.params as unknown as AggParamsHistogram;
|
||||
|
||||
if (!params.used_interval || typeof params.used_interval === 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
return params.used_interval;
|
||||
}
|
||||
|
||||
getTotalCount(table: Datatable): number | undefined {
|
||||
return table.meta?.statistics?.totalCount;
|
||||
}
|
||||
|
||||
hasPrecisionError(column: DatatableColumn) {
|
||||
return column.meta.sourceParams?.hasPrecisionError;
|
||||
}
|
||||
|
||||
isFilterable(column: DatatableColumn): boolean {
|
||||
if (column.meta.source !== 'esaggs') {
|
||||
return false;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { DatatableUtilitiesService } from './datatable_utilities_service';
|
||||
import { DatatableUtilitiesService } from './datatable_utilities_service';
|
||||
|
||||
export function createDatatableUtilitiesMock(): jest.Mocked<DatatableUtilitiesService> {
|
||||
return {
|
||||
|
@ -14,8 +14,13 @@ export function createDatatableUtilitiesMock(): jest.Mocked<DatatableUtilitiesSe
|
|||
clearFieldFormat: jest.fn(),
|
||||
getAggConfig: jest.fn(),
|
||||
getDataView: jest.fn(),
|
||||
getDateHistogramMeta: jest.fn(DatatableUtilitiesService.prototype.getDateHistogramMeta),
|
||||
getField: jest.fn(),
|
||||
getFieldFormat: jest.fn(),
|
||||
getNumberHistogramInterval: jest.fn(
|
||||
DatatableUtilitiesService.prototype.getNumberHistogramInterval
|
||||
),
|
||||
hasPrecisionError: jest.fn(DatatableUtilitiesService.prototype.hasPrecisionError),
|
||||
isFilterable: jest.fn(),
|
||||
setFieldFormat: jest.fn(),
|
||||
} as unknown as jest.Mocked<DatatableUtilitiesService>;
|
||||
|
|
|
@ -1,40 +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 { DatatableColumn } from '@kbn/expressions-plugin/common';
|
||||
import { TimeRange } from '../../../types';
|
||||
import type { AggParamsDateHistogram } from '../buckets';
|
||||
import { BUCKET_TYPES } from '../buckets/bucket_agg_types';
|
||||
|
||||
/**
|
||||
* Helper function returning the used interval, used time zone and applied time filters for data table column created by the date_histogramm agg type.
|
||||
* "auto" will get expanded to the actually used interval.
|
||||
* If the column is not a column created by a date_histogram aggregation of the esaggs data source,
|
||||
* this function will return undefined.
|
||||
*/
|
||||
export const getDateHistogramMetaDataByDatatableColumn = (
|
||||
column: DatatableColumn,
|
||||
defaults: Partial<{
|
||||
timeZone: string;
|
||||
}> = {}
|
||||
) => {
|
||||
if (column.meta.source !== 'esaggs') return;
|
||||
if (column.meta.sourceParams?.type !== BUCKET_TYPES.DATE_HISTOGRAM) return;
|
||||
const params = column.meta.sourceParams.params as unknown as AggParamsDateHistogram;
|
||||
|
||||
let interval: string | undefined;
|
||||
if (params.used_interval && params.used_interval !== 'auto') {
|
||||
interval = params.used_interval;
|
||||
}
|
||||
|
||||
return {
|
||||
interval,
|
||||
timeZone: params.used_time_zone || defaults.timeZone,
|
||||
timeRange: column.meta.sourceParams.appliedTimeRange as TimeRange | undefined,
|
||||
};
|
||||
};
|
|
@ -1,98 +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 { getNumberHistogramIntervalByDatatableColumn } from '.';
|
||||
import { BUCKET_TYPES } from '../buckets';
|
||||
|
||||
describe('getNumberHistogramIntervalByDatatableColumn', () => {
|
||||
it('returns nothing on column from other data source', () => {
|
||||
expect(
|
||||
getNumberHistogramIntervalByDatatableColumn({
|
||||
id: 'test',
|
||||
name: 'test',
|
||||
meta: {
|
||||
type: 'date',
|
||||
source: 'essql',
|
||||
},
|
||||
})
|
||||
).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('returns nothing on non histogram column', () => {
|
||||
expect(
|
||||
getNumberHistogramIntervalByDatatableColumn({
|
||||
id: 'test',
|
||||
name: 'test',
|
||||
meta: {
|
||||
type: 'date',
|
||||
source: 'esaggs',
|
||||
sourceParams: {
|
||||
type: BUCKET_TYPES.TERMS,
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('returns interval on resolved auto interval', () => {
|
||||
expect(
|
||||
getNumberHistogramIntervalByDatatableColumn({
|
||||
id: 'test',
|
||||
name: 'test',
|
||||
meta: {
|
||||
type: 'date',
|
||||
source: 'esaggs',
|
||||
sourceParams: {
|
||||
type: BUCKET_TYPES.HISTOGRAM,
|
||||
params: {
|
||||
interval: 'auto',
|
||||
used_interval: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual(20);
|
||||
});
|
||||
|
||||
it('returns interval on fixed interval', () => {
|
||||
expect(
|
||||
getNumberHistogramIntervalByDatatableColumn({
|
||||
id: 'test',
|
||||
name: 'test',
|
||||
meta: {
|
||||
type: 'date',
|
||||
source: 'esaggs',
|
||||
sourceParams: {
|
||||
type: BUCKET_TYPES.HISTOGRAM,
|
||||
params: {
|
||||
interval: 7,
|
||||
used_interval: 7,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual(7);
|
||||
});
|
||||
|
||||
it('returns undefined if information is not available', () => {
|
||||
expect(
|
||||
getNumberHistogramIntervalByDatatableColumn({
|
||||
id: 'test',
|
||||
name: 'test',
|
||||
meta: {
|
||||
type: 'date',
|
||||
source: 'esaggs',
|
||||
sourceParams: {
|
||||
type: BUCKET_TYPES.HISTOGRAM,
|
||||
params: {},
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual(undefined);
|
||||
});
|
||||
});
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 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 { DatatableColumn } from '@kbn/expressions-plugin/common';
|
||||
import type { AggParamsHistogram } from '../buckets';
|
||||
import { BUCKET_TYPES } from '../buckets/bucket_agg_types';
|
||||
|
||||
/**
|
||||
* Helper function returning the used interval for data table column created by the histogramm agg type.
|
||||
* "auto" will get expanded to the actually used interval.
|
||||
* If the column is not a column created by a histogram aggregation of the esaggs data source,
|
||||
* this function will return undefined.
|
||||
*/
|
||||
export const getNumberHistogramIntervalByDatatableColumn = (column: DatatableColumn) => {
|
||||
if (column.meta.source !== 'esaggs') return;
|
||||
if (column.meta.sourceParams?.type !== BUCKET_TYPES.HISTOGRAM) return;
|
||||
const params = column.meta.sourceParams.params as unknown as AggParamsHistogram;
|
||||
|
||||
if (!params.used_interval || typeof params.used_interval === 'string') {
|
||||
return undefined;
|
||||
}
|
||||
return params.used_interval;
|
||||
};
|
|
@ -7,8 +7,6 @@
|
|||
*/
|
||||
|
||||
export * from './calculate_auto_time_expression';
|
||||
export { getNumberHistogramIntervalByDatatableColumn } from './get_number_histogram_interval';
|
||||
export { getDateHistogramMetaDataByDatatableColumn } from './get_date_histogram_meta';
|
||||
export * from './date_interval_utils';
|
||||
export * from './get_aggs_formats';
|
||||
export * from './ip_address';
|
||||
|
|
|
@ -9,4 +9,3 @@
|
|||
export { tabifyDocs, flattenHit } from './tabify_docs';
|
||||
export { tabifyAggResponse } from './tabify';
|
||||
export { tabifyGetColumns } from './get_columns';
|
||||
export { checkColumnForPrecisionError } from './utils';
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 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 { checkColumnForPrecisionError } from './utils';
|
||||
import type { DatatableColumn } from '@kbn/expressions-plugin';
|
||||
|
||||
describe('tabify utils', () => {
|
||||
describe('checkDatatableForPrecisionError', () => {
|
||||
test('should return true if there is a precision error in the column', () => {
|
||||
expect(
|
||||
checkColumnForPrecisionError({
|
||||
meta: {
|
||||
sourceParams: {
|
||||
hasPrecisionError: true,
|
||||
},
|
||||
},
|
||||
} as unknown as DatatableColumn)
|
||||
).toBeTruthy();
|
||||
});
|
||||
test('should return false if there is no precision error in the column', () => {
|
||||
expect(
|
||||
checkColumnForPrecisionError({
|
||||
meta: {
|
||||
sourceParams: {
|
||||
hasPrecisionError: false,
|
||||
},
|
||||
},
|
||||
} as unknown as DatatableColumn)
|
||||
).toBeFalsy();
|
||||
});
|
||||
test('should return false if precision error is not defined', () => {
|
||||
expect(
|
||||
checkColumnForPrecisionError({
|
||||
meta: {
|
||||
sourceParams: {},
|
||||
},
|
||||
} as unknown as DatatableColumn)
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -115,13 +115,10 @@ import {
|
|||
parseInterval,
|
||||
toAbsoluteDates,
|
||||
boundsDescendingRaw,
|
||||
getNumberHistogramIntervalByDatatableColumn,
|
||||
getDateHistogramMetaDataByDatatableColumn,
|
||||
getResponseInspectorStats,
|
||||
// tabify
|
||||
tabifyAggResponse,
|
||||
tabifyGetColumns,
|
||||
checkColumnForPrecisionError,
|
||||
} from '../common';
|
||||
|
||||
export { AggGroupLabels, AggGroupNames, METRIC_TYPES, BUCKET_TYPES } from '../common';
|
||||
|
@ -225,13 +222,10 @@ export const search = {
|
|||
termsAggFilter,
|
||||
toAbsoluteDates,
|
||||
boundsDescendingRaw,
|
||||
getNumberHistogramIntervalByDatatableColumn,
|
||||
getDateHistogramMetaDataByDatatableColumn,
|
||||
},
|
||||
getResponseInspectorStats,
|
||||
tabifyAggResponse,
|
||||
tabifyGetColumns,
|
||||
checkColumnForPrecisionError,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -37,7 +37,6 @@ export {
|
|||
parseSearchSourceJSON,
|
||||
SearchSource,
|
||||
SortDirection,
|
||||
checkColumnForPrecisionError,
|
||||
} from '../../common/search';
|
||||
export type {
|
||||
ISessionService,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import moment from 'moment';
|
||||
import type { Datatable } from '@kbn/expressions-plugin/common';
|
||||
import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks';
|
||||
import type { TimeRange } from '@kbn/data-plugin/common';
|
||||
import { functionWrapper } from '@kbn/expressions-plugin/common/expression_functions/specs/tests/utils';
|
||||
|
||||
|
@ -27,7 +28,7 @@ import type { TimeScaleArgs } from './types';
|
|||
|
||||
describe('time_scale', () => {
|
||||
let timeScaleWrapped: (input: Datatable, args: TimeScaleArgs) => Promise<Datatable>;
|
||||
const timeScale = getTimeScale(() => 'UTC');
|
||||
const timeScale = getTimeScale(createDatatableUtilitiesMock, () => 'UTC');
|
||||
|
||||
const emptyTable: Datatable = {
|
||||
type: 'datatable',
|
||||
|
@ -395,7 +396,7 @@ describe('time_scale', () => {
|
|||
resolveTimezonePromise = res;
|
||||
});
|
||||
const timeScaleResolved = jest.fn((x) => x);
|
||||
const delayedTimeScale = getTimeScale(() => timezonePromise);
|
||||
const delayedTimeScale = getTimeScale(createDatatableUtilitiesMock, () => timezonePromise);
|
||||
const delayedTimeScaleWrapper = functionWrapper(delayedTimeScale);
|
||||
const result = delayedTimeScaleWrapper(
|
||||
{
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ExecutionContext } from '@kbn/expressions-plugin/common';
|
||||
import type { TimeScaleExpressionFunction } from './types';
|
||||
import type { timeScaleFn } from './time_scale_fn';
|
||||
|
||||
export const getTimeScale = (
|
||||
getTimezone: (context: ExecutionContext) => string | Promise<string>
|
||||
...timeScaleFnParameters: Parameters<typeof timeScaleFn>
|
||||
): TimeScaleExpressionFunction => ({
|
||||
name: 'lens_time_scale',
|
||||
type: 'datatable',
|
||||
|
@ -45,6 +45,6 @@ export const getTimeScale = (
|
|||
async fn(...args) {
|
||||
/** Build optimization: prevent adding extra code into initial bundle **/
|
||||
const { timeScaleFn } = await import('./time_scale_fn');
|
||||
return timeScaleFn(getTimezone)(...args);
|
||||
return timeScaleFn(...timeScaleFnParameters)(...args);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -8,11 +8,7 @@
|
|||
import moment from 'moment-timezone';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { buildResultColumns, Datatable, ExecutionContext } from '@kbn/expressions-plugin/common';
|
||||
import {
|
||||
calculateBounds,
|
||||
getDateHistogramMetaDataByDatatableColumn,
|
||||
parseInterval,
|
||||
} from '@kbn/data-plugin/common';
|
||||
import { calculateBounds, DatatableUtilitiesService, parseInterval } from '@kbn/data-plugin/common';
|
||||
import type { TimeScaleExpressionFunction, TimeScaleUnit, TimeScaleArgs } from './types';
|
||||
|
||||
const unitInMs: Record<TimeScaleUnit, number> = {
|
||||
|
@ -24,6 +20,9 @@ const unitInMs: Record<TimeScaleUnit, number> = {
|
|||
|
||||
export const timeScaleFn =
|
||||
(
|
||||
getDatatableUtilities: (
|
||||
context: ExecutionContext
|
||||
) => DatatableUtilitiesService | Promise<DatatableUtilitiesService>,
|
||||
getTimezone: (context: ExecutionContext) => string | Promise<string>
|
||||
): TimeScaleExpressionFunction['fn'] =>
|
||||
async (
|
||||
|
@ -59,7 +58,8 @@ export const timeScaleFn =
|
|||
}
|
||||
|
||||
const targetUnitInMs = unitInMs[targetUnit];
|
||||
const timeInfo = getDateHistogramMetaDataByDatatableColumn(dateColumnDefinition, {
|
||||
const datatableUtilities = await getDatatableUtilities(context);
|
||||
const timeInfo = datatableUtilities.getDateHistogramMeta(dateColumnDefinition, {
|
||||
timeZone: await getTimezone(context),
|
||||
});
|
||||
const intervalDuration = timeInfo?.interval && parseInterval(timeInfo.interval);
|
||||
|
|
|
@ -301,7 +301,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
|
|||
plugins.uiActions.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({
|
||||
data: {
|
||||
...event.data,
|
||||
timeFieldName: inferTimeField(event.data),
|
||||
timeFieldName: inferTimeField(plugins.data.datatableUtilities, event.data),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -309,7 +309,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
|
|||
plugins.uiActions.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({
|
||||
data: {
|
||||
...event.data,
|
||||
timeFieldName: inferTimeField(event.data),
|
||||
timeFieldName: inferTimeField(plugins.data.datatableUtilities, event.data),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -322,7 +322,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
|
|||
);
|
||||
}
|
||||
},
|
||||
[plugins.uiActions, activeVisualization, dispatchLens]
|
||||
[plugins.data.datatableUtilities, plugins.uiActions, activeVisualization, dispatchLens]
|
||||
);
|
||||
|
||||
const hasCompatibleActions = useCallback(
|
||||
|
|
|
@ -107,6 +107,8 @@ const attributeServiceMockFromSavedVis = (document: Document): LensAttributeServ
|
|||
return service;
|
||||
};
|
||||
|
||||
const dataMock = dataPluginMock.createStartContract();
|
||||
|
||||
describe('embeddable', () => {
|
||||
let mountpoint: HTMLDivElement;
|
||||
let expressionRenderer: jest.Mock<null, [ReactExpressionRendererProps]>;
|
||||
|
@ -139,6 +141,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
indexPatternService: {} as DataViewsContract,
|
||||
|
@ -188,6 +191,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
indexPatternService: {} as DataViewsContract,
|
||||
|
@ -238,6 +242,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -298,6 +303,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
|
@ -347,6 +353,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -396,6 +403,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -443,6 +451,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -494,6 +503,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -549,6 +559,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -602,6 +613,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -662,6 +674,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -723,6 +736,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -787,6 +801,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -836,6 +851,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -887,6 +903,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -935,6 +952,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -998,6 +1016,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -1080,6 +1099,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -1137,6 +1157,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -1191,6 +1212,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService,
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
@ -1266,6 +1288,7 @@ describe('embeddable', () => {
|
|||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService: attributeServiceMockFromSavedVis(visDocument),
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
|
|
|
@ -12,6 +12,7 @@ import { render, unmountComponentAtNode } from 'react-dom';
|
|||
import { DataViewBase, Filter } from '@kbn/es-query';
|
||||
import type { PaletteOutput } from '@kbn/coloring';
|
||||
import {
|
||||
DataPublicPluginStart,
|
||||
ExecutionContextSearch,
|
||||
Query,
|
||||
TimefilterContract,
|
||||
|
@ -112,6 +113,7 @@ export interface LensEmbeddableOutput extends EmbeddableOutput {
|
|||
|
||||
export interface LensEmbeddableDeps {
|
||||
attributeService: LensAttributeService;
|
||||
data: DataPublicPluginStart;
|
||||
documentToExpression: (
|
||||
doc: Document
|
||||
) => Promise<{ ast: Ast | null; errors: ErrorMessage[] | undefined }>;
|
||||
|
@ -612,7 +614,9 @@ export class Embeddable
|
|||
this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({
|
||||
data: {
|
||||
...event.data,
|
||||
timeFieldName: event.data.timeFieldName || inferTimeField(event.data),
|
||||
timeFieldName:
|
||||
event.data.timeFieldName ||
|
||||
inferTimeField(this.deps.data.datatableUtilities, event.data),
|
||||
},
|
||||
embeddable: this,
|
||||
});
|
||||
|
@ -625,7 +629,9 @@ export class Embeddable
|
|||
this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({
|
||||
data: {
|
||||
...event.data,
|
||||
timeFieldName: event.data.timeFieldName || inferTimeField(event.data),
|
||||
timeFieldName:
|
||||
event.data.timeFieldName ||
|
||||
inferTimeField(this.deps.data.datatableUtilities, event.data),
|
||||
},
|
||||
embeddable: this,
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { RecursiveReadonly } from '@kbn/utility-types';
|
||||
import { Ast } from '@kbn/interpreter';
|
||||
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
|
||||
import { FilterManager, TimefilterContract } from '@kbn/data-plugin/public';
|
||||
import { DataPublicPluginStart, FilterManager, TimefilterContract } from '@kbn/data-plugin/public';
|
||||
import type { DataViewsContract } from '@kbn/data-views-plugin/public';
|
||||
import { ReactExpressionRendererType } from '@kbn/expressions-plugin/public';
|
||||
import { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public';
|
||||
|
@ -26,6 +26,7 @@ import { extract, inject } from '../../common/embeddable_factory';
|
|||
import { DatasourceMap, VisualizationMap } from '../types';
|
||||
|
||||
export interface LensEmbeddableStartServices {
|
||||
data: DataPublicPluginStart;
|
||||
timefilter: TimefilterContract;
|
||||
coreHttp: HttpSetup;
|
||||
inspector: InspectorStart;
|
||||
|
@ -85,6 +86,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition {
|
|||
|
||||
async create(input: LensEmbeddableInput, parent?: IContainer) {
|
||||
const {
|
||||
data,
|
||||
timefilter,
|
||||
expressionRenderer,
|
||||
documentToExpression,
|
||||
|
@ -107,6 +109,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition {
|
|||
return new Embeddable(
|
||||
{
|
||||
attributeService,
|
||||
data,
|
||||
indexPatternService,
|
||||
timefilter,
|
||||
inspector,
|
||||
|
|
|
@ -17,7 +17,8 @@ import { collapse } from '../common/expressions';
|
|||
export const setupExpressions = (
|
||||
expressions: ExpressionsSetup,
|
||||
formatFactory: Parameters<typeof getDatatable>[0],
|
||||
getTimeZone: Parameters<typeof getTimeScale>[0]
|
||||
getDatatableUtilities: Parameters<typeof getTimeScale>[0],
|
||||
getTimeZone: Parameters<typeof getTimeScale>[1]
|
||||
) => {
|
||||
[
|
||||
collapse,
|
||||
|
@ -26,6 +27,6 @@ export const setupExpressions = (
|
|||
renameColumns,
|
||||
datatableColumn,
|
||||
getDatatable(formatFactory),
|
||||
getTimeScale(getTimeZone),
|
||||
getTimeScale(getDatatableUtilities, getTimeZone),
|
||||
].forEach((expressionFn) => expressions.registerFunction(expressionFn));
|
||||
};
|
||||
|
|
|
@ -809,6 +809,7 @@ export function DimensionEditor(props: DimensionEditorProps) {
|
|||
selectedOperationDefinition.shiftable &&
|
||||
selectedColumn.timeShift !== undefined ? (
|
||||
<TimeShift
|
||||
datatableUtilities={services.data.datatableUtilities}
|
||||
indexPattern={currentIndexPattern}
|
||||
selectedColumn={selectedColumn}
|
||||
columnId={columnId}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { EuiComboBox } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Query } from '@kbn/data-plugin/public';
|
||||
import { parseTimeShift } from '@kbn/data-plugin/common';
|
||||
import { DatatableUtilitiesService, parseTimeShift } from '@kbn/data-plugin/common';
|
||||
import {
|
||||
adjustTimeScaleLabelSuffix,
|
||||
GenericIndexPatternColumn,
|
||||
|
@ -61,6 +61,7 @@ export function setTimeShift(
|
|||
}
|
||||
|
||||
export function TimeShift({
|
||||
datatableUtilities,
|
||||
selectedColumn,
|
||||
columnId,
|
||||
layer,
|
||||
|
@ -70,6 +71,7 @@ export function TimeShift({
|
|||
activeData,
|
||||
layerId,
|
||||
}: {
|
||||
datatableUtilities: DatatableUtilitiesService;
|
||||
selectedColumn: GenericIndexPatternColumn;
|
||||
indexPattern: IndexPattern;
|
||||
columnId: string;
|
||||
|
@ -89,7 +91,13 @@ export function TimeShift({
|
|||
return null;
|
||||
}
|
||||
|
||||
const dateHistogramInterval = getDateHistogramInterval(layer, indexPattern, activeData, layerId);
|
||||
const dateHistogramInterval = getDateHistogramInterval(
|
||||
datatableUtilities,
|
||||
layer,
|
||||
indexPattern,
|
||||
activeData,
|
||||
layerId
|
||||
);
|
||||
const { isValueTooSmall, isValueNotMultiple, isInvalid, canShift } =
|
||||
getLayerTimeShiftChecks(dateHistogramInterval);
|
||||
|
||||
|
|
|
@ -618,8 +618,14 @@ export function getIndexPatternDatasource({
|
|||
},
|
||||
getWarningMessages: (state, frame, setState) => {
|
||||
return [
|
||||
...(getStateTimeShiftWarningMessages(state, frame) || []),
|
||||
...getPrecisionErrorWarningMessages(state, frame, core.docLinks, setState),
|
||||
...(getStateTimeShiftWarningMessages(data.datatableUtilities, state, frame) || []),
|
||||
...getPrecisionErrorWarningMessages(
|
||||
data.datatableUtilities,
|
||||
state,
|
||||
frame,
|
||||
core.docLinks,
|
||||
setState
|
||||
),
|
||||
];
|
||||
},
|
||||
checkIntegrity: (state) => {
|
||||
|
|
|
@ -66,6 +66,7 @@ export const WrappedFormulaEditor = ({
|
|||
...rest
|
||||
}: ParamEditorProps<FormulaIndexPatternColumn>) => {
|
||||
const dateHistogramInterval = getDateHistogramInterval(
|
||||
rest.data.datatableUtilities,
|
||||
rest.layer,
|
||||
rest.indexPattern,
|
||||
activeData,
|
||||
|
|
|
@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import React from 'react';
|
||||
import { uniq } from 'lodash';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { DatatableUtilitiesService } from '@kbn/data-plugin/common';
|
||||
import { Datatable } from '@kbn/expressions-plugin';
|
||||
import { search } from '@kbn/data-plugin/public';
|
||||
import { parseTimeShift } from '@kbn/data-plugin/common';
|
||||
|
@ -98,6 +99,7 @@ export const timeShiftOptionOrder = timeShiftOptions.reduce<{ [key: string]: num
|
|||
);
|
||||
|
||||
export function getDateHistogramInterval(
|
||||
datatableUtilities: DatatableUtilitiesService,
|
||||
layer: IndexPatternLayer,
|
||||
indexPattern: IndexPattern,
|
||||
activeData: Record<string, Datatable> | undefined,
|
||||
|
@ -112,8 +114,7 @@ export function getDateHistogramInterval(
|
|||
if (dateHistogramColumn && activeData && activeData[layerId] && activeData[layerId]) {
|
||||
const column = activeData[layerId].columns.find((col) => col.id === dateHistogramColumn);
|
||||
if (column) {
|
||||
const expression =
|
||||
search.aggs.getDateHistogramMetaDataByDatatableColumn(column)?.interval || '';
|
||||
const expression = datatableUtilities.getDateHistogramMeta(column)?.interval || '';
|
||||
return {
|
||||
interval: search.aggs.parseInterval(expression),
|
||||
expression,
|
||||
|
@ -184,6 +185,7 @@ export function getDisallowedPreviousShiftMessage(
|
|||
}
|
||||
|
||||
export function getStateTimeShiftWarningMessages(
|
||||
datatableUtilities: DatatableUtilitiesService,
|
||||
state: IndexPatternPrivateState,
|
||||
{ activeData }: FramePublicAPI
|
||||
) {
|
||||
|
@ -195,6 +197,7 @@ export function getStateTimeShiftWarningMessages(
|
|||
return;
|
||||
}
|
||||
const dateHistogramInterval = getDateHistogramInterval(
|
||||
datatableUtilities,
|
||||
layer,
|
||||
layerIndexPattern,
|
||||
activeData,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks';
|
||||
import { getPrecisionErrorWarningMessages } from './utils';
|
||||
import type { IndexPatternPrivateState, GenericIndexPatternColumn } from './types';
|
||||
import type { FramePublicAPI } from '../types';
|
||||
|
@ -18,6 +19,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
|
||||
describe('indexpattern_datasource utils', () => {
|
||||
describe('getPrecisionErrorWarningMessages', () => {
|
||||
const datatableUtilitites = createDatatableUtilitiesMock();
|
||||
let state: IndexPatternPrivateState;
|
||||
let framePublicAPI: FramePublicAPI;
|
||||
let docLinks: DocLinksStart;
|
||||
|
@ -72,7 +74,13 @@ describe('indexpattern_datasource utils', () => {
|
|||
});
|
||||
test('should not show precisionError if hasPrecisionError is false', () => {
|
||||
expect(
|
||||
getPrecisionErrorWarningMessages(state, framePublicAPI, docLinks, () => {})
|
||||
getPrecisionErrorWarningMessages(
|
||||
datatableUtilitites,
|
||||
state,
|
||||
framePublicAPI,
|
||||
docLinks,
|
||||
() => {}
|
||||
)
|
||||
).toHaveLength(0);
|
||||
});
|
||||
|
||||
|
@ -80,7 +88,13 @@ describe('indexpattern_datasource utils', () => {
|
|||
delete framePublicAPI.activeData!.id.columns[0].meta.sourceParams!.hasPrecisionError;
|
||||
|
||||
expect(
|
||||
getPrecisionErrorWarningMessages(state, framePublicAPI, docLinks, () => {})
|
||||
getPrecisionErrorWarningMessages(
|
||||
datatableUtilitites,
|
||||
state,
|
||||
framePublicAPI,
|
||||
docLinks,
|
||||
() => {}
|
||||
)
|
||||
).toHaveLength(0);
|
||||
});
|
||||
|
||||
|
@ -95,6 +109,7 @@ describe('indexpattern_datasource utils', () => {
|
|||
const setStateMock = jest.fn();
|
||||
|
||||
const warningMessages = getPrecisionErrorWarningMessages(
|
||||
datatableUtilitites,
|
||||
state,
|
||||
framePublicAPI,
|
||||
docLinks,
|
||||
|
@ -119,6 +134,7 @@ describe('indexpattern_datasource utils', () => {
|
|||
(state.layers.id.columns.col1 as TermsIndexPatternColumn).params.accuracyMode = true;
|
||||
|
||||
const warningMessages = getPrecisionErrorWarningMessages(
|
||||
datatableUtilitites,
|
||||
state,
|
||||
framePublicAPI,
|
||||
docLinks,
|
||||
|
@ -157,7 +173,13 @@ describe('indexpattern_datasource utils', () => {
|
|||
} as unknown as GenericIndexPatternColumn,
|
||||
};
|
||||
const setState = jest.fn();
|
||||
const warnings = getPrecisionErrorWarningMessages(state, framePublicAPI, docLinks, setState);
|
||||
const warnings = getPrecisionErrorWarningMessages(
|
||||
datatableUtilitites,
|
||||
state,
|
||||
framePublicAPI,
|
||||
docLinks,
|
||||
setState
|
||||
);
|
||||
|
||||
expect(warnings).toHaveLength(1);
|
||||
const DummyComponent = () => <>{warnings[0]}</>;
|
||||
|
|
|
@ -9,12 +9,13 @@ import React from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { DocLinksStart } from '@kbn/core/public';
|
||||
import type { DatatableUtilitiesService } from '@kbn/data-plugin/common';
|
||||
import { TimeRange } from '@kbn/es-query';
|
||||
import { EuiLink, EuiTextColor, EuiButton, EuiSpacer } from '@elastic/eui';
|
||||
|
||||
import { DatatableColumn } from '@kbn/expressions-plugin';
|
||||
import type { DatatableColumn } from '@kbn/expressions-plugin';
|
||||
import { groupBy, escape } from 'lodash';
|
||||
import { checkColumnForPrecisionError, Query } from '@kbn/data-plugin/common';
|
||||
import type { Query } from '@kbn/data-plugin/common';
|
||||
import type { FramePublicAPI, StateSetter } from '../types';
|
||||
import type { IndexPattern, IndexPatternLayer, IndexPatternPrivateState } from './types';
|
||||
import type { ReferenceBasedIndexPatternColumn } from './operations/definitions/column_types';
|
||||
|
@ -159,6 +160,7 @@ const accuracyModeEnabledWarning = (columnName: string, docLink: string) => (
|
|||
);
|
||||
|
||||
export function getPrecisionErrorWarningMessages(
|
||||
datatableUtilities: DatatableUtilitiesService,
|
||||
state: IndexPatternPrivateState,
|
||||
{ activeData }: FramePublicAPI,
|
||||
docLinks: DocLinksStart,
|
||||
|
@ -178,7 +180,7 @@ export function getPrecisionErrorWarningMessages(
|
|||
.forEach(({ layerId, column }) => {
|
||||
const currentLayer = state.layers[layerId];
|
||||
const currentColumn = currentLayer?.columns[column.id];
|
||||
if (currentLayer && currentColumn && checkColumnForPrecisionError(column)) {
|
||||
if (currentLayer && currentColumn && datatableUtilities.hasPrecisionError(column)) {
|
||||
const indexPattern = state.indexPatterns[currentLayer.indexPatternId];
|
||||
// currentColumnIsTerms is mostly a type guard. If there's a precision error,
|
||||
// we already know that we're dealing with a terms-based operation (at least for now).
|
||||
|
|
|
@ -272,6 +272,7 @@ export class LensPlugin {
|
|||
attributeService: getLensAttributeService(coreStart, plugins),
|
||||
capabilities: coreStart.application.capabilities,
|
||||
coreHttp: coreStart.http,
|
||||
data: plugins.data,
|
||||
timefilter: plugins.data.query.timefilter.timefilter,
|
||||
expressionRenderer: plugins.expressions.ReactExpressionRenderer,
|
||||
documentToExpression: this.editorFrameService!.documentToExpression,
|
||||
|
@ -306,6 +307,7 @@ export class LensPlugin {
|
|||
setupExpressions(
|
||||
expressions,
|
||||
() => startServices().plugins.fieldFormats.deserialize,
|
||||
() => startServices().plugins.data.datatableUtilities,
|
||||
async () => {
|
||||
const { getTimeZone } = await import('./utils');
|
||||
return getTimeZone(core.uiSettings);
|
||||
|
|
|
@ -5,9 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks';
|
||||
import { Datatable } from '@kbn/expressions-plugin/public';
|
||||
import { inferTimeField } from './utils';
|
||||
|
||||
const datatableUtilities = createDatatableUtilitiesMock();
|
||||
|
||||
const table: Datatable = {
|
||||
type: 'datatable',
|
||||
rows: [],
|
||||
|
@ -52,7 +55,7 @@ describe('utils', () => {
|
|||
describe('inferTimeField', () => {
|
||||
test('infer time field for brush event', () => {
|
||||
expect(
|
||||
inferTimeField({
|
||||
inferTimeField(datatableUtilities, {
|
||||
table,
|
||||
column: 0,
|
||||
range: [1, 2],
|
||||
|
@ -62,7 +65,7 @@ describe('utils', () => {
|
|||
|
||||
test('do not return time field if time range is not bound', () => {
|
||||
expect(
|
||||
inferTimeField({
|
||||
inferTimeField(datatableUtilities, {
|
||||
table: tableWithoutAppliedTimeRange,
|
||||
column: 0,
|
||||
range: [1, 2],
|
||||
|
@ -72,7 +75,7 @@ describe('utils', () => {
|
|||
|
||||
test('infer time field for click event', () => {
|
||||
expect(
|
||||
inferTimeField({
|
||||
inferTimeField(datatableUtilities, {
|
||||
data: [
|
||||
{
|
||||
table,
|
||||
|
@ -87,7 +90,7 @@ describe('utils', () => {
|
|||
|
||||
test('do not return time field for negated click event', () => {
|
||||
expect(
|
||||
inferTimeField({
|
||||
inferTimeField(datatableUtilities, {
|
||||
data: [
|
||||
{
|
||||
table,
|
||||
|
@ -103,7 +106,7 @@ describe('utils', () => {
|
|||
|
||||
test('do not return time field for click event without bound time field', () => {
|
||||
expect(
|
||||
inferTimeField({
|
||||
inferTimeField(datatableUtilities, {
|
||||
data: [
|
||||
{
|
||||
table: tableWithoutAppliedTimeRange,
|
||||
|
|
|
@ -11,7 +11,7 @@ import moment from 'moment-timezone';
|
|||
import type { TimefilterContract } from '@kbn/data-plugin/public';
|
||||
import type { IUiSettingsClient, SavedObjectReference } from '@kbn/core/public';
|
||||
import type { DataView, DataViewsContract } from '@kbn/data-views-plugin/public';
|
||||
import { search } from '@kbn/data-plugin/public';
|
||||
import type { DatatableUtilitiesService } from '@kbn/data-plugin/common';
|
||||
import { BrushTriggerEvent, ClickTriggerEvent } from '@kbn/charts-plugin/public';
|
||||
import type { Document } from './persistence/saved_object_store';
|
||||
import type { Datasource, DatasourceMap, Visualization, StateSetter } from './types';
|
||||
|
@ -147,7 +147,10 @@ export function getRemoveOperation(
|
|||
return layerCount === 1 ? 'clear' : 'remove';
|
||||
}
|
||||
|
||||
export function inferTimeField(context: BrushTriggerEvent['data'] | ClickTriggerEvent['data']) {
|
||||
export function inferTimeField(
|
||||
datatableUtilities: DatatableUtilitiesService,
|
||||
context: BrushTriggerEvent['data'] | ClickTriggerEvent['data']
|
||||
) {
|
||||
const tablesAndColumns =
|
||||
'table' in context
|
||||
? [{ table: context.table, column: context.column }]
|
||||
|
@ -159,7 +162,7 @@ export function inferTimeField(context: BrushTriggerEvent['data'] | ClickTrigger
|
|||
.map(({ table, column }) => {
|
||||
const tableColumn = table.columns[column];
|
||||
const hasTimeRange = Boolean(
|
||||
tableColumn && search.aggs.getDateHistogramMetaDataByDatatableColumn(tableColumn)?.timeRange
|
||||
tableColumn && datatableUtilities.getDateHistogramMeta(tableColumn)?.timeRange
|
||||
);
|
||||
if (hasTimeRange) {
|
||||
return tableColumn.meta.field;
|
||||
|
|
|
@ -29,11 +29,12 @@ export class XyVisualization {
|
|||
) {
|
||||
editorFrame.registerVisualization(async () => {
|
||||
const { getXyVisualization } = await import('../async_services');
|
||||
const [, { charts, fieldFormats, eventAnnotation }] = await core.getStartServices();
|
||||
const [, { charts, data, fieldFormats, eventAnnotation }] = await core.getStartServices();
|
||||
const palettes = await charts.palettes.getPalettes();
|
||||
const eventAnnotationService = await eventAnnotation.getService();
|
||||
const useLegacyTimeAxis = core.uiSettings.get(LEGACY_TIME_AXIS);
|
||||
return getXyVisualization({
|
||||
datatableUtilities: data.datatableUtilities,
|
||||
paletteService: palettes,
|
||||
eventAnnotationService,
|
||||
fieldFormats,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { Ast, fromExpression } from '@kbn/interpreter';
|
||||
import { Position } from '@elastic/charts';
|
||||
import { chartPluginMock } from '@kbn/charts-plugin/public/mocks';
|
||||
import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks';
|
||||
import { getXyVisualization } from './xy_visualization';
|
||||
import { OperationDescriptor } from '../types';
|
||||
import { createMockDatasource, createMockFramePublicAPI } from '../mocks';
|
||||
|
@ -20,6 +21,7 @@ import { LegendSize } from '@kbn/visualizations-plugin/common';
|
|||
|
||||
describe('#toExpression', () => {
|
||||
const xyVisualization = getXyVisualization({
|
||||
datatableUtilities: createDatatableUtilitiesMock(),
|
||||
paletteService: chartPluginMock.createPaletteRegistry(),
|
||||
fieldFormats: fieldFormatsServiceMock.createStartContract(),
|
||||
kibanaTheme: themeServiceMock.createStartContract(),
|
||||
|
|
|
@ -16,6 +16,7 @@ import type {
|
|||
XYDataLayerConfig,
|
||||
XYReferenceLineLayerConfig,
|
||||
} from './types';
|
||||
import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks';
|
||||
import type { SeriesType } from '@kbn/expression-xy-plugin/common';
|
||||
import { layerTypes } from '../../common';
|
||||
import { createMockDatasource, createMockFramePublicAPI } from '../mocks';
|
||||
|
@ -68,6 +69,7 @@ const paletteServiceMock = chartPluginMock.createPaletteRegistry();
|
|||
const fieldFormatsMock = fieldFormatsServiceMock.createStartContract();
|
||||
|
||||
const xyVisualization = getXyVisualization({
|
||||
datatableUtilities: createDatatableUtilitiesMock(),
|
||||
paletteService: paletteServiceMock,
|
||||
fieldFormats: fieldFormatsMock,
|
||||
useLegacyTimeAxis: false,
|
||||
|
|
|
@ -11,6 +11,7 @@ import { Position } from '@elastic/charts';
|
|||
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { PaletteRegistry } from '@kbn/coloring';
|
||||
import type { DatatableUtilitiesService } from '@kbn/data-plugin/common';
|
||||
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
||||
import { ThemeServiceStart } from '@kbn/core/public';
|
||||
import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public';
|
||||
|
@ -72,12 +73,14 @@ import { DimensionTrigger } from '../shared_components/dimension_trigger';
|
|||
import { defaultAnnotationLabel } from './annotations/helpers';
|
||||
|
||||
export const getXyVisualization = ({
|
||||
datatableUtilities,
|
||||
paletteService,
|
||||
fieldFormats,
|
||||
useLegacyTimeAxis,
|
||||
kibanaTheme,
|
||||
eventAnnotationService,
|
||||
}: {
|
||||
datatableUtilities: DatatableUtilitiesService;
|
||||
paletteService: PaletteRegistry;
|
||||
eventAnnotationService: EventAnnotationServiceType;
|
||||
fieldFormats: FieldFormatsStart;
|
||||
|
@ -496,6 +499,7 @@ export const getXyVisualization = ({
|
|||
renderDimensionEditor(domElement, props) {
|
||||
const allProps = {
|
||||
...props,
|
||||
datatableUtilities,
|
||||
formatFactory: fieldFormats.deserialize,
|
||||
paletteService,
|
||||
};
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
RangeEventAnnotationConfig,
|
||||
} from '@kbn/event-annotation-plugin/common/types';
|
||||
import { pick } from 'lodash';
|
||||
import type { DatatableUtilitiesService } from '@kbn/data-plugin/common';
|
||||
import { search } from '@kbn/data-plugin/public';
|
||||
import {
|
||||
defaultAnnotationColor,
|
||||
|
@ -56,6 +57,7 @@ export const toLineAnnotationColor = (color = defaultAnnotationRangeColor) => {
|
|||
};
|
||||
|
||||
export const getEndTimestamp = (
|
||||
datatableUtilities: DatatableUtilitiesService,
|
||||
startTime: string,
|
||||
{ activeData, dateRange }: FramePublicAPI,
|
||||
dataLayers: XYDataLayerConfig[]
|
||||
|
@ -81,7 +83,7 @@ export const getEndTimestamp = (
|
|||
return fallbackValue;
|
||||
}
|
||||
|
||||
const dateInterval = search.aggs.getDateHistogramMetaDataByDatatableColumn(xColumn)?.interval;
|
||||
const dateInterval = datatableUtilities.getDateHistogramMeta(xColumn)?.interval;
|
||||
if (!dateInterval) return fallbackValue;
|
||||
const intervalDuration = search.aggs.parseInterval(dateInterval);
|
||||
if (!intervalDuration) return fallbackValue;
|
||||
|
@ -117,6 +119,7 @@ const sanitizeProperties = (annotation: EventAnnotationConfig) => {
|
|||
|
||||
export const AnnotationsPanel = (
|
||||
props: VisualizationDimensionEditorProps<State> & {
|
||||
datatableUtilities: DatatableUtilitiesService;
|
||||
formatFactory: FormatFactory;
|
||||
paletteService: PaletteRegistry;
|
||||
}
|
||||
|
@ -255,6 +258,7 @@ export const AnnotationsPanel = (
|
|||
|
||||
<ConfigPanelApplyAsRangeSwitch
|
||||
annotation={currentAnnotation}
|
||||
datatableUtilities={props.datatableUtilities}
|
||||
onChange={setAnnotations}
|
||||
frame={frame}
|
||||
state={state}
|
||||
|
@ -366,11 +370,13 @@ export const AnnotationsPanel = (
|
|||
|
||||
const ConfigPanelApplyAsRangeSwitch = ({
|
||||
annotation,
|
||||
datatableUtilities,
|
||||
onChange,
|
||||
frame,
|
||||
state,
|
||||
}: {
|
||||
annotation?: EventAnnotationConfig;
|
||||
datatableUtilities: DatatableUtilitiesService;
|
||||
onChange: (annotations: Partial<EventAnnotationConfig> | undefined) => void;
|
||||
frame: FramePublicAPI;
|
||||
state: XYState;
|
||||
|
@ -411,7 +417,12 @@ const ConfigPanelApplyAsRangeSwitch = ({
|
|||
key: {
|
||||
type: 'range',
|
||||
timestamp: annotation.key.timestamp,
|
||||
endTimestamp: getEndTimestamp(fromTimestamp.toISOString(), frame, dataLayers),
|
||||
endTimestamp: getEndTimestamp(
|
||||
datatableUtilities,
|
||||
fromTimestamp.toISOString(),
|
||||
frame,
|
||||
dataLayers
|
||||
),
|
||||
},
|
||||
id: annotation.id,
|
||||
label:
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { mountWithIntl as mount } from '@kbn/test-jest-helpers';
|
||||
import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks';
|
||||
import { AnnotationsPanel } from '.';
|
||||
import { FramePublicAPI } from '../../../types';
|
||||
import { layerTypes } from '../../..';
|
||||
|
@ -36,6 +37,7 @@ const customLineStaticAnnotation = {
|
|||
};
|
||||
|
||||
describe('AnnotationsPanel', () => {
|
||||
const datatableUtilities = createDatatableUtilitiesMock();
|
||||
let frame: FramePublicAPI;
|
||||
|
||||
function testState(): State {
|
||||
|
@ -68,6 +70,7 @@ describe('AnnotationsPanel', () => {
|
|||
accessor="ann1"
|
||||
groupId="left"
|
||||
state={state}
|
||||
datatableUtilities={datatableUtilities}
|
||||
formatFactory={jest.fn()}
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
panelRef={React.createRef()}
|
||||
|
@ -128,6 +131,7 @@ describe('AnnotationsPanel', () => {
|
|||
accessor="ann1"
|
||||
groupId="left"
|
||||
state={state}
|
||||
datatableUtilities={datatableUtilities}
|
||||
formatFactory={jest.fn()}
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
panelRef={React.createRef()}
|
||||
|
@ -166,6 +170,7 @@ describe('AnnotationsPanel', () => {
|
|||
accessor="ann1"
|
||||
groupId="left"
|
||||
state={state}
|
||||
datatableUtilities={datatableUtilities}
|
||||
formatFactory={jest.fn()}
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
panelRef={React.createRef()}
|
||||
|
|
|
@ -9,6 +9,7 @@ import React, { useCallback, useMemo } from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiButtonGroup, EuiFormRow, htmlIdGenerator } from '@elastic/eui';
|
||||
import type { PaletteRegistry } from '@kbn/coloring';
|
||||
import type { DatatableUtilitiesService } from '@kbn/data-plugin/common';
|
||||
import { YAxisMode, ExtendedYConfig } from '@kbn/expression-xy-plugin/common';
|
||||
import type { VisualizationDimensionEditorProps } from '../../types';
|
||||
import { State, XYState, XYDataLayerConfig } from '../types';
|
||||
|
@ -43,6 +44,7 @@ export const idPrefix = htmlIdGenerator()();
|
|||
|
||||
export function DimensionEditor(
|
||||
props: VisualizationDimensionEditorProps<State> & {
|
||||
datatableUtilities: DatatableUtilitiesService;
|
||||
formatFactory: FormatFactory;
|
||||
paletteService: PaletteRegistry;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import React from 'react';
|
||||
import { mountWithIntl as mount, shallowWithIntl as shallow } from '@kbn/test-jest-helpers';
|
||||
import { EuiButtonGroupProps, EuiButtonGroup } from '@elastic/eui';
|
||||
import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks';
|
||||
import { XyToolbar } from '.';
|
||||
import { DimensionEditor } from './dimension_editor';
|
||||
import { AxisSettingsPopover } from './axis_settings_popover';
|
||||
|
@ -221,6 +222,8 @@ describe('XY Config panels', () => {
|
|||
});
|
||||
|
||||
describe('Dimension Editor', () => {
|
||||
const datatableUtilities = createDatatableUtilitiesMock();
|
||||
|
||||
test('shows the correct axis side options when in horizontal mode', () => {
|
||||
const state = testState();
|
||||
const component = mount(
|
||||
|
@ -234,6 +237,7 @@ describe('XY Config panels', () => {
|
|||
...state,
|
||||
layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig],
|
||||
}}
|
||||
datatableUtilities={datatableUtilities}
|
||||
formatFactory={jest.fn()}
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
panelRef={React.createRef()}
|
||||
|
@ -258,6 +262,7 @@ describe('XY Config panels', () => {
|
|||
accessor="bar"
|
||||
groupId="left"
|
||||
state={state}
|
||||
datatableUtilities={datatableUtilities}
|
||||
formatFactory={jest.fn()}
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
panelRef={React.createRef()}
|
||||
|
@ -303,6 +308,7 @@ describe('XY Config panels', () => {
|
|||
accessor="bar"
|
||||
groupId="left"
|
||||
state={state}
|
||||
datatableUtilities={datatableUtilities}
|
||||
formatFactory={jest.fn()}
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
panelRef={React.createRef()}
|
||||
|
@ -345,6 +351,7 @@ describe('XY Config panels', () => {
|
|||
accessor="bar"
|
||||
groupId="left"
|
||||
state={state}
|
||||
datatableUtilities={datatableUtilities}
|
||||
formatFactory={jest.fn()}
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
panelRef={React.createRef()}
|
||||
|
@ -387,6 +394,7 @@ describe('XY Config panels', () => {
|
|||
accessor="bar"
|
||||
groupId="left"
|
||||
state={state}
|
||||
datatableUtilities={datatableUtilities}
|
||||
formatFactory={jest.fn()}
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
panelRef={React.createRef()}
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
import { generateId } from '../id_generator';
|
||||
import { getXyVisualization } from './xy_visualization';
|
||||
import { chartPluginMock } from '@kbn/charts-plugin/public/mocks';
|
||||
import { createDatatableUtilitiesMock } from '@kbn/data-plugin/common/mocks';
|
||||
import { eventAnnotationServiceMock } from '@kbn/event-annotation-plugin/public/mocks';
|
||||
import type { PaletteOutput } from '@kbn/coloring';
|
||||
import { layerTypes } from '../../common';
|
||||
|
@ -26,6 +27,7 @@ import { themeServiceMock } from '@kbn/core/public/mocks';
|
|||
jest.mock('../id_generator');
|
||||
|
||||
const xyVisualization = getXyVisualization({
|
||||
datatableUtilities: createDatatableUtilitiesMock(),
|
||||
paletteService: chartPluginMock.createPaletteRegistry(),
|
||||
fieldFormats: fieldFormatsServiceMock.createStartContract(),
|
||||
useLegacyTimeAxis: false,
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
getTimeScale,
|
||||
getDatatable,
|
||||
} from '../../common/expressions';
|
||||
import { getFormatFactory, getTimeZoneFactory } from './utils';
|
||||
import { getDatatableUtilitiesFactory, getFormatFactory, getTimeZoneFactory } from './utils';
|
||||
|
||||
import type { PluginStartContract } from '../plugin';
|
||||
|
||||
|
@ -27,6 +27,6 @@ export const setupExpressions = (
|
|||
formatColumn,
|
||||
renameColumns,
|
||||
getDatatable(getFormatFactory(core)),
|
||||
getTimeScale(getTimeZoneFactory(core)),
|
||||
getTimeScale(getDatatableUtilitiesFactory(core), getTimeZoneFactory(core)),
|
||||
].forEach((expressionFn) => expressions.registerFunction(expressionFn));
|
||||
};
|
||||
|
|
|
@ -43,3 +43,20 @@ export const getTimeZoneFactory =
|
|||
/** if `Browser`, hardcode it to 'UTC' so the export has data that makes sense **/
|
||||
return timezone === 'Browser' ? 'UTC' : timezone;
|
||||
};
|
||||
|
||||
/** @internal **/
|
||||
export const getDatatableUtilitiesFactory =
|
||||
(core: CoreSetup<PluginStartContract>) => async (context: ExecutionContext) => {
|
||||
const kibanaRequest = context.getKibanaRequest?.();
|
||||
|
||||
if (!kibanaRequest) {
|
||||
throw new Error('expression function cannot be executed without a KibanaRequest');
|
||||
}
|
||||
|
||||
const [{ elasticsearch, savedObjects }, { data }] = await core.getStartServices();
|
||||
const elasticsearchClient = elasticsearch.client.asScoped(kibanaRequest).asCurrentUser;
|
||||
const savedObjectsClient = savedObjects.getScopedClient(kibanaRequest);
|
||||
const { datatableUtilities } = data;
|
||||
|
||||
return datatableUtilities.asScopedToClient(savedObjectsClient, elasticsearchClient);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue