mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[TSVB] Fix series containing colon (#123897)
This commit is contained in:
parent
af9d3a1602
commit
52f0ea20fe
15 changed files with 38 additions and 27 deletions
|
@ -11,6 +11,7 @@ export const UI_SETTINGS = {
|
|||
ALLOW_STRING_INDICES: 'metrics:allowStringIndices',
|
||||
ALLOW_CHECKING_FOR_FAILED_SHARDS: 'metrics:allowCheckingForFailedShards',
|
||||
};
|
||||
export const SERIES_SEPARATOR = '╰┄►';
|
||||
export const INDEXES_SEPARATOR = ',';
|
||||
export const AUTO_INTERVAL = 'auto';
|
||||
export const ROUTES = {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { XYChartSeriesIdentifier, GeometryValue } from '@elastic/charts';
|
|||
import { getClickFilterData } from './get_click_filter_data';
|
||||
import type { TSVBTables } from './types';
|
||||
import { TimeseriesVisParams } from '../../../types';
|
||||
import { SERIES_SEPARATOR } from '../../../../common/constants';
|
||||
|
||||
describe('getClickFilterData', () => {
|
||||
test('gets the correct data for a group by everything timeseries chart', () => {
|
||||
|
@ -102,7 +103,7 @@ describe('getClickFilterData', () => {
|
|||
},
|
||||
{
|
||||
key: 'groupId{yaxis_6e0353a0-ad9b-11eb-b112-89cce8e43380_main_group}spec{61ca57f1-469d-11e7-af02-69e470af7417:1}yAccessor{1}splitAccessors{}',
|
||||
specId: '61ca57f1-469d-11e7-af02-69e470af7417:1',
|
||||
specId: '61ca57f1-469d-11e7-af02-69e470af7417╰┄►1',
|
||||
},
|
||||
],
|
||||
] as Array<[GeometryValue, XYChartSeriesIdentifier]>;
|
||||
|
@ -199,7 +200,7 @@ describe('getClickFilterData', () => {
|
|||
expect(data[1].column).toEqual(2);
|
||||
expect(data[1].row).toEqual(10);
|
||||
// expect(data).toEqual([]);
|
||||
const splitValue = points[0][1].specId.split(':');
|
||||
const splitValue = points[0][1].specId.split(SERIES_SEPARATOR);
|
||||
expect(data[1].value).toEqual(parseInt(splitValue[1], 10));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ import { X_ACCESSOR_INDEX } from '../../visualizations/constants';
|
|||
import { BUCKET_TYPES } from '../../../../common/enums';
|
||||
import { TimeseriesVisParams } from '../../../types';
|
||||
import type { TSVBTables } from './types';
|
||||
import { SERIES_SEPARATOR } from '../../../../common/constants';
|
||||
|
||||
export const getClickFilterData = (
|
||||
points: Array<[GeometryValue, XYChartSeriesIdentifier]>,
|
||||
|
@ -23,7 +24,7 @@ export const getClickFilterData = (
|
|||
const { specId } = point[1];
|
||||
// specId for a split series has the format
|
||||
// 61ca57f1-469d-11e7-af02-69e470af7417:Men's Accessories, <layer_id>:<split_label>
|
||||
const [layerId, splitLabel] = specId.split(':');
|
||||
const [layerId, splitLabel] = specId.split(SERIES_SEPARATOR);
|
||||
const table = tables[layerId];
|
||||
|
||||
const layer = model.series.filter(({ id }) => id === layerId);
|
||||
|
|
|
@ -20,6 +20,7 @@ import { sortBy, first, get } from 'lodash';
|
|||
import { DATA_FORMATTERS } from '../../../../../common/enums';
|
||||
import { getOperator, shouldOperate } from '../../../../../common/operators_utils';
|
||||
import { ExternalUrlErrorModal } from '../../lib/external_url_error_modal';
|
||||
import { SERIES_SEPARATOR } from '../../../../../common/constants';
|
||||
|
||||
function sortByDirection(data, direction, fn) {
|
||||
if (direction === 'desc') {
|
||||
|
@ -32,7 +33,7 @@ function sortSeries(visData, model) {
|
|||
const series = get(visData, `${model.id}.series`, []);
|
||||
return model.series.reduce((acc, item) => {
|
||||
const itemSeries = series.filter((s) => {
|
||||
const id = first(s.id.split(/:/));
|
||||
const id = first(s.id.split(SERIES_SEPARATOR));
|
||||
return id === item.id;
|
||||
});
|
||||
const direction = item.terms_direction || 'desc';
|
||||
|
@ -47,7 +48,7 @@ function TopNVisualization(props) {
|
|||
const { backgroundColor, model, visData, fieldFormatMap, getConfig } = props;
|
||||
|
||||
const series = sortSeries(visData, model).map((item) => {
|
||||
const id = first(item.id.split(/:/));
|
||||
const id = first(item.id.split(SERIES_SEPARATOR));
|
||||
const seriesConfig = model.series.find((s) => s.id === id);
|
||||
if (seriesConfig) {
|
||||
const tickFormatter =
|
||||
|
|
|
@ -13,6 +13,7 @@ import { labelDateFormatter } from './lib/label_date_formatter';
|
|||
import { findIndex, first } from 'lodash';
|
||||
import { getValueOrEmpty } from '../../../common/empty_label';
|
||||
import { getSplitByTermsColor } from '../lib/get_split_by_terms_color';
|
||||
import { SERIES_SEPARATOR } from '../../../common/constants';
|
||||
|
||||
export function visWithSplits(WrappedComponent) {
|
||||
function SplitVisComponent(props) {
|
||||
|
@ -43,12 +44,12 @@ export function visWithSplits(WrappedComponent) {
|
|||
);
|
||||
|
||||
if (!model || !visData || !visData[model.id]) return <WrappedComponent {...props} />;
|
||||
if (visData[model.id].series.every((s) => s.id.split(':').length === 1)) {
|
||||
if (visData[model.id].series.every((s) => s.id.split(SERIES_SEPARATOR).length === 1)) {
|
||||
return <WrappedComponent {...props} />;
|
||||
}
|
||||
|
||||
const splitsVisData = visData[model.id].series.reduce((acc, series) => {
|
||||
const [seriesId, splitId] = series.id.split(':');
|
||||
const [seriesId, splitId] = series.id.split(SERIES_SEPARATOR);
|
||||
const seriesModel = model.series.find((s) => s.id === seriesId);
|
||||
if (!seriesModel) return acc;
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ describe('getSplits(resp, panel, series)', () => {
|
|||
|
||||
expect(await getSplits(resp, panel, series, undefined, () => [])).toEqual([
|
||||
{
|
||||
id: 'SERIES:example-01',
|
||||
id: 'SERIES╰┄►example-01',
|
||||
key: 'example-01',
|
||||
label: 'example-01',
|
||||
labelFormatted: '',
|
||||
|
@ -90,7 +90,7 @@ describe('getSplits(resp, panel, series)', () => {
|
|||
SIBAGG: { value: 1 },
|
||||
},
|
||||
{
|
||||
id: 'SERIES:example-02',
|
||||
id: 'SERIES╰┄►example-02',
|
||||
key: 'example-02',
|
||||
label: 'example-02',
|
||||
labelFormatted: '',
|
||||
|
@ -138,7 +138,7 @@ describe('getSplits(resp, panel, series)', () => {
|
|||
const panel = { type: 'top_n' } as Panel;
|
||||
expect(await getSplits(resp, panel, series, undefined, () => [])).toEqual([
|
||||
{
|
||||
id: 'SERIES:example-01',
|
||||
id: 'SERIES╰┄►example-01',
|
||||
key: 'example-01',
|
||||
label: '--example-01--',
|
||||
labelFormatted: '',
|
||||
|
@ -149,7 +149,7 @@ describe('getSplits(resp, panel, series)', () => {
|
|||
SIBAGG: { value: 1 },
|
||||
},
|
||||
{
|
||||
id: 'SERIES:example-02',
|
||||
id: 'SERIES╰┄►example-02',
|
||||
key: 'example-02',
|
||||
label: '--example-02--',
|
||||
labelFormatted: '',
|
||||
|
@ -200,7 +200,7 @@ describe('getSplits(resp, panel, series)', () => {
|
|||
|
||||
expect(await getSplits(resp, panel, series, undefined, () => [])).toEqual([
|
||||
{
|
||||
id: 'SERIES:example-01',
|
||||
id: 'SERIES╰┄►example-01',
|
||||
key: 'example-01',
|
||||
key_as_string: 'false',
|
||||
label: '--example-01--',
|
||||
|
@ -212,7 +212,7 @@ describe('getSplits(resp, panel, series)', () => {
|
|||
SIBAGG: { value: 1 },
|
||||
},
|
||||
{
|
||||
id: 'SERIES:example-02',
|
||||
id: 'SERIES╰┄►example-02',
|
||||
key: 'example-02',
|
||||
key_as_string: 'true',
|
||||
label: '--example-02--',
|
||||
|
@ -256,7 +256,7 @@ describe('getSplits(resp, panel, series)', () => {
|
|||
|
||||
expect(await getSplits(resp, panel, series, undefined, () => [])).toEqual([
|
||||
{
|
||||
id: 'SERIES:filter-1',
|
||||
id: 'SERIES╰┄►filter-1',
|
||||
key: 'filter-1',
|
||||
label: '200s',
|
||||
meta: { bucketSize: 10 },
|
||||
|
@ -265,7 +265,7 @@ describe('getSplits(resp, panel, series)', () => {
|
|||
timeseries: { buckets: [] },
|
||||
},
|
||||
{
|
||||
id: 'SERIES:filter-2',
|
||||
id: 'SERIES╰┄►filter-2',
|
||||
key: 'filter-2',
|
||||
label: '300s',
|
||||
splitByLabel: 'Count',
|
||||
|
|
|
@ -11,6 +11,7 @@ import { get, isPlainObject } from 'lodash';
|
|||
import { overwrite } from '../helpers';
|
||||
|
||||
import { calculateLabel } from '../../../../common/calculate_label';
|
||||
import { SERIES_SEPARATOR } from '../../../../common/constants';
|
||||
import { getLastMetric } from './get_last_metric';
|
||||
import { formatKey } from './format_key';
|
||||
|
||||
|
@ -66,7 +67,7 @@ export async function getSplits<TRawResponse = unknown, TMeta extends BaseMeta =
|
|||
};
|
||||
}
|
||||
|
||||
bucket.id = `${series.id}:${bucket.key}`;
|
||||
bucket.id = `${series.id}${SERIES_SEPARATOR}${bucket.key}`;
|
||||
bucket.splitByLabel = splitByLabel;
|
||||
bucket.label = formatKey(bucket.key, series);
|
||||
bucket.labelFormatted = bucket.key_as_string ? formatKey(bucket.key_as_string, series) : '';
|
||||
|
@ -79,7 +80,7 @@ export async function getSplits<TRawResponse = unknown, TMeta extends BaseMeta =
|
|||
if (series.split_mode === 'filters' && isPlainObject(buckets)) {
|
||||
return (series.split_filters || []).map((filter) => {
|
||||
const bucket = get(resp, `aggregations.${series.id}.buckets.${filter.id}`);
|
||||
bucket.id = `${series.id}:${filter.id}`;
|
||||
bucket.id = `${series.id}${SERIES_SEPARATOR}${filter.id}`;
|
||||
bucket.key = filter.id;
|
||||
bucket.splitByLabel = splitByLabel;
|
||||
bucket.color = filter.color;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { convertIntervalToUnit } from '../../helpers/unit_to_seconds';
|
||||
import { SERIES_SEPARATOR } from '../../../../../common/constants';
|
||||
|
||||
const percentileValueMatch = /\[([0-9\.]+)\]$/;
|
||||
import { startsWith, flatten, values, first, last } from 'lodash';
|
||||
|
@ -20,7 +21,7 @@ export function mathAgg(resp, panel, series, meta, extractFields) {
|
|||
// Filter the results down to only the ones that match the series.id. Sometimes
|
||||
// there will be data from other series mixed in.
|
||||
results = results.filter((s) => {
|
||||
if (s.id.split(/:/)[0] === series.id) {
|
||||
if (s.id.split(SERIES_SEPARATOR)[0] === series.id) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -93,7 +93,7 @@ describe('math(resp, panel, series)', () => {
|
|||
expect(results).toHaveLength(1);
|
||||
|
||||
expect(results[0]).toEqual({
|
||||
id: 'test:example-01',
|
||||
id: 'test╰┄►example-01',
|
||||
label: 'example-01',
|
||||
color: 'rgb(255, 0, 0)',
|
||||
stack: false,
|
||||
|
|
|
@ -11,6 +11,7 @@ import { getDefaultDecoration } from '../../helpers/get_default_decoration';
|
|||
import { getSplits } from '../../helpers/get_splits';
|
||||
import { getLastMetric } from '../../helpers/get_last_metric';
|
||||
import { TSVB_METRIC_TYPES } from '../../../../../common/enums';
|
||||
import { SERIES_SEPARATOR } from '../../../../../common/constants';
|
||||
|
||||
export function percentile(resp, panel, series, meta, extractFields) {
|
||||
return (next) => async (results) => {
|
||||
|
@ -23,7 +24,7 @@ export function percentile(resp, panel, series, meta, extractFields) {
|
|||
(await getSplits(resp, panel, series, meta, extractFields)).forEach((split) => {
|
||||
metric.percentiles.forEach((percentile) => {
|
||||
const percentileValue = percentile.value ? percentile.value : 0;
|
||||
const id = `${split.id}:${percentile.id}`;
|
||||
const id = `${split.id}${SERIES_SEPARATOR}${percentile.id}`;
|
||||
const data = split.timeseries.buckets.map((bucket) => {
|
||||
const higherMetric = { ...metric, percent: percentileValue };
|
||||
const serieData = [bucket.key, getAggValue(bucket, higherMetric)];
|
||||
|
|
|
@ -83,7 +83,7 @@ describe('percentile(resp, panel, series)', () => {
|
|||
|
||||
expect(results).toHaveLength(2);
|
||||
|
||||
expect(results[0]).toHaveProperty('id', 'test:10-90');
|
||||
expect(results[0]).toHaveProperty('id', 'test╰┄►10-90');
|
||||
expect(results[0]).toHaveProperty('color', '#000028');
|
||||
expect(results[0]).toHaveProperty('label', 'Percentile of cpu');
|
||||
expect(results[0]).toHaveProperty('lines');
|
||||
|
@ -100,7 +100,7 @@ describe('percentile(resp, panel, series)', () => {
|
|||
[2, 1.2, 5.3],
|
||||
]);
|
||||
|
||||
expect(results[1]).toHaveProperty('id', 'test:50');
|
||||
expect(results[1]).toHaveProperty('id', 'test╰┄►50');
|
||||
expect(results[1]).toHaveProperty('color', 'rgb(255, 0, 0)');
|
||||
expect(results[1]).toHaveProperty('label', '(50) Percentile of cpu');
|
||||
expect(results[1]).toHaveProperty('stack', false);
|
||||
|
|
|
@ -12,6 +12,7 @@ import { getSplits } from '../../helpers/get_splits';
|
|||
import { getLastMetric } from '../../helpers/get_last_metric';
|
||||
import { toPercentileNumber } from '../../../../../common/to_percentile_number';
|
||||
import { TSVB_METRIC_TYPES } from '../../../../../common/enums';
|
||||
import { SERIES_SEPARATOR } from '../../../../../common/constants';
|
||||
|
||||
export function percentileRank(resp, panel, series, meta, extractFields) {
|
||||
return (next) => async (results) => {
|
||||
|
@ -33,7 +34,7 @@ export function percentileRank(resp, panel, series, meta, extractFields) {
|
|||
|
||||
results.push({
|
||||
data,
|
||||
id: `${split.id}:${percentileRank}:${index}`,
|
||||
id: `${split.id}${SERIES_SEPARATOR}${percentileRank}${SERIES_SEPARATOR}${index}`,
|
||||
label: `(${percentileRank || 0}) ${split.label}`,
|
||||
color:
|
||||
series.split_mode === 'everything' && metric.colors
|
||||
|
|
|
@ -75,7 +75,7 @@ describe('percentile_rank(resp, panel, series, meta, extractFields)', () => {
|
|||
|
||||
expect(results).toHaveLength(2);
|
||||
|
||||
expect(results[0]).toHaveProperty('id', 'test:1000:0');
|
||||
expect(results[0]).toHaveProperty('id', 'test╰┄►1000╰┄►0');
|
||||
expect(results[0]).toHaveProperty('color', '#000028');
|
||||
expect(results[0]).toHaveProperty('label', '(1000) Percentile Rank of cpu');
|
||||
expect(results[0].data).toEqual([
|
||||
|
@ -83,7 +83,7 @@ describe('percentile_rank(resp, panel, series, meta, extractFields)', () => {
|
|||
[2, 1],
|
||||
]);
|
||||
|
||||
expect(results[1]).toHaveProperty('id', 'test:500:1');
|
||||
expect(results[1]).toHaveProperty('id', 'test╰┄►500╰┄►1');
|
||||
expect(results[1]).toHaveProperty('color', '#0000FF');
|
||||
expect(results[1]).toHaveProperty('label', '(500) Percentile Rank of cpu');
|
||||
expect(results[1].data).toEqual([
|
||||
|
|
|
@ -9,6 +9,7 @@ import { last, first } from 'lodash';
|
|||
import { SeriesAgg } from './_series_agg';
|
||||
import { getDefaultDecoration } from '../../helpers/get_default_decoration';
|
||||
import { calculateLabel } from '../../../../../common/calculate_label';
|
||||
import { SERIES_SEPARATOR } from '../../../../../common/constants';
|
||||
|
||||
export function seriesAgg(resp, panel, series, meta, extractFields) {
|
||||
return (next) => async (results) => {
|
||||
|
@ -19,7 +20,7 @@ export function seriesAgg(resp, panel, series, meta, extractFields) {
|
|||
// Filter out the seires with the matching metric and store them
|
||||
// in targetSeries
|
||||
results = results.filter((s) => {
|
||||
if (s.id.split(/:/)[0] === series.id) {
|
||||
if (s.id.split(SERIES_SEPARATOR)[0] === series.id) {
|
||||
targetSeries.push(s.data);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import { last } from 'lodash';
|
||||
|
||||
import { calculateLabel } from '../../../../../common/calculate_label';
|
||||
import { SERIES_SEPARATOR } from '../../../../../common/constants';
|
||||
|
||||
// @ts-expect-error no typed yet
|
||||
import { SeriesAgg } from './_series_agg';
|
||||
|
@ -26,7 +27,7 @@ export const seriesAgg: TableResponseProcessorsFunction =
|
|||
// Filter out the seires with the matching metric and store them
|
||||
// in targetSeries
|
||||
results = results.filter((s) => {
|
||||
if (s.id && s.id.split(/:/)[0] === series.id) {
|
||||
if (s.id && s.id.split(SERIES_SEPARATOR)[0] === series.id) {
|
||||
targetSeries.push(s.data!);
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue