From 609bcc1882d8105f167482c9467a766242b439d1 Mon Sep 17 00:00:00 2001 From: Ashish Agrawal Date: Wed, 10 May 2023 14:28:42 -0700 Subject: [PATCH] Finalize eligibility check for augmenting visualizations by vis augmenter (#3687) * Add elibility checker for visualizations that can allow vis_augmenter to augment Signed-off-by: Ashish Agrawal --------- Signed-off-by: Ashish Agrawal --- src/plugins/vis_augmenter/public/index.ts | 1 + .../vis_augmenter/public/test_constants.ts | 33 +++ .../vis_augmenter/public/utils/utils.test.ts | 259 +++++++++++++++++- .../vis_augmenter/public/utils/utils.ts | 22 +- .../__snapshots__/helpers.test.js.snap | 4 +- .../public/expressions/helpers.test.js | 45 ++- .../public/expressions/helpers.ts | 31 ++- .../opensearch_dashboards.json | 2 +- .../public/line_to_expression.ts | 14 +- 9 files changed, 383 insertions(+), 28 deletions(-) diff --git a/src/plugins/vis_augmenter/public/index.ts b/src/plugins/vis_augmenter/public/index.ts index bef66db8fd02..78657ba6c224 100644 --- a/src/plugins/vis_augmenter/public/index.ts +++ b/src/plugins/vis_augmenter/public/index.ts @@ -29,3 +29,4 @@ export * from './utils'; export * from './constants'; export * from './vega'; export * from './saved_augment_vis'; +export * from './test_constants'; diff --git a/src/plugins/vis_augmenter/public/test_constants.ts b/src/plugins/vis_augmenter/public/test_constants.ts index 6bdaa7a66d78..d8c4386d0f69 100644 --- a/src/plugins/vis_augmenter/public/test_constants.ts +++ b/src/plugins/vis_augmenter/public/test_constants.ts @@ -7,7 +7,9 @@ import { OpenSearchDashboardsDatatable } from '../../expressions/public'; import { VIS_LAYER_COLUMN_TYPE, VisLayerTypes, HOVER_PARAM } from './'; const TEST_X_AXIS_ID = 'test-x-axis-id'; +const TEST_X_AXIS_ID_DIRTY = 'test.x.axis.id'; const TEST_VALUE_AXIS_ID = 'test-value-axis-id'; +const TEST_VALUE_AXIS_ID_DIRTY = 'test.value.axis.id'; const TEST_X_AXIS_TITLE = 'time'; const TEST_VALUE_AXIS_TITLE = 'avg value'; const TEST_PLUGIN = 'test-plugin'; @@ -53,6 +55,20 @@ const TEST_VALUES_NO_VIS_LAYERS = [ { [TEST_X_AXIS_ID]: 50, [TEST_VALUE_AXIS_ID]: 5 }, ]; +const TEST_VALUES_NO_VIS_LAYERS_DIRTY = [ + { [TEST_X_AXIS_ID_DIRTY]: 0, [TEST_VALUE_AXIS_ID_DIRTY]: 5 }, + { [TEST_X_AXIS_ID_DIRTY]: 5, [TEST_VALUE_AXIS_ID_DIRTY]: 10 }, + { [TEST_X_AXIS_ID_DIRTY]: 10, [TEST_VALUE_AXIS_ID_DIRTY]: 6 }, + { [TEST_X_AXIS_ID_DIRTY]: 15, [TEST_VALUE_AXIS_ID_DIRTY]: 4 }, + { [TEST_X_AXIS_ID_DIRTY]: 20, [TEST_VALUE_AXIS_ID_DIRTY]: 5 }, + { [TEST_X_AXIS_ID_DIRTY]: 25 }, + { [TEST_X_AXIS_ID_DIRTY]: 30 }, + { [TEST_X_AXIS_ID_DIRTY]: 35 }, + { [TEST_X_AXIS_ID_DIRTY]: 40 }, + { [TEST_X_AXIS_ID_DIRTY]: 45, [TEST_VALUE_AXIS_ID_DIRTY]: 3 }, + { [TEST_X_AXIS_ID_DIRTY]: 50, [TEST_VALUE_AXIS_ID_DIRTY]: 5 }, +]; + const TEST_VALUES_SINGLE_VIS_LAYER = [ { [TEST_X_AXIS_ID]: 0, [TEST_VALUE_AXIS_ID]: 5 }, { [TEST_X_AXIS_ID]: 5, [TEST_VALUE_AXIS_ID]: 10, [TEST_PLUGIN_RESOURCE_ID]: 2 }, @@ -111,6 +127,17 @@ export const TEST_COLUMNS_NO_VIS_LAYERS = [ }, ]; +export const TEST_COLUMNS_NO_VIS_LAYERS_DIRTY = [ + { + id: TEST_X_AXIS_ID_DIRTY, + name: TEST_X_AXIS_TITLE, + }, + { + id: TEST_VALUE_AXIS_ID_DIRTY, + name: TEST_VALUE_AXIS_TITLE, + }, +]; + export const TEST_COLUMNS_SINGLE_VIS_LAYER = [ ...TEST_COLUMNS_NO_VIS_LAYERS, { @@ -157,6 +184,12 @@ export const TEST_DATATABLE_NO_VIS_LAYERS = { rows: TEST_VALUES_NO_VIS_LAYERS, } as OpenSearchDashboardsDatatable; +export const TEST_DATATABLE_NO_VIS_LAYERS_DIRTY = { + type: 'opensearch_dashboards_datatable', + columns: TEST_COLUMNS_NO_VIS_LAYERS_DIRTY, + rows: TEST_VALUES_NO_VIS_LAYERS_DIRTY, +} as OpenSearchDashboardsDatatable; + export const TEST_DATATABLE_SINGLE_VIS_LAYER_EMPTY = { ...TEST_DATATABLE_NO_VIS_LAYERS, columns: TEST_COLUMNS_SINGLE_VIS_LAYER, diff --git a/src/plugins/vis_augmenter/public/utils/utils.test.ts b/src/plugins/vis_augmenter/public/utils/utils.test.ts index 0820d6e9c362..c37e87194c97 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.test.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.test.ts @@ -9,37 +9,278 @@ import { getAugmentVisSavedObjs, getAnyErrors, isEligibleForVisLayers, -} from './utils'; -import { createSavedAugmentVisLoader, SavedObjectOpenSearchDashboardsServicesWithAugmentVis, getMockAugmentVisSavedObjectClient, generateAugmentVisSavedObject, ISavedAugmentVis, - VisLayerExpressionFn, + generateVisLayer, VisLayerTypes, + VisLayerExpressionFn, } from '../'; -import { generateVisLayer } from './'; +import { AggConfigs, AggTypesRegistryStart, IndexPattern } from '../../../data/common'; +import { mockAggTypesRegistry } from '../../../data/common/search/aggs/test_helpers'; describe('utils', () => { - // TODO: redo / update this test suite when eligibility is finalized. - // Tracked in https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3268 describe('isEligibleForVisLayers', () => { - it('vis is ineligible with invalid type', async () => { + const validConfigStates = [ + { + enabled: true, + type: 'max', + params: {}, + schema: 'metric', + }, + { + enabled: true, + type: 'date_histogram', + params: {}, + schema: 'segment', + }, + ]; + const stubIndexPatternWithFields = { + id: '1234', + title: 'logstash-*', + fields: [ + { + name: 'response', + type: 'number', + esTypes: ['integer'], + aggregatable: true, + filterable: true, + searchable: true, + }, + ], + }; + const typesRegistry: AggTypesRegistryStart = mockAggTypesRegistry(); + const aggs = new AggConfigs(stubIndexPatternWithFields as IndexPattern, validConfigStates, { + typesRegistry, + }); + const validVis = ({ + params: { + type: 'line', + seriesParams: [ + { + type: 'line', + }, + ], + categoryAxes: [ + { + position: 'bottom', + }, + ], + }, + data: { + aggs, + }, + } as unknown) as Vis; + it('vis is ineligible with invalid non-line type', async () => { const vis = ({ params: { type: 'not-line', + seriesParams: [], + categoryAxes: [ + { + position: 'bottom', + }, + ], + }, + data: { + aggs, }, } as unknown) as Vis; expect(isEligibleForVisLayers(vis)).toEqual(false); }); - it('vis is eligible with valid type', async () => { + it('vis is ineligible with no date_histogram', async () => { + const invalidConfigStates = [ + { + enabled: true, + type: 'histogram', + params: {}, + }, + { + enabled: true, + type: 'metrics', + params: {}, + }, + ]; + const invalidAggs = new AggConfigs( + stubIndexPatternWithFields as IndexPattern, + invalidConfigStates, + { + typesRegistry, + } + ); + const vis = ({ + params: { + type: 'line', + seriesParams: [], + }, + data: { + invalidAggs, + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(vis)).toEqual(false); + }); + it('vis is ineligible with invalid aggs counts', async () => { + const invalidConfigStates = [ + ...validConfigStates, + { + enabled: true, + type: 'dot', + params: {}, + schema: 'radius', + }, + ]; + const invalidAggs = new AggConfigs( + stubIndexPatternWithFields as IndexPattern, + invalidConfigStates, + { + typesRegistry, + } + ); const vis = ({ params: { type: 'line', + seriesParams: [], + }, + data: { + invalidAggs, }, } as unknown) as Vis; - expect(isEligibleForVisLayers(vis)).toEqual(true); + expect(isEligibleForVisLayers(vis)).toEqual(false); + }); + it('vis is ineligible with no metric aggs', async () => { + const invalidConfigStates = [ + { + enabled: true, + type: 'date_histogram', + params: {}, + }, + ]; + const invalidAggs = new AggConfigs( + stubIndexPatternWithFields as IndexPattern, + invalidConfigStates, + { + typesRegistry, + } + ); + const vis = ({ + params: { + type: 'line', + seriesParams: [], + }, + data: { + invalidAggs, + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(vis)).toEqual(false); + }); + it('vis is ineligible with series param is not line type', async () => { + const vis = ({ + params: { + type: 'line', + seriesParams: [ + { + type: 'area', + }, + ], + categoryAxes: [ + { + position: 'bottom', + }, + ], + }, + data: { + aggs, + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(vis)).toEqual(false); + }); + it('vis is ineligible with series param not all being line type', async () => { + const vis = ({ + params: { + type: 'line', + seriesParams: [ + { + type: 'area', + }, + { + type: 'line', + }, + ], + categoryAxes: [ + { + position: 'bottom', + }, + ], + }, + data: { + aggs, + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(vis)).toEqual(false); + }); + it('vis is ineligible with invalid x-axis due to no segment aggregation', async () => { + const badConfigStates = [ + { + enabled: true, + type: 'max', + params: {}, + schema: 'metric', + }, + { + enabled: true, + type: 'max', + params: {}, + schema: 'metric', + }, + ]; + const badAggs = new AggConfigs(stubIndexPatternWithFields as IndexPattern, badConfigStates, { + typesRegistry, + }); + const invalidVis = ({ + params: { + type: 'line', + seriesParams: [ + { + type: 'line', + }, + ], + categoryAxes: [ + { + position: 'bottom', + }, + ], + }, + data: { + badAggs, + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(invalidVis)).toEqual(false); + }); + it('vis is ineligible with xaxis not on bottom', async () => { + const invalidVis = ({ + params: { + type: 'line', + seriesParams: [ + { + type: 'line', + }, + ], + categoryAxes: [ + { + position: 'top', + }, + ], + }, + data: { + aggs, + }, + } as unknown) as Vis; + expect(isEligibleForVisLayers(invalidVis)).toEqual(false); + }); + it('vis is eligible with valid type', async () => { + expect(isEligibleForVisLayers(validVis)).toEqual(true); }); }); diff --git a/src/plugins/vis_augmenter/public/utils/utils.ts b/src/plugins/vis_augmenter/public/utils/utils.ts index 096b5769b20f..0ec4ad486383 100644 --- a/src/plugins/vis_augmenter/public/utils/utils.ts +++ b/src/plugins/vis_augmenter/public/utils/utils.ts @@ -19,10 +19,26 @@ import { isVisLayerWithError, } from '../'; -// TODO: provide a deeper eligibility check. -// Tracked in https://github.com/opensearch-project/OpenSearch-Dashboards/issues/3268 export const isEligibleForVisLayers = (vis: Vis): boolean => { - return vis.params.type === 'line'; + // Only support date histogram and ensure there is only 1 x-axis and it has to be on the bottom. + // Additionally to have a valid x-axis, there needs to be a segment aggregation + const hasValidXaxis = + vis.data.aggs !== undefined && + vis.data.aggs?.byTypeName('date_histogram').length === 1 && + vis.params.categoryAxes.length === 1 && + vis.params.categoryAxes[0].position === 'bottom' && + vis.data.aggs?.bySchemaName('segment').length > 0; + // Support 1 segment for x axis bucket (that is date_histogram) and support metrics for + // multiple supported yaxis only. If there are other aggregation types, this is not + // valid for augmentation + const hasCorrectAggregationCount = + vis.data.aggs !== undefined && + vis.data.aggs?.bySchemaName('metric').length > 0 && + vis.data.aggs?.bySchemaName('metric').length === vis.data.aggs?.aggs.length - 1; + const hasOnlyLineSeries = + vis.params.seriesParams.every((seriesParam: { type: string }) => seriesParam.type === 'line') && + vis.params.type === 'line'; + return hasValidXaxis && hasCorrectAggregationCount && hasOnlyLineSeries; }; /** diff --git a/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap b/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap index 69af7eba10b3..7515b07fb9d2 100644 --- a/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap +++ b/src/plugins/vis_type_vega/public/expressions/__snapshots__/helpers.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`helpers createSpecFromDatatable() build complicated line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44,\\"col-2-3\\":60.9375},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154,\\"col-2-3\\":79.5},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144,\\"col-2-3\\":75.875},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133,\\"col-2-3\\":259.25},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149,\\"col-2-3\\":90},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152,\\"col-2-3\\":79.0625},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166,\\"col-2-3\\":85.25},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151,\\"col-2-3\\":92},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143,\\"col-2-3\\":90.75},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148,\\"col-2-3\\":92},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146,\\"col-2-3\\":83.25},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137,\\"col-2-3\\":98},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151,\\"col-2-3\\":87.4375},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157,\\"col-2-3\\":63.75},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151,\\"col-2-3\\":81.5625},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152,\\"col-2-3\\":100.6875},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142,\\"col-2-3\\":98},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151,\\"col-2-3\\":100.8125},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156,\\"col-2-3\\":85.8125},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153,\\"col-2-3\\":98},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162,\\"col-2-3\\":75.9375},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159,\\"col-2-3\\":73.625},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165,\\"col-2-3\\":72.8125},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149,\\"col-2-3\\":82.5},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94,\\"col-2-3\\":54}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"bottom\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":\\"ValueAxis-1\\",\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}},{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":\\"ValueAxis-1\\",\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Max products.min_price\\"}],\\"color\\":{\\"datum\\":\\"Max products.min_price\\"}}},{\\"mark\\":\\"rule\\",\\"encoding\\":{\\"x\\":{\\"type\\":\\"temporal\\",\\"field\\":\\"now_field\\"},\\"color\\":{\\"value\\":\\"red\\"},\\"size\\":{\\"value\\":1}}},{\\"mark\\":{\\"type\\":\\"rule\\",\\"color\\":\\"#E7664C\\",\\"strokeDash\\":[8,8]},\\"encoding\\":{\\"y\\":{\\"datum\\":100}}}],\\"transform\\":[{\\"calculate\\":\\"now()\\",\\"as\\":\\"now_field\\"}]}"`; +exports[`helpers createSpecFromDatatable() build complicated line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44,\\"col-2-3\\":60.9375},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154,\\"col-2-3\\":79.5},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144,\\"col-2-3\\":75.875},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133,\\"col-2-3\\":259.25},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149,\\"col-2-3\\":90},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152,\\"col-2-3\\":79.0625},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144,\\"col-2-3\\":82.5},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166,\\"col-2-3\\":85.25},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151,\\"col-2-3\\":92},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143,\\"col-2-3\\":90.75},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148,\\"col-2-3\\":92},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146,\\"col-2-3\\":83.25},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137,\\"col-2-3\\":98},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151,\\"col-2-3\\":87.4375},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157,\\"col-2-3\\":63.75},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151,\\"col-2-3\\":81.5625},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152,\\"col-2-3\\":100.6875},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142,\\"col-2-3\\":98},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151,\\"col-2-3\\":100.8125},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163,\\"col-2-3\\":83.6875},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156,\\"col-2-3\\":85.8125},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153,\\"col-2-3\\":98},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162,\\"col-2-3\\":75.9375},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159,\\"col-2-3\\":73.625},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165,\\"col-2-3\\":72.8125},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153,\\"col-2-3\\":113.375},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149,\\"col-2-3\\":82.5},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94,\\"col-2-3\\":54}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"bottom\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":true,\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}},{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":true,\\"orient\\":\\"right\\",\\"labels\\":true,\\"labelAngle\\":75},\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-2-3\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Max products.min_price\\"}],\\"color\\":{\\"datum\\":\\"Max products.min_price\\"}}},{\\"mark\\":\\"rule\\",\\"encoding\\":{\\"x\\":{\\"type\\":\\"temporal\\",\\"field\\":\\"now_field\\"},\\"color\\":{\\"value\\":\\"red\\"},\\"size\\":{\\"value\\":1}}},{\\"mark\\":{\\"type\\":\\"rule\\",\\"color\\":\\"#E7664C\\",\\"strokeDash\\":[8,8]},\\"encoding\\":{\\"y\\":{\\"datum\\":100}}}],\\"transform\\":[{\\"calculate\\":\\"now()\\",\\"as\\":\\"now_field\\"}]}"`; exports[`helpers createSpecFromDatatable() build empty chart if no x-axis is defined" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"right\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[]}"`; -exports[`helpers createSpecFromDatatable() build simple line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"right\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"orient\\":\\"left\\",\\"labels\\":true,\\"labelAngle\\":0},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}}]}"`; +exports[`helpers createSpecFromDatatable() build simple line chart" 1`] = `"{\\"$schema\\":\\"https://vega.github.io/schema/vega-lite/v5.json\\",\\"data\\":{\\"values\\":[{\\"col-0-2\\":1672214400000,\\"col-1-1\\":44},{\\"col-0-2\\":1672300800000,\\"col-1-1\\":150},{\\"col-0-2\\":1672387200000,\\"col-1-1\\":154},{\\"col-0-2\\":1672473600000,\\"col-1-1\\":144},{\\"col-0-2\\":1672560000000,\\"col-1-1\\":133},{\\"col-0-2\\":1672646400000,\\"col-1-1\\":149},{\\"col-0-2\\":1672732800000,\\"col-1-1\\":152},{\\"col-0-2\\":1672819200000,\\"col-1-1\\":144},{\\"col-0-2\\":1672905600000,\\"col-1-1\\":166},{\\"col-0-2\\":1672992000000,\\"col-1-1\\":151},{\\"col-0-2\\":1673078400000,\\"col-1-1\\":143},{\\"col-0-2\\":1673164800000,\\"col-1-1\\":148},{\\"col-0-2\\":1673251200000,\\"col-1-1\\":146},{\\"col-0-2\\":1673337600000,\\"col-1-1\\":137},{\\"col-0-2\\":1673424000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673510400000,\\"col-1-1\\":152},{\\"col-0-2\\":1673596800000,\\"col-1-1\\":151},{\\"col-0-2\\":1673683200000,\\"col-1-1\\":157},{\\"col-0-2\\":1673769600000,\\"col-1-1\\":151},{\\"col-0-2\\":1673856000000,\\"col-1-1\\":152},{\\"col-0-2\\":1673942400000,\\"col-1-1\\":142},{\\"col-0-2\\":1674028800000,\\"col-1-1\\":151},{\\"col-0-2\\":1674115200000,\\"col-1-1\\":163},{\\"col-0-2\\":1674201600000,\\"col-1-1\\":156},{\\"col-0-2\\":1674288000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674374400000,\\"col-1-1\\":162},{\\"col-0-2\\":1674460800000,\\"col-1-1\\":152},{\\"col-0-2\\":1674547200000,\\"col-1-1\\":159},{\\"col-0-2\\":1674633600000,\\"col-1-1\\":165},{\\"col-0-2\\":1674720000000,\\"col-1-1\\":153},{\\"col-0-2\\":1674806400000,\\"col-1-1\\":149},{\\"col-0-2\\":1674892800000,\\"col-1-1\\":94}]},\\"config\\":{\\"view\\":{\\"stroke\\":null},\\"concat\\":{\\"spacing\\":0},\\"legend\\":{\\"orient\\":\\"right\\"},\\"kibana\\":{\\"hideWarnings\\":true}},\\"layer\\":[{\\"mark\\":{\\"type\\":\\"line\\",\\"interpolate\\":\\"linear\\",\\"strokeWidth\\":2,\\"point\\":true},\\"encoding\\":{\\"x\\":{\\"axis\\":{\\"title\\":\\"order_date per day\\",\\"grid\\":false},\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\"},\\"y\\":{\\"axis\\":{\\"title\\":\\"Count\\",\\"grid\\":false,\\"orient\\":\\"left\\",\\"labels\\":true,\\"labelAngle\\":0},\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\"},\\"tooltip\\":[{\\"field\\":\\"col-0-2\\",\\"type\\":\\"temporal\\",\\"title\\":\\"order_date per day\\"},{\\"field\\":\\"col-1-1\\",\\"type\\":\\"quantitative\\",\\"title\\":\\"Count\\"}],\\"color\\":{\\"datum\\":\\"Count\\"}}}]}"`; diff --git a/src/plugins/vis_type_vega/public/expressions/helpers.test.js b/src/plugins/vis_type_vega/public/expressions/helpers.test.js index f09b1b8db3d6..08e347dca175 100644 --- a/src/plugins/vis_type_vega/public/expressions/helpers.test.js +++ b/src/plugins/vis_type_vega/public/expressions/helpers.test.js @@ -21,10 +21,25 @@ import { simpleDimensions, noXAxisDimensions, } from './__mocks__'; +import { + TEST_DATATABLE_NO_VIS_LAYERS, + TEST_DATATABLE_NO_VIS_LAYERS_DIRTY, +} from '../../../vis_augmenter/public'; describe('helpers', function () { + describe('formatDatatable()', function () { + it('formatSimpleDatatable', function () { + expect(formatDatatable(TEST_DATATABLE_NO_VIS_LAYERS)).toBe(TEST_DATATABLE_NO_VIS_LAYERS); + }); + it('formatDirtyDatatable', function () { + expect(formatDatatable(TEST_DATATABLE_NO_VIS_LAYERS_DIRTY)).toStrictEqual( + TEST_DATATABLE_NO_VIS_LAYERS + ); + }); + }); + describe('cleanString()', function () { - it('string should not contain "', function () { + it('string should not contain quotation marks', function () { const dirtyString = '"someString"'; expect(cleanString(dirtyString)).toBe('someString'); }); @@ -141,6 +156,34 @@ describe('helpers', function () { vegaYAxis.axis.title = 'columnName'; expect(buildYAxis(column, valueAxis, visParams)).toStrictEqual(vegaYAxis); }); + it('build YAxis with percentile rank', function () { + const valueAxis = { + id: 'someId', + labels: { + rotate: 75, + show: false, + }, + position: 'left', + title: { + text: 'someText', + }, + }; + const column = { name: 'columnName', id: 'columnId', meta: { type: 'percentile_ranks' } }; + const visParams = { grid: { valueAxis: true } }; + const vegaYAxis = { + axis: { + title: 'someText', + grid: true, + orient: 'left', + labels: false, + labelAngle: 75, + format: '.0%', + }, + field: 'columnId', + type: 'quantitative', + }; + expect(buildYAxis(column, valueAxis, visParams)).toStrictEqual(vegaYAxis); + }); }); describe('createSpecFromDatatable()', function () { diff --git a/src/plugins/vis_type_vega/public/expressions/helpers.ts b/src/plugins/vis_type_vega/public/expressions/helpers.ts index ca31367bf119..e904ccb1af87 100644 --- a/src/plugins/vis_type_vega/public/expressions/helpers.ts +++ b/src/plugins/vis_type_vega/public/expressions/helpers.ts @@ -6,6 +6,7 @@ import { OpenSearchDashboardsDatatable, OpenSearchDashboardsDatatableColumn, + OpenSearchDashboardsDatatableRow, } from '../../../expressions/public'; import { VislibDimensions, VisParams } from '../../../visualizations/public'; import { isVisLayerColumn } from '../../../vis_augmenter/public'; @@ -49,7 +50,20 @@ export const formatDatatable = ( datatable.columns.forEach((column) => { // clean quotation marks from names in columns column.name = cleanString(column.name); + // clean ids to remove "." as that will cause vega to not process it correctly. + // This happens for different metric types + column.id = column.id.replaceAll('.', '-'); }); + + // clean row keys to remove "." as that will cause vega to not process it correctly + const updatedRows: OpenSearchDashboardsDatatableRow[] = datatable.rows.map((row) => + Object.entries(row).reduce((updatedRow, [key, value]) => { + const cleanKey = key.replaceAll('.', '-'); + return Object.assign(updatedRow, { [cleanKey]: value }); + }, {}) + ); + + datatable.rows = updatedRows; return datatable; }; @@ -111,14 +125,17 @@ export const buildYAxis = ( valueAxis: ValueAxis, visParams: VisParams ) => { + const subAxis = { + title: cleanString(valueAxis.title.text) || column.name, + grid: visParams.grid.valueAxis !== undefined, + orient: valueAxis.position, + labels: valueAxis.labels.show, + labelAngle: valueAxis.labels.rotate, + }; + // Percentile ranks aggregation metric needs percentile formatting. + if (column.meta?.type === 'percentile_ranks') Object.assign(subAxis, { format: '.0%' }); return { - axis: { - title: cleanString(valueAxis.title.text) || column.name, - grid: visParams.grid.valueAxis, - orient: valueAxis.position, - labels: valueAxis.labels.show, - labelAngle: valueAxis.labels.rotate, - }, + axis: subAxis, field: column.id, type: 'quantitative', }; diff --git a/src/plugins/vis_type_vislib/opensearch_dashboards.json b/src/plugins/vis_type_vislib/opensearch_dashboards.json index 0cef84e463f6..c498d121e99d 100644 --- a/src/plugins/vis_type_vislib/opensearch_dashboards.json +++ b/src/plugins/vis_type_vislib/opensearch_dashboards.json @@ -5,5 +5,5 @@ "ui": true, "requiredPlugins": ["charts", "data", "expressions", "visualizations", "opensearchDashboardsLegacy", "visTypeVega"], "optionalPlugins": ["visTypeXy"], - "requiredBundles": ["opensearchDashboardsUtils", "visDefaultEditor"] + "requiredBundles": ["opensearchDashboardsUtils", "visDefaultEditor", "visAugmenter"] } diff --git a/src/plugins/vis_type_vislib/public/line_to_expression.ts b/src/plugins/vis_type_vislib/public/line_to_expression.ts index 2648e70af38d..79a716f2e800 100644 --- a/src/plugins/vis_type_vislib/public/line_to_expression.ts +++ b/src/plugins/vis_type_vislib/public/line_to_expression.ts @@ -3,13 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { buildVislibDimensions, Vis } from '../../visualizations/public'; +import { buildVislibDimensions, Vis, VislibDimensions } from '../../visualizations/public'; import { buildExpression, buildExpressionFunction } from '../../expressions/public'; import { OpenSearchaggsExpressionFunctionDefinition } from '../../data/common/search/expressions'; import { VegaExpressionFunctionDefinition, LineVegaSpecExpressionFunctionDefinition, } from '../../vis_type_vega/public'; +import { isEligibleForVisLayers } from '../../vis_augmenter/public'; export const toExpressionAst = async (vis: Vis, params: any) => { // Construct the existing expr fns that are ran for vislib line chart, up until the render fn. @@ -26,9 +27,13 @@ export const toExpressionAst = async (vis: Vis, params: any) => { ); // Checks if there are vislayers to overlay. If not, default to the vislib implementation. - if (params.visLayers == null || Object.keys(params.visLayers).length === 0) { - // This wont work but is needed so then it will default to the original vis lib renderer - const dimensions = await buildVislibDimensions(vis, params); + const dimensions: VislibDimensions = await buildVislibDimensions(vis, params); + if ( + params.visLayers == null || + Object.keys(params.visLayers).length === 0 || + !isEligibleForVisLayers(vis) + ) { + // Render using vislib instead of vega-lite const visConfig = { ...vis.params, dimensions }; const vislib = buildExpressionFunction('vislib', { type: 'line', @@ -37,7 +42,6 @@ export const toExpressionAst = async (vis: Vis, params: any) => { const ast = buildExpression([opensearchaggsFn, vislib]); return ast.toAst(); } else { - const dimensions = await buildVislibDimensions(vis, params); // adding the new expr fn here that takes the datatable and converts to a vega spec const vegaSpecFn = buildExpressionFunction( 'line_vega_spec',