From 5970b0e50cf78b76ed0f301dc4499a37d1658f95 Mon Sep 17 00:00:00 2001 From: Eric Wei Date: Wed, 7 Sep 2022 11:30:30 -0700 Subject: [PATCH 01/20] Query manager integration (#987) * Bump prismjs from 1.25.0 to 1.27.0 in /dashboards-observability (#508) (#574) Bumps [prismjs](https://github.com/PrismJS/prism) from 1.25.0 to 1.27.0. - [Release notes](https://github.com/PrismJS/prism/releases) - [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md) - [Commits](https://github.com/PrismJS/prism/compare/v1.25.0...v1.27.0) --- updated-dependencies: - dependency-name: prismjs dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commit b4f491a16f81725db6d63f604a3020e5b89dd720) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * change to support java 8 in compile and runtime (#575) (#576) Signed-off-by: Zhongnan Su (cherry picked from commit 5c43e9dac336b37fa4f6f002709e0965015383aa) Co-authored-by: Zhongnan Su * Add 1.3.0 release notes (#580) (#582) Signed-off-by: Eugene Lee * query manager Signed-off-by: Eric Wei * removed aggregations from dimensions Signed-off-by: Eric Wei * qm improvements Signed-off-by: Eric Wei * types/code cleanups/error corrections Signed-off-by: Eric Wei * qm fixes for query builder Signed-off-by: Eric Wei * viz timestamp selector Signed-off-by: Eric Wei * query manager integration with bar step 1 Signed-off-by: Eric Wei * span fix Signed-off-by: Eric Wei * use postinstall for antlr output files Signed-off-by: Eric Wei * query building fix Signed-off-by: Eric Wei * updated snapshots Signed-off-by: Eric Wei * fixed file not found issue Signed-off-by: Eric Wei * remove output files Signed-off-by: Eric Wei Signed-off-by: Eugene Lee Signed-off-by: Eric Wei Co-authored-by: opensearch-trigger-bot[bot] <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Zhongnan Su --- .../common/constants/explorer.ts | 18 +- .../query_manager/ast/expression/span.ts | 2 +- .../common/types/explorer.ts | 9 + dashboards-observability/public/.DS_Store | Bin 0 -> 6148 bytes .../components/common/search/autocomplete.tsx | 10 +- .../components/common/search/search.tsx | 17 +- .../__snapshots__/utils.test.tsx.snap | 700 ++++++------------ .../event_analytics/explorer/explorer.tsx | 87 +-- .../data_config_panel_item.tsx | 539 ++++++++++---- .../explorer/visualizations/index.tsx | 3 +- .../hooks/use_fetch_visualizations.ts | 28 +- .../__tests__/__snapshots__/bar.test.tsx.snap | 9 +- .../visualizations/charts/bar/bar.tsx | 259 +++---- .../public/services/requests/ppl.ts | 2 +- 14 files changed, 829 insertions(+), 854 deletions(-) create mode 100644 dashboards-observability/public/.DS_Store diff --git a/dashboards-observability/common/constants/explorer.ts b/dashboards-observability/common/constants/explorer.ts index 9d52923fe..564c68167 100644 --- a/dashboards-observability/common/constants/explorer.ts +++ b/dashboards-observability/common/constants/explorer.ts @@ -91,31 +91,31 @@ export const VIZ_CONTAIN_XY_AXIS = [ // default ppl aggregation method options export const AGGREGATION_OPTIONS = [ { - label: 'COUNT', + label: 'count', }, { - label: 'SUM', + label: 'sum', }, { - label: 'AVERAGE', + label: 'avg', }, { - label: 'MAX', + label: 'max', }, { - label: 'MIN', + label: 'min', }, { - label: 'VAR_SAMP', + label: 'var_samp', }, { - label: 'VAR_POP', + label: 'var_pop', }, { - label: 'STDDEV_SAMP', + label: 'stddev_samp', }, { - label: 'STDDEV_POP', + label: 'stddev_pop', }, ]; diff --git a/dashboards-observability/common/query_manager/ast/expression/span.ts b/dashboards-observability/common/query_manager/ast/expression/span.ts index d69152b44..d2a570915 100644 --- a/dashboards-observability/common/query_manager/ast/expression/span.ts +++ b/dashboards-observability/common/query_manager/ast/expression/span.ts @@ -23,6 +23,6 @@ export class Span extends PPLNode { } toString(): string { - return `${this.spanExpression.toString()} as ${this.alias}`; + return `${this.spanExpression.toString()}${this.alias ? ` as ${this.alias}` : ''}`; } } diff --git a/dashboards-observability/common/types/explorer.ts b/dashboards-observability/common/types/explorer.ts index 9d3cb5072..a734d7150 100644 --- a/dashboards-observability/common/types/explorer.ts +++ b/dashboards-observability/common/types/explorer.ts @@ -265,12 +265,21 @@ export interface ConfigListEntry { side: string; type: string; } + export interface HistogramConfigList { bucketSize: string; bucketOffset: string; } +export interface DimensionSpan { + time_field: IField; + interval: number; + unit: string; +} + export interface ConfigList { dimensions?: ConfigListEntry[] | HistogramConfigList[]; metrics?: ConfigListEntry[]; + breakdowns?: ConfigListEntry[] | HistogramConfigList[]; + span?: DimensionSpan; } diff --git a/dashboards-observability/public/.DS_Store b/dashboards-observability/public/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..dbd07c1a1c1d289c1b490405a326e8a55ae29325 GIT binary patch literal 6148 zcmeHKJ8r^25S>XVP-t9I?iIMf%5qNN3m_#C4KXMr)UI+aj>emhq98+&f+l(+&Ai>& zd29I<9*>A<`}MjKX+&fWH%Fk~B9M^^Pys6Nqkw%M3f!hP6`=yd49* i9b;qdc*nm7hJ9eJk%`7>a;(5S$@6*vQa;1$mR literal 0 HcmV?d00001 diff --git a/dashboards-observability/public/components/common/search/autocomplete.tsx b/dashboards-observability/public/components/common/search/autocomplete.tsx index 3d3fdac26..a8c4007a7 100644 --- a/dashboards-observability/public/components/common/search/autocomplete.tsx +++ b/dashboards-observability/public/components/common/search/autocomplete.tsx @@ -185,7 +185,13 @@ export const Autocomplete = (props: AutocompleteProps) => {
-
+ + {fullWord.slice(0, -item.suggestion.length)} + {item.suggestion} + +
+ {/*
{ }
`, }} - /> + /> */}
diff --git a/dashboards-observability/public/components/common/search/search.tsx b/dashboards-observability/public/components/common/search/search.tsx index b4e2ebb16..ed9f85381 100644 --- a/dashboards-observability/public/components/common/search/search.tsx +++ b/dashboards-observability/public/components/common/search/search.tsx @@ -16,8 +16,8 @@ import { EuiBadge, EuiContextMenuPanel, EuiToolTip, + EuiCallOut } from '@elastic/eui'; -import _ from 'lodash'; import { DatePicker } from './date_picker'; import '@algolia/autocomplete-theme-classic'; import { Autocomplete } from './autocomplete'; @@ -82,10 +82,10 @@ export const Search = (props: any) => { stopLive, setIsLiveTailPopoverOpen, liveTailName, + searchError = null, } = props; - + const appLogEvents = tabId.match(APP_ANALYTICS_TAB_ID_REGEX); - const [isSavePanelOpen, setIsSavePanelOpen] = useState(false); const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); @@ -247,6 +247,17 @@ export const Search = (props: any) => { )} + { searchError && searchError.error && ( + + + +

+ {JSON.parse(searchError.message).error.details} +

+
+
+
) + } {flyout} ); diff --git a/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap b/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap index 24c8f5e45..e31ae697b 100644 --- a/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap +++ b/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap @@ -1477,188 +1477,80 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` } } > - - +
- - + className="euiText euiText--extraSmall lnsChart__empty" + data-test-subj="vizWorkspace__noData" + > + +
+ +
+ + + + + + +
+ +

+ + + No results found + + +

+
+ +
+ +
+ + @@ -4307,188 +4199,80 @@ exports[`Utils helper functions renders displayVisualization function 3`] = ` } } > - - +
- - + className="euiText euiText--extraSmall lnsChart__empty" + data-test-subj="vizWorkspace__noData" + > + +
+ +
+ + + + + + +
+ +

+ + + No results found + + +

+
+ +
+ +
+ + @@ -5225,132 +5009,80 @@ exports[`Utils helper functions renders displayVisualization function 4`] = ` } } > - - +
- - + className="euiText euiText--extraSmall lnsChart__empty" + data-test-subj="vizWorkspace__noData" + > + +
+ +
+ + + + + + +
+ +

+ + + No results found + + +

+
+ +
+ +
+ + diff --git a/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx b/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx index b375987bf..b1c2751f5 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx @@ -66,6 +66,7 @@ import { selectFields, updateFields, sortFields } from '../redux/slices/field_sl import { updateTabName } from '../redux/slices/query_tab_slice'; import { selectCountDistribution } from '../redux/slices/count_distribution_slice'; import { selectExplorerVisualization } from '../redux/slices/visualization_slice'; +import { change as changeVizConfig } from '../redux/slices/viualization_config_slice'; import { selectVisualizationConfig, change as changeVisualizationConfig, @@ -490,6 +491,7 @@ export const Explorer = ({ handleQuerySearch(availability); }; + /** * Toggle fields between selected and unselected sets * @param field field to be toggled @@ -821,51 +823,6 @@ export const Explorer = ({ ); }; - const testAntlr = (query: string) => { - const qm = new QueryManager(); - // const pplQueryBuilder = new PPLQueryBuilder(); - - // build query - const res = - qm - .queryBuilder() - .addSource('index') - .addPipe() - .addStats() - .addMetrics([ - { - field: '', - agg_func: 'count' - }, - { - field: 'bytes', - agg_func: 'avg', - alias: 'avg_bytes' - } - ]) - .addBy() - .addGroupBy([{ - field: 'host' - }, - { - field: 'tags' - } - ]) - .getQuery(); - - console.log('built res: ', res); - - // parse query - const parsed_res = - qm - .queryParser() - .parse(query) - .getStats(); - - console.log('parsed res: ', parsed_res); - - }; - const handleQuerySearch = useCallback( async (availability?: boolean) => { @@ -881,10 +838,39 @@ export const Explorer = ({ if (availability !== true) { await updateQueryInStore(tempQuery); } - testAntlr(query[RAW_QUERY]); - fetchData(); + await fetchData(); + + if (selectedContentTabId === TAB_CHART_ID) { + // parse stats section on every search + const qm = new QueryManager(); + const statsTokens = + qm + .queryParser() + .parse(tempQuery) + .getStats(); + + await dispatch( + changeVizConfig({ + tabId, + vizId: curVisId, + data: { + dataConfig: { + metrics: statsTokens.aggregations.map((agg) => ({ + label: agg.function?.value_expression, + name: agg.function?.value_expression, + aggregation: agg.function?.name, + })), + dimensions: statsTokens.groupby?.group_fields?.map((agg) => ({ + label: agg.name ?? '', + name: agg.name ?? '', + })), + }, + }, + }) + ); + } }, - [tempQuery, query[RAW_QUERY]] + [tempQuery, query, selectedContentTabId] ); const handleQueryChange = async (newQuery: string) => setTempQuery(newQuery); @@ -1197,6 +1183,10 @@ export const Explorer = ({ explorerVisualizations, setToast, pplService, + handleQuerySearch, + handleQueryChange, + setTempQuery, + fetchData, explorerFields, explorerData, http, @@ -1236,6 +1226,7 @@ export const Explorer = ({ stopLive={stopLive} setIsLiveTailPopoverOpen={setIsLiveTailPopoverOpen} liveTailName={liveTailNameRef.current} + searchError={explorerVisualizations} /> ({ + name, + label: name, + type, +}); export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => { const dispatch = useDispatch(); - const { tabId, curVisId, changeVisualizationConfig } = useContext(TabContext); + const { tabId, handleQuerySearch, handleQueryChange, setTempQuery, fetchData } = useContext( + TabContext + ); + const explorerVisualizationConfigs = useSelector(selectVisualizationConfig)[tabId]; const { data } = visualizations; - const { userConfigs } = data; - const { data: vizData = {}, metadata: { fields = [] } = {} } = data?.rawVizData; - - const initialConfigEntry = { - label: '', - aggregation: '', - custom_label: '', - name: '', - side: 'right', - type: '', - }; - + const { + indexFields: { availableFields }, + } = data; const [configList, setConfigList] = useState({}); useEffect(() => { - if (userConfigs && userConfigs.dataConfig && userConfigs.dataConfig.valueOptions) { + if ( + data.rawVizData?.[visualizations.vis.name] && + data.rawVizData?.[visualizations.vis.name].dataConfig + ) { + setConfigList((staleState) => { + return { + ...staleState, + ...data.rawVizData[visualizations.vis.name].dataConfig, + }; + }); + } else if (some(SPECIAL_RENDERING_VIZS, (visType) => visType === visualizations.vis.name)) { + // any vis that doesn't conform normal metrics/dimensions data confiurations setConfigList({ - ...userConfigs.dataConfig.valueOptions, + ...DEFAULT_DATA_CONFIGS[visualizations.vis.name], }); + } else { + // default + const qm = new QueryManager(); + const statsTokens = qm.queryParser().parse(data.query.rawQuery).getStats(); + if (!statsTokens) { + setConfigList({ + metrics: [], + dimensions: [], + }); + } else { + const fieldInfo = statsTokens.groupby?.span?.span_expression?.field; + setConfigList({ + metrics: statsTokens.aggregations.map((agg) => ({ + alias: agg.alias, + label: agg.function?.value_expression, + name: agg.function?.value_expression, + aggregation: agg.function?.name, + })), + dimensions: statsTokens.groupby?.group_fields?.map((agg) => ({ + label: agg.name ?? '', + name: agg.name ?? '', + })), + span: { + time_field: statsTokens.groupby?.span?.span_expression?.field + ? [getStandardedOuiField(fieldInfo, 'timestamp')] + : [], + interval: statsTokens.groupby?.span?.span_expression?.literal_value ?? '0', + unit: statsTokens.groupby?.span?.span_expression?.time_unit + ? [getStandardedOuiField(statsTokens.groupby?.span?.span_expression?.time_unit)] + : [], + }, + }); + } } - }, [userConfigs?.dataConfig?.valueOptions, visualizations.vis.name]); + }, [ + data.defaultAxes, + data.rawVizData?.[visualizations.vis.name]?.dataConfig, + visualizations.vis.name, + ]); const updateList = (value: string, index: number, name: string, field: string) => { - let list = { ...configList }; + const list = { ...configList }; let listItem = { ...list[name][index] }; listItem = { ...listItem, @@ -79,7 +162,7 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => const updateHistogramConfig = (configName: string, fieldName: string, value: string) => { const list = { ...configList }; - let listItem = { ...list[configName][0] }; + const listItem = { ...list[configName][0] }; listItem[fieldName] = value; const updatedList = { ...list, @@ -101,35 +184,47 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => setConfigList(updatedList); }; - const updateChart = () => { - dispatch( - changeVisualizationConfig({ - tabId, - vizId: curVisId, - data: { - ...userConfigs, - dataConfig: { - ...userConfigs.dataConfig, - valueOptions: { - dimensions: configList.dimensions, - metrics: configList.metrics, + const updateChart = (updatedConfigList = configList) => { + const qm = new QueryManager(); + const statsTokens = qm.queryParser().parse(data.query.rawQuery).getStats(); + const newQuery = qm + .queryBuilder() + .build(data.query.rawQuery, composeAggregations(updatedConfigList, statsTokens)); + + batch(async () => { + await handleQueryChange(newQuery); + await dispatch( + changeQuery({ + tabId, + query: { + ...data.query, + [RAW_QUERY]: newQuery, + }, + }) + ); + await fetchData(); + await dispatch( + changeVizConfig({ + tabId, + vizId: visualizations.vis.name, + data: { + dataConfig: { + metrics: updatedConfigList.metrics, + dimensions: updatedConfigList.dimensions, + breakdowns: updatedConfigList.breakdowns, + span: updatedConfigList.span, }, }, - }, - }) - ); + }) + ); + }); }; const isPositionButtonVisible = (sectionName: string) => - sectionName === 'metrics' && - (visualizations.vis.name === visChartTypes.Line || - visualizations.vis.name === visChartTypes.Scatter); + sectionName === 'metrics' && visualizations.vis.name === visChartTypes.Line; const getOptionsAvailable = (sectionName: string) => { - let selectedFields = {}; - for (const key in configList) { - configList[key] && configList[key].forEach((field) => (selectedFields[field.label] = true)); - } + const selectedFields = {}; const unselectedFields = fieldOptionList.filter((field) => !selectedFields[field.label]); return sectionName === 'metrics' ? unselectedFields @@ -138,106 +233,133 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => : unselectedFields; }; - const getCommonUI = (lists, sectionName: string) => - lists && - lists.map((singleField, index: number) => ( - -
-
- {sectionName === 'dimensions' && visualizations.vis.name === visChartTypes.HeatMap && ( - -
{index === 0 ? 'X-Axis' : 'Y-Axis'}
-
- )} - - {sectionName == 'metrics' && ( - - handleServiceRemove(index, sectionName)} - /> - - ) - } - > - - updateList(e.length > 0 ? e[0].label : '', index, sectionName, 'aggregation') - } - /> - - )} - - - updateList(e.length > 0 ? e[0].label : '', index, sectionName, 'label') - } - /> - - - - updateList(e.target.value, index, sectionName, 'custom_label')} - aria-label="Use aria labels when no actual label is in use" - /> - - - {isPositionButtonVisible(sectionName) && ( - - updateList(id, index, sectionName, 'side')} - /> - - )} + const getCommonUI = (lists, sectionName: string) => { + return ( + <> + {Array.isArray(lists) && + lists.map((singleField, index: number) => ( + <> +
+
+ {sectionName === 'dimensions' && + visualizations.vis.name === visChartTypes.HeatMap && ( + +
{index === 0 ? 'X-Axis' : 'Y-Axis'}
+
+ )} + + {sectionName === 'metrics' && ( + + handleServiceRemove(index, sectionName)} + /> + + ) + } + > + + updateList( + e.length > 0 ? e[0].label : '', + index, + sectionName, + 'aggregation' + ) + } + /> + + )} + + handleServiceRemove(index, sectionName)} + /> + + ) + } + > + + updateList(e.length > 0 ? e[0].label : '', index, sectionName, 'label') + } + /> + + + + updateList(e.target.value, index, sectionName, 'custom_label') + } + aria-label="Use aria labels when no actual label is in use" + /> + + {isPositionButtonVisible(sectionName) && ( + + + updateList(id, index, sectionName, 'side') + } + /> + + )} + + +
+
- {visualizations.vis.name !== visChartTypes.HeatMap && lists.length - 1 === index && ( - - handleServiceAdd(sectionName)} - disabled={ - sectionName === 'dimensions' && visualizations.vis.name === visChartTypes.Line - } - > - Add - - - )} -
-
-
- -
- )); + + ))} + {visualizations.vis.name !== visChartTypes.HeatMap && ( + + handleServiceAdd(sectionName)} + disabled={ + sectionName === 'dimensions' && visualizations.vis.name === visChartTypes.Line + } + > + Add + + + )} + + ); + }; const getNumberField = (type: string) => ( <> @@ -259,6 +381,124 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => ); + const getBreakDownFields = useCallback( + (configList) => { + return configList.dimensions; + }, + [configList.dimensions] + ); + + const Breakdowns = useMemo(() => { + return ( + <> +
+
+ + + { + setConfigList((staleState) => { + return { + ...staleState, + breakdowns: fields, + }; + }); + }} + /> + + +
+
+ + ); + }, [configList.dimensions, configList.breakdowns]); + + const DateHistogram = useMemo(() => { + return ( + <> +
+
+ + + idxField.type === 'timestamp') + .map((field) => ({ ...field, label: field.name }))} + selectedOptions={ + configList.span?.time_field ? [...configList.span?.time_field] : [] + } + onChange={(field) => { + setConfigList((staleState) => { + return { + ...staleState, + span: { + ...staleState.span, + time_field: field, + }, + }; + }); + }} + /> + + + { + setConfigList((staleState) => { + return { + ...staleState, + span: { + ...staleState.span, + interval: e.target.value, + }, + }; + }); + }} + aria-label="Use aria labels when no actual label is in use" + /> + + + { + return { + ...option, + label: option.text, + }; + })} + selectedOptions={configList.span?.unit ? [...configList.span?.unit] : []} + onChange={(unit) => { + setConfigList((staleState) => { + return { + ...staleState, + span: { + ...staleState.span, + unit, + }, + }; + }); + }} + /> + + +
+
+ + ); + }, [availableFields, configList.span]); + return ( <> @@ -267,16 +507,26 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => {visualizations.vis.name !== visChartTypes.Histogram ? ( <> + +

Series

+
+ + {getCommonUI(configList.metrics, 'metrics')} +

Dimensions

+ {getCommonUI(configList.dimensions, 'dimensions')} - -

Metrics

+

Date Histogram

- {getCommonUI(configList.metrics, 'metrics')} + {DateHistogram} + {/* +

Breakdowns

+
+ {Breakdowns} */} ) : ( <> @@ -292,11 +542,12 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => {getNumberField('bucketOffset')} )} + updateChart()} size="s" > Update chart diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx index 2aa7c062d..2e51c1a7e 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx @@ -52,7 +52,8 @@ export const ExplorerVisualizations = ({ const { data, vis } = visualizations; const { data: vizData = {}, metadata: { fields = [] } = {} } = data?.rawVizData; - const fieldOptionList = fields.map((field) => { + const fieldOptionList = explorerFields.availableFields.map((field) => { + // const fieldOptionList = fields.map((field) => { return { ...field, label: field.name }; }); diff --git a/dashboards-observability/public/components/event_analytics/hooks/use_fetch_visualizations.ts b/dashboards-observability/public/components/event_analytics/hooks/use_fetch_visualizations.ts index 866921201..446bcf229 100644 --- a/dashboards-observability/public/components/event_analytics/hooks/use_fetch_visualizations.ts +++ b/dashboards-observability/public/components/event_analytics/hooks/use_fetch_visualizations.ts @@ -37,7 +37,8 @@ export const useFetchVisualizations = ({ const fetchVisualizations = async ( { query }: { query: string }, format: string, - handler: (res: any) => void + successHandler: (res: any) => void, + errorHandler: (error: any) => void ) => { setIsVisLoading(true); @@ -45,16 +46,16 @@ export const useFetchVisualizations = ({ .fetch({ query, format, + }, (error) => { + errorHandler(error); + setIsVisLoading(false); }) .then((res: any) => { - handler(res); - }) - .catch((err: any) => { - console.error(err); - }) - .finally(() => { + if (res && res.status === 200) { + successHandler(res); + } setIsVisLoading(false); - }); + }) }; const getCountVisualizations = (interval: string) => { @@ -74,7 +75,8 @@ export const useFetchVisualizations = ({ data: res, }) ); - } + }, + (error: Error) => {} ); }; @@ -118,6 +120,14 @@ export const useFetchVisualizations = ({ }) ); }); + }, + (error: any) => { + dispatch( + renderExplorerVis({ + tabId: requestParams.tabId, + data: error.body, + }) + ); } ); }; diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap index 34b400f3a..9a88fe43b 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap @@ -533,9 +533,7 @@ exports[`Bar component Renders bar component 1`] = ` } } > - + { const DEFAULT_LABEL_SIZE = 10; - const { vis } = visualizations; const { - data, - metadata: { fields }, - } = visualizations.data.rawVizData; + data: { + defaultAxes, + indexFields, + query, + rawVizData: { + data: queriedVizData, + metadata: { fields }, + }, + userConfigs, + }, + vis: visMetaData, + }: IVisualizationContainerProps = visualizations; const lastIndex = fields.length - 1; - const { - dataConfig = {}, - layoutConfig = {}, - availabilityConfig = {}, - } = visualizations?.data?.userConfigs; - const dataConfigTab = - visualizations.data?.rawVizData?.bar?.dataConfig && - visualizations.data.rawVizData.bar.dataConfig; - const xaxis = dataConfig?.valueOptions?.dimensions - ? dataConfig.valueOptions.dimensions.filter((item) => item.label) - : []; - const yaxis = dataConfig?.valueOptions?.metrics - ? dataConfig.valueOptions.metrics.filter((item) => item.label) - : []; - const barOrientation = dataConfig?.chartStyles?.orientation || vis.orientation; - const isVertical = barOrientation === vis.orientation; - const tooltipMode = - dataConfig?.tooltipOptions?.tooltipMode !== undefined - ? dataConfig.tooltipOptions.tooltipMode - : 'show'; - const tooltipText = - dataConfig?.tooltipOptions?.tooltipText !== undefined - ? dataConfig.tooltipOptions.tooltipText - : 'all'; - let bars, valueSeries, valueForXSeries; - - if (!isEmpty(xaxis) && !isEmpty(yaxis)) { - valueSeries = isVertical ? [...yaxis] : [...xaxis]; - valueForXSeries = isVertical ? [...xaxis] : [...yaxis]; - } else { - return ; - } - - const tickAngle = dataConfig?.chartStyles?.rotateBarLabels || vis.labelangle; - const lineWidth = dataConfig?.chartStyles?.lineWidth || vis.linewidth; + const { dataConfig = {}, layoutConfig = {}, availabilityConfig = {} } = userConfigs; + console.log('bar dataConfig: ', dataConfig); + + if ( + isEmpty(queriedVizData) || + !Array.isArray(dataConfig.dimensions) || + !Array.isArray(dataConfig.metrics) || + (dataConfig.breakdowns && !Array.isArray(dataConfig.breakdowns)) + ) + return ; + + /** + * determine stylings + */ + const barOrientation = dataConfig.chartStyles?.orientation || visMetaData.orientation; + const isVertical = barOrientation === visMetaData.orientation; + + const tickAngle = dataConfig?.chartStyles?.rotateBarLabels || visMetaData.labelangle; + const lineWidth = dataConfig?.chartStyles?.lineWidth || visMetaData.linewidth; const fillOpacity = dataConfig?.chartStyles?.fillOpacity !== undefined ? dataConfig?.chartStyles?.fillOpacity / FILLOPACITY_DIV_FACTOR - : vis.fillOpacity / FILLOPACITY_DIV_FACTOR; - const barWidth = 1 - (dataConfig?.chartStyles?.barWidth || vis.barwidth); - const groupWidth = 1 - (dataConfig?.chartStyles?.groupWidth || vis.groupwidth); + : visMetaData.fillOpacity / FILLOPACITY_DIV_FACTOR; + const barWidth = 1 - (dataConfig?.chartStyles?.barWidth || visMetaData.barwidth); + const groupWidth = 1 - (dataConfig?.chartStyles?.groupWidth || visMetaData.groupwidth); const showLegend = !( - dataConfig?.legend?.showLegend && dataConfig.legend.showLegend !== vis.showlegend + dataConfig?.legend?.showLegend && dataConfig.legend.showLegend !== visMetaData.showlegend ); - const legendPosition = dataConfig?.legend?.position || vis.legendposition; + const legendPosition = dataConfig?.legend?.position || visMetaData.legendposition; visualizations.data?.rawVizData?.dataConfig?.metrics ? visualizations.data?.rawVizData?.dataConfig?.metrics : []; @@ -77,103 +70,77 @@ export const Bar = ({ visualizations, layout, config }: any) => { ?.color) || PLOTLY_COLOR[index % PLOTLY_COLOR.length]; - const prepareData = (valueForXSeries) => { - return valueForXSeries - .map((dimension: any) => data[dimension.label]) - ?.reduce((prev, cur) => { - return prev.map((i, j) => `${i}, ${cur[j]}`); - }); - }; + let bars, valueSeries, valueForXSeries; - const createNameData = (nameData, metricName: string) => - nameData?.map((el) => el + ',' + metricName); - - // for multiple dimention and metrics with timestamp - if (valueForXSeries.some((e) => e.type === 'timestamp')) { - const nameData = - valueForXSeries.length > 1 - ? valueForXSeries - .filter((item) => item.type !== 'timestamp') - .map((dimension) => data[dimension.label]) - .reduce((prev, cur) => { - return prev.map((i, j) => `${i}, ${cur[j]}`); - }) - : []; - - let dimensionsData = valueForXSeries - .filter((item) => item.type === 'timestamp') - .map((dimension) => data[dimension.label]) - .flat(); - - bars = valueSeries - .map((field: any, index: number) => { - const selectedColor = getSelectedColorTheme(field, index); - return dimensionsData.map((dimension: any, j: number) => { - return { - x: isVertical - ? !isEmpty(xaxis) - ? dimension - : data[fields[lastIndex].name] - : data[field.label], - y: isVertical ? data[field.label][j] : dimensionsData, // TODO: orinetation - type: vis.type, - marker: { - color: hexToRgb(selectedColor, fillOpacity), - line: { - color: selectedColor, - width: lineWidth, - }, - }, - name: nameData.length > 0 ? createNameData(nameData, field.label)[j] : field.label, // dimensionsData[index]+ ',' + field.label, - hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText, - orientation: barOrientation, - }; - }); - }) - .flat(); - - // merging x, y for same names - bars = Object.values( - bars?.reduce((acc, { x, y, name, type, marker, orientation, hoverinfo }) => { - acc[name] = acc[name] || { x: [], y: [], name, type, marker, orientation, hoverinfo }; - acc[name].x.push(x); - acc[name].y.push(y); - - return acc; - }, {}) - ); - } else { - // for multiple dimention and metrics without timestamp - const dimensionsData = prepareData(valueForXSeries); - const metricsData = prepareData(valueSeries); - bars = valueSeries.map((field: any, index: number) => { - const selectedColor = getSelectedColorTheme(field, index); - return { - x: isVertical - ? !isEmpty(xaxis) - ? dimensionsData - : data[fields[lastIndex].name] - : data[field.name], - y: isVertical ? data[field.name] : metricsData, // TODO: add if isempty true - type: vis.type, - marker: { - color: hexToRgb(selectedColor, fillOpacity), - line: { - color: selectedColor, - width: lineWidth, - }, + /** + * determine x axis + */ + const xaxes = useMemo(() => { + // breakdown selections + if (dataConfig.breakdowns) { + return [ + ...dataConfig.dimensions.filter( + (dimension) => + !some(dataConfig.breakdowns, (breakdown) => breakdown.label === dimension.label) + ), + ]; + } + + // span selection + const timestampField = find(fields, (field) => field.type === 'timestamp'); + if (dataConfig.span && dataConfig.span.time_field && timestampField) { + return [timestampField, ...dataConfig.dimensions]; + } + + return [...dataConfig.dimensions]; + }, [dataConfig.dimensions, dataConfig.breakdowns]); + + /** + * determine y axis + */ + const yaxes = useMemo(() => { + return Array.isArray(dataConfig.metrics) ? [...dataConfig.metrics] : []; + }, [dataConfig.metrics]); + + /** + * prepare data for visualization, map x-xais to y-xais + */ + const chartAxis = useMemo(() => { + return Array.isArray(queriedVizData[`${yaxes[0].aggregation}(${yaxes[0].name})`]) + ? queriedVizData[`${yaxes[0].aggregation}(${yaxes[0].name})`].map((_, idx) => { + // let combineXaxis = ''; + const xaxisName = xaxes.map((xaxis) => { + return queriedVizData[xaxis.name] && queriedVizData[xaxis.name][idx] + ? queriedVizData[xaxis.name][idx] + : ''; + }); + return xaxisName.join(', '); + }) + : []; + }, [queriedVizData, xaxes, yaxes]); + + bars = yaxes?.map((yMetric, idx) => { + return { + y: isVertical ? queriedVizData[`${yMetric.aggregation}(${yMetric.name})`] : chartAxis, + x: isVertical ? chartAxis : queriedVizData[`${yMetric.aggregation}(${yMetric.name})`], + type: visMetaData.type, + marker: { + color: getSelectedColorTheme(yMetric, idx), + line: { + color: getSelectedColorTheme(yMetric, idx), + width: lineWidth, }, - name: field.name, - hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText, - orientation: barOrientation, - }; - }); - } + }, + name: yMetric.name, + orientation: barOrientation, + }; + }); // If chart has length of result buckets < 16 // then use the LONG_CHART_COLOR for all the bars in the chart const plotlyColorway = - data[fields[lastIndex].name].length < 16 ? PLOTLY_COLOR : [LONG_CHART_COLOR]; + queriedVizData[fields[lastIndex].name].length < 16 ? PLOTLY_COLOR : [LONG_CHART_COLOR]; + const mergedLayout = { colorway: plotlyColorway, ...layout, @@ -208,15 +175,17 @@ export const Bar = ({ visualizations, layout, config }: any) => { const mapToLine = (list: ThresholdUnitType[] | AvailabilityUnitType[], lineStyle: any) => { return list.map((thr: ThresholdUnitType) => { thresholdTraces.x.push( - data[!isEmpty(xaxis) ? xaxis[xaxis.length - 1]?.label : fields[lastIndex].name][0] + queriedVizData[ + !isEmpty(xaxis) ? xaxis[xaxis.length - 1]?.label : fields[lastIndex].name + ][0] ); thresholdTraces.y.push(thr.value * (1 + 0.06)); thresholdTraces.text.push(thr.name); return { type: 'line', - x0: data[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name][0], + x0: queriedVizData[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name][0], y0: thr.value, - x1: last(data[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name]), + x1: last(queriedVizData[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name]), y1: thr.value, name: thr.name || '', opacity: 0.7, @@ -232,13 +201,11 @@ export const Bar = ({ visualizations, layout, config }: any) => { mergedLayout.shapes = [...mapToLine(thresholds, { dash: 'dashdot' }), ...mapToLine(levels, {})]; bars = [...bars, thresholdTraces]; } - const mergedConfigs = useMemo( - () => ({ - ...config, - ...(layoutConfig.config && layoutConfig.config), - }), - [config, layoutConfig.config] - ); + + const mergedConfigs = { + ...config, + ...(layoutConfig.config && layoutConfig.config), + }; return ; }; diff --git a/dashboards-observability/public/services/requests/ppl.ts b/dashboards-observability/public/services/requests/ppl.ts index f9343bc93..4fe252f22 100644 --- a/dashboards-observability/public/services/requests/ppl.ts +++ b/dashboards-observability/public/services/requests/ppl.ts @@ -24,7 +24,7 @@ export default class PPLService { body: JSON.stringify(params), }) .catch((error) => { - console.error(error); + console.error('fetch error: ', error.body); if (errorHandler) errorHandler(error); }); }; From d9f5846cb6fca44902a4d7b194551062df5b62f9 Mon Sep 17 00:00:00 2001 From: ruchika-narang <79983862+ruchika-narang@users.noreply.github.com> Date: Thu, 8 Sep 2022 00:02:08 +0530 Subject: [PATCH 02/20] Bug/color-theme-options: Resolved color theme issue #960 (#971) * Resolved to show only metrics in the dropdown instead of all the fields Signed-off-by: ruchika-narang * Removed unncessary import Signed-off-by: ruchika-narang * Updated snapshots after rebasing with main Signed-off-by: ruchika-narang Signed-off-by: ruchika-narang --- .../config_controls/config_color_theme.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_color_theme.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_color_theme.tsx index c2aa95813..07801e5fe 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_color_theme.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_color_theme.tsx @@ -18,7 +18,7 @@ import { } from '@elastic/eui'; import { isEmpty } from 'lodash'; import { ADD_BUTTON_TEXT } from '../../../../../../../../common/constants/explorer'; -import { NUMERICAL_FIELDS } from '../../../../../../../../common/constants/shared'; +import { visChartTypes } from '../../../../../../../../common/constants/shared'; export const ConfigColorTheme = ({ visualizations, @@ -27,8 +27,10 @@ export const ConfigColorTheme = ({ handleConfigChange, sectionName = 'Color theme', }: any) => { - const { data } = visualizations; + const { data, vis } = visualizations; + const { defaultAxes = {} } = data; const { data: vizData = {}, metadata: { fields = [] } = {} } = data?.rawVizData; + const { dataConfig = {} } = data?.userConfigs; const addButtonText = ADD_BUTTON_TEXT; const getColorThemeRow = () => ({ ctid: htmlIdGenerator('ct')(), @@ -36,7 +38,12 @@ export const ConfigColorTheme = ({ color: '#FC0505', }); - const options = fields.map((item) => ({ + const options = (dataConfig?.valueOptions?.metrics && dataConfig.valueOptions.metrics.length !== 0 + ? dataConfig.valueOptions.metrics + : vis.name === visChartTypes.Histogram + ? defaultAxes.yaxis ?? [] + : fields + ).map((item) => ({ ...item, label: item.name, })); From 67e7b5edec70c08cfde6b2e6803581ea6f881689 Mon Sep 17 00:00:00 2001 From: nidhisinghai <103416937+nidhisinghai@users.noreply.github.com> Date: Thu, 8 Sep 2022 00:02:37 +0530 Subject: [PATCH 03/20] cypress box plot (#983) * cypress box plot Signed-off-by: nidhisinghai * cypress box plot new Signed-off-by: nidhisinghai Signed-off-by: nidhisinghai Co-authored-by: nidhisinghai --- .../11_horizontalBar_chart.spec.js | 0 .../12_boxPlot_chart.spec.js | 248 ++++++++++++++++++ .../9_coordinateMap_chart.spec.js} | 0 3 files changed, 248 insertions(+) rename dashboards-observability/.cypress/integration/{VisualizationCharts.spec.js => VisualizationCharts}/11_horizontalBar_chart.spec.js (100%) create mode 100644 dashboards-observability/.cypress/integration/VisualizationCharts/12_boxPlot_chart.spec.js rename dashboards-observability/.cypress/integration/{VisualizationsCharts/9_coordinateMap.spec.js => VisualizationCharts/9_coordinateMap_chart.spec.js} (100%) diff --git a/dashboards-observability/.cypress/integration/VisualizationCharts.spec.js/11_horizontalBar_chart.spec.js b/dashboards-observability/.cypress/integration/VisualizationCharts/11_horizontalBar_chart.spec.js similarity index 100% rename from dashboards-observability/.cypress/integration/VisualizationCharts.spec.js/11_horizontalBar_chart.spec.js rename to dashboards-observability/.cypress/integration/VisualizationCharts/11_horizontalBar_chart.spec.js diff --git a/dashboards-observability/.cypress/integration/VisualizationCharts/12_boxPlot_chart.spec.js b/dashboards-observability/.cypress/integration/VisualizationCharts/12_boxPlot_chart.spec.js new file mode 100644 index 000000000..d4dae465e --- /dev/null +++ b/dashboards-observability/.cypress/integration/VisualizationCharts/12_boxPlot_chart.spec.js @@ -0,0 +1,248 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { + delay, + TEST_QUERIES, + querySearch, + landOnEventVisualizations, +} from '../../utils/event_constants'; + +const numberOfWindow = 4; +const labelSize = 20; +const rotateLevel = 45; +const boxSize = 7; +const boxSizeUpdated = 1; +const markerSize = 5; +const markerSizeUpdated = 1; +const jitter = 2; +const jitterUpdated = .1; +const fillOpacity = 10; +const fillOpacityUpdated = 50; +const numberOfColor = 24; + +const renderBoxPlot = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[4].query, TEST_QUERIES[4].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Box plot') + .type('{enter}'); +}; + +describe('Render box plot and verify default behaviour ', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot and verify by default the data gets render', () => { + cy.get('.xy').should('exist'); + }); + + it('Render box plot and verify you see data configuration panel and chart panel', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Dimensions').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Metrics').should('exist'); + cy.get('.euiIEFlexWrapFix').contains('Panel options').click(); + cy.get('.euiIEFlexWrapFix').contains('Legend').click(); + cy.get('.euiIEFlexWrapFix').contains('Chart styles').click(); + cy.get('.euiIEFlexWrapFix').contains('Color Theme').click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); + + it('Render box plot and verify the data configuration panel and chart panel are collapsable', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); +}); + +describe('Render box plot for data configuration panel', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot and verify data config panel', () => { + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(0).should('contain', 'tags'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(1).should('contain', 'count()'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(2).should('contain', 'avg(bytes)'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(3).should('contain', 'host'); + }); + + it('Render box plot and verify data config panel restrict user to select a duplicate field on dimension field', () => { + cy.get('[data-test-subj="comboBoxInput"]').eq(1).click(); + cy.get('[data-test-subj="comboBoxClearButton"]').eq(0).click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(1).click(); + cy.get('.euiComboBoxOption__content').should('have.length', 1); + cy.get('.euiComboBoxOption__content').contains('tags'); + }); + + it('Render box plot and verify data config panel Restrict user to select a duplicate field on Metrics field', () => { + cy.get('.euiText.euiText--extraSmall').eq(1).click(); + cy.get('[data-test-subj="comboBoxClearButton"]').eq(1).click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(3).click(); + cy.get('.euiComboBoxOption__content').should('not.contain', 'tags'); + cy.get('.euiComboBoxOption__content').should('have.length', 2); + }); + + it('Render box plot and verify data config panel no result found if metric is missing', () => { + cy.get('.euiText.euiText--extraSmall').eq(0).click(); + cy.get('.euiText.euiText--extraSmall').eq(1).click(); + cy.get('[data-test-subj="comboBoxClearButton"]').eq(1).click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(0).click(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.euiTextColor.euiTextColor--subdued').contains('No results found').should('exist'); + cy.get('[data-test-subj="comboBoxInput"]').eq(3).click(); + cy.get('.euiComboBoxOption__content').contains('avg(bytes)').click(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.main-svg').contains('No results found').should('not.exist'); + }); +}); + +describe('Render box plot for panel options', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot and verify the title gets updated according to user input ', () => { + cy.get('input[name="title"]').type('box plot'); + cy.get('textarea[name="description"]').should('exist').click(); + cy.get('.gtitle').contains('box plot').should('exist'); + }); +}); + +describe('Render box plot for legend', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot and verify legends for Show and Hidden', () => { + cy.get('[data-text="Show"]').should('have.text', 'Show'); + cy.get('[data-text="Show"] [data-test-subj="show"]').should('have.attr', 'checked'); + cy.get('[data-text="Hidden"]').should('have.text', 'Hidden').click(); + cy.get('[data-text="Hidden"] [data-test-subj="hidden"]').should('not.have.attr', 'checked'); + cy.get('[data-unformatted="max(bytes)"]').should('not.exist'); + }); + + it('Render box plot and verify legends for position Right and Bottom', () => { + cy.get('[data-text="Right"]').should('have.text', 'Right'); + cy.get('[data-text="Right"] [data-test-subj="v"]').should('have.attr', 'checked'); + cy.get('[data-text="Bottom"]').should('have.text', 'Bottom').click(); + cy.get('[data-text="Bottom"] [data-test-subj="h"]').should('not.have.attr', 'checked'); + }); +}); + +describe('Render box plot for chart style options', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot and increase Label Size ', () => { + cy.get('[data-test-subj="valueFieldNumber"]').click().type(labelSize); + cy.get('textarea[name="description"]').should('exist').click(); + cy.get('[data-unformatted="count()"]').should('have.css', 'font-size', '20px'); + }); + + it('Render box plot and "Rotate box labels"', () => { + cy.get('input[type="range"]') + .eq(0) + .then(($el) => $el[0].stepUp(rotateLevel)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(0).should('have.value', rotateLevel); + }); + + it('Render box plot and change "Box gap"', () => { + cy.get('input[type="range"]') + .eq(1) + .then(($el) => $el[0].stepUp(boxSize)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(1).should('have.value', boxSizeUpdated); + }); + + it('Render box plot and change "Marker size"', () => { + cy.get('input[type="range"]') + .eq(2) + .then(($el) => $el[0].stepDown(markerSize)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(2).should('have.value', markerSizeUpdated); + }); + + it('Render box plot and change "Jitter"', () => { + cy.get('input[type="range"]') + .eq(3) + .then(($el) => $el[0].stepDown(jitter)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(3).should('have.value', jitterUpdated); + }); + + it('Render box plot and change "Fill opacity"', () => { + cy.get('input[type="range"]') + .eq(4) + .then(($el) => $el[0].stepUp(fillOpacity)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(4).should('have.value', fillOpacityUpdated); + }); +}); + +describe('Render box plot for color theme', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot and "Add color theme"', () => { + cy.get('.euiButton__text').contains('+ Add color theme').click(); + cy.wait(delay); + cy.get('[data-test-subj="comboBoxInput"]').eq(9).click(); + cy.get('.euiComboBoxOption__content').contains('avg(bytes)').click(); + cy.get('.points').find('path[style*="rgb(252, 5, 5)"]').should('have.length', numberOfColor); + }); +}); + +describe('Render box plot and verify if reset works properly', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot with all feild data then click on reset and verify reset works properly', () => { + cy.get('input[placeholder="Title"]').type('box plot'); + cy.get('textarea[placeholder="Description"]').type('Description For box plot'); + cy.get('.euiButton__text').contains('Hidden').click(); + cy.get('.euiButton__text').contains('Horizontal').click(); + cy.get('[data-test-subj="valueFieldNumber"]').click().type(labelSize); + cy.get('input[type="range"]') + .eq(0) + .then(($el) => $el[0].stepUp(rotateLevel)) + .trigger('change'); + cy.get('input[type="range"]') + .eq(1) + .then(($el) => $el[0].stepUp(boxSize)) + .trigger('change'); + cy.get('input[type="range"]') + .eq(2) + .then(($el) => $el[0].stepDown(markerSize)) + .trigger('change'); + cy.get('input[type="range"]') + .eq(3) + .then(($el) => $el[0].stepUp(jitter)) + .trigger('change'); + cy.get('input[type="range"]') + .eq(4) + .then(($el) => $el[0].stepUp(fillOpacity)) + .trigger('change'); + cy.get('.euiButtonEmpty__text').contains('Reset').click(); + cy.get('input[placeholder="Title"]').should('not.have.value', 'box plot'); + cy.get('textarea[placeholder="Description"]').should( + 'not.have.value', + 'Description For box plot' + ); + cy.get('[data-text="Show"] [data-test-subj="show"]').should('have.attr', 'checked'); + cy.get('[data-text="Right"] [data-test-subj="v"]').should('have.attr', 'checked'); + cy.get('[data-text="Vertical"] [data-test-subj="v"]').should('have.attr', 'checked'); + cy.get('[data-text="Overlay"] [data-test-subj="overlay"]').should('have.attr', 'checked'); + cy.get('[data-test-subj="valueFieldNumber"]').should('have.value', ''); + }); +}); diff --git a/dashboards-observability/.cypress/integration/VisualizationsCharts/9_coordinateMap.spec.js b/dashboards-observability/.cypress/integration/VisualizationCharts/9_coordinateMap_chart.spec.js similarity index 100% rename from dashboards-observability/.cypress/integration/VisualizationsCharts/9_coordinateMap.spec.js rename to dashboards-observability/.cypress/integration/VisualizationCharts/9_coordinateMap_chart.spec.js From d590d106f8d416ac785dadffe700f7905ac62fd6 Mon Sep 17 00:00:00 2001 From: SivaprasadAluri <110654651+SivaprasadAluri@users.noreply.github.com> Date: Thu, 8 Sep 2022 00:02:55 +0530 Subject: [PATCH 04/20] fix: reset fontSize on click of reset (#986) Signed-off-by: SivaprasadAluri Signed-off-by: SivaprasadAluri --- .../__snapshots__/config_panel.test.tsx.snap | 4 +- .../config_bar_chart_styles.tsx | 179 +++++++++--------- .../config_controls/config_number_input.tsx | 9 +- 3 files changed, 105 insertions(+), 87 deletions(-) diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap index 9d76f4d7a..0ec47e963 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap @@ -13204,8 +13204,8 @@ exports[`Config panel component Renders config panel with visualization data 1`] /> @@ -13233,6 +13233,7 @@ exports[`Config panel component Renders config panel with visualization data 1`] onBlur={[Function]} onChange={[Function]} placeholder="auto" + value="" > { - const { data } = visualizations; - const { data: vizData = {}, metadata: { fields = [] } = {} } = data?.rawVizData; + const { data } = visualizations; + const { data: vizData = {}, metadata: { fields = [] } = {} } = data?.rawVizData; - const handleConfigurationChange = useCallback( - (stateFieldName) => { - return (changes) => { - handleConfigChange({ - ...vizState, - [stateFieldName]: changes, - }); - }; - }, - [handleConfigChange, vizState] - ); + const handleConfigurationChange = useCallback( + (stateFieldName) => { + return (changes) => { + handleConfigChange({ + ...vizState, + [stateFieldName]: changes, + }); + }; + }, + [handleConfigChange, vizState] + ); - /* To update the schema options based on current style mode selection */ - const currentSchemas = useMemo(() => { - if (vizState?.orientation === 'h') { - return schemas.filter((schema: IConfigPanelOptionSection) => schema.mapTo !== 'rotateBarLabels'); - } - return schemas; - }, [vizState]); + /* To update the schema options based on current style mode selection */ + const currentSchemas = useMemo(() => { + if (vizState?.orientation === 'h') { + return schemas.filter( + (schema: IConfigPanelOptionSection) => schema.mapTo !== 'rotateBarLabels' + ); + } + return schemas; + }, [vizState]); - const dimensions = useMemo(() => - currentSchemas.map((schema: IConfigPanelOptionSection, index: number) => { - let params = { - title: schema.name, - vizState, - ...schema.props, - } - const DimensionComponent = schema.component || ButtonGroupItem; + const dimensions = useMemo( + () => + currentSchemas + .map((schema: IConfigPanelOptionSection, index: number) => { + let params = { + title: schema.name, + vizState, + ...schema.props, + }; + const DimensionComponent = schema.component || ButtonGroupItem; - const createDimensionComponent = (dimProps) => ( - - - - - ) - if (schema.eleType === 'buttons') { - params = { - ...params, - legend: schema.name, - groupOptions: schema?.props?.options.map((btn: { name: string }) => ({ ...btn, label: btn.name })), - idSelected: vizState[schema.mapTo] || schema?.props?.defaultSelections[0]?.id, - handleButtonChange: handleConfigurationChange(schema.mapTo), - }; - return createDimensionComponent(params); - } - if (schema.eleType === 'input') { - params = { - title: schema.name, - currentValue: vizState[schema.mapTo] || '', - handleInputChange: handleConfigurationChange(schema.mapTo), - vizState, - ...schema.props, - }; - return createDimensionComponent(params); - } - if (schema.eleType === 'slider') { - params = { - ...params, - minRange: schema?.props?.min || 0, - maxRange: schema?.props?.max || 100, - step: schema?.props?.step || 1, - currentRange: vizState[schema.mapTo] || schema?.defaultState, - ticks: schema?.props?.ticks, - showTicks: schema?.props?.showTicks || false, - handleSliderChange: handleConfigurationChange(schema.mapTo), - }; - return createDimensionComponent(params); - } - }).filter(item => item) - , [schemas, vizState, handleConfigurationChange]); + const createDimensionComponent = (dimProps) => ( + + + + + ); + if (schema.eleType === 'buttons') { + params = { + ...params, + legend: schema.name, + groupOptions: schema?.props?.options.map((btn: { name: string }) => ({ + ...btn, + label: btn.name, + })), + idSelected: vizState[schema.mapTo] || schema?.props?.defaultSelections[0]?.id, + handleButtonChange: handleConfigurationChange(schema.mapTo), + }; + return createDimensionComponent(params); + } + if (schema.eleType === 'input') { + params = { + title: schema.name, + numValue: vizState[schema.mapTo] || '', + handleInputChange: handleConfigurationChange(schema.mapTo), + vizState, + ...schema.props, + }; + return createDimensionComponent(params); + } + if (schema.eleType === 'slider') { + params = { + ...params, + minRange: schema?.props?.min || 0, + maxRange: schema?.props?.max || 100, + step: schema?.props?.step || 1, + currentRange: vizState[schema.mapTo] || schema?.defaultState, + ticks: schema?.props?.ticks, + showTicks: schema?.props?.showTicks || false, + handleSliderChange: handleConfigurationChange(schema.mapTo), + }; + return createDimensionComponent(params); + } + }) + .filter((item) => item), + [schemas, vizState, handleConfigurationChange] + ); - return ( - - {dimensions} - - ); + return ( + + {dimensions} + + ); }; diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_number_input.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_number_input.tsx index 79cb59a31..f9d283493 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_number_input.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_number_input.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { EuiFieldNumber, EuiTitle, EuiSpacer, htmlIdGenerator } from '@elastic/eui'; interface InputFieldProps { @@ -19,6 +19,13 @@ export const InputFieldItem: React.FC = ({ }) => { const [fieldValue, setFieldValue] = useState(numValue); + useEffect(() => { + setFieldValue(''); + if (numValue !== undefined || numValue !== '') { + setFieldValue(numValue); + } + }, [numValue]); + return ( <> From e755d2b09b524cc6dd1edd84d5e2dff7bdef7847 Mon Sep 17 00:00:00 2001 From: Pratibha <103417380+pratibhapandey16@users.noreply.github.com> Date: Thu, 8 Sep 2022 00:03:09 +0530 Subject: [PATCH 05/20] Cypress automation for Logs view (#995) * Added cypress scripts for Logs view Signed-off-by: Pratibha Pandey * Added screenshots files Signed-off-by: Pratibha Pandey * Added snapshots files Signed-off-by: Pratibha Pandey Signed-off-by: Pratibha Pandey --- .../VisualizationCharts/13_logsView.spec.js | 241 ++++++++++++++++++ .../.cypress/utils/event_constants.js | 121 +++++---- 2 files changed, 318 insertions(+), 44 deletions(-) create mode 100644 dashboards-observability/.cypress/integration/VisualizationCharts/13_logsView.spec.js diff --git a/dashboards-observability/.cypress/integration/VisualizationCharts/13_logsView.spec.js b/dashboards-observability/.cypress/integration/VisualizationCharts/13_logsView.spec.js new file mode 100644 index 000000000..542e3d776 --- /dev/null +++ b/dashboards-observability/.cypress/integration/VisualizationCharts/13_logsView.spec.js @@ -0,0 +1,241 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { + delay, + TEST_QUERIES, + querySearch, + landOnEventVisualizations, + saveVisualizationAndVerify, + deleteVisualization, +} from '../../utils/event_constants'; + +const renderLogsView = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[8].query, TEST_QUERIES[8].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Logs view') + .type('{enter}'); +}; + +const renderLogsViewChart = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[2].query, TEST_QUERIES[2].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Logs view') + .type('{enter}'); +}; + +const fieldName = 'host'; + +describe('Render Logs view and verify default behavior', () => { + beforeEach(() => { + renderLogsView(); + }); + + it('Render Logs view and verify the default data', () => { + cy.get('.logs-view-container').should('exist'); + }); + + it('Render Logs view and verify Data Configuration panel default behavior', () => { + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Columns'); + cy.get('.euiFormLabel.euiFormRow__label').contains('Field'); + cy.get('.euiButton.euiButton--primary.euiButton--fullWidth.euiButton-isDisabled').should( + 'be.disabled' + ); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').should('be.disabled'); + }); + + it('Render Logs view and verify Style section for Logs view', () => { + cy.get('.vis-config-tabs .euiTab__content').contains('Style').should('exist'); + cy.get('.euiAccordion__triggerWrapper').contains('Panel options').should('exist'); + cy.get('#configPanel__panelOptions .euiFormRow__labelWrapper') + .contains('Title') + .should('exist'); + cy.get('#configPanel__panelOptions .euiFormRow__labelWrapper') + .contains('Description') + .should('exist'); + }); + + it('Table view should be enabled for Logs view', () => { + cy.get('.euiSwitch__label').contains('Table view').should('exist'); + cy.get('[data-test-subj="workspace__dataTableViewSwitch"][aria-checked="false"]').click(); + cy.get('.ag-header.ag-pivot-off').should('exist'); + }); + + it('Verify Style section for Logs view', () => { + cy.get('#data-panel').contains('Style').should('exist'); + cy.get('.euiAccordion__button').contains('Panel options').should('exist'); + cy.get('#configPanel__panelOptions').contains('Title').should('exist'); + cy.get('.euiFormHelpText.euiFormRow__text').contains('Name your visualization').should('exist'); + cy.get('#configPanel__panelOptions').contains('Description').should('exist'); + }); + + it('Add and Remove toggle buttons for fields section', () => { + cy.get('#available_fields').contains('Available Fields').should('exist'); + cy.get('[aria-label="Add agent to table"]').should('be.disabled'); + cy.get('#selected_fields').contains('Query fields').should('exist'); + cy.get('[aria-label="Remove clientip from table"]').should('be.disabled'); + }); +}); + +describe('Save and Delete Visualization', () => { + beforeEach(() => { + renderLogsView(); + }); + + it('Render Logs view, Save and Delete Visualization', () => { + saveVisualizationAndVerify(); + deleteVisualization(); + }); +}); + +describe('Render Logs view with no stats section in the query', () => { + beforeEach(() => { + renderLogsViewChart(); + }); + + it('Disabled Table view toogle button', () => { + cy.get('[data-test-subj="workspace__dataTableViewSwitch"]').should('be.disabled'); + }); + + it('Save toast message', () => { + const vis_name_sub_string = Math.floor(Math.random() * 100); + cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); + cy.get('[data-test-subj="eventExplorer__querySaveComboBox"]').click(); + cy.get('.euiComboBoxOptionsList__rowWrap .euiFilterSelectItem').eq(0).click(); + cy.get( + '.euiPopover__panel .euiFormControlLayoutIcons [data-test-subj="comboBoxToggleListButton"]' + ) + .eq(0) + .click(); + cy.get('.euiPopover__panel input') + .eq(1) + .type(`Test visualization` + vis_name_sub_string); + cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); + cy.get('[data-test-subj="euiToastHeader"]') + .contains('There is no query or(and) visualization to save') + .should('exist'); + }); + + it('Verify Logs view details when PPL query does not have stats section ', () => { + cy.get('[data-test-subj="docTable"]').should('exist'); + cy.get('.osdDocTableHeader').contains('Time').should('exist'); + cy.get('.osdDocTableHeader').contains('_source').should('exist'); + }); + + it('Add and Remove toggle buttons for fields section should be enabled', () => { + //Add field + cy.get('[data-test-subj="fieldToggle-agent"]').click(); + cy.get('[data-test-subj="field-agent"]').should('exist'); + //Remove field + cy.get('[data-test-subj="fieldToggle-agent"]').click(); + cy.get('[aria-labelledby="selected_fields"] [data-test-subj="field-agent-showDetails"]').should( + 'not.exist' + ); + }); + + it('Search engine for fields under Visualizations', () => { + cy.get('[data-test-subj="eventExplorer__sidebarSearch"]').should('exist').type(fieldName); + cy.get('[data-test-subj="fieldToggle-host"]').click(); + cy.get('[data-test-subj="fieldList-selected"]').should('exist'); + }); + + it('View surrounding events button enabled', () => { + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + cy.get('.euiButton__text').eq(4).should('not.be.disabled'); + }); +}); + +describe('Event Details overlay', () => { + beforeEach(() => { + renderLogsView(); + }); + + it('Verify Event Details overaly should get opened after clicking on details toggle button', () => { + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + }); + + it('Options in Event Details overlay', () => { + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + cy.get('.euiTabs .euiTab__content').contains('Table'); + cy.get('.table.table-condensed.osdDocViewerTable').should('exist'); + cy.get('.euiTabs .euiTab__content').contains('JSON').click(); + cy.get('.euiCodeBlock__code.json').should('exist'); + cy.get('.euiTabs .euiTab__content').contains('Traces').click(); + cy.get('.euiCallOutHeader__title').contains('No Trace Id found in the event.').should('exist'); + cy.get('.euiLink.euiLink--primary').contains('Trace Analytics').click(); + cy.get('#trace-analytics').contains('Trace Analytics').should('exist'); + cy.get('.euiLink.euiLink--primary').contains('Log Correlation').click(); + cy.get('#log-correlation').contains('Log Correlation').should('exist'); + }); + + it('View surrounding events button disabled', () => { + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + cy.get('.euiButton.euiButton--primary.euiButton-isDisabled.header-button') + .contains('View surrounding events') + .should('be.disabled'); + }); + + it('Event Details overlay resizable and "X" buttons', () => { + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + cy.get('[title="Resize"]').click(); + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + cy.get('[data-test-subj="euiFlyoutCloseButton"]').should('exist').click(); + }); + + it('Table details on Event Details overlay', () => { + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + cy.get('[data-test-subj="docTable"]').should('exist'); + }); +}); + +describe('Data Configuration panel when no stats in the query', () => { + beforeEach(() => { + renderLogsViewChart(); + }); + + it('Data Configuration panel default behavior', () => { + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Columns').should('exist'); + cy.get('.euiButton__text').eq(2).contains('Add').should('not.be.disabled'); + cy.get('.euiButton__text').eq(3).contains('Update Chart').should('not.be.disabled'); + }); + + it('Add button in Data Configuration panel', () => { + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Columns').should('exist'); + cy.get('.euiButton__text').eq(2).contains('Add').should('not.be.disabled').click(); + cy.get('.euiFormLabel.euiFormRow__label').contains('Field'); + cy.get('.euiIcon.euiIcon--medium.euiIcon--danger').should('exist'); + cy.get('[data-test-subj="comboBoxToggleListButton"]').eq(0).click(); + cy.get('.euiFlexItem.euiFilterSelectItem__content').eq(1).click(); + cy.get('.euiButton__text').contains('Update Chart').should('not.be.disabled').click(); + cy.get('[data-test-subj="docTable"]').should('exist'); + }); +}); diff --git a/dashboards-observability/.cypress/utils/event_constants.js b/dashboards-observability/.cypress/utils/event_constants.js index 6d7ebbb05..9e0f89697 100644 --- a/dashboards-observability/.cypress/utils/event_constants.js +++ b/dashboards-observability/.cypress/utils/event_constants.js @@ -3,42 +3,53 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { supressResizeObserverIssue } from './constants' +import { supressResizeObserverIssue } from './constants'; export const delay = 1000; -export const YEAR_TO_DATE_DOM_ID = '[data-test-subj="superDatePickerCommonlyUsed_Year_to date"]' +export const YEAR_TO_DATE_DOM_ID = '[data-test-subj="superDatePickerCommonlyUsed_Year_to date"]'; export const TEST_QUERIES = [ { query: 'source = opensearch_dashboards_sample_data_flights', - dateRangeDOM: YEAR_TO_DATE_DOM_ID + dateRangeDOM: YEAR_TO_DATE_DOM_ID, }, { - query: 'source = opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by Carrier', - dateRangeDOM: YEAR_TO_DATE_DOM_ID + query: + 'source = opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by Carrier', + dateRangeDOM: YEAR_TO_DATE_DOM_ID, }, { - query: 'source = opensearch_dashboards_sample_data_logs' + query: 'source = opensearch_dashboards_sample_data_logs', }, { - query: 'source=opensearch_dashboards_sample_data_flights | stats max(AvgTicketPrice) by DestCountry, DestCityName, Carrier', - dateRangeDOM: YEAR_TO_DATE_DOM_ID + query: + 'source=opensearch_dashboards_sample_data_flights | stats max(AvgTicketPrice) by DestCountry, DestCityName, Carrier', + dateRangeDOM: YEAR_TO_DATE_DOM_ID, }, { - query: 'source = opensearch_dashboards_sample_data_logs | stats count(), avg(bytes) by host, tags', - dateRangeDOM: YEAR_TO_DATE_DOM_ID + query: + 'source = opensearch_dashboards_sample_data_logs | stats count(), avg(bytes) by host, tags', + dateRangeDOM: YEAR_TO_DATE_DOM_ID, }, { - query: 'source=opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by DestCountry, DestCityName', - dateRangeDOM: YEAR_TO_DATE_DOM_ID + query: + 'source=opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by DestCountry, DestCityName', + dateRangeDOM: YEAR_TO_DATE_DOM_ID, }, { - query: "source = opensearch_dashboards_sample_data_logs | where response='503' or response='404' | stats count() by span(timestamp,1d)", - dateRangeDOM: YEAR_TO_DATE_DOM_ID + query: + "source = opensearch_dashboards_sample_data_logs | where response='503' or response='404' | stats count() by span(timestamp,1d)", + dateRangeDOM: YEAR_TO_DATE_DOM_ID, }, { - query: 'source=opensearch_dashboards_sample_data_flights |where FlightDelayMin > 0 | stats sum(FlightDelayMin) as total_delay_min, count() as total_delayed by Carrier |eval avg_delay=total_delay_min / total_delayed | sort - avg_delay', - dateRangeDOM: YEAR_TO_DATE_DOM_ID + query: + 'source=opensearch_dashboards_sample_data_flights |where FlightDelayMin > 0 | stats sum(FlightDelayMin) as total_delay_min, count() as total_delayed by Carrier |eval avg_delay=total_delay_min / total_delayed | sort - avg_delay', + dateRangeDOM: YEAR_TO_DATE_DOM_ID, + }, + { + query: + 'source = opensearch_dashboards_sample_data_logs | stats count(), max(bytes) by span(timestamp,1d), clientip, host', + dateRangeDOM: YEAR_TO_DATE_DOM_ID, }, ]; @@ -48,7 +59,17 @@ export const SAVE_QUERY2 = 'Mock Flight count by destination'; export const SAVE_QUERY3 = 'Mock Flight count by destination save to panel'; export const SAVE_QUERY4 = 'Mock Flight peek'; -export const aggregationValues = ["COUNT", "SUM", "AVERAGE", "MAX", "MIN", "VAR_SAMP", "VAR_POP", "STDDEV_SAMP", "STDDEV_POP"]; +export const aggregationValues = [ + 'COUNT', + 'SUM', + 'AVERAGE', + 'MAX', + 'MIN', + 'VAR_SAMP', + 'VAR_POP', + 'STDDEV_SAMP', + 'STDDEV_POP', +]; export const querySearch = (query, rangeSelected) => { cy.get('[data-test-subj="searchAutocompleteTextArea"]').type(query); @@ -91,7 +112,9 @@ export const saveVisualizationAndVerify = () => { cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); cy.get('[data-test-subj="eventExplorer__querySaveComboBox"]').click(); cy.get('.euiComboBoxOptionsList__rowWrap .euiFilterSelectItem').eq(0).click(); - cy.get('.euiPopover__panel .euiFormControlLayoutIcons [data-test-subj="comboBoxToggleListButton"]') + cy.get( + '.euiPopover__panel .euiFormControlLayoutIcons [data-test-subj="comboBoxToggleListButton"]' + ) .eq(0) .click(); cy.get('.euiPopover__panel input') @@ -110,9 +133,9 @@ export const saveVisualizationAndVerify = () => { export const deleteVisualization = () => { cy.get('a[href = "#/event_analytics"]').click(); cy.get('.euiFlexGroup .euiFormControlLayout__childrenWrapper input') - .eq(0) - .type(`Test visualization` + vis_name_sub_string) - .type('{enter}'); + .eq(0) + .type(`Test visualization` + vis_name_sub_string) + .type('{enter}'); cy.get('input[data-test-subj = "checkboxSelectAll"]').click(); cy.get('.euiButtonContent.euiButtonContent--iconRight.euiButton__content').click(); cy.get('.euiContextMenuItem .euiContextMenuItem__text').eq(0).click(); @@ -141,27 +164,33 @@ export const renderPieChart = () => { cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Pie').click(); cy.wait(delay); - cy.get('#configPanel__panelOptions .euiFieldText').click().type('Pie chart'); - cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Pie chart'); - cy.get('[aria-controls="configPanel__legend"]').contains('Legend').should('exist'); - cy.get('#configPanel__legend .euiTitle.euiTitle--xxsmall').eq(0).contains('Show Legend'); - cy.get('span[data-text="Show"]').contains('Show').should('exist'); - cy.get('#configPanel__legend .euiTitle.euiTitle--xxsmall').eq(1).contains('Position'); - cy.get('span[data-text="Right"]').contains('Right').should('exist'); - cy.get('#configPanel__legend .euiTitle.euiTitle--xxsmall').eq(2).contains('Legend Size'); - cy.get('[aria-controls="configPanel__chartStyles"]').contains('Chart Styles').should('exist'); - cy.get('#configPanel__chartStyles .euiTitle.euiTitle--xxsmall').eq(0).contains('Mode').click(); - cy.get('#configPanel__chartStyles .euiComboBox__inputWrap.euiComboBox__inputWrap--noWrap.euiComboBox__inputWrap-isClearable').click(); - cy.get('.euiComboBoxOption__content').contains('Donut').click(); - cy.get('#configPanel__chartStyles .euiTitle.euiTitle--xxsmall').eq(1).contains('Label Size'); - cy.get('#configPanel__chartStyles input[type="number"]').click().type('10'); - cy.get('#configPanel__chartStyles .euiTitle.euiTitle--xxsmall').eq(2).contains('Color Theme'); - cy.get('.euiSuperSelectControl').click(); - cy.get('.euiColorPalettePicker__item').eq(1).contains('Single Color').click(); - cy.get('.euiFieldText.euiColorPicker__input.euiFieldText--withIcon').click(); - cy.get('[aria-label="Select #D36086 as the color"]').click(); - cy.get('.visEditorSidebar__controls [data-test-subj="visualizeEditorRenderButton"]').contains('Preview').click(); - cy.get('.plot-container.plotly').should('exist'); + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Pie chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]') + .click() + .type('This is the description for Pie chart'); + cy.get('[aria-controls="configPanel__legend"]').contains('Legend').should('exist'); + cy.get('#configPanel__legend .euiTitle.euiTitle--xxsmall').eq(0).contains('Show Legend'); + cy.get('span[data-text="Show"]').contains('Show').should('exist'); + cy.get('#configPanel__legend .euiTitle.euiTitle--xxsmall').eq(1).contains('Position'); + cy.get('span[data-text="Right"]').contains('Right').should('exist'); + cy.get('#configPanel__legend .euiTitle.euiTitle--xxsmall').eq(2).contains('Legend Size'); + cy.get('[aria-controls="configPanel__chartStyles"]').contains('Chart Styles').should('exist'); + cy.get('#configPanel__chartStyles .euiTitle.euiTitle--xxsmall').eq(0).contains('Mode').click(); + cy.get( + '#configPanel__chartStyles .euiComboBox__inputWrap.euiComboBox__inputWrap--noWrap.euiComboBox__inputWrap-isClearable' + ).click(); + cy.get('.euiComboBoxOption__content').contains('Donut').click(); + cy.get('#configPanel__chartStyles .euiTitle.euiTitle--xxsmall').eq(1).contains('Label Size'); + cy.get('#configPanel__chartStyles input[type="number"]').click().type('10'); + cy.get('#configPanel__chartStyles .euiTitle.euiTitle--xxsmall').eq(2).contains('Color Theme'); + cy.get('.euiSuperSelectControl').click(); + cy.get('.euiColorPalettePicker__item').eq(1).contains('Single Color').click(); + cy.get('.euiFieldText.euiColorPicker__input.euiFieldText--withIcon').click(); + cy.get('[aria-label="Select #D36086 as the color"]').click(); + cy.get('.visEditorSidebar__controls [data-test-subj="visualizeEditorRenderButton"]') + .contains('Preview') + .click(); + cy.get('.plot-container.plotly').should('exist'); }; export const renderDataConfig = () => { @@ -207,11 +236,15 @@ export const renderAddParent = () => { cy.get('.first-division .euiFormLabel.euiFormRow__label').contains('Parent 3').should('exist'); cy.get('p.euiComboBoxPlaceholder').click({ force: true }); cy.get('.euiComboBoxOption__content').eq(2).click(); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Line').type('{enter}'); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Line') + .type('{enter}'); }; export const renderGaugeChart = () => { landOnEventVisualizations(); querySearch(TEST_QUERIES[1].query, TEST_QUERIES[1].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Gauge').type('{enter}'); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Gauge') + .type('{enter}'); }; From 25bd43b9861d091589b5447235cb3d8ce548f694 Mon Sep 17 00:00:00 2001 From: Eric Wei Date: Fri, 9 Sep 2022 15:33:57 -0700 Subject: [PATCH 06/20] release notes for 2.3.0 (#1003) Signed-off-by: Eric Wei Signed-off-by: Eric Wei --- .../opensearch-observability.release-notes-2.3.0.0.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 release-notes/opensearch-observability.release-notes-2.3.0.0.md diff --git a/release-notes/opensearch-observability.release-notes-2.3.0.0.md b/release-notes/opensearch-observability.release-notes-2.3.0.0.md new file mode 100644 index 000000000..1aab83838 --- /dev/null +++ b/release-notes/opensearch-observability.release-notes-2.3.0.0.md @@ -0,0 +1,5 @@ +## Version 2.3.0.0 Release Notes +Compatible with OpenSearch and OpenSearch Dashboards Version 2.3.0 + +### Maintenance +* Bump version to 2.3.0 ([#997](https://github.com/opensearch-project/observability/pull/997)) \ No newline at end of file From 06c4b30ec5f336599ab824e37014e3b89cd51b14 Mon Sep 17 00:00:00 2001 From: ruchika-narang <79983862+ruchika-narang@users.noreply.github.com> Date: Wed, 14 Sep 2022 00:13:40 +0530 Subject: [PATCH 07/20] Resolving conflicts after merge of antlr code (#1021) * Resolved initial render of bar chart Signed-off-by: ruchika-narang * Made the charts workable after merging of antlr code to main Signed-off-by: ruchika-narang * Changes of condition for userconfigs Signed-off-by: ruchika-narang * Worked on line chart, treemap with the new antlr code Signed-off-by: ruchika-narang Signed-off-by: ruchika-narang --- .../__snapshots__/utils.test.tsx.snap | 288 +----------------- .../data_config_panel_item.tsx | 107 ++----- .../logs_view_config_panel_item.tsx | 6 +- .../treemap_config_panel_item.tsx | 68 +++-- .../visualizations/charts/bar/bar.tsx | 1 - .../charts/financial/gauge/gauge.tsx | 11 +- .../charts/helpers/viz_types.ts | 156 +++++++--- .../visualizations/charts/lines/line.tsx | 24 +- .../visualizations/charts/maps/heatmap.tsx | 24 +- .../visualizations/charts/pie/pie.tsx | 138 +++++---- 10 files changed, 308 insertions(+), 515 deletions(-) diff --git a/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap b/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap index e31ae697b..ac6bc701d 100644 --- a/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap +++ b/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap @@ -133,26 +133,7 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "size": 4, "status": 200, }, - "userConfigs": Object { - "dataConfig": Object { - "valueOptions": Object { - "dimensions": Array [ - Object { - "label": "Carrier", - "name": "Carrier", - "type": "keyword", - }, - ], - "metrics": Array [ - Object { - "label": "avg(FlightDelayMin)", - "name": "avg(FlightDelayMin)", - "type": "double", - }, - ], - }, - }, - }, + "userConfigs": Object {}, }, "vis": Object { "barwidth": 0.97, @@ -617,26 +598,7 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "size": 4, "status": 200, }, - "userConfigs": Object { - "dataConfig": Object { - "valueOptions": Object { - "dimensions": Array [ - Object { - "label": "Carrier", - "name": "Carrier", - "type": "keyword", - }, - ], - "metrics": Array [ - Object { - "label": "avg(FlightDelayMin)", - "name": "avg(FlightDelayMin)", - "type": "double", - }, - ], - }, - }, - }, + "userConfigs": Object {}, }, "vis": Object { "barwidth": 0.97, @@ -1124,26 +1086,7 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "size": 4, "status": 200, }, - "userConfigs": Object { - "dataConfig": Object { - "valueOptions": Object { - "dimensions": Array [ - Object { - "label": "Carrier", - "name": "Carrier", - "type": "keyword", - }, - ], - "metrics": Array [ - Object { - "label": "avg(FlightDelayMin)", - "name": "avg(FlightDelayMin)", - "type": "double", - }, - ], - }, - }, - }, + "userConfigs": Object {}, }, "vis": Object { "barwidth": 0.97, @@ -1567,12 +1510,8 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "defaultAxes": Object { "xaxis": Array [ Object { - "aggregation": "", - "custom_label": "", "label": "", "name": "", - "side": "right", - "type": "", }, ], "yaxis": Array [ @@ -1698,34 +1637,7 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "size": 4, "status": 200, }, - "userConfigs": Object { - "dataConfig": Object { - "valueOptions": Object { - "dimensions": Array [ - Object { - "aggregation": "", - "custom_label": "", - "label": "", - "name": "", - "side": "right", - "type": "", - }, - ], - "metrics": Array [ - Object { - "label": "avg(FlightDelayMin)", - "name": "avg(FlightDelayMin)", - "type": "double", - }, - Object { - "label": "Carrier", - "name": "Carrier", - "type": "keyword", - }, - ], - }, - }, - }, + "userConfigs": Object {}, }, "vis": Object { "category": "Visualizations", @@ -2104,12 +2016,8 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "defaultAxes": Object { "xaxis": Array [ Object { - "aggregation": "", - "custom_label": "", "label": "", "name": "", - "side": "right", - "type": "", }, ], "yaxis": Array [ @@ -2235,34 +2143,7 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "size": 4, "status": 200, }, - "userConfigs": Object { - "dataConfig": Object { - "valueOptions": Object { - "dimensions": Array [ - Object { - "aggregation": "", - "custom_label": "", - "label": "", - "name": "", - "side": "right", - "type": "", - }, - ], - "metrics": Array [ - Object { - "label": "avg(FlightDelayMin)", - "name": "avg(FlightDelayMin)", - "type": "double", - }, - Object { - "label": "Carrier", - "name": "Carrier", - "type": "keyword", - }, - ], - }, - }, - }, + "userConfigs": Object {}, }, "vis": Object { "category": "Visualizations", @@ -2695,12 +2576,8 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "defaultAxes": Object { "xaxis": Array [ Object { - "aggregation": "", - "custom_label": "", "label": "", "name": "", - "side": "right", - "type": "", }, ], "yaxis": Array [ @@ -2826,34 +2703,7 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "size": 4, "status": 200, }, - "userConfigs": Object { - "dataConfig": Object { - "valueOptions": Object { - "dimensions": Array [ - Object { - "aggregation": "", - "custom_label": "", - "label": "", - "name": "", - "side": "right", - "type": "", - }, - ], - "metrics": Array [ - Object { - "label": "avg(FlightDelayMin)", - "name": "avg(FlightDelayMin)", - "type": "double", - }, - Object { - "label": "Carrier", - "name": "Carrier", - "type": "keyword", - }, - ], - }, - }, - }, + "userConfigs": Object {}, }, "vis": Object { "category": "Visualizations", @@ -3440,26 +3290,7 @@ exports[`Utils helper functions renders displayVisualization function 3`] = ` "size": 4, "status": 200, }, - "userConfigs": Object { - "dataConfig": Object { - "valueOptions": Object { - "dimensions": Array [ - Object { - "label": "Carrier", - "name": "Carrier", - "type": "keyword", - }, - ], - "metrics": Array [ - Object { - "label": "avg(FlightDelayMin)", - "name": "avg(FlightDelayMin)", - "type": "double", - }, - ], - }, - }, - }, + "userConfigs": Object {}, }, "vis": Object { "category": "Visualizations", @@ -3729,26 +3560,7 @@ exports[`Utils helper functions renders displayVisualization function 3`] = ` "size": 4, "status": 200, }, - "userConfigs": Object { - "dataConfig": Object { - "valueOptions": Object { - "dimensions": Array [ - Object { - "label": "Carrier", - "name": "Carrier", - "type": "keyword", - }, - ], - "metrics": Array [ - Object { - "label": "avg(FlightDelayMin)", - "name": "avg(FlightDelayMin)", - "type": "double", - }, - ], - }, - }, - }, + "userConfigs": Object {}, }, "vis": Object { "category": "Visualizations", @@ -4041,26 +3853,7 @@ exports[`Utils helper functions renders displayVisualization function 3`] = ` "size": 4, "status": 200, }, - "userConfigs": Object { - "dataConfig": Object { - "valueOptions": Object { - "dimensions": Array [ - Object { - "label": "Carrier", - "name": "Carrier", - "type": "keyword", - }, - ], - "metrics": Array [ - Object { - "label": "avg(FlightDelayMin)", - "name": "avg(FlightDelayMin)", - "type": "double", - }, - ], - }, - }, - }, + "userConfigs": Object {}, }, "vis": Object { "category": "Visualizations", @@ -4358,26 +4151,7 @@ exports[`Utils helper functions renders displayVisualization function 4`] = ` "size": 0, "status": 200, }, - "userConfigs": Object { - "dataConfig": Object { - "valueOptions": Object { - "dimensions": Array [ - Object { - "label": "span(timestamp,1h)", - "name": "span(timestamp,1h)", - "type": "timestamp", - }, - ], - "metrics": Array [ - Object { - "label": "count('ip')", - "name": "count('ip')", - "type": "integer", - }, - ], - }, - }, - }, + "userConfigs": Object {}, }, "vis": Object { "category": "Visualizations", @@ -4593,26 +4367,7 @@ exports[`Utils helper functions renders displayVisualization function 4`] = ` "size": 0, "status": 200, }, - "userConfigs": Object { - "dataConfig": Object { - "valueOptions": Object { - "dimensions": Array [ - Object { - "label": "span(timestamp,1h)", - "name": "span(timestamp,1h)", - "type": "timestamp", - }, - ], - "metrics": Array [ - Object { - "label": "count('ip')", - "name": "count('ip')", - "type": "integer", - }, - ], - }, - }, - }, + "userConfigs": Object {}, }, "vis": Object { "category": "Visualizations", @@ -4851,26 +4606,7 @@ exports[`Utils helper functions renders displayVisualization function 4`] = ` "size": 0, "status": 200, }, - "userConfigs": Object { - "dataConfig": Object { - "valueOptions": Object { - "dimensions": Array [ - Object { - "label": "span(timestamp,1h)", - "name": "span(timestamp,1h)", - "type": "timestamp", - }, - ], - "metrics": Array [ - Object { - "label": "count('ip')", - "name": "count('ip')", - "type": "integer", - }, - ], - }, - }, - }, + "userConfigs": Object {}, }, "vis": Object { "category": "Visualizations", diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx index d616dc156..87d6802d0 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx @@ -19,16 +19,9 @@ import { EuiFieldNumber, htmlIdGenerator, } from '@elastic/eui'; -import { useDispatch, useSelector, batch } from 'react-redux'; -import { - render as renderExplorerVis, - selectExplorerVisualization, -} from '../../../../../redux/slices/visualization_slice'; +import { useDispatch, batch } from 'react-redux'; import { changeQuery } from '../../../../../redux/slices/query_slice'; -import { - change as changeVizConfig, - selectVisualizationConfig, -} from '../../../../../redux/slices/viualization_config_slice'; +import { change as changeVizConfig } from '../../../../../redux/slices/viualization_config_slice'; import { AGGREGATION_OPTIONS, numericalTypes, @@ -42,111 +35,47 @@ import { TabContext } from '../../../../../hooks'; import { QueryManager } from '../../../../../../../../common/query_manager'; import { composeAggregations } from '../../../../../../../../common/query_manager/utils'; -const initialConfigEntry = { +const initialDimensionEntry = { label: '', - aggregation: '', - custom_label: '', name: '', - side: 'right', - type: '', }; -const DEFAULT_DATA_CONFIGS = { - [visChartTypes.HeatMap]: { - dimensions: [initialConfigEntry, initialConfigEntry], - metrics: [initialConfigEntry], - }, - [visChartTypes.Histogram]: { - dimensions: [{ bucketSize: '', bucketOffset: '' }], - }, +const initialMetricEntry = { + alias: '', + label: '', + name: '', + aggregation: 'count', }; -const SPECIAL_RENDERING_VIZS = [visChartTypes.HeatMap, visChartTypes.Histogram]; - -const getStandardedOuiField = (name?: string, type?: string) => ({ - name, - label: name, - type, -}); - export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => { const dispatch = useDispatch(); const { tabId, handleQuerySearch, handleQueryChange, setTempQuery, fetchData } = useContext( TabContext ); - const explorerVisualizationConfigs = useSelector(selectVisualizationConfig)[tabId]; const { data } = visualizations; const { data: vizData = {}, metadata: { fields = [] } = {} } = data?.rawVizData; const { indexFields: { availableFields }, } = data; const [configList, setConfigList] = useState({}); + const { userConfigs } = data; useEffect(() => { - if ( - data.rawVizData?.[visualizations.vis.name] && - data.rawVizData?.[visualizations.vis.name].dataConfig - ) { - setConfigList((staleState) => { - return { - ...staleState, - ...data.rawVizData[visualizations.vis.name].dataConfig, - }; - }); - } else if (some(SPECIAL_RENDERING_VIZS, (visType) => visType === visualizations.vis.name)) { - // any vis that doesn't conform normal metrics/dimensions data confiurations + if (userConfigs && userConfigs.dataConfig) { setConfigList({ - ...DEFAULT_DATA_CONFIGS[visualizations.vis.name], + ...userConfigs.dataConfig, }); - } else { - // default - const qm = new QueryManager(); - const statsTokens = qm.queryParser().parse(data.query.rawQuery).getStats(); - if (!statsTokens) { - setConfigList({ - metrics: [], - dimensions: [], - }); - } else { - const fieldInfo = statsTokens.groupby?.span?.span_expression?.field; - setConfigList({ - metrics: statsTokens.aggregations.map((agg) => ({ - alias: agg.alias, - label: agg.function?.value_expression, - name: agg.function?.value_expression, - aggregation: agg.function?.name, - })), - dimensions: statsTokens.groupby?.group_fields?.map((agg) => ({ - label: agg.name ?? '', - name: agg.name ?? '', - })), - span: { - time_field: statsTokens.groupby?.span?.span_expression?.field - ? [getStandardedOuiField(fieldInfo, 'timestamp')] - : [], - interval: statsTokens.groupby?.span?.span_expression?.literal_value ?? '0', - unit: statsTokens.groupby?.span?.span_expression?.time_unit - ? [getStandardedOuiField(statsTokens.groupby?.span?.span_expression?.time_unit)] - : [], - }, - }); - } } - }, [ - data.defaultAxes, - data.rawVizData?.[visualizations.vis.name]?.dataConfig, - visualizations.vis.name, - ]); + }, [userConfigs?.dataConfig, visualizations.vis.name]); const updateList = (value: string, index: number, name: string, field: string) => { const list = { ...configList }; let listItem = { ...list[name][index] }; listItem = { ...listItem, - [field]: value, + [field === 'custom_label' ? 'alias' : field]: value, }; if (field === 'label') { - listItem.type = value !== '' ? fields.find((x) => x.name === value)?.type : ''; listItem.name = value; } const updatedList = { @@ -180,8 +109,14 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => }; const handleServiceAdd = (name: string) => { - const updatedList = { ...configList, [name]: [...configList[name], initialConfigEntry] }; - setConfigList(updatedList); + const list = { + ...configList, + [name]: [ + ...configList[name], + name === 'metrics' ? initialMetricEntry : initialDimensionEntry, + ], + }; + setConfigList(list); }; const updateChart = (updatedConfigList = configList) => { diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/logs_view_config_panel_item.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/logs_view_config_panel_item.tsx index 7c4c33870..6f9bb5388 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/logs_view_config_panel_item.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/logs_view_config_panel_item.tsx @@ -47,12 +47,12 @@ export const LogsViewConfigPanelItem = ({ fieldOptionList, visualizations }: any const [configList, setConfigList] = useState({}); useEffect(() => { - if (userConfigs && userConfigs.dataConfig && userConfigs.dataConfig.valueOptions) { + if (userConfigs && userConfigs.dataConfig) { setConfigList({ - ...userConfigs.dataConfig.valueOptions, + ...userConfigs.dataConfig, }); } - }, [userConfigs?.dataConfig?.valueOptions, visualizations.vis.name]); + }, [userConfigs?.dataConfig, visualizations.vis.name]); useEffect(() => { if (fieldOptionList.length === 0) { diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/treemap_config_panel_item.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/treemap_config_panel_item.tsx index 06cb1e8f5..895a1708a 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/treemap_config_panel_item.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/treemap_config_panel_item.tsx @@ -13,14 +13,23 @@ import { EuiFormRow, EuiPanel, } from '@elastic/eui'; -import { useDispatch } from 'react-redux'; +import { useDispatch, batch } from 'react-redux'; +import { changeQuery } from '../../../../../redux/slices/query_slice'; +import { + change as changeVizConfig, + selectVisualizationConfig, +} from '../../../../../redux/slices/viualization_config_slice'; import { ConfigTreemapParentFields } from './config_treemap_parents'; -import { numericalTypes } from '../../../../../../../../common/constants/explorer'; +import { numericalTypes, RAW_QUERY } from '../../../../../../../../common/constants/explorer'; import { TabContext } from '../../../../../hooks'; +import { QueryManager } from '../../../../../../../../common/query_manager'; +import { composeAggregations } from '../../../../../../../../common/query_manager/utils'; export const TreemapConfigPanelItem = ({ fieldOptionList, visualizations, tabID }: any) => { const dispatch = useDispatch(); - const { tabId, curVisId, changeVisualizationConfig } = useContext(TabContext); + const { tabId, curVisId, changeVisualizationConfig, fetchData, handleQueryChange } = useContext< + any + >(TabContext); const { data } = visualizations; const { userConfigs } = data; @@ -34,12 +43,12 @@ export const TreemapConfigPanelItem = ({ fieldOptionList, visualizations, tabID }); useEffect(() => { - if (userConfigs && userConfigs.dataConfig && userConfigs.dataConfig.valueOptions) { + if (userConfigs && userConfigs.dataConfig) { setConfigList({ - ...userConfigs.dataConfig.valueOptions, + ...userConfigs.dataConfig, }); } - }, [userConfigs?.dataConfig?.valueOptions, visualizations.vis.name]); + }, [userConfigs?.dataConfig, visualizations.vis.name]); const updateList = (configName: string, fieldName: string, value: string | any[]) => { let list = { ...configList }; @@ -58,23 +67,38 @@ export const TreemapConfigPanelItem = ({ fieldOptionList, visualizations, tabID setConfigList(newList); }; - const updateChart = () => { - dispatch( - changeVisualizationConfig({ - tabId, - vizId: curVisId, - data: { - ...userConfigs, - dataConfig: { - ...userConfigs.dataConfig, - valueOptions: { - dimensions: configList.dimensions, - metrics: configList.metrics, + const updateChart = (updatedConfigList = configList) => { + const qm = new QueryManager(); + const statsTokens = qm.queryParser().parse(data.query.rawQuery).getStats(); + const newQuery = qm + .queryBuilder() + .build(data.query.rawQuery, composeAggregations(updatedConfigList, statsTokens)); + + batch(async () => { + await handleQueryChange(newQuery); + await dispatch( + changeQuery({ + tabId, + query: { + ...data.query, + [RAW_QUERY]: newQuery, + }, + }) + ); + await fetchData(); + await dispatch( + changeVizConfig({ + tabId, + vizId: visualizations.vis.name, + data: { + dataConfig: { + metrics: updatedConfigList.metrics, + dimensions: updatedConfigList.dimensions, }, }, - }, - }) - ); + }) + ); + }); }; const getOptionsAvailable = (sectionName: string) => { @@ -169,7 +193,7 @@ export const TreemapConfigPanelItem = ({ fieldOptionList, visualizations, tabID updateChart()} size="s" > Update chart diff --git a/dashboards-observability/public/components/visualizations/charts/bar/bar.tsx b/dashboards-observability/public/components/visualizations/charts/bar/bar.tsx index f52409b89..02690816e 100644 --- a/dashboards-observability/public/components/visualizations/charts/bar/bar.tsx +++ b/dashboards-observability/public/components/visualizations/charts/bar/bar.tsx @@ -31,7 +31,6 @@ export const Bar = ({ visualizations, layout, config }: any) => { }: IVisualizationContainerProps = visualizations; const lastIndex = fields.length - 1; const { dataConfig = {}, layoutConfig = {}, availabilityConfig = {} } = userConfigs; - console.log('bar dataConfig: ', dataConfig); if ( isEmpty(queriedVizData) || diff --git a/dashboards-observability/public/components/visualizations/charts/financial/gauge/gauge.tsx b/dashboards-observability/public/components/visualizations/charts/financial/gauge/gauge.tsx index 7882f79c6..b9b9f1549 100644 --- a/dashboards-observability/public/components/visualizations/charts/financial/gauge/gauge.tsx +++ b/dashboards-observability/public/components/visualizations/charts/financial/gauge/gauge.tsx @@ -27,12 +27,10 @@ export const Gauge = ({ visualizations, layout, config }: any) => { // data config parametrs const { dataConfig = {}, layoutConfig = {} } = visualizations.data.userConfigs; - const dimensions = dataConfig?.valueOptions?.dimensions - ? dataConfig.valueOptions.dimensions.filter((item) => item.name !== '') - : []; - const metrics = dataConfig?.valueOptions?.metrics - ? dataConfig.valueOptions.metrics.filter((item) => item.name !== '') + const dimensions = dataConfig?.dimensions + ? dataConfig.dimensions.filter((item) => item.name !== '') : []; + const metrics = dataConfig?.metrics ? dataConfig.metrics.filter((item) => item.name !== '') : []; const dimensionsLength = dimensions.length; const metricsLength = metrics.length; const numberOfGauges = dataConfig?.panelOptions?.numberOfGauges || DisplayDefaultGauges; @@ -70,9 +68,8 @@ export const Gauge = ({ visualizations, layout, config }: any) => { .reduce((prev, cur) => { return prev.map((i, j) => `${i}, ${cur[j]}`); }); - const selectedMetricsData = metrics.map((metric: any) => - data[metric.name].slice(0, numberOfGauges) + data[`${metric.aggregation}(${metric.name})`].slice(0, numberOfGauges) ); selectedMetricsData.map((metricSlice: any, metricSliceIndex: number) => { diff --git a/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts b/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts index cd79672eb..ef4b0c320 100644 --- a/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts +++ b/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts @@ -12,7 +12,7 @@ import { ExplorerData, } from '../../../../../common/types/explorer'; import { visChartTypes } from '../../../../../common/constants/shared'; - +import { QueryManager } from '../../../../../common/query_manager'; interface IVizContainerProps { vizId: string; appData?: { fromApp: boolean }; @@ -27,13 +27,16 @@ interface IVizContainerProps { explorer?: ExplorerData; } -const initialConfigEntry = { +const initialDimensionEntry = { + label: '', + name: '', +}; + +const initialMetricEntry = { + alias: '', label: '', - aggregation: '', - custom_label: '', name: '', - side: 'right', - type: '', + aggregation: 'count', }; const initialEntryTreemap = { label: '', name: '' }; @@ -49,7 +52,7 @@ const getDefaultXYAxisLabels = (vizFields: IField[], visName: string) => { const xaxis = vizFieldsWithLabel.filter((field) => field.type === 'timestamp'); return visName === visChartTypes.Line ? xaxis.length === 0 - ? [initialConfigEntry] + ? [initialDimensionEntry] : xaxis : [vizFieldsWithLabel[vizFieldsWithLabel.length - 1]]; }; @@ -65,20 +68,119 @@ const getDefaultXYAxisLabels = (vizFields: IField[], visName: string) => { return { xaxis: mapXaxis(), yaxis: mapYaxis() }; }; -const getUserConfigs = (userSelectedConfigs: object, vizFields: IField[], visName: string) => { +const getStandardedOuiField = (name?: string, type?: string) => ({ + name, + label: name, + type, +}); + +const defaultUserConfigs = (queryString, visualizationName: string) => { + let tempUserConfigs = {}; + const qm = new QueryManager(); + const statsTokens = qm.queryParser().parse(queryString.rawQuery).getStats(); + if (!statsTokens) { + tempUserConfigs = { + metrics: [], + dimensions: [], + }; + } else { + const fieldInfo = statsTokens.groupby?.span?.span_expression?.field; + tempUserConfigs = { + span: { + time_field: statsTokens.groupby?.span?.span_expression?.field + ? [getStandardedOuiField(fieldInfo, 'timestamp')] + : [], + interval: statsTokens.groupby?.span?.span_expression?.literal_value ?? '0', + unit: statsTokens.groupby?.span?.span_expression?.time_unit + ? [getStandardedOuiField(statsTokens.groupby?.span?.span_expression?.time_unit)] + : [], + }, + }; + if (visualizationName === visChartTypes.LogsView) { + tempUserConfigs = { + ...tempUserConfigs, + metrics: [], + dimensions: statsTokens.aggregations + .map((agg) => ({ + label: agg.name ?? '', + name: agg.name ?? '', + })) + .concat( + statsTokens.groupby?.group_fields?.map((agg) => ({ + label: agg.name ?? '', + name: agg.name ?? '', + })) + ), + }; + } else if (visualizationName === visChartTypes.HeatMap) { + tempUserConfigs = { + ...tempUserConfigs, + dimensions: [initialDimensionEntry, initialDimensionEntry], + metrics: [initialMetricEntry], + }; + } else if (visualizationName === visChartTypes.TreeMap) { + tempUserConfigs = { + dimensions: [ + { + childField: { + ...(statsTokens.groupby?.group_fields.length > 0 + ? { + label: statsTokens.groupby?.group_fields[0].name, + name: statsTokens.groupby?.group_fields[0].name, + } + : initialEntryTreemap), + }, + parentFields: [], + }, + ], + metrics: [ + { + valueField: { + ...(statsTokens.aggregations.length > 0 + ? { + label: statsTokens.aggregations[0].function?.value_expression, + name: statsTokens.aggregations[0].function?.value_expression, + } + : initialEntryTreemap), + }, + }, + ], + }; + } else { + tempUserConfigs = { + ...tempUserConfigs, + metrics: statsTokens.aggregations.map((agg) => ({ + alias: agg.alias, + label: agg.function?.value_expression, + name: agg.function?.value_expression, + aggregation: agg.function?.name, + })), + dimensions: statsTokens.groupby?.group_fields?.map((agg) => ({ + label: agg.name ?? '', + name: agg.name ?? '', + })), + }; + } + } + return tempUserConfigs; +}; + +const getUserConfigs = ( + userSelectedConfigs: object, + vizFields: IField[], + visName: string, + query +) => { let configOfUser = userSelectedConfigs; const axesData = getDefaultXYAxisLabels(vizFields, visName); - if (!userSelectedConfigs.dataConfig?.valueOptions) { + if (!(userSelectedConfigs.dataConfig?.dimensions || userSelectedConfigs.dataConfig?.metrics)) { switch (visName) { case visChartTypes.HeatMap: configOfUser = { ...userSelectedConfigs, dataConfig: { ...userSelectedConfigs?.dataConfig, - valueOptions: { - dimensions: [initialConfigEntry, initialConfigEntry], - metrics: [initialConfigEntry], - }, + ...defaultUserConfigs(query, visName), }, }; break; @@ -87,17 +189,7 @@ const getUserConfigs = (userSelectedConfigs: object, vizFields: IField[], visNam ...userSelectedConfigs, dataConfig: { ...userSelectedConfigs?.dataConfig, - valueOptions: { - dimensions: [ - { - childField: { ...(axesData.xaxis ? axesData.xaxis[0] : initialEntryTreemap) }, - parentFields: [], - }, - ], - metrics: [ - { valueField: { ...(axesData.yaxis ? axesData.yaxis[0] : initialEntryTreemap) } }, - ], - }, + ...defaultUserConfigs(query, visName), }, }; break; @@ -118,11 +210,7 @@ const getUserConfigs = (userSelectedConfigs: object, vizFields: IField[], visNam ...userSelectedConfigs, dataConfig: { ...userSelectedConfigs?.dataConfig, - valueOptions: { - dimensions: - axesData.xaxis && axesData.yaxis ? axesData.xaxis.concat(axesData.yaxis) : [], - metrics: [], - }, + ...defaultUserConfigs(query, visName), }, }; break; @@ -131,10 +219,7 @@ const getUserConfigs = (userSelectedConfigs: object, vizFields: IField[], visNam ...userSelectedConfigs, dataConfig: { ...userSelectedConfigs?.dataConfig, - valueOptions: { - metrics: axesData.yaxis ?? [], - dimensions: axesData.xaxis ?? [], - }, + ...defaultUserConfigs(query, visName), }, }; break; @@ -157,6 +242,9 @@ export const getVizContainerProps = ({ ? { ...getVisType(vizId, { type: vizId }) } : { ...getVisType(vizId) }; + const userSetConfigs = isEmpty(query) + ? userConfigs + : getUserConfigs(userConfigs, rawVizData?.metadata?.fields, getVisTypeData().name, query); return { data: { appData: { ...appData }, @@ -164,7 +252,7 @@ export const getVizContainerProps = ({ query: { ...query }, indexFields: { ...indexFields }, userConfigs: { - ...getUserConfigs(userConfigs, rawVizData?.metadata?.fields, getVisTypeData().name), + ...userSetConfigs, }, defaultAxes: { ...getDefaultXYAxisLabels(rawVizData?.metadata?.fields, getVisTypeData().name), diff --git a/dashboards-observability/public/components/visualizations/charts/lines/line.tsx b/dashboards-observability/public/components/visualizations/charts/lines/line.tsx index 80c22b8e8..3745a363b 100644 --- a/dashboards-observability/public/components/visualizations/charts/lines/line.tsx +++ b/dashboards-observability/public/components/visualizations/charts/lines/line.tsx @@ -4,7 +4,7 @@ */ import React, { useMemo } from 'react'; -import { take, isEmpty, last } from 'lodash'; +import { take, isEmpty, last, find } from 'lodash'; import { Plt } from '../../plotly/plot'; import { AvailabilityUnitType } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability'; import { ThresholdUnitType } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_thresholds'; @@ -40,12 +40,8 @@ export const Line = ({ visualizations, layout, config }: any) => { availabilityConfig = {}, } = visualizations?.data?.userConfigs; - const xaxis = dataConfig?.valueOptions?.dimensions - ? dataConfig.valueOptions.dimensions.filter((item) => item.label) - : []; - const yaxis = dataConfig?.valueOptions?.metrics - ? dataConfig.valueOptions.metrics.filter((item) => item.label) - : []; + // const xaxis = dataConfig?.dimensions ? dataConfig.dimensions.filter((item) => item.label) : []; + const yaxis = dataConfig?.metrics ? dataConfig.metrics.filter((item) => item.label) : []; const tooltipMode = dataConfig?.tooltipOptions?.tooltipMode !== undefined ? dataConfig.tooltipOptions.tooltipMode @@ -81,6 +77,18 @@ export const Line = ({ visualizations, layout, config }: any) => { dataConfig.colorTheme.find((colorSelected) => colorSelected.name.name === field.name) ?.color) || PLOTLY_COLOR[index % PLOTLY_COLOR.length]; + /** + * determine x axis + */ + const xaxis = useMemo(() => { + // span selection + const timestampField = find(fields, (field) => field.type === 'timestamp'); + if (dataConfig.span && dataConfig.span.time_field && timestampField) { + return [timestampField, ...dataConfig.dimensions]; + } + + return dataConfig.dimensions; + }, [dataConfig.dimensions]); if (isEmpty(xaxis) || isEmpty(yaxis)) return ; @@ -135,7 +143,7 @@ export const Line = ({ visualizations, layout, config }: any) => { return { x: data[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name], - y: data[field.label], + y: data[`${field.aggregation}(${field.name})`], type: isBarMode ? 'bar' : 'scatter', name: field.label, mode, diff --git a/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx b/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx index 11e6dfe48..30dce8305 100644 --- a/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx +++ b/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx @@ -27,9 +27,9 @@ export const HeatMap = ({ visualizations, layout, config }: any) => { if (fields.length < 3) return ; - const xaxisField = dataConfig?.valueOptions?.dimensions[0]; - const yaxisField = dataConfig?.valueOptions?.dimensions[1]; - const zMetrics = dataConfig?.valueOptions?.metrics[0]; + const xaxisField = dataConfig?.dimensions[0]; + const yaxisField = dataConfig?.dimensions[1]; + const zMetrics = dataConfig?.metrics[0]; if ( isEmpty(xaxisField) || @@ -37,8 +37,8 @@ export const HeatMap = ({ visualizations, layout, config }: any) => { isEmpty(zMetrics) || isEmpty(data[xaxisField.label]) || isEmpty(data[yaxisField.label]) || - isEmpty(data[zMetrics.label]) || - indexOf(NUMERICAL_FIELDS, zMetrics.type) < 0 + isEmpty(data[`${zMetrics.aggregation}(${zMetrics.name})`]) + // indexOf(NUMERICAL_FIELDS, zMetrics.type) < 0 ) return ; @@ -80,7 +80,7 @@ export const HeatMap = ({ visualizations, layout, config }: any) => { // maps bukcets to metrics for (let i = 0; i < data[xaxisField.label].length; i++) { buckets[`${data[xaxisField.label][i]},${data[yaxisField.label][i]}`] = - data[zMetrics.label][i]; + data[`${zMetrics.aggregation}(${zMetrics.name})`][i]; } // initialize empty 2 dimensional array, inner loop for each xaxis field, outer loop for yaxis @@ -131,11 +131,13 @@ export const HeatMap = ({ visualizations, layout, config }: any) => { title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || '', }; - - const mergedConfigs = useMemo(() => ({ - ...config, - ...(layoutConfig.config && layoutConfig.config), - }), [config, layoutConfig.config]); + const mergedConfigs = useMemo( + () => ({ + ...config, + ...(layoutConfig.config && layoutConfig.config), + }), + [config, layoutConfig.config] + ); return ; }; diff --git a/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx b/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx index 912f3f6d2..64b1a5cf2 100644 --- a/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx +++ b/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx @@ -17,12 +17,8 @@ export const Pie = ({ visualizations, layout, config }: any) => { } = visualizations.data.rawVizData; const { defaultAxes } = visualizations.data; const { dataConfig = {}, layoutConfig = {} } = visualizations?.data?.userConfigs; - const xaxis = dataConfig?.valueOptions?.dimensions - ? dataConfig.valueOptions.dimensions.filter((item) => item.label) - : []; - const yaxis = dataConfig?.valueOptions?.metrics - ? dataConfig.valueOptions.metrics.filter((item) => item.label) - : []; + const xaxis = dataConfig?.dimensions ? dataConfig.dimensions.filter((item) => item.label) : []; + const yaxis = dataConfig?.metrics ? dataConfig.metrics.filter((item) => item.label) : []; const type = dataConfig?.chartStyles?.mode ? dataConfig?.chartStyles?.mode[0]?.modeId : 'pie'; const lastIndex = fields.length - 1; const colorTheme = dataConfig?.chartStyles?.colorTheme @@ -44,12 +40,12 @@ export const Pie = ({ visualizations, layout, config }: any) => { if (isEmpty(xaxis) || isEmpty(yaxis)) return ; - let valueSeries; - if (!isEmpty(xaxis) && !isEmpty(yaxis)) { - valueSeries = [...yaxis]; - } else { - valueSeries = defaultAxes.yaxis || take(fields, lastIndex > 0 ? lastIndex : 1); - } + let valueSeries; + if (!isEmpty(xaxis) && !isEmpty(yaxis)) { + valueSeries = [...yaxis]; + } else { + valueSeries = defaultAxes.yaxis || take(fields, lastIndex > 0 ? lastIndex : 1); + } const invertHex = (hex: string) => (Number(`0x1${hex}`) ^ HEX_CONTRAST_COLOR).toString(16).substr(1).toUpperCase(); @@ -75,59 +71,63 @@ export const Pie = ({ visualizations, layout, config }: any) => { }, [xaxis, data, fields, createLegendLabels]); const hexColor = invertHex(colorTheme); - const pies = useMemo(() => valueSeries.map((field: any, index: number) => { - const marker = - colorTheme.name !== DEFAULT_PALETTE - ? { - marker: { - colors: [...Array(data[field.name].length).fill(colorTheme.childColor)], - line: { - color: hexColor, - width: 1, - }, + const pies = useMemo( + () => + valueSeries.map((field: any, index: number) => { + const marker = + colorTheme.name !== DEFAULT_PALETTE + ? { + marker: { + colors: [...Array(data[field.name].length).fill(colorTheme.childColor)], + line: { + color: hexColor, + width: 1, + }, + }, + } + : undefined; + return { + labels: labelsOfXAxis, + values: data[field.label], + type: 'pie', + name: field.name, + hole: type === 'pie' ? 0 : 0.5, + text: field.name, + textinfo: 'percent', + hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText, + automargin: true, + textposition: 'outside', + domain: { + row: Math.floor(index / 3), + column: index % 3, }, - } - : undefined; - return { - labels: labelsOfXAxis, - values: data[field.label], - type: 'pie', - name: field.name, - hole: type === 'pie' ? 0 : 0.5, - text: field.name, - textinfo: 'percent', - hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText, - automargin: true, - textposition: 'outside', - domain: { - row: Math.floor(index / 3), - column: index % 3, - }, - ...marker, - outsidetextfont: { - size: labelSize, - }, - }; - }) - , [valueSeries, valueSeries, data, labelSize, labelsOfXAxis, colorTheme]); + ...marker, + outsidetextfont: { + size: labelSize, + }, + }; + }), + [valueSeries, valueSeries, data, labelSize, labelsOfXAxis, colorTheme] + ); const isAtleastOneFullRow = Math.floor(valueSeries.length / 3) > 0; - const mergedLayout = useMemo(() => ({ - grid: { - rows: Math.floor(valueSeries.length / 3) + 1, - columns: isAtleastOneFullRow ? 3 : valueSeries.length, - }, - ...layout, - ...(layoutConfig.layout && layoutConfig.layout), - title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || '', - legend: { - ...layout.legend, - orientation: legendPosition, - font: { size: legendSize }, - }, - showlegend: showLegend, - }), + const mergedLayout = useMemo( + () => ({ + grid: { + rows: Math.floor(valueSeries.length / 3) + 1, + columns: isAtleastOneFullRow ? 3 : valueSeries.length, + }, + ...layout, + ...(layoutConfig.layout && layoutConfig.layout), + title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || '', + legend: { + ...layout.legend, + orientation: legendPosition, + font: { size: legendSize }, + }, + showlegend: showLegend, + }), [ valueSeries, isAtleastOneFullRow, @@ -136,13 +136,17 @@ export const Pie = ({ visualizations, layout, config }: any) => { layoutConfig.layout?.title, layout.legend, legendPosition, - legendSize - ]); + legendSize, + ] + ); - const mergedConfigs = useMemo(() => ({ - ...config, - ...(layoutConfig.config && layoutConfig.config), - }), [config, layoutConfig.config]); + const mergedConfigs = useMemo( + () => ({ + ...config, + ...(layoutConfig.config && layoutConfig.config), + }), + [config, layoutConfig.config] + ); return ; }; From 45406d64b684968f9713885735c0dc9ce95852a6 Mon Sep 17 00:00:00 2001 From: ramneet-persistent <105915936+ramneet-persistent@users.noreply.github.com> Date: Wed, 14 Sep 2022 00:13:57 +0530 Subject: [PATCH 08/20] tooltip for multiple traces: fixed (#1019) Signed-off-by: Ramneet Chopra Signed-off-by: Ramneet Chopra --- .../public/components/visualizations/charts/lines/line.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dashboards-observability/public/components/visualizations/charts/lines/line.tsx b/dashboards-observability/public/components/visualizations/charts/lines/line.tsx index 3745a363b..c5d55ffef 100644 --- a/dashboards-observability/public/components/visualizations/charts/lines/line.tsx +++ b/dashboards-observability/public/components/visualizations/charts/lines/line.tsx @@ -53,7 +53,7 @@ export const Line = ({ visualizations, layout, config }: any) => { const lastIndex = fields.length - 1; - let visType: string = visualizations.vis.name; + const visType: string = visualizations.vis.name; const mode = dataConfig?.chartStyles?.style || (visType === visChartTypes.Line ? DefaultModeLine : DefaultModeScatter); @@ -136,7 +136,7 @@ export const Line = ({ visualizations, layout, config }: any) => { size: labelSize, }), }, - overlaying: 'y', + ...(index > 0 && { overlaying: 'y' }), side: field.side, }, }; @@ -162,7 +162,7 @@ export const Line = ({ visualizations, layout, config }: any) => { }; }); - let layoutForBarMode = { + const layoutForBarMode = { barmode: 'group', }; const mergedLayout = { From b7da13c5b1aa44b57816846758f48e9ead11bf11 Mon Sep 17 00:00:00 2001 From: Pratibha <103417380+pratibhapandey16@users.noreply.github.com> Date: Wed, 14 Sep 2022 00:14:10 +0530 Subject: [PATCH 09/20] Cypress automation for Pie chart (#1017) * Added cypress scripts for Logs view Signed-off-by: Pratibha Pandey * Added screenshots files Signed-off-by: Pratibha Pandey * Added snapshots files Signed-off-by: Pratibha Pandey * Added Pie chart file and cypress test cases Signed-off-by: Pratibha Pandey * Small name change Signed-off-by: Pratibha Pandey * Added pie chart test cases Signed-off-by: Pratibha Pandey * Added snapshots files Signed-off-by: Pratibha Pandey Signed-off-by: Pratibha Pandey --- .../VisualizationCharts/7_pie_chart.spec.js | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 dashboards-observability/.cypress/integration/VisualizationCharts/7_pie_chart.spec.js diff --git a/dashboards-observability/.cypress/integration/VisualizationCharts/7_pie_chart.spec.js b/dashboards-observability/.cypress/integration/VisualizationCharts/7_pie_chart.spec.js new file mode 100644 index 000000000..b731db10b --- /dev/null +++ b/dashboards-observability/.cypress/integration/VisualizationCharts/7_pie_chart.spec.js @@ -0,0 +1,202 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { + delay, + TEST_QUERIES, + querySearch, + landOnEventVisualizations, + renderDataConfig, + saveVisualizationAndVerify, + deleteVisualization, + } from '../../utils/event_constants'; + + const renderPieChart = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Pie').click(); + }; + + const aggregationValues = [ + 'COUNT', + 'SUM', + 'AVERAGE', + 'MAX', + 'MIN', + 'VAR_SAMP', + 'VAR_POP', + 'STDDEV_SAMP', + 'STDDEV_POP', + ]; + + describe.only('Render Pie chart and verify default behavior', () => { + beforeEach(() => { + renderPieChart(); + }); + + it('Render Pie chart and verify the default data', () => { + cy.get('.plot-container.plotly').should('exist'); + }); + + it('Render Pie chart and verify Data Configuration panel default behavior', () => { + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Dimensions').should('exist'); + cy.get('.first-division .euiFormRow__labelWrapper').contains('Aggregation').should('exist'); + cy.get('.first-division .euiFormLabel.euiFormRow__label').contains('Field').should('exist'); + cy.get('.first-division .euiFormLabel.euiFormRow__label') + .contains('Custom label') + .should('exist'); + cy.get('.first-division .euiButton__text').contains('Add').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Metrics').should('exist'); + cy.get('.euiButton__text').contains('Update chart').should('exist'); + }); + + it('Render Pie chart and verify Style section for Pie chart', () => { + cy.get('#data-panel').contains('Style').should('exist'); + cy.get('[aria-controls="configPanel__panelOptions"]').contains('Panel options').should('exist'); + cy.get('[aria-controls="configPanel__legend"]').contains('Legend').should('exist'); + cy.get('.euiForm.visEditorSidebar__form .euiIEFlexWrapFix') + .contains('Tooltip options') + .should('exist'); + cy.get('[aria-controls="configPanel__chartStyles"]').contains('Chart styles').should('exist'); + }); + + it('Options under Legend section', () => { + cy.get('#configPanel__legend').contains('Legend'); + cy.get('.euiTitle.euiTitle--xxsmall').eq(3).contains('Show legend'); + cy.get('[data-text="Show"]').eq(0).contains('Show'); + cy.get('[data-text="Hidden"]').eq(0).contains('Hidden'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Position'); + cy.get('[data-text="Right"]').contains('Right'); + cy.get('[data-text="Bottom"]').contains('Bottom'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Legend size').should('exist'); + }); + + it('Options under Tooltip options section', () => { + cy.get('.euiIEFlexWrapFix').contains('Tooltip options').should('exist'); + cy.get('[data-text="Show"]').eq(1).contains('Show'); + cy.get('[data-text="Hidden"]').eq(1).contains('Hidden'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Tooltip text'); + cy.get('[data-text="All"]').contains('All'); + cy.get('[data-text="Label"]').contains('Label'); + cy.get('[data-text="Value"]').contains('Value'); + cy.get('[data-text="Percent"]').contains('Percent'); + }); + + it('Options under Chart Styles section', () => { + cy.get('.euiIEFlexWrapFix').contains('Chart styles').should('exist'); + cy.get('#configPanel__chartStyles').contains('Mode'); + cy.get('.euiTitle.euiTitle--xxsmall').eq(9).contains('Label size'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Color theme'); + }); + + it('Table view should be enabled for Pie chart', () => { + cy.get('.euiSwitch__label').contains('Table view').should('exist'); + cy.get('[data-test-subj="workspace__dataTableViewSwitch"][aria-checked="false"]').click(); + cy.get('.ag-header.ag-pivot-off').should('exist'); + }); + + it('Render Pie chart and verify legends for Position Right and Bottom', () => { + cy.get('[data-text="Right"]').should('have.text', 'Right'); + cy.get('[data-text="Right"] [data-test-subj="v"]').should('have.attr', 'checked'); + cy.get('[data-text="Bottom"]').should('have.text', 'Bottom').click(); + cy.get('[data-text="Bottom"] [data-test-subj="h"]').should('not.have.attr', 'checked'); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true, multiple: true }); + }); + + it('Render Pie chart and verify legends for Show and Hidden', () => { + cy.get('[data-text="Show"]').eq(0).should('have.text', 'Show'); + cy.get('[data-text="Show"] [data-test-subj="show"]').should('have.attr', 'checked'); + cy.get('[data-text="Hidden"]').eq(0).should('have.text', 'Hidden').click(); + cy.get('[data-text="Hidden"] [data-test-subj="hidden"]').should('not.have.attr', 'checked'); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true, multiple: true }); + }); + }); + + describe('Save and Delete Visualization', () => { + beforeEach(() => { + renderPieChart(); + }); + + it('Render Pie chart, Save and Delete Visualization', () => { + saveVisualizationAndVerify(); + deleteVisualization(); + }); + }); + + describe('Color Theme section', () => { + beforeEach(() => { + renderPieChart(); + }); + + it('Default option in Color Theme', () => { + cy.get('.euiTitle.euiTitle--xxsmall') + .contains('Color Theme') + .scrollIntoView() + .should('be.visible'); + cy.get('.euiSuperSelectControl').contains('Default').should('exist'); + }); + + it('Render pie chart with single color option', () => { + cy.get('.euiTitle.euiTitle--xxsmall') + .contains('Color Theme') + .scrollIntoView() + .should('be.visible'); + cy.get('.euiSuperSelectControl').contains('Default').click(); + cy.get('.euiColorPalettePicker__item').contains('Single Color').click(); + cy.get('[data-test-subj="euiColorPickerAnchor"]').click(); + cy.get('.euiColorPickerSwatch.euiColorPicker__swatchSelect').eq(2).click(); + cy.get('.pielayer').should('exist'); + }); + }); + + describe('Renders Pie chart and Data Configurations section for Pie chart', () => { + beforeEach(() => { + landOnEventVisualizations(); + renderPieChart(); + }); + + it('Renders Dimensions and Metrics under Data Configurations for Pie chart', () => { + renderDataConfig(); + }); + + it('Validate "Add" and "X" buttons', () => { + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Configurations'); + cy.get('.euiText.euiText--extraSmall').eq(0).click(); + cy.get('.euiButton.euiButton--primary.euiButton--fullWidth').contains('Add').click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(2).click(); + cy.get('.euiComboBoxOption__content').eq(0).click(); + cy.get('.euiIcon.euiIcon--medium.euiIcon--danger').eq(1).click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(4).click(); + cy.get('.euiComboBoxOption__content').eq(1).click(); + cy.get('.euiFieldText[placeholder="Custom label"]').eq(1).type('Demo field'); + cy.get('.euiIcon.euiIcon--medium.euiIcon--danger').eq(1).click(); + cy.get('.euiButton.euiButton--primary.euiButton--fullWidth').contains('Add').should('exist'); + }); + + it('Verify drop down values for Aggregation', () => { + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Configurations'); + cy.get('.euiTitle.euiTitle--xxsmall').eq(1).contains('Dimensions').should('exist'); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(0).contains('Aggregation'); + cy.get('.euiText.euiText--extraSmall').eq(0).click(); + cy.get('[data-test-subj="comboBoxSearchInput"]').eq(0).click(); + aggregationValues.forEach(function (value) { + cy.get('.euiComboBoxOption__content').contains(value); + }); + }); + + it('Collapsible mode for Data Configuration panel', () => { + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Configurations'); + cy.get('.euiResizableButton.euiResizableButton--horizontal').eq(1).click(); + cy.get('[data-test-subj="panel-1-toggle"]').click(); + cy.get('[class*="euiResizableToggleButton-isCollapsed"]').eq(1).should('exist'); + cy.get('.euiResizablePanel.euiResizablePanel--middle') + .contains('Data Configurations') + .should('not.be.focused'); + }); + }); + \ No newline at end of file From cbc66bb0436c716ce1e65c3e0f3118132a8112bf Mon Sep 17 00:00:00 2001 From: Chris Moore <107723039+cwillum@users.noreply.github.com> Date: Tue, 13 Sep 2022 12:47:07 -0700 Subject: [PATCH 10/20] fix#921-README-forum-link-observability (#999) Signed-off-by: cwillum Signed-off-by: cwillum --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 87feb1c1e..ed0aa2544 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ See [developer guide](DEVELOPER_GUIDE.md) and [how to contribute to this project If you find a bug, or have a feature request, please don't hesitate to open an issue in this repository. -For more information, see [project website](https://opensearch.org/) and [documentation](https://opensearch.org/docs). If you need help and are unsure where to open an issue, try [forums](https://discuss.opendistrocommunity.dev/). +For more information, see [project website](https://opensearch.org/) and [documentation](https://opensearch.org/docs). If you need help and are unsure where to open an issue, try the [Forum](https://forum.opensearch.org/c/plugins/observability/49). ## Code of Conduct From 54d7b9402090320eb20894cce848ea16f9b32294 Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Tue, 13 Sep 2022 12:50:58 -0700 Subject: [PATCH 11/20] Update developer guide (#961) * Update developer guide Signed-off-by: Joshua Li * Update wording Signed-off-by: Joshua Li Signed-off-by: Joshua Li --- DEVELOPER_GUIDE.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index 7a1fde32a..b63759f91 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -14,14 +14,13 @@ By default, tests use the same runtime as `JAVA_HOME`. ### Setup -1. Download OpenSearch for the version that matches the [OpenSearch Dashboards version specified in package.json](./dashboards-observability/package.json#L5). -1. Download the OpenSearch Dashboards source code for the [version specified in package.json](./dashboards-observability/package.json#L5) you want to set up. - +1. Download OpenSearch for the version that matches the [OpenSearch Dashboards version specified in opensearch_dashboards.json](./dashboards-observability/opensearch_dashboards.json#L3) from [opensearch.org](https://opensearch.org/downloads.html). +1. Download the OpenSearch Dashboards source code for the [version specified in opensearch_dashboards.json](./dashboards-observability/opensearch_dashboards.json#L3) you want to set up. 1. Change your node version to the version specified in `.node-version` inside the OpenSearch Dashboards root directory. -1. cd into `plugins` directory in the OpenSearch Dashboards source code directory. -1. Check out this package from version control into the `plugins` directory. +1. cd into `OpenSearch-Dashboards` and remove the `plugins` directory. +1. Check out this package from version control as the `plugins` directory. ```bash -git clone git@github.com:opensearch-project/observability.git plugins --no-checkout +git clone https://github.com/opensearch-project/observability plugins --no-checkout cd plugins echo 'dashboards-observability/*' >> .git/info/sparse-checkout git config core.sparseCheckout true @@ -46,9 +45,7 @@ Example output: `./build/observability*.zip` ### Run -- `yarn start` - - Starts OpenSearch Dashboards and includes this plugin. OpenSearch Dashboards will be available on `localhost:5601`. +cd back to `OpenSearch-Dashboards` directory and run `yarn start` to start OpenSearch Dashboards including this plugin. OpenSearch Dashboards will be available on `localhost:5601`. ### Submitting Changes @@ -60,4 +57,4 @@ The Github workflow in [`backport.yml`](.github/workflows/backport.yml) creates with an appropriate label `backport ` is merged to main with the backport workflow run successfully on the PR. For example, if a PR on main needs to be backported to `1.x` branch, add a label `backport 1.x` to the PR and make sure the backport workflow runs on the PR along with other checks. Once this PR is merged to main, the workflow will create a backport PR -to the `1.x` branch. \ No newline at end of file +to the `1.x` branch. From a37542d919e268c151bd9243801518743ad68c6b Mon Sep 17 00:00:00 2001 From: NamanChaturvedi13 <78581256+NamanChaturvedi13@users.noreply.github.com> Date: Tue, 13 Sep 2022 19:53:23 +0000 Subject: [PATCH 12/20] Cypress Automation for Stats Chart (#1016) * Added new branch code for cypress stats chart Signed-off-by: Naman Chaturvedi * Added screenshots files Signed-off-by: Naman Chaturvedi Signed-off-by: Naman Chaturvedi --- .../13_stats_chart.spec.js | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 dashboards-observability/.cypress/integration/VisualizationCharts/13_stats_chart.spec.js diff --git a/dashboards-observability/.cypress/integration/VisualizationCharts/13_stats_chart.spec.js b/dashboards-observability/.cypress/integration/VisualizationCharts/13_stats_chart.spec.js new file mode 100644 index 000000000..657686dd2 --- /dev/null +++ b/dashboards-observability/.cypress/integration/VisualizationCharts/13_stats_chart.spec.js @@ -0,0 +1,224 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { + delay, + TEST_QUERIES, + querySearch, + landOnEventVisualizations, +} from '../../utils/event_constants'; + +const numberOfWindow = 4; +const metricsPrecisionUpdated = 2; +const metricUnit = 'cm'; +const titleSize = '25.5px'; +const titleSizeUpdated = '40px'; +const valueSize = '60.8px'; +const valueSizeUpdated = '73.0px'; + +const renderStatsChart = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[4].query, TEST_QUERIES[4].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Stats') + .type('{enter}'); +}; + +describe('Render stats chart and verify default behaviour ', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verify by default the data gets render', () => { + cy.get('.xy').should('exist'); + }); + + it('Render scatter chart and verify you see data configuration panel and chart panel', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Dimensions').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Metrics').should('exist'); + cy.get('.euiIEFlexWrapFix').contains('Panel options').click(); + cy.get('.euiIEFlexWrapFix').contains('Chart styles').click(); + cy.get('.euiIEFlexWrapFix').contains('Thresholds').click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); + + it('Render stats chart and verify the data configuration panel and chart panel are collapsable', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); +}); + +describe('Render stats chart for data configuration panel', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verify data config panel', () => { + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(0).should('contain', 'tags'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(1).should('contain', 'count()'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(2).should('contain', 'avg(bytes)'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(3).should('contain', 'host'); + }); + + it('Render stats chart and verify no result found message if the dimension is removed', () => { + cy.get('[data-test-subj="comboBoxClearButton"]').eq(0).click(); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); + cy.get('.euiTextColor.euiTextColor--subdued').contains('No results found').should('exist'); + cy.get('[data-test-subj="comboBoxInput"]').eq(1).click(); + cy.get('.euiComboBoxOption__content').click(); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); + cy.get('.main-svg').contains('No results found').should('not.exist'); + }); + + it('Render stats chart and verify data config panel no result found if metric is missing', () => { + cy.get('.euiText.euiText--extraSmall').eq(0).click(); + cy.get('.euiText.euiText--extraSmall').eq(1).click(); + cy.get('[data-test-subj="comboBoxClearButton"]').eq(1).click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(0).click(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.euiTextColor.euiTextColor--subdued').contains('No results found').should('exist'); + cy.get('[data-test-subj="comboBoxInput"]').eq(3).click(); + cy.get('.euiComboBoxOption__content').contains('avg(bytes)').click(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.main-svg').contains('No results found').should('not.exist'); + }); +}); + +describe('Render stats chart for panel options', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verify the title gets updated according to user input ', () => { + cy.get('input[name="title"]').type('stats chart'); + cy.get('textarea[name="description"]').should('exist').click(); + cy.get('.gtitle').contains('stats chart').should('exist'); + }); +}); + +describe('Render stats chart verfiy functionality for Tooltip mode', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verfiy the Show and Hidden Tooltip modes', () => { + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(0).should('have.text', 'Show'); + cy.get('.euiButton__text.euiButtonGroupButton__textShift') + .eq(1) + .should('have.text', 'Hidden') + .click(); + }); +}); + +describe('Render stats chart verfiy functionality for Tooltip text', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verfiy the Tootltip text -> All , Dimension , Metric', () => { + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(2).should('have.text', 'All'); + cy.get('.euiButton__text.euiButtonGroupButton__textShift') + .eq(3) + .should('have.text', 'Dimension') + .click(); + cy.get('.euiButton__text.euiButtonGroupButton__textShift') + .eq(4) + .should('have.text', 'Metrics') + .click(); + }); +}); + +describe('Render stats chart for Chart Styles ', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verify the various chart type selected', () => { + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(5).should('have.text', 'Auto'); + cy.get('.euiButton__text.euiButtonGroupButton__textShift') + .eq(6) + .should('have.text', 'Horizontal') + .click(); + cy.get('.euiButton__text.euiButtonGroupButton__textShift') + .eq(7) + .should('have.text', 'Text mode') + .click(); + }); + + it('Render stats chart and verify the various chart orientation selected', () => { + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(8).should('have.text', 'Auto'); + cy.get('.euiButton__text.euiButtonGroupButton__textShift') + .eq(9) + .should('have.text', 'Horizontal') + .click(); + cy.get('.euiButton__text.euiButtonGroupButton__textShift') + .eq(10) + .should('have.text', 'Vertical') + .click(); + }); + + it('Render stats chart and verify Metric unit and Metric Precision on chart ', () => { + cy.get('[data-test-subj="valueFieldText"]').click().type(metricUnit); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).click().type(metricsPrecisionUpdated); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); + }); + + it('Render stats chart and verify behaviour for Title size and Value size on chart ', () => { + cy.get('.annotation-text').eq(0).should('have.css', 'font-size', titleSize); + cy.get('.annotation-text').eq(2).should('have.css', 'font-size', titleSize); + cy.get('.annotation-text').eq(4).should('have.css', 'font-size', titleSize); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).click().type(titleSizeUpdated); + cy.get('.annotation-text').eq(1).should('have.css', 'font-size', valueSize); + cy.get('.annotation-text').eq(3).should('have.css', 'font-size', valueSize); + cy.get('.annotation-text').eq(5).should('have.css', 'font-size', valueSize); + cy.get('[data-test-subj="valueFieldNumber"]').eq(2).click().type(valueSizeUpdated); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); + }); +}); + +describe('Render stats chart and verify the Text Mode options', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verify text modes ', () => { + cy.get('[data-text="Names"]').should('have.text', 'Names').click(); + cy.get('[data-text="Values"]').should('have.text', 'Values').click(); + cy.get('[data-text="Values + Names"]').should('have.text', 'Values + Names').click(); + }); +}); + +describe('Render stats chart and verify the +add threshold button option', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verify the +Add Threshold button for color picker', () => { + cy.get('[data-test-subj="euiColorPickerAnchor"]').click(); + cy.get('.euiColorPickerSwatch.euiColorPicker__swatchSelect').eq(5).click(); + }); +}); + +describe('Render stats chart and verify the reset button', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it.only('Render stats chart and test the Reset button functionality', () => { + cy.get('[data-test-subj="valueFieldText"]').click().type(metricUnit); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).click().type(metricsPrecisionUpdated); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).click().type(titleSizeUpdated); + cy.get('[data-test-subj="valueFieldNumber"]').eq(2).click().type(valueSizeUpdated); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); + cy.get('[data-test-subj="euiColorPickerAnchor"]').click(); + cy.get('.euiColorPickerSwatch.euiColorPicker__swatchSelect').eq(5).click(); + cy.get('[data-test-subj="visualizeEditorResetButton"]').click(); + }); +}); From a1490cce4ffc1da4d3984328623d2b1dd2358ac7 Mon Sep 17 00:00:00 2001 From: ramneet-persistent <105915936+ramneet-persistent@users.noreply.github.com> Date: Wed, 14 Sep 2022 01:23:43 +0530 Subject: [PATCH 13/20] Jest test cases (#985) * gauge chart test case added, event-anlytics utils test case added Signed-off-by: Ramneet Chopra * build error resolve: updated snapshots Signed-off-by: Ramneet Chopra Signed-off-by: Ramneet Chopra --- .../__snapshots__/config_panel.test.tsx.snap | 283 +++++++++- .../utils/__tests__/utils.test.tsx | 83 +++ .../__tests__/__snapshots__/bar.test.tsx.snap | 22 +- .../__snapshots__/gauge.test.tsx.snap | 522 ++++++++++++++++++ .../__snapshots__/heatmap.test.tsx.snap | 22 +- .../__snapshots__/histogram.test.tsx.snap | 22 +- .../__snapshots__/line.test.tsx.snap | 22 +- .../__snapshots__/logs_view.test.tsx.snap | 22 +- .../__tests__/__snapshots__/pie.test.tsx.snap | 265 ++++++--- .../__snapshots__/text.test.tsx.snap | 22 +- .../__snapshots__/treemap.test.tsx.snap | 22 +- .../charts/__tests__/gauge.test.tsx | 30 + .../test/event_analytics_constants.ts | 242 ++++---- 13 files changed, 1376 insertions(+), 203 deletions(-) create mode 100644 dashboards-observability/public/components/event_analytics/utils/__tests__/utils.test.tsx create mode 100644 dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/gauge.test.tsx.snap create mode 100644 dashboards-observability/public/components/visualizations/charts/__tests__/gauge.test.tsx diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap index 0ec47e963..324b855b4 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap @@ -198,7 +198,27 @@ exports[`Config panel component Renders config panel with visualization data 1`] "status": 200, }, }, - "userConfigs": Object {}, + "userConfigs": Object { + "dataConfig": Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + }, + }, }, "vis": Object { "barwidth": 0.97, @@ -6029,7 +6049,27 @@ exports[`Config panel component Renders config panel with visualization data 1`] "status": 200, }, }, - "userConfigs": Object {}, + "userConfigs": Object { + "dataConfig": Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + }, + }, }, "vis": Object { "barwidth": 0.97, @@ -6362,6 +6402,27 @@ exports[`Config panel component Renders config panel with visualization data 1`] }, } } + vizState={ + Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + } + } />, "id": "data-panel", "name": "Style", @@ -6820,7 +6881,27 @@ exports[`Config panel component Renders config panel with visualization data 1`] "status": 200, }, }, - "userConfigs": Object {}, + "userConfigs": Object { + "dataConfig": Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + }, + }, }, "vis": Object { "barwidth": 0.97, @@ -7153,6 +7234,27 @@ exports[`Config panel component Renders config panel with visualization data 1`] }, } } + vizState={ + Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + } + } />, "id": "data-panel", "name": "Style", @@ -7367,7 +7469,27 @@ exports[`Config panel component Renders config panel with visualization data 1`] "status": 200, }, }, - "userConfigs": Object {}, + "userConfigs": Object { + "dataConfig": Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + }, + }, }, "vis": Object { "barwidth": 0.97, @@ -8247,7 +8369,27 @@ exports[`Config panel component Renders config panel with visualization data 1`] "status": 200, }, }, - "userConfigs": Object {}, + "userConfigs": Object { + "dataConfig": Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + }, + }, }, "vis": Object { "barwidth": 0.97, @@ -8580,6 +8722,27 @@ exports[`Config panel component Renders config panel with visualization data 1`] }, } } + vizState={ + Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + } + } >
{ + configure({ adapter: new Adapter() }); + + it('validates hexToRgb function', () => { + expect(hexToRgb()).toBe('rgba(60,161,199,1)'); + expect(hexToRgb('test', 1, true)).toBe('rgba(96,353,409,1)'); + expect(hexToRgb('#000000', 1, true)).toBe('rgba(0,0,0,1)'); + }); + + it('validates lightenColor function', () => { + expect(lightenColor('#00000', 10)).toBe('#1a1a1a'); + }); + + it('validates formatError function', () => { + expect(formatError('Warning', 'This is a warning', 'Test warning description')).toStrictEqual({ + body: { + attributes: { error: { caused_by: { reason: 'Test warning description', type: '' } } }, + }, + message: 'This is a warning', + name: 'Warning', + }); + }); + + it('validates isValidTraceId function', () => { + expect(isValidTraceId('#00000')).toBe(false); + expect(isValidTraceId('abcdefghijklmnopqrstuvwxyzabcdef')).toBe(true); + }); + + it('validates rangeNumDocs function', () => { + expect(rangeNumDocs(11000)).toBe(10000); + expect(rangeNumDocs(-200)).toBe(0); + expect(rangeNumDocs(2000)).toBe(2000); + }); + + it('validates getHeaders function', () => { + expect( + getHeaders( + [ + { + name: 'host', + type: 'text', + }, + { + name: 'ip_count', + type: 'integer', + }, + { + name: 'per_ip_bytes', + type: 'long', + }, + { + name: 'resp_code', + type: 'text', + }, + { + name: 'sum_bytes', + type: 'long', + }, + ], + ['', 'Time', '_source'], + undefined + ) + ).toBeTruthy(); + expect(getHeaders([], ['', 'Time', '_source'], undefined)).toBeTruthy(); + }); +}); diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap index 9a88fe43b..b51fac742 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap @@ -199,7 +199,27 @@ exports[`Bar component Renders bar component 1`] = ` "status": 200, }, }, - "userConfigs": Object {}, + "userConfigs": Object { + "dataConfig": Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + }, + }, }, "vis": Object { "barwidth": 0.97, diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/gauge.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/gauge.test.tsx.snap new file mode 100644 index 000000000..ad8e359f1 --- /dev/null +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/gauge.test.tsx.snap @@ -0,0 +1,522 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Gauge component Renders gauge component 1`] = ` + + + +
+ + + +`; diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/heatmap.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/heatmap.test.tsx.snap index 881df5490..f41925f35 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/heatmap.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/heatmap.test.tsx.snap @@ -199,7 +199,27 @@ exports[`Heatmap component Renders heatmap component 1`] = ` "status": 200, }, }, - "userConfigs": Object {}, + "userConfigs": Object { + "dataConfig": Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + }, + }, }, "vis": Object { "barwidth": 0.97, diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/histogram.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/histogram.test.tsx.snap index 16f79ad74..9c199a9ab 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/histogram.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/histogram.test.tsx.snap @@ -199,7 +199,27 @@ exports[`Histogram component Renders histogram component 1`] = ` "status": 200, }, }, - "userConfigs": Object {}, + "userConfigs": Object { + "dataConfig": Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + }, + }, }, "vis": Object { "barwidth": 0.97, diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap index 15f4453bf..80aad5341 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/line.test.tsx.snap @@ -199,7 +199,27 @@ exports[`Line component Renders line component 1`] = ` "status": 200, }, }, - "userConfigs": Object {}, + "userConfigs": Object { + "dataConfig": Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + }, + }, }, "vis": Object { "barwidth": 0.97, diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/logs_view.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/logs_view.test.tsx.snap index bb2936a04..cfdb61c63 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/logs_view.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/logs_view.test.tsx.snap @@ -199,7 +199,27 @@ exports[`Logs View component Renders logs view component 1`] = ` "status": 200, }, }, - "userConfigs": Object {}, + "userConfigs": Object { + "dataConfig": Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + }, + }, }, "vis": Object { "barwidth": 0.97, diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap index 60d2671fd..e36bda4ca 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap @@ -199,7 +199,27 @@ exports[`Pie component Renders pie component 1`] = ` "status": 200, }, }, - "userConfigs": Object {}, + "userConfigs": Object { + "dataConfig": Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + }, + }, }, "vis": Object { "barwidth": 0.97, @@ -533,82 +553,177 @@ exports[`Pie component Renders pie component 1`] = ` } } > - -
- -
- -
- - - - - - -
- -

- - - No results found - - -

-
- -
- -
- - + id="explorerPlotComponent" + style={ + Object { + "height": "100%", + "width": "100%", + } + } + /> + + `; diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/text.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/text.test.tsx.snap index 90a14da93..66f79027c 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/text.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/text.test.tsx.snap @@ -182,7 +182,27 @@ exports[`Text component Renders text component 1`] = ` "status": 200, }, }, - "userConfigs": Object {}, + "userConfigs": Object { + "dataConfig": Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + }, + }, }, "vis": Object { "barwidth": 0.97, diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/treemap.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/treemap.test.tsx.snap index 39626b437..f9ebdaabb 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/treemap.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/treemap.test.tsx.snap @@ -199,7 +199,27 @@ exports[`Treemap component Renders treemap component 1`] = ` "status": 200, }, }, - "userConfigs": Object {}, + "userConfigs": Object { + "dataConfig": Object { + "valueOptions": Object { + "dimensions": Array [ + Object { + "label": "tags", + "name": "tags", + "type": "text", + }, + ], + "metrics": Array [ + Object { + "label": "count()", + "name": "count()", + "side": "left", + "type": "integer", + }, + ], + }, + }, + }, }, "vis": Object { "barwidth": 0.97, diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/gauge.test.tsx b/dashboards-observability/public/components/visualizations/charts/__tests__/gauge.test.tsx new file mode 100644 index 000000000..6b539445f --- /dev/null +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/gauge.test.tsx @@ -0,0 +1,30 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { configure, mount } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import React from 'react'; +import { waitFor } from '@testing-library/react'; +import { Gauge } from '../financial/gauge/gauge'; +import { + LAYOUT_CONFIG, + GAUGE_TEST_VISUALIZATIONS_DATA, +} from '../../../../../test/event_analytics_constants'; + +describe('Gauge component', () => { + configure({ adapter: new Adapter() }); + + it('Renders gauge component', async () => { + const wrapper = mount( + + ); + + wrapper.update(); + + await waitFor(() => { + expect(wrapper).toMatchSnapshot(); + }); + }); +}); diff --git a/dashboards-observability/test/event_analytics_constants.ts b/dashboards-observability/test/event_analytics_constants.ts index f885dd37e..48054dd5e 100644 --- a/dashboards-observability/test/event_analytics_constants.ts +++ b/dashboards-observability/test/event_analytics_constants.ts @@ -5,6 +5,7 @@ import { LONG_CHART_COLOR } from '../common/constants/shared'; import { createBarTypeDefinition } from '../public/components/visualizations/charts/bar/bar_type'; +import { createGaugeTypeDefinition } from '../public/components/visualizations/charts/financial/gauge/gauge_type'; import { SELECTED_FIELDS, AVAILABLE_FIELDS as AVAILABLE_FIELDS_NAME, @@ -15,111 +16,111 @@ import { export const AVAILABLE_FIELDS = [ { name: 'agent', - type: 'string' + type: 'string', }, { name: 'bytes', - type: 'long' + type: 'long', }, { name: 'clientip', - type: 'ip' + type: 'ip', }, { name: 'event', - type: 'struct' + type: 'struct', }, { name: 'extension', - type: 'string' + type: 'string', }, { name: 'geo', - type: 'struct' + type: 'struct', }, { name: 'host', - type: 'string' + type: 'string', }, { name: 'index', - type: 'string' + type: 'string', }, { name: 'ip', - type: 'ip' + type: 'ip', }, { name: 'machine', - type: 'struct' + type: 'struct', }, { name: 'memory', - type: 'double' + type: 'double', }, { name: 'message', - type: 'string' + type: 'string', }, { name: 'phpmemory', - type: 'long' + type: 'long', }, { name: 'referer', - type: 'string' + type: 'string', }, { name: 'request', - type: 'string' + type: 'string', }, { name: 'response', - type: 'string' + type: 'string', }, { name: 'tags', - type: 'string' + type: 'string', }, { name: 'timestamp', - type: 'timestamp' + type: 'timestamp', }, { name: 'url', - type: 'string' + type: 'string', }, { name: 'utc_time', - type: 'timestamp' - } + type: 'timestamp', + }, ]; export const QUERY_FIELDS = [ { name: 'double_per_ip_bytes', - type: 'long' + type: 'long', }, { name: 'host', - type: 'text' + type: 'text', }, { name: 'ip_count', - type: 'integer' + type: 'integer', }, { name: 'per_ip_bytes', - type: 'long' + type: 'long', }, { name: 'resp_code', - type: 'text' + type: 'text', }, { name: 'sum_bytes', - type: 'long' - } + type: 'long', + }, ]; export const JSON_DATA = [ @@ -129,7 +130,7 @@ export const JSON_DATA = [ host: 'artifacts.opensearch.org', resp_code: '404', per_ip_bytes: 5803, - double_per_ip_bytes: 11606 + double_per_ip_bytes: 11606, }, { ip_count: 111, @@ -137,7 +138,7 @@ export const JSON_DATA = [ host: 'www.opensearch.org', resp_code: '404', per_ip_bytes: 5050, - double_per_ip_bytes: 10100 + double_per_ip_bytes: 10100, }, { ip_count: 94, @@ -145,7 +146,7 @@ export const JSON_DATA = [ host: 'artifacts.opensearch.org', resp_code: '503', per_ip_bytes: 0, - double_per_ip_bytes: 0 + double_per_ip_bytes: 0, }, { ip_count: 78, @@ -153,7 +154,7 @@ export const JSON_DATA = [ host: 'www.opensearch.org', resp_code: '503', per_ip_bytes: 0, - double_per_ip_bytes: 0 + double_per_ip_bytes: 0, }, { ip_count: 43, @@ -161,7 +162,7 @@ export const JSON_DATA = [ host: 'cdn.opensearch-opensearch-opensearch.org', resp_code: '404', per_ip_bytes: 5763, - double_per_ip_bytes: 11526 + double_per_ip_bytes: 11526, }, { ip_count: 34, @@ -169,7 +170,7 @@ export const JSON_DATA = [ host: 'cdn.opensearch-opensearch-opensearch.org', resp_code: '503', per_ip_bytes: 0, - double_per_ip_bytes: 0 + double_per_ip_bytes: 0, }, { ip_count: 13, @@ -177,7 +178,7 @@ export const JSON_DATA = [ host: 'opensearch-opensearch-opensearch.org', resp_code: '404', per_ip_bytes: 4441, - double_per_ip_bytes: 8882 + double_per_ip_bytes: 8882, }, { ip_count: 6, @@ -185,8 +186,8 @@ export const JSON_DATA = [ host: 'opensearch-opensearch-opensearch.org', resp_code: '503', per_ip_bytes: 0, - double_per_ip_bytes: 0 - } + double_per_ip_bytes: 0, + }, ]; export const JSON_DATA_ALL = [ @@ -196,7 +197,8 @@ export const JSON_DATA_ALL = [ agent: 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1', extension: 'deb', memory: 'null', - geo: '{"srcdest":"IN:US","src":"IN","coordinates":{"lat":39.41042861,"lon":-88.8454325},"dest":"US"}', + geo: + '{"srcdest":"IN:US","src":"IN","coordinates":{"lat":39.41042861,"lon":-88.8454325},"dest":"US"}', utc_time: '2021-11-14 00:39:02.912', clientip: '223.87.60.27', host: 'artifacts.opensearch.org', @@ -205,12 +207,13 @@ export const JSON_DATA_ALL = [ timestamp: '2021-11-14 00:39:02.912', ip: '223.87.60.27', index: 'opensearch_dashboards_sample_data_logs', - message: '223.87.60.27 - - [2018-07-22T00:39:02.912Z] "GET /opensearch/opensearch-1.0.0.deb_1 HTTP/1.1" 200 6219 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1"', + message: + '223.87.60.27 - - [2018-07-22T00:39:02.912Z] "GET /opensearch/opensearch-1.0.0.deb_1 HTTP/1.1" 200 6219 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1"', url: 'https://artifacts.opensearch.org/downloads/opensearch/opensearch-1.0.0.deb_1', tags: 'success', bytes: 6219, machine: '{"os":"win 8","ram":8589934592}', - response: '200' + response: '200', }, { referer: 'http://www.opensearch-opensearch-opensearch.com/success/james-mcdivitt', @@ -218,7 +221,8 @@ export const JSON_DATA_ALL = [ agent: 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1', extension: '', memory: 'null', - geo: '{"srcdest":"JP:IN","src":"JP","coordinates":{"lat":38.58338806,"lon":-86.46248778},"dest":"IN"}', + geo: + '{"srcdest":"JP:IN","src":"JP","coordinates":{"lat":38.58338806,"lon":-86.46248778},"dest":"IN"}', utc_time: '2021-11-14 03:26:21.326', clientip: '130.246.123.197', host: 'www.opensearch.org', @@ -227,7 +231,8 @@ export const JSON_DATA_ALL = [ timestamp: '2021-11-14 03:26:21.326', ip: '130.246.123.197', index: 'opensearch_dashboards_sample_data_logs', - message: '130.246.123.197 - - [2018-07-22T03:26:21.326Z] "GET /beats/metricbeat_1 HTTP/1.1" 200 6850 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1"', + message: + '130.246.123.197 - - [2018-07-22T03:26:21.326Z] "GET /beats/metricbeat_1 HTTP/1.1" 200 6850 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1"', url: 'https://www.opensearch.org/downloads/beats/metricbeat_1', tags: 'success', bytes: 6850, @@ -237,10 +242,12 @@ export const JSON_DATA_ALL = [ { referer: 'http://twitter.com/success/konstantin-feoktistov', request: '/styles/main.css', - agent: 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24', + agent: + 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24', extension: 'css', memory: 'null', - geo: '{"srcdest":"CO:DE","src":"CO","coordinates":{"lat":36.96015,"lon":-78.18499861},"dest":"DE"}', + geo: + '{"srcdest":"CO:DE","src":"CO","coordinates":{"lat":36.96015,"lon":-78.18499861},"dest":"DE"}', utc_time: '2021-11-14 03:30:25.131', clientip: '120.49.143.213', host: 'cdn.opensearch-opensearch-opensearch.org', @@ -249,18 +256,19 @@ export const JSON_DATA_ALL = [ timestamp: '2021-11-14 03:30:25.131', ip: '120.49.143.213', index: 'opensearch_dashboards_sample_data_logs', - message: '120.49.143.213 - - [2018-07-22T03:30:25.131Z] "GET /styles/main.css_1 HTTP/1.1" 503 0 "-" "Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24"', + message: + '120.49.143.213 - - [2018-07-22T03:30:25.131Z] "GET /styles/main.css_1 HTTP/1.1" 503 0 "-" "Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24"', url: 'https://cdn.opensearch-opensearch-opensearch.org/styles/main.css_1', tags: 'success', bytes: 0, machine: '{"os":"ios","ram":20401094656}', - response: '503' + response: '503', }, ]; export const AGENT_FIELD = { name: 'agent', - type: 'string' + type: 'string', }; export const SAVED_HISTORIES = [ @@ -271,21 +279,22 @@ export const SAVED_HISTORIES = [ savedVisualization: { description: '', name: 'Mock Flight count by destination save to panel', - query: 'source = opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by Carrier', + query: + 'source = opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by Carrier', type: 'bar', selected_date_range: { end: 'now', start: 'now-15m', - text: '' + text: '', }, selected_fields: { text: '', - tokens: [] + tokens: [], }, selected_timestamp: { name: 'timestamp', - type: 'timestamp' - } + type: 'timestamp', + }, }, tenant: '', }, @@ -296,24 +305,25 @@ export const SAVED_HISTORIES = [ savedVisualization: { description: '', name: 'Mock Flight count by destination', - query: 'source = opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by Carrier', + query: + 'source = opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by Carrier', type: 'bar', selected_date_range: { end: 'now', start: 'now-15m', - text: '' + text: '', }, selected_fields: { text: '', - tokens: [] + tokens: [], }, selected_timestamp: { name: 'timestamp', - type: 'timestamp' - } + type: 'timestamp', + }, }, tenant: '', - } + }, ]; export const SELECTED_PANELS_OPTIONS = [ @@ -323,7 +333,7 @@ export const SELECTED_PANELS_OPTIONS = [ dateCreated: 1637781403888, dateModified: 1637781403888, id: 'uRZgU30B661cwDZT-ILw', - name: '[Logs] Web traffic Panel' + name: '[Logs] Web traffic Panel', }, }, { @@ -332,9 +342,9 @@ export const SELECTED_PANELS_OPTIONS = [ dateCreated: 1637781403888, dateModified: 1637781403888, id: 'uRZgU30B661cwDZT-ILw', - name: '[Logs] Web traffic Panel' + name: '[Logs] Web traffic Panel', }, - } + }, ]; export const DATA_GRID_ROWS = [ @@ -346,9 +356,9 @@ export const DATA_GRID_ROWS = [ DestAirportID: 'SYD', DestCityName: 'Sydney', DestCountry: 'AU', - DestLocation: '{\"lat\":-33.94609833,\"lon\":151.177002}', + DestLocation: '{"lat":-33.94609833,"lon":151.177002}', DestRegion: 'SE-BD', - DestWeather: "Rain", + DestWeather: 'Rain', DistanceKilometers: 16492.326, DistanceMiles: 10247.856, FlightDelay: 'false', @@ -358,14 +368,14 @@ export const DATA_GRID_ROWS = [ FlightTimeHour: '17.179506930998397', FlightTimeMin: 1030.7704, Origin: 'Frankfurt am Main Airport', - OriginAirportID: "FRA", + OriginAirportID: 'FRA', OriginCityName: 'Frankfurt am Main', OriginCountry: 'DE', - OriginLocation: '{\"lat\":50.033333,\"lon\":8.570556}', + OriginLocation: '{"lat":50.033333,"lon":8.570556}', OriginRegion: 'DE-HE', OriginWeather: 'Sunny', dayOfWeek: 0, - timestamp: "2021-05-24 00:00:00" + timestamp: '2021-05-24 00:00:00', }, { AvgTicketPrice: 882.98267, @@ -375,9 +385,9 @@ export const DATA_GRID_ROWS = [ DestAirportID: 'VE05', DestCityName: 'Venice', DestCountry: 'IT', - DestLocation: '{\"lat\":45.505299,\"lon\":12.3519}', + DestLocation: '{"lat":45.505299,"lon":12.3519}', DestRegion: 'IT-34', - DestWeather: "Sunny", + DestWeather: 'Sunny', DistanceKilometers: 8823.4, DistanceMiles: 5482.6064, FlightDelay: 'false', @@ -387,52 +397,48 @@ export const DATA_GRID_ROWS = [ FlightTimeHour: '7.73982468459836', FlightTimeMin: 464.3895, Origin: 'Cape Town International Airport', - OriginAirportID: "CPT", + OriginAirportID: 'CPT', OriginCityName: 'Cape Town', OriginCountry: 'ZA', - OriginLocation: '{\"lat\":-33.96480179,\"lon\":18.60169983}', + OriginLocation: '{"lat":-33.96480179,"lon":18.60169983}', OriginRegion: 'SE-BD', OriginWeather: 'Clear', dayOfWeek: 0, - timestamp: "2021-05-24 18:27:00" - } + timestamp: '2021-05-24 18:27:00', + }, ]; export const SAMPLE_VISUALIZATIONS = { data: { 'count()': [2549, 9337, 1173], - 'span(timestamp,1M)': [ - '2021-05-01 00:00:00', - '2021-06-01 00:00:00', - '2021-07-01 00:00:00' - ], + 'span(timestamp,1M)': ['2021-05-01 00:00:00', '2021-06-01 00:00:00', '2021-07-01 00:00:00'], }, jsonData: [ { 'count()': 2549, - 'span(timestamp,1M)': '2021-05-01 00:00:00' + 'span(timestamp,1M)': '2021-05-01 00:00:00', }, { 'count()': 9337, - 'span(timestamp,1M)': '2021-06-01 00:00:00' + 'span(timestamp,1M)': '2021-06-01 00:00:00', }, { 'count()': 2549, - 'span(timestamp,1M)': '2021-07-01 00:00:00' - } + 'span(timestamp,1M)': '2021-07-01 00:00:00', + }, ], metadata: { fields: [ { name: 'count()', - type: 'integer' + type: 'integer', }, { name: 'span(timestamp,1M)', - type: 'timestamp' - } - ] - } + type: 'timestamp', + }, + ], + }, }; export const VISUALIZATION_TYPES = [ @@ -441,28 +447,28 @@ export const VISUALIZATION_TYPES = [ id: 'bar', label: 'bar', selection: { - dataLoss: 'nothing' + dataLoss: 'nothing', }, - visualizationid: 'vis-bar-6636' + visualizationid: 'vis-bar-6636', }, { fulllabel: 'H. Bar', id: 'horizontal_bar', label: 'H. Bar', selection: { - dataLoss: 'nothing' + dataLoss: 'nothing', }, - visualizationid: 'vis-bar-6637' + visualizationid: 'vis-bar-6637', }, { fulllabel: 'Line', id: 'line', label: 'line', selection: { - dataLoss: 'nothing' + dataLoss: 'nothing', }, - visualizationid: 'vis-bar-6638' - } + visualizationid: 'vis-bar-6638', + }, ]; export const LAYOUT_CONFIG = { @@ -488,47 +494,61 @@ export const EXPLORER_FIELDS = { export const EXPLORER_VISUALIZATIONS = { data: { 'count()': [154, 1753, 116, 468, 1964, 219], - tags:["error", "info", "login", "security", "success", "warning"], + tags: ['error', 'info', 'login', 'security', 'success', 'warning'], }, jsonData: [ - {'count()': 154, tags: "error"}, - {'count()': 1753, tags: "info"}, - {'count()': 116, tags: "login"}, - {'count()': 468, tags: "security"}, - {'count()': 1964, tags: "success"}, - {'count()': 219, tags: "warning"} + { 'count()': 154, tags: 'error' }, + { 'count()': 1753, tags: 'info' }, + { 'count()': 116, tags: 'login' }, + { 'count()': 468, tags: 'security' }, + { 'count()': 1964, tags: 'success' }, + { 'count()': 219, tags: 'warning' }, ], metadata: { fields: [ - {name: "count()", type: "integer"}, - {name: "tags", type: "text"} + { name: 'count()', type: 'integer' }, + { name: 'tags', type: 'text' }, ], size: 6, - status: 200 + status: 200, }, }; +export const VALUE_OPTIONS = { + dimensions: [{ name: 'tags', type: 'text', label: 'tags' }], + metrics: [{ name: 'count()', type: 'integer', label: 'count()', side: 'left' }], +}; + export const TEST_VISUALIZATIONS_DATA = { data: { - appData: {fromApp: false}, + appData: { fromApp: false }, defaultAxes: {}, indexFields: EXPLORER_FIELDS, query: {}, rawVizData: EXPLORER_VISUALIZATIONS, - userConfigs: {} + userConfigs: { + dataConfig: { + valueOptions: VALUE_OPTIONS, + }, + }, }, - vis: createBarTypeDefinition({}) + vis: createBarTypeDefinition({}), }; export const PIE_TEST_VISUALIZATIONS_DATA = { data: { ...TEST_VISUALIZATIONS_DATA.data, defaultAxes: { - xaxis: [{name: "tags", type: "text"}], - yaxis: [{name: "count()", type: "integer"}] - } + xaxis: [{ name: 'tags', type: 'text' }], + yaxis: [{ name: 'count()', type: 'integer' }], + }, }, vis: { - ...TEST_VISUALIZATIONS_DATA.vis - } + ...TEST_VISUALIZATIONS_DATA.vis, + }, +}; + +export const GAUGE_TEST_VISUALIZATIONS_DATA = { + ...TEST_VISUALIZATIONS_DATA, + vis: createGaugeTypeDefinition(), }; From 46b26c5404653eabfffd4cc80fa4bb01f76d18e8 Mon Sep 17 00:00:00 2001 From: DipraAich <61182817+DipraAich@users.noreply.github.com> Date: Sat, 17 Sep 2022 08:13:42 +0530 Subject: [PATCH 14/20] [Bug]: Date Histogram 2 way sync issue fix (#1033) * Date-histogram 2 way sync issue fixed Signed-off-by: Dipra Aich * Test run snapshot update changes Signed-off-by: Dipra Aich Signed-off-by: Dipra Aich --- .../event_analytics/explorer/explorer.tsx | 47 +++- .../__snapshots__/gauge.test.tsx.snap | 205 ++++++--------- .../__tests__/__snapshots__/pie.test.tsx.snap | 243 ++++++------------ .../charts/helpers/viz_types.ts | 10 +- 4 files changed, 190 insertions(+), 315 deletions(-) diff --git a/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx b/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx index b1c2751f5..dca34b9d3 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx @@ -143,7 +143,9 @@ export const Explorer = ({ const [browserTabFocus, setBrowserTabFocus] = useState(true); const [liveTimestamp, setLiveTimestamp] = useState(DATE_PICKER_FORMAT); const [triggerAvailability, setTriggerAvailability] = useState(false); - const [isValidDataConfigOptionSelected, setIsValidDataConfigOptionSelected] = useState(false); + const [isValidDataConfigOptionSelected, setIsValidDataConfigOptionSelected] = useState( + false + ); const queryRef = useRef(); const appBasedRef = useRef(''); @@ -491,7 +493,6 @@ export const Explorer = ({ handleQuerySearch(availability); }; - /** * Toggle fields between selected and unselected sets * @param field field to be toggled @@ -630,7 +631,7 @@ export const Explorer = ({ 0 )} showResetButton={false} - onResetQuery={() => {}} + onResetQuery={() => { }} /> @@ -671,7 +672,7 @@ export const Explorer = ({ {}} + onResetQuery={() => { }} /> since {liveTimestamp} @@ -746,7 +747,7 @@ export const Explorer = ({ }; const changeIsValidConfigOptionState = (isValidConfig: Boolean) => - setIsValidDataConfigOptionSelected(isValidConfig); + setIsValidDataConfigOptionSelected(isValidConfig); const getExplorerVis = () => { return ( @@ -825,14 +826,12 @@ export const Explorer = ({ const handleQuerySearch = useCallback( async (availability?: boolean) => { - // clear previous selected timestamp when index pattern changes if ( !isEmpty(tempQuery) && !isEmpty(query[RAW_QUERY]) && isIndexPatternChanged(tempQuery, query[RAW_QUERY]) ) { - await updateCurrentTimeStamp(''); } if (availability !== true) { @@ -843,12 +842,31 @@ export const Explorer = ({ if (selectedContentTabId === TAB_CHART_ID) { // parse stats section on every search const qm = new QueryManager(); - const statsTokens = - qm - .queryParser() - .parse(tempQuery) - .getStats(); - + const statsTokens = qm.queryParser().parse(tempQuery).getStats(); + const timeUnitValue = TIME_INTERVAL_OPTIONS.find( + (time_unit) => time_unit.value === statsTokens.groupby?.span.span_expression.time_unit + )?.text; + const span = + statsTokens.groupby?.span !== null + ? { + time_field: [ + { + name: statsTokens.groupby?.span.span_expression.field, + type: 'timestamp', + label: statsTokens.groupby?.span.span_expression.field, + }, + ], + unit: [ + { + text: timeUnitValue, + value: statsTokens.groupby?.span.span_expression.time_unit, + label: timeUnitValue, + }, + ], + interval: statsTokens.groupby?.span.span_expression.literal_value, + } + : undefined; + await dispatch( changeVizConfig({ tabId, @@ -864,6 +882,7 @@ export const Explorer = ({ label: agg.name ?? '', name: agg.name ?? '', })), + span, }, }, }) @@ -1190,7 +1209,7 @@ export const Explorer = ({ explorerFields, explorerData, http, - query + query, }} >
diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/gauge.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/gauge.test.tsx.snap index ad8e359f1..742cbbc53 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/gauge.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/gauge.test.tsx.snap @@ -384,139 +384,82 @@ exports[`Gauge component Renders gauge component 1`] = ` } } > - -
- - + className="euiText euiText--extraSmall lnsChart__empty" + data-test-subj="vizWorkspace__noData" + > + +
+ +
+ + + + + + +
+ +

+ + + No results found + + +

+
+ +
+ +
+ + `; diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap index e36bda4ca..ec6cad064 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/pie.test.tsx.snap @@ -553,177 +553,82 @@ exports[`Pie component Renders pie component 1`] = ` } } > - -
- - + className="euiText euiText--extraSmall lnsChart__empty" + data-test-subj="vizWorkspace__noData" + > + +
+ +
+ + + + + + +
+ +

+ + + No results found + + +

+
+ +
+ +
+ + `; diff --git a/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts b/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts index ef4b0c320..48124408d 100644 --- a/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts +++ b/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts @@ -13,6 +13,7 @@ import { } from '../../../../../common/types/explorer'; import { visChartTypes } from '../../../../../common/constants/shared'; import { QueryManager } from '../../../../../common/query_manager'; +import { TIME_INTERVAL_OPTIONS } from '../../../../../common/constants/explorer'; interface IVizContainerProps { vizId: string; appData?: { fromApp: boolean }; @@ -92,7 +93,14 @@ const defaultUserConfigs = (queryString, visualizationName: string) => { : [], interval: statsTokens.groupby?.span?.span_expression?.literal_value ?? '0', unit: statsTokens.groupby?.span?.span_expression?.time_unit - ? [getStandardedOuiField(statsTokens.groupby?.span?.span_expression?.time_unit)] + ? [ + getStandardedOuiField( + TIME_INTERVAL_OPTIONS.find( + (time_unit) => + time_unit.value === statsTokens.groupby?.span.span_expression.time_unit + )?.text + ), + ] : [], }, }; From e05fb74b9189fde5ca3dedafc6c490b8aaf09ca0 Mon Sep 17 00:00:00 2001 From: DipraAich <61182817+DipraAich@users.noreply.github.com> Date: Tue, 20 Sep 2022 02:46:20 +0530 Subject: [PATCH 15/20] [Feature]: Heatmap 2way sync (#1031) * Heatmap doesnt render if more than 1 metric or 2 dimensions are added Signed-off-by: Dipra Aich * Test cases resolution Signed-off-by: Dipra Aich * Eliminated unnecessary commented code Signed-off-by: Dipra Aich Signed-off-by: Dipra Aich --- .../public/components/visualizations/charts/maps/heatmap.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx b/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx index 30dce8305..221ff5556 100644 --- a/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx +++ b/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx @@ -37,8 +37,9 @@ export const HeatMap = ({ visualizations, layout, config }: any) => { isEmpty(zMetrics) || isEmpty(data[xaxisField.label]) || isEmpty(data[yaxisField.label]) || - isEmpty(data[`${zMetrics.aggregation}(${zMetrics.name})`]) - // indexOf(NUMERICAL_FIELDS, zMetrics.type) < 0 + isEmpty(data[`${zMetrics.aggregation}(${zMetrics.name})`]) || + dataConfig?.dimensions.length > 2 || + dataConfig?.metrics.length > 1 ) return ; From 0a5d543bd3f88627e0583a87af5eb801135556b3 Mon Sep 17 00:00:00 2001 From: vkushwah <77838340+vkushwah@users.noreply.github.com> Date: Tue, 20 Sep 2022 02:46:38 +0530 Subject: [PATCH 16/20] fixed backspace crashing UI issue (#1034) Signed-off-by: Vishal Kushwah Signed-off-by: Vishal Kushwah Co-authored-by: Vishal Kushwah --- .../config_panes/config_controls/data_config_panel_item.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx index 87d6802d0..7e7d54f26 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx @@ -389,12 +389,13 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => value={configList.span?.interval ?? 1} min={1} onChange={(e) => { + e.persist(); setConfigList((staleState) => { return { ...staleState, span: { ...staleState.span, - interval: e.target.value, + interval: e.target?.value ?? 1, }, }; }); From 52f91684a79d5c58333b1adb0559a7a7e1268d34 Mon Sep 17 00:00:00 2001 From: ruchika-narang <79983862+ruchika-narang@users.noreply.github.com> Date: Tue, 20 Sep 2022 02:46:54 +0530 Subject: [PATCH 17/20] [Feature]: Timeseries/Scatter 2 way sync (#1030) * Fixed 2 way sync issues for line Signed-off-by: ruchika-narang * Updated snapshots Signed-off-by: ruchika-narang * Removed extra line Signed-off-by: ruchika-narang Signed-off-by: ruchika-narang --- .../config_controls/data_config_panel_item.tsx | 11 ++++++++--- .../visualizations/charts/lines/line.tsx | 18 +++++++----------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx index 7e7d54f26..adf8a6b51 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx @@ -156,14 +156,17 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => }; const isPositionButtonVisible = (sectionName: string) => - sectionName === 'metrics' && visualizations.vis.name === visChartTypes.Line; + sectionName === 'metrics' && + (visualizations.vis.name === visChartTypes.Line || + visualizations.vis.name === visChartTypes.Scatter); const getOptionsAvailable = (sectionName: string) => { const selectedFields = {}; const unselectedFields = fieldOptionList.filter((field) => !selectedFields[field.label]); return sectionName === 'metrics' ? unselectedFields - : visualizations.vis.name === visChartTypes.Line + : visualizations.vis.name === visChartTypes.Line || + visualizations.vis.name === visChartTypes.Scatter ? unselectedFields.filter((i) => i.type === 'timestamp') : unselectedFields; }; @@ -285,7 +288,9 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => color="primary" onClick={() => handleServiceAdd(sectionName)} disabled={ - sectionName === 'dimensions' && visualizations.vis.name === visChartTypes.Line + sectionName === 'dimensions' && + (visualizations.vis.name === visChartTypes.Line || + visualizations.vis.name === visChartTypes.Scatter) } > Add diff --git a/dashboards-observability/public/components/visualizations/charts/lines/line.tsx b/dashboards-observability/public/components/visualizations/charts/lines/line.tsx index c5d55ffef..7234d4430 100644 --- a/dashboards-observability/public/components/visualizations/charts/lines/line.tsx +++ b/dashboards-observability/public/components/visualizations/charts/lines/line.tsx @@ -77,18 +77,14 @@ export const Line = ({ visualizations, layout, config }: any) => { dataConfig.colorTheme.find((colorSelected) => colorSelected.name.name === field.name) ?.color) || PLOTLY_COLOR[index % PLOTLY_COLOR.length]; - /** - * determine x axis - */ - const xaxis = useMemo(() => { - // span selection - const timestampField = find(fields, (field) => field.type === 'timestamp'); - if (dataConfig.span && dataConfig.span.time_field && timestampField) { - return [timestampField, ...dataConfig.dimensions]; - } + let xaxis; + const timestampField = find(fields, (field) => field.type === 'timestamp'); - return dataConfig.dimensions; - }, [dataConfig.dimensions]); + if (dataConfig.span && dataConfig.span.time_field && timestampField) { + xaxis = [timestampField, ...dataConfig.dimensions]; + } else { + xaxis = dataConfig.dimensions; + } if (isEmpty(xaxis) || isEmpty(yaxis)) return ; From 61ab8f67b29c91836bc0f571f78d2994e5b060fa Mon Sep 17 00:00:00 2001 From: ramneet-persistent <105915936+ramneet-persistent@users.noreply.github.com> Date: Tue, 20 Sep 2022 22:29:59 +0530 Subject: [PATCH 18/20] Move qm to plugins #1023 (#1036) * move qm: in progress Signed-off-by: Ramneet Chopra * qm instances prop adedd + yarn test Signed-off-by: Ramneet Chopra * main rebase Signed-off-by: Ramneet Chopra * explorereFields type fixed Signed-off-by: Ramneet Chopra * querymanager types added, renamed to qm Signed-off-by: Ramneet Chopra Signed-off-by: Ramneet Chopra --- .../common/types/explorer.ts | 43 +++++++++++++++++- .../public/components/app.tsx | 4 ++ .../event_analytics/explorer/explorer.tsx | 44 +++++++++---------- .../event_analytics/explorer/log_explorer.tsx | 2 + .../data_config_panel_item.tsx | 10 +++-- .../logs_view_config_panel_item.tsx | 15 +++++-- .../treemap_config_panel_item.tsx | 13 +++--- .../explorer/visualizations/index.tsx | 29 +++++++----- .../components/event_analytics/index.tsx | 6 ++- .../public/components/index.tsx | 5 ++- .../charts/helpers/viz_types.ts | 6 +-- dashboards-observability/public/plugin.ts | 6 ++- 12 files changed, 127 insertions(+), 56 deletions(-) diff --git a/dashboards-observability/common/types/explorer.ts b/dashboards-observability/common/types/explorer.ts index a734d7150..dade11d67 100644 --- a/dashboards-observability/common/types/explorer.ts +++ b/dashboards-observability/common/types/explorer.ts @@ -5,6 +5,7 @@ import { History } from 'history'; import Plotly from 'plotly.js-dist'; +import { QueryManager } from 'common/query_manager'; import { RAW_QUERY, SELECTED_FIELDS, @@ -16,12 +17,18 @@ import { SELECTED_TIMESTAMP, SELECTED_DATE_RANGE, } from '../constants/explorer'; -import { CoreStart, HttpSetup, HttpStart, NotificationsStart } from '../../../../src/core/public'; +import { + CoreStart, + CoreSetup, + HttpSetup, + HttpStart, + NotificationsStart, +} from '../../../../src/core/public'; import SavedObjects from '../../public/services/saved_objects/event_analytics/saved_objects'; import TimestampUtils from '../../public/services/timestamp/timestamp'; import PPLService from '../../public/services/requests/ppl'; import DSLService from '../../public/services/requests/dsl'; - +import { SavedObjectsStart } from '../../../../src/core/public/saved_objects'; export interface IQueryTab { id: string; name: React.ReactNode | string; @@ -34,6 +41,13 @@ export interface IField { label?: string; } +export interface ExplorerFields { + availableFields: IField[]; + queriedFields: IField[]; + selectedFields: IField[]; + unselectedFields: IField[]; +} + export interface ITabQueryResults { [tabId: string]: any; } @@ -83,6 +97,7 @@ export interface ILogExplorerProps { ) => void; savedObjectId: string; getExistingEmptyTab: (params: EmptyTabParams) => string; + qm: QueryManager; } export interface IExplorerProps { @@ -113,6 +128,7 @@ export interface IExplorerProps { appBaseQuery?: string; callback?: any; callbackInApp?: any; + qm: QueryManager; } export interface SavedQuery { @@ -283,3 +299,26 @@ export interface ConfigList { breakdowns?: ConfigListEntry[] | HistogramConfigList[]; span?: DimensionSpan; } + +export interface Breadcrumbs { + text: string; + href: string; +} + +export interface EventAnalyticsProps { + chrome: CoreSetup; + parentBreadcrumbs: Breadcrumbs[]; + pplService: any; + dslService: any; + savedObjects: SavedObjectsStart; + timestampUtils: TimestampUtils; + http: HttpStart; + notifications: NotificationsStart; + qm: QueryManager; +} + +export interface DataConfigPanelProps { + fieldOptionList: IField[]; + visualizations: IVisualizationContainerProps; + qm?: QueryManager; +} diff --git a/dashboards-observability/public/components/app.tsx b/dashboards-observability/public/components/app.tsx index b4d164331..6e69cd69d 100644 --- a/dashboards-observability/public/components/app.tsx +++ b/dashboards-observability/public/components/app.tsx @@ -7,6 +7,7 @@ import { I18nProvider } from '@osd/i18n/react'; import React from 'react'; import { Provider } from 'react-redux'; import { HashRouter, Route, Switch } from 'react-router-dom'; +import { QueryManager } from 'common/query_manager'; import { CoreStart } from '../../../../src/core/public'; import { observabilityID, observabilityTitle } from '../../common/constants/shared'; import store from '../framework/redux/store'; @@ -24,6 +25,7 @@ interface ObservabilityAppDeps { dslService: any; savedObjects: any; timestampUtils: any; + qm: QueryManager; } // for cypress to test redux store @@ -38,6 +40,7 @@ export const App = ({ dslService, savedObjects, timestampUtils, + qm, }: ObservabilityAppDeps) => { const { chrome, http, notifications } = CoreStartProp; const parentBreadcrumb = { @@ -130,6 +133,7 @@ export const App = ({ timestampUtils={timestampUtils} http={http} notifications={notifications} + qm={qm} {...props} /> ); diff --git a/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx b/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx index dca34b9d3..dc2fe3b53 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/explorer.tsx @@ -78,7 +78,6 @@ import { getVizContainerProps } from '../../visualizations/charts/helpers'; import { parseGetSuggestions, onItemSelect } from '../../common/search/autocomplete_logic'; import { formatError } from '../utils'; import { sleep } from '../../common/live_tail/live_tail_button'; -import { QueryManager } from '../../../../common/query_manager/ppl_query_manager'; const TYPE_TAB_MAPPING = { [SAVED_QUERY]: TAB_EVENT_ID, @@ -107,6 +106,7 @@ export const Explorer = ({ setEndTime, callback, callbackInApp, + qm, }: IExplorerProps) => { const dispatch = useDispatch(); const requestParams = { tabId }; @@ -143,7 +143,7 @@ export const Explorer = ({ const [browserTabFocus, setBrowserTabFocus] = useState(true); const [liveTimestamp, setLiveTimestamp] = useState(DATE_PICKER_FORMAT); const [triggerAvailability, setTriggerAvailability] = useState(false); - const [isValidDataConfigOptionSelected, setIsValidDataConfigOptionSelected] = useState( + const [isValidDataConfigOptionSelected, setIsValidDataConfigOptionSelected] = useState( false ); @@ -631,7 +631,7 @@ export const Explorer = ({ 0 )} showResetButton={false} - onResetQuery={() => { }} + onResetQuery={() => {}} /> @@ -672,7 +672,7 @@ export const Explorer = ({ { }} + onResetQuery={() => {}} /> since {liveTimestamp} @@ -746,7 +746,7 @@ export const Explorer = ({ } }; - const changeIsValidConfigOptionState = (isValidConfig: Boolean) => + const changeIsValidConfigOptionState = (isValidConfig: boolean) => setIsValidDataConfigOptionSelected(isValidConfig); const getExplorerVis = () => { @@ -764,6 +764,7 @@ export const Explorer = ({ handleOverrideTimestamp={handleOverrideTimestamp} callback={callbackForConfig} changeIsValidConfigOptionState={changeIsValidConfigOptionState} + qm={qm} /> ); }; @@ -841,7 +842,6 @@ export const Explorer = ({ if (selectedContentTabId === TAB_CHART_ID) { // parse stats section on every search - const qm = new QueryManager(); const statsTokens = qm.queryParser().parse(tempQuery).getStats(); const timeUnitValue = TIME_INTERVAL_OPTIONS.find( (time_unit) => time_unit.value === statsTokens.groupby?.span.span_expression.time_unit @@ -849,22 +849,22 @@ export const Explorer = ({ const span = statsTokens.groupby?.span !== null ? { - time_field: [ - { - name: statsTokens.groupby?.span.span_expression.field, - type: 'timestamp', - label: statsTokens.groupby?.span.span_expression.field, - }, - ], - unit: [ - { - text: timeUnitValue, - value: statsTokens.groupby?.span.span_expression.time_unit, - label: timeUnitValue, - }, - ], - interval: statsTokens.groupby?.span.span_expression.literal_value, - } + time_field: [ + { + name: statsTokens.groupby?.span.span_expression.field, + type: 'timestamp', + label: statsTokens.groupby?.span.span_expression.field, + }, + ], + unit: [ + { + text: timeUnitValue, + value: statsTokens.groupby?.span.span_expression.time_unit, + label: timeUnitValue, + }, + ], + interval: statsTokens.groupby?.span.span_expression.literal_value, + } : undefined; await dispatch( diff --git a/dashboards-observability/public/components/event_analytics/explorer/log_explorer.tsx b/dashboards-observability/public/components/event_analytics/explorer/log_explorer.tsx index 53bd15be4..1d84a5815 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/log_explorer.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/log_explorer.tsx @@ -49,6 +49,7 @@ export const LogExplorer = ({ history, notifications, http, + qm, }: ILogExplorerProps) => { const dispatch = useDispatch(); const tabIds = useSelector(selectQueryTabs).queryTabIds.filter( @@ -184,6 +185,7 @@ export const LogExplorer = ({ curSelectedTabId={curSelectedTabIdRef} http={http} searchBarConfigs={searchBarConfigs} + qm={qm} /> ), diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx index adf8a6b51..53f16bff5 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/data_config_panel_item.tsx @@ -30,9 +30,8 @@ import { } from '../../../../../../../../common/constants/explorer'; import { ButtonGroupItem } from './config_button_group'; import { visChartTypes } from '../../../../../../../../common/constants/shared'; -import { ConfigList } from '../../../../../../../../common/types/explorer'; +import { ConfigList, DataConfigPanelProps } from '../../../../../../../../common/types/explorer'; import { TabContext } from '../../../../../hooks'; -import { QueryManager } from '../../../../../../../../common/query_manager'; import { composeAggregations } from '../../../../../../../../common/query_manager/utils'; const initialDimensionEntry = { @@ -47,7 +46,11 @@ const initialMetricEntry = { aggregation: 'count', }; -export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => { +export const DataConfigPanelItem = ({ + fieldOptionList, + visualizations, + qm, +}: DataConfigPanelProps) => { const dispatch = useDispatch(); const { tabId, handleQuerySearch, handleQueryChange, setTempQuery, fetchData } = useContext( TabContext @@ -120,7 +123,6 @@ export const DataConfigPanelItem = ({ fieldOptionList, visualizations }: any) => }; const updateChart = (updatedConfigList = configList) => { - const qm = new QueryManager(); const statsTokens = qm.queryParser().parse(data.query.rawQuery).getStats(); const newQuery = qm .queryBuilder() diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/logs_view_config_panel_item.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/logs_view_config_panel_item.tsx index 6f9bb5388..d5ac25e73 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/logs_view_config_panel_item.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/logs_view_config_panel_item.tsx @@ -15,20 +15,27 @@ import { EuiText, } from '@elastic/eui'; import { batch, useDispatch, useSelector } from 'react-redux'; +import { cloneDeep } from 'lodash'; import { AVAILABLE_FIELDS, SELECTED_FIELDS, } from '../../../../../../../../common/constants/explorer'; -import { ConfigList, ConfigListEntry } from '../../../../../../../../common/types/explorer'; +import { + ConfigList, + ConfigListEntry, + DataConfigPanelProps, +} from '../../../../../../../../common/types/explorer'; import { TabContext } from '../../../../../hooks'; import { selectFields, sortFields, updateFields, } from '../../../../../../event_analytics/redux/slices/field_slice'; -import { cloneDeep } from 'lodash'; -export const LogsViewConfigPanelItem = ({ fieldOptionList, visualizations }: any) => { +export const LogsViewConfigPanelItem = ({ + fieldOptionList, + visualizations, +}: DataConfigPanelProps) => { const dispatch = useDispatch(); const { tabId, curVisId, changeVisualizationConfig } = useContext(TabContext); const explorerFields = useSelector(selectFields)[tabId]; @@ -159,7 +166,7 @@ export const LogsViewConfigPanelItem = ({ fieldOptionList, visualizations }: any }; const getLogsViewUI = () => { - let list = configList.dimensions ? configList.dimensions : []; + const list = configList.dimensions ? configList.dimensions : []; const listUI = list.map((field, index) => ( { +export const TreemapConfigPanelItem = ({ + fieldOptionList, + visualizations, + qm, +}: DataConfigPanelProps) => { const dispatch = useDispatch(); const { tabId, curVisId, changeVisualizationConfig, fetchData, handleQueryChange } = useContext< any @@ -51,7 +55,7 @@ export const TreemapConfigPanelItem = ({ fieldOptionList, visualizations, tabID }, [userConfigs?.dataConfig, visualizations.vis.name]); const updateList = (configName: string, fieldName: string, value: string | any[]) => { - let list = { ...configList }; + const list = { ...configList }; let listItem = { ...list[configName][0] }; const newField = { @@ -68,7 +72,6 @@ export const TreemapConfigPanelItem = ({ fieldOptionList, visualizations, tabID }; const updateChart = (updatedConfigList = configList) => { - const qm = new QueryManager(); const statsTokens = qm.queryParser().parse(data.query.rawQuery).getStats(); const newQuery = qm .queryBuilder() @@ -103,7 +106,7 @@ export const TreemapConfigPanelItem = ({ fieldOptionList, visualizations, tabID const getOptionsAvailable = (sectionName: string) => { const { dimensions, metrics } = configList; - let selectedFields = {}; + const selectedFields = {}; let allSelectedFields = []; for (const key in configList) { diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx index 2e51c1a7e..17d817bc3 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx @@ -9,8 +9,14 @@ import { isEmpty } from 'lodash'; import React, { useContext } from 'react'; import { EuiPanel, EuiResizableContainer, EuiSpacer } from '@elastic/eui'; +import { QueryManager } from 'common/query_manager'; import { RAW_QUERY, SELECTED_TIMESTAMP } from '../../../../../common/constants/explorer'; -import { IField, IQuery, IVisualizationContainerProps } from '../../../../../common/types/explorer'; +import { + IField, + IQuery, + IVisualizationContainerProps, + ExplorerFields, +} from '../../../../../common/types/explorer'; import { WorkspacePanel } from './workspace_panel'; import { ConfigPanel } from './config_panel'; import { Sidebar } from '../sidebar'; @@ -19,19 +25,21 @@ import { TabContext } from '../../hooks'; import { PPL_STATS_REGEX, visChartTypes } from '../../../../../common/constants/shared'; import { TreemapConfigPanelItem } from './config_panel/config_panes/config_controls/treemap_config_panel_item'; import { LogsViewConfigPanelItem } from './config_panel/config_panes/config_controls/logs_view_config_panel_item'; + interface IExplorerVisualizationsProps { query: IQuery; curVisId: string; setCurVisId: (visId: string) => void; explorerVis: any; - explorerFields: IField[]; + explorerFields: ExplorerFields; explorerData: any; handleAddField: (field: IField) => void; handleRemoveField: (field: IField) => void; visualizations: IVisualizationContainerProps; handleOverrideTimestamp: (field: IField) => void; callback?: any; - changeIsValidConfigOptionState: (isValidConfigOptionSelected: Boolean) => void; + changeIsValidConfigOptionState: (isValidConfigOptionSelected: boolean) => void; + qm: QueryManager; } export const ExplorerVisualizations = ({ @@ -47,15 +55,15 @@ export const ExplorerVisualizations = ({ handleOverrideTimestamp, callback, changeIsValidConfigOptionState, + qm, }: IExplorerVisualizationsProps) => { const { tabId } = useContext(TabContext); const { data, vis } = visualizations; const { data: vizData = {}, metadata: { fields = [] } = {} } = data?.rawVizData; - - const fieldOptionList = explorerFields.availableFields.map((field) => { - // const fieldOptionList = fields.map((field) => { - return { ...field, label: field.name }; - }); + const fieldOptionList = explorerFields.availableFields.map((field) => ({ + ...field, + label: field.name, + })); const renderDataConfigContainer = () => { switch (curVisId) { @@ -64,7 +72,7 @@ export const ExplorerVisualizations = ({ ); case visChartTypes.LogsView: @@ -72,7 +80,6 @@ export const ExplorerVisualizations = ({ ); default: @@ -80,7 +87,7 @@ export const ExplorerVisualizations = ({ ); } diff --git a/dashboards-observability/public/components/event_analytics/index.tsx b/dashboards-observability/public/components/event_analytics/index.tsx index edac5e494..42265ba4f 100644 --- a/dashboards-observability/public/components/event_analytics/index.tsx +++ b/dashboards-observability/public/components/event_analytics/index.tsx @@ -5,7 +5,7 @@ import { EuiGlobalToastList } from '@elastic/eui'; import { Toast } from '@elastic/eui/src/components/toast/global_toast_list'; -import { EmptyTabParams } from 'common/types/explorer'; +import { EmptyTabParams, EventAnalyticsProps } from 'common/types/explorer'; import { isEmpty } from 'lodash'; import React, { ReactChild, useState } from 'react'; import { HashRouter, Route, Switch, useHistory } from 'react-router-dom'; @@ -23,8 +23,9 @@ export const EventAnalytics = ({ timestampUtils, http, notifications, + qm, ...props -}: any) => { +}: EventAnalyticsProps) => { const history = useHistory(); const [toasts, setToasts] = useState([]); @@ -84,6 +85,7 @@ export const EventAnalytics = ({ getExistingEmptyTab={getExistingEmptyTab} history={history} notifications={notifications} + qm={qm} /> ); }} diff --git a/dashboards-observability/public/components/index.tsx b/dashboards-observability/public/components/index.tsx index 267cb6b5d..be4f1a040 100644 --- a/dashboards-observability/public/components/index.tsx +++ b/dashboards-observability/public/components/index.tsx @@ -5,6 +5,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { QueryManager } from 'common/query_manager'; import { AppMountParameters, CoreStart } from '../../../../src/core/public'; import { AppPluginStartDependencies } from '../types'; import { App } from './app'; @@ -16,7 +17,8 @@ export const Observability = ( pplService: any, dslService: any, savedObjects: any, - timestampUtils: any + timestampUtils: any, + qm: QueryManager ) => { ReactDOM.render( , AppMountParametersProp.element ); diff --git a/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts b/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts index 48124408d..e23968693 100644 --- a/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts +++ b/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts @@ -44,12 +44,12 @@ const initialEntryTreemap = { label: '', name: '' }; const getDefaultXYAxisLabels = (vizFields: IField[], visName: string) => { if (isEmpty(vizFields)) return {}; - const vizFieldsWithLabel: { [key: string]: string }[] = vizFields.map((vizField) => ({ + const vizFieldsWithLabel: Array<{ [key: string]: string }> = vizFields.map((vizField) => ({ ...vizField, label: vizField.name, })); - const mapXaxis = (): { [key: string]: string }[] => { + const mapXaxis = (): Array<{ [key: string]: string }> => { const xaxis = vizFieldsWithLabel.filter((field) => field.type === 'timestamp'); return visName === visChartTypes.Line ? xaxis.length === 0 @@ -58,7 +58,7 @@ const getDefaultXYAxisLabels = (vizFields: IField[], visName: string) => { : [vizFieldsWithLabel[vizFieldsWithLabel.length - 1]]; }; - const mapYaxis = (): { [key: string]: string }[] => + const mapYaxis = (): Array<{ [key: string]: string }> => visName === visChartTypes.Line ? vizFieldsWithLabel.filter((field) => field.type !== 'timestamp') : take( diff --git a/dashboards-observability/public/plugin.ts b/dashboards-observability/public/plugin.ts index 4c3b7f7a2..a14131546 100644 --- a/dashboards-observability/public/plugin.ts +++ b/dashboards-observability/public/plugin.ts @@ -17,7 +17,7 @@ import { AppPluginStartDependencies, ObservabilitySetup, ObservabilityStart } fr import { convertLegacyNotebooksUrl } from './components/notebooks/components/helpers/legacy_route_helpers'; import { convertLegacyTraceAnalyticsUrl } from './components/trace_analytics/components/common/legacy_route_helpers'; import { uiSettingsService } from '../common/utils'; - +import { QueryManager } from '../common/query_manager'; export class ObservabilityPlugin implements Plugin { public setup(core: CoreSetup): ObservabilitySetup { uiSettingsService.init(core.uiSettings, core.notifications); @@ -48,6 +48,7 @@ export class ObservabilityPlugin implements Plugin Date: Sat, 24 Sep 2022 02:16:38 +0530 Subject: [PATCH 19/20] Pie chart 2way sync (#1029) * mode bug fixed code: added, 2 way changes testing: in progress Signed-off-by: Ramneet Chopra * pie two sync done Signed-off-by: Ramneet Chopra * yarn test Signed-off-by: Ramneet Chopra * pr feedback Signed-off-by: Ramneet Chopra * spaces added in between lines Signed-off-by: Ramneet Chopra * usememo removed from xlables Signed-off-by: Ramneet Chopra Signed-off-by: Ramneet Chopra --- .../common/constants/explorer.ts | 39 ++++- .../common/types/explorer.ts | 4 + .../__snapshots__/config_panel.test.tsx.snap | 28 ++-- .../event_analytics/utils/utils.tsx | 20 ++- .../visualizations/charts/pie/pie.tsx | 154 +++++++++--------- .../visualizations/charts/pie/pie_type.ts | 62 +++---- 6 files changed, 172 insertions(+), 135 deletions(-) diff --git a/dashboards-observability/common/constants/explorer.ts b/dashboards-observability/common/constants/explorer.ts index 564c68167..d801ff9fb 100644 --- a/dashboards-observability/common/constants/explorer.ts +++ b/dashboards-observability/common/constants/explorer.ts @@ -120,7 +120,16 @@ export const AGGREGATION_OPTIONS = [ ]; // numeric fields type for metrics -export const numericalTypes = ['float', 'double', 'bigint', 'long', 'octet', 'short', 'byte', 'integer']; +export const numericalTypes = [ + 'float', + 'double', + 'bigint', + 'long', + 'octet', + 'short', + 'byte', + 'integer', +]; // Data table constants export const GRID_HEADER_COLUMN_MAX_WIDTH = '150px'; export const GRID_PAGE_RANGE_DISPLAY = 5; @@ -136,13 +145,13 @@ export const HEADER_HEIGHT = 35; // gauge chart default parameters export interface DefaultGaugeChartParametersProps { - GaugeTitleSize: number, - DisplayDefaultGauges: number, - OrientationDefault: string, - TickLength: number, - LegendPlacement: string, - ThresholdsMaxLimit: number -}; + GaugeTitleSize: number; + DisplayDefaultGauges: number; + OrientationDefault: string; + TickLength: number; + LegendPlacement: string; + ThresholdsMaxLimit: number; +} export const DefaultGaugeChartParameters: DefaultGaugeChartParametersProps = { GaugeTitleSize: 14, @@ -150,5 +159,17 @@ export const DefaultGaugeChartParameters: DefaultGaugeChartParametersProps = { OrientationDefault: 'h', TickLength: 5, LegendPlacement: 'center', - ThresholdsMaxLimit: 1 + ThresholdsMaxLimit: 1, +}; + +// pie chart default parameters +export const PLOTLY_PIE_COLUMN_NUMBER = 2; +export const PIE_XAXIS_GAP = 0.2; +export const PIE_YAXIS_GAP = 0.1; +export interface DefaultPieChartParametersProps { + DefaultMode: string; } + +export const DefaultPieChartParameters: DefaultPieChartParametersProps = { + DefaultMode: 'pie', +}; diff --git a/dashboards-observability/common/types/explorer.ts b/dashboards-observability/common/types/explorer.ts index dade11d67..2c34b30cd 100644 --- a/dashboards-observability/common/types/explorer.ts +++ b/dashboards-observability/common/types/explorer.ts @@ -322,3 +322,7 @@ export interface DataConfigPanelProps { visualizations: IVisualizationContainerProps; qm?: QueryManager; } +export interface GetTooltipHoverInfoType { + tooltipMode: string; + tooltipText: string; +} diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap index 324b855b4..3a5752339 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap @@ -1422,25 +1422,25 @@ exports[`Config panel component Renders config panel with visualization data 1`] "name": "Chart styles", "schemas": Array [ Object { - "component": null, - "defaultState": Array [ - Object { - "label": "Pie", - "modeId": "pie", - "name": "Pie", - }, - ], + "component": [Function], + "eleType": "buttons", "isSingleSelection": true, "mapTo": "mode", "name": "Mode", "props": Object { - "dropdownList": Array [ + "defaultSelections": Array [ Object { - "modeId": "pie", + "id": "pie", + "name": "Pie", + }, + ], + "options": Array [ + Object { + "id": "pie", "name": "Pie", }, Object { - "modeId": "donut", + "id": "donut", "name": "Donut", }, ], @@ -1493,12 +1493,10 @@ exports[`Config panel component Renders config panel with visualization data 1`] "id": "pie", "label": "Pie", "legendposition": "v", + "mode": "pie", "name": "pie", - "selection": Object { - "dataLoss": "nothing", - }, "seriesaxis": "yaxis", - "showlegend": true, + "showlegend": "show", "type": "pie", "visconfig": Object { "config": Object { diff --git a/dashboards-observability/public/components/event_analytics/utils/utils.tsx b/dashboards-observability/public/components/event_analytics/utils/utils.tsx index dafc1e2f2..41fe02467 100644 --- a/dashboards-observability/public/components/event_analytics/utils/utils.tsx +++ b/dashboards-observability/public/components/event_analytics/utils/utils.tsx @@ -8,7 +8,11 @@ import { uniqueId } from 'lodash'; import React from 'react'; import moment from 'moment'; import dateMath from '@elastic/datemath'; -import { IExplorerFields, IField } from '../../../../common/types/explorer'; +import { + IExplorerFields, + IField, + GetTooltipHoverInfoType, +} from '../../../../common/types/explorer'; import { DocViewRow, IDocType } from '../explorer/events_views'; import { HttpStart } from '../../../../../../src/core/public'; import PPLService from '../../../services/requests/ppl'; @@ -121,8 +125,8 @@ export const populateDataGrid = ( )} {explorerFields?.queriedFields && - explorerFields?.queriedFields?.length > 0 && - explorerFields.selectedFields?.length === 0 ? null : ( + explorerFields?.queriedFields?.length > 0 && + explorerFields.selectedFields?.length === 0 ? null : ( {header2}{body2} @@ -354,3 +358,13 @@ export const fetchConfigObject = (editor: string, propsOptions: any) => { return null; } }; + +export const getTooltipHoverInfo = ({ tooltipMode, tooltipText }: GetTooltipHoverInfoType) => { + if (tooltipMode === 'hidden') { + return 'none'; + } + if (tooltipText === undefined) { + return 'all'; + } + return tooltipText; +}; diff --git a/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx b/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx index 64b1a5cf2..89d1657fe 100644 --- a/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx +++ b/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx @@ -4,10 +4,17 @@ */ import React, { useMemo } from 'react'; -import { take, isEmpty } from 'lodash'; +import { isEmpty, find } from 'lodash'; import { Plt } from '../../plotly/plot'; -import { DEFAULT_PALETTE, HEX_CONTRAST_COLOR } from '../../../../../common/constants/colors'; import { EmptyPlaceholder } from '../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder'; +import { getTooltipHoverInfo } from '../../../event_analytics/utils/utils'; +import { ConfigListEntry } from '../../../../../common/types/explorer'; +import { DEFAULT_PALETTE, HEX_CONTRAST_COLOR } from '../../../../../common/constants/colors'; +import { + PLOTLY_PIE_COLUMN_NUMBER, + PIE_YAXIS_GAP, + PIE_XAXIS_GAP, +} from '../../../../../common/constants/explorer'; export const Pie = ({ visualizations, layout, config }: any) => { const { vis } = visualizations; @@ -15,70 +22,63 @@ export const Pie = ({ visualizations, layout, config }: any) => { data, metadata: { fields }, } = visualizations.data.rawVizData; - const { defaultAxes } = visualizations.data; - const { dataConfig = {}, layoutConfig = {} } = visualizations?.data?.userConfigs; - const xaxis = dataConfig?.dimensions ? dataConfig.dimensions.filter((item) => item.label) : []; - const yaxis = dataConfig?.metrics ? dataConfig.metrics.filter((item) => item.label) : []; - const type = dataConfig?.chartStyles?.mode ? dataConfig?.chartStyles?.mode[0]?.modeId : 'pie'; - const lastIndex = fields.length - 1; - const colorTheme = dataConfig?.chartStyles?.colorTheme - ? dataConfig?.chartStyles?.colorTheme - : { name: DEFAULT_PALETTE }; - const showLegend = dataConfig?.legend?.showLegend === 'hidden' ? false : vis.showlegend; - const legendPosition = dataConfig?.legend?.position || vis.legendposition; - const legendSize = dataConfig?.legend?.size || vis.legendSize; - const labelSize = dataConfig?.chartStyles?.labelSize || vis.labelSize; - const tooltipMode = - dataConfig?.tooltipOptions?.tooltipMode !== undefined - ? dataConfig.tooltipOptions.tooltipMode - : 'show'; - const tooltipText = - dataConfig?.tooltipOptions?.tooltipText !== undefined - ? dataConfig.tooltipOptions.tooltipText - : 'all'; - - if (isEmpty(xaxis) || isEmpty(yaxis)) - return ; + const { + dataConfig: { + chartStyles = {}, + dimensions = [], + metrics = [], + span = {}, + legend = {}, + panelOptions = {}, + tooltipOptions = {}, + }, + layoutConfig = {}, + } = visualizations?.data?.userConfigs; + const type = chartStyles.mode || vis.mode; + const colorTheme = chartStyles.colorTheme ? chartStyles.colorTheme : { name: DEFAULT_PALETTE }; + const showLegend = legend.showLegend === 'hidden' ? false : vis.showlegend; + const legendSize = legend.size || vis.legendSize; + const labelSize = chartStyles.labelSize || vis.labelSize; + const title = panelOptions.title || layoutConfig.layout?.title || ''; + const timestampField = find(fields, (field) => field.type === 'timestamp'); - let valueSeries; - if (!isEmpty(xaxis) && !isEmpty(yaxis)) { - valueSeries = [...yaxis]; + /** + * determine x axis + */ + let xaxes: ConfigListEntry[]; + if (span && span.time_field && timestampField) { + xaxes = [timestampField, ...dimensions]; } else { - valueSeries = defaultAxes.yaxis || take(fields, lastIndex > 0 ? lastIndex : 1); + xaxes = dimensions; + } + + if (isEmpty(xaxes) || isEmpty(metrics)) { + return ; } const invertHex = (hex: string) => (Number(`0x1${hex}`) ^ HEX_CONTRAST_COLOR).toString(16).substr(1).toUpperCase(); - const createLegendLabels = (dimLabels: string[], xaxisLables: string[]) => { - return dimLabels.map((label: string, index: number) => { - return [xaxisLables[index], label].join(','); - }); - }; - - const labelsOfXAxis = useMemo(() => { - let legendLabels = []; - if (xaxis.length > 0) { - let dimLabelsArray = data[xaxis[0].label]; - for (let i = 0; i < xaxis.length - 1; i++) { - dimLabelsArray = createLegendLabels(dimLabelsArray, data[xaxis[i + 1].label]); - } - legendLabels = dimLabelsArray; - } else { - legendLabels = data[fields[lastIndex].name]; + const labelsOfXAxis = xaxes.reduce((prev, cur) => { + if (data[cur.name]) { + if (prev.length === 0) return data[cur.name].flat(); + return prev.map( + (item: string | number, index: number) => `${item}, ${data[cur.name][index]}` + ); } - return legendLabels; - }, [xaxis, data, fields, createLegendLabels]); + }, []); const hexColor = invertHex(colorTheme); + const pies = useMemo( () => - valueSeries.map((field: any, index: number) => { + metrics.map((field: any, index: number) => { + const fieldName = field.alias ? field.alias : `${field.aggregation}(${field.name})`; const marker = colorTheme.name !== DEFAULT_PALETTE ? { marker: { - colors: [...Array(data[field.name].length).fill(colorTheme.childColor)], + colors: [...Array(data[fieldName].length).fill(colorTheme.childColor)], line: { color: hexColor, width: 1, @@ -88,18 +88,22 @@ export const Pie = ({ visualizations, layout, config }: any) => { : undefined; return { labels: labelsOfXAxis, - values: data[field.label], + values: data[fieldName], type: 'pie', - name: field.name, + name: fieldName, hole: type === 'pie' ? 0 : 0.5, - text: field.name, + text: fieldName, textinfo: 'percent', - hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText, + hoverinfo: getTooltipHoverInfo({ + tooltipMode: tooltipOptions.tooltipMode, + tooltipText: tooltipOptions.tooltipText, + }), automargin: true, textposition: 'outside', + title: { text: fieldName }, domain: { - row: Math.floor(index / 3), - column: index % 3, + row: Math.floor(index / PLOTLY_PIE_COLUMN_NUMBER), + column: index % PLOTLY_PIE_COLUMN_NUMBER, }, ...marker, outsidetextfont: { @@ -107,38 +111,32 @@ export const Pie = ({ visualizations, layout, config }: any) => { }, }; }), - [valueSeries, valueSeries, data, labelSize, labelsOfXAxis, colorTheme] + [metrics, data, labelSize, labelsOfXAxis, colorTheme] ); - const isAtleastOneFullRow = Math.floor(valueSeries.length / 3) > 0; - - const mergedLayout = useMemo( - () => ({ + const mergedLayout = useMemo(() => { + const isAtleastOneFullRow = Math.floor(metrics.length / PLOTLY_PIE_COLUMN_NUMBER) > 0; + return { grid: { - rows: Math.floor(valueSeries.length / 3) + 1, - columns: isAtleastOneFullRow ? 3 : valueSeries.length, + xgap: PIE_XAXIS_GAP, + ygap: PIE_YAXIS_GAP, + rows: Math.floor(metrics.length / PLOTLY_PIE_COLUMN_NUMBER) + 1, + columns: isAtleastOneFullRow ? PLOTLY_PIE_COLUMN_NUMBER : metrics.length, + pattern: 'independent', }, ...layout, ...(layoutConfig.layout && layoutConfig.layout), - title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || '', + title, legend: { ...layout.legend, - orientation: legendPosition, - font: { size: legendSize }, + orientation: legend.position || vis.legendposition, + ...(legendSize && { + font: { size: legendSize }, + }), }, showlegend: showLegend, - }), - [ - valueSeries, - isAtleastOneFullRow, - layoutConfig.layout, - dataConfig?.panelOptions?.title, - layoutConfig.layout?.title, - layout.legend, - legendPosition, - legendSize, - ] - ); + }; + }, [metrics, layoutConfig.layout, title, layout.legend]); const mergedConfigs = useMemo( () => ({ diff --git a/dashboards-observability/public/components/visualizations/charts/pie/pie_type.ts b/dashboards-observability/public/components/visualizations/charts/pie/pie_type.ts index 51c041e65..c8fea3d51 100644 --- a/dashboards-observability/public/components/visualizations/charts/pie/pie_type.ts +++ b/dashboards-observability/public/components/visualizations/charts/pie/pie_type.ts @@ -6,7 +6,6 @@ import { Pie } from './pie'; import { getPlotlySharedConfigs, getPlotlyCategory } from '../shared/shared_configs'; import { LensIconChartPie } from '../../assets/chart_pie'; -import { PLOTLY_COLOR } from '../../../../../common/constants/shared'; import { VizDataPanel } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/default_vis_editor'; import { ConfigEditor } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/json_editor'; import { @@ -14,13 +13,19 @@ import { ConfigChartOptions, ConfigLegend, InputFieldItem, + ButtonGroupItem, } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls'; -import { DEFAULT_PALETTE, PIE_PALETTES } from '../../../../../common/constants/colors'; import { fetchConfigObject } from '../../../../components/event_analytics/utils/utils'; +import { DEFAULT_PALETTE, PIE_PALETTES } from '../../../../../common/constants/colors'; +import { PLOTLY_COLOR, DefaultChartStyles } from '../../../../../common/constants/shared'; +import { DefaultPieChartParameters } from '../../../../../common/constants/explorer'; const sharedConfigs = getPlotlySharedConfigs(); const VIS_CATEGORY = getPlotlyCategory(); +const { ShowLegend, LegendPosition } = DefaultChartStyles; +const { DefaultMode } = DefaultPieChartParameters; + export const createPieTypeDefinition = (params: any) => ({ name: 'pie', type: 'pie', @@ -29,13 +34,11 @@ export const createPieTypeDefinition = (params: any) => ({ fulllabel: 'Pie', icontype: 'visPie', category: VIS_CATEGORY.BASICS, - showlegend: true, - legendposition: 'v', - selection: { - dataLoss: 'nothing', - }, + showlegend: ShowLegend, + legendposition: LegendPosition, categoryaxis: 'xaxis', seriesaxis: 'yaxis', + mode: DefaultMode, icon: LensIconChartPie, editorconfig: { panelTabs: [ @@ -57,10 +60,10 @@ export const createPieTypeDefinition = (params: any) => ({ component: null, props: { options: [ - { name: 'Show', id: 'show' }, + { name: 'Show', id: ShowLegend }, { name: 'Hidden', id: 'hidden' }, ], - defaultSelections: [{ name: 'Show', id: 'show' }], + defaultSelections: [{ name: 'Show', id: ShowLegend }], }, }, { @@ -69,10 +72,10 @@ export const createPieTypeDefinition = (params: any) => ({ component: null, props: { options: [ - { name: 'Right', id: 'v' }, + { name: 'Right', id: LegendPosition }, { name: 'Bottom', id: 'h' }, ], - defaultSelections: [{ name: 'Right', id: 'v' }], + defaultSelections: [{ name: 'Right', id: LegendPosition }], }, }, { @@ -101,15 +104,16 @@ export const createPieTypeDefinition = (params: any) => ({ { name: 'Mode', isSingleSelection: true, - component: null, + component: ButtonGroupItem, + eleType: 'buttons', mapTo: 'mode', props: { - dropdownList: [ - { name: 'Pie', modeId: 'pie' }, - { name: 'Donut', modeId: 'donut' }, + options: [ + { name: 'Pie', id: DefaultMode }, + { name: 'Donut', id: 'donut' }, ], + defaultSelections: [{ name: 'Pie', id: DefaultMode }], }, - defaultState: [{ name: 'Pie', modeId: 'pie', label: 'Pie' }], }, { name: 'Label size', @@ -142,20 +146,18 @@ export const createPieTypeDefinition = (params: any) => ({ visconfig: { layout: { ...sharedConfigs.layout, - ...{ - colorway: PLOTLY_COLOR, - plot_bgcolor: 'rgba(0, 0, 0, 0)', - paper_bgcolor: 'rgba(0, 0, 0, 0)', - xaxis: { - fixedrange: true, - showgrid: false, - visible: true, - }, - yaxis: { - fixedrange: true, - showgrid: false, - visible: true, - }, + colorway: PLOTLY_COLOR, + plot_bgcolor: 'rgba(0, 0, 0, 0)', + paper_bgcolor: 'rgba(0, 0, 0, 0)', + xaxis: { + fixedrange: true, + showgrid: false, + visible: true, + }, + yaxis: { + fixedrange: true, + showgrid: false, + visible: true, }, }, config: { From 3ce8d87b368bed8625f569c0f22ed3457c373dce Mon Sep 17 00:00:00 2001 From: ruchika-narang <79983862+ruchika-narang@users.noreply.github.com> Date: Mon, 26 Sep 2022 22:48:04 +0530 Subject: [PATCH 20/20] [BUG]: Revert code for treemap and histogram. (#1037) * Revert code for histogram and treemap after antlr merge because of crashes in app Signed-off-by: ruchika-narang * Fixes after rebasing with main and test cases failing Signed-off-by: ruchika-narang * Removed commented code Signed-off-by: ruchika-narang * Working on review comments, renaming metrics to series Signed-off-by: ruchika-narang * Removing extra import after rebasing main Signed-off-by: ruchika-narang * Working on review comments Signed-off-by: ruchika-narang * Replacing series and dimension with dynamic naming Signed-off-by: ruchika-narang * Updated snapshots Signed-off-by: ruchika-narang * Fixed failing test case issue Signed-off-by: ruchika-narang * Resolving review comment Signed-off-by: ruchika-narang Signed-off-by: ruchika-narang --- .../common/constants/explorer.ts | 20 +-- .../common/constants/shared.ts | 38 ++--- .../common/query_manager/ast/types/stats.ts | 4 +- .../common/query_manager/utils/index.ts | 2 +- .../common/types/explorer.ts | 6 +- .../__snapshots__/utils.test.tsx.snap | 27 ++-- .../event_analytics/explorer/explorer.tsx | 123 ++++++++++----- .../explorer/sidebar/field_insights.tsx | 6 +- .../config_panel/config_panel.tsx | 10 +- .../config_controls/config_color_theme.tsx | 4 +- .../config_line_chart_styles.tsx | 4 +- .../config_panel_option_gauge.tsx | 8 +- .../config_controls/config_panel_options.tsx | 10 +- .../data_config_panel_item.tsx | 149 ++++++++++-------- .../logs_view_config_panel_item.tsx | 28 ++-- .../treemap_config_panel_item.tsx | 99 +++++------- .../explorer/visualizations/index.tsx | 8 +- .../__tests__/__snapshots__/bar.test.tsx.snap | 9 +- .../visualizations/charts/bar/bar.tsx | 30 ++-- .../visualizations/charts/bar/bar_type.ts | 4 +- .../charts/financial/gauge/gauge.tsx | 81 +++++----- .../charts/financial/gauge/gauge_type.ts | 4 +- .../charts/helpers/viz_types.ts | 92 +++++------ .../charts/histogram/histogram.tsx | 40 +++-- .../charts/histogram/histogram_type.ts | 4 +- .../visualizations/charts/lines/line.tsx | 57 ++++--- .../visualizations/charts/lines/line_type.ts | 12 +- .../charts/logs_view/logs_view.tsx | 2 +- .../visualizations/charts/maps/heatmap.tsx | 52 +++--- .../charts/maps/treemap_type.ts | 4 +- .../visualizations/charts/maps/treemaps.tsx | 74 +++++---- .../visualizations/charts/pie/pie.tsx | 66 +++++--- .../visualizations/charts/pie/pie_type.ts | 8 +- .../visualizations/visualization.tsx | 4 +- 34 files changed, 595 insertions(+), 494 deletions(-) diff --git a/dashboards-observability/common/constants/explorer.ts b/dashboards-observability/common/constants/explorer.ts index d801ff9fb..a56bd529d 100644 --- a/dashboards-observability/common/constants/explorer.ts +++ b/dashboards-observability/common/constants/explorer.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { visChartTypes } from './shared'; +import { VIS_CHART_TYPES } from './shared'; export const EVENT_ANALYTICS_DOCUMENTATION_URL = 'https://opensearch.org/docs/latest/observability-plugin/event-analytics/'; export const OPEN_TELEMETRY_LOG_CORRELATION_LINK = @@ -81,11 +81,11 @@ export const DEFAULT_AVAILABILITY_QUERY = 'stats count() by span( timestamp, 1h export const ADD_BUTTON_TEXT = '+ Add color theme'; export const VIZ_CONTAIN_XY_AXIS = [ - visChartTypes.Bar, - visChartTypes.Histogram, - visChartTypes.Line, - visChartTypes.Pie, - visChartTypes.Scatter, + VIS_CHART_TYPES.Bar, + VIS_CHART_TYPES.Histogram, + VIS_CHART_TYPES.Line, + VIS_CHART_TYPES.Pie, + VIS_CHART_TYPES.Scatter, ]; // default ppl aggregation method options @@ -153,7 +153,7 @@ export interface DefaultGaugeChartParametersProps { ThresholdsMaxLimit: number; } -export const DefaultGaugeChartParameters: DefaultGaugeChartParametersProps = { +export const DEFAULT_GAUGE_CHART_PARAMETERS: DefaultGaugeChartParametersProps = { GaugeTitleSize: 14, DisplayDefaultGauges: 1, OrientationDefault: 'h', @@ -166,10 +166,12 @@ export const DefaultGaugeChartParameters: DefaultGaugeChartParametersProps = { export const PLOTLY_PIE_COLUMN_NUMBER = 2; export const PIE_XAXIS_GAP = 0.2; export const PIE_YAXIS_GAP = 0.1; -export interface DefaultPieChartParametersProps { +export interface DefaultPieChartParameterProps { DefaultMode: string; } -export const DefaultPieChartParameters: DefaultPieChartParametersProps = { +export const DEFAULT_PIE_CHART_PARAMETERS: DefaultPieChartParameterProps = { DefaultMode: 'pie', }; +export const GROUPBY = 'dimensions'; +export const AGGREGATIONS = 'series'; diff --git a/dashboards-observability/common/constants/shared.ts b/dashboards-observability/common/constants/shared.ts index e61909789..71f46f706 100644 --- a/dashboards-observability/common/constants/shared.ts +++ b/dashboards-observability/common/constants/shared.ts @@ -2,8 +2,6 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ - -import { IField } from '../../common/types/explorer'; import CSS from 'csstype'; // Client route @@ -72,7 +70,7 @@ export const pageStyles: CSS.Properties = { maxWidth: '1130px', }; -export enum visChartTypes { +export enum VIS_CHART_TYPES { Bar = 'bar', HorizontalBar = 'horizontal_bar', Line = 'line', @@ -86,30 +84,20 @@ export enum visChartTypes { LogsView = 'logs_view', } -export interface ValueOptionsAxes { - xaxis?: IField[]; - yaxis?: IField[]; - zaxis?: IField[]; - childField?: IField[]; - valueField?: IField[]; - series?: IField[]; - value?: IField[]; -} - export const NUMERICAL_FIELDS = ['short', 'integer', 'long', 'float', 'double']; export const ENABLED_VIS_TYPES = [ - visChartTypes.Bar, - visChartTypes.HorizontalBar, - visChartTypes.Line, - visChartTypes.Pie, - visChartTypes.HeatMap, - visChartTypes.Text, - visChartTypes.TreeMap, - visChartTypes.Gauge, - visChartTypes.Histogram, - visChartTypes.Scatter, - visChartTypes.LogsView, + VIS_CHART_TYPES.Bar, + VIS_CHART_TYPES.HorizontalBar, + VIS_CHART_TYPES.Line, + VIS_CHART_TYPES.Pie, + VIS_CHART_TYPES.HeatMap, + VIS_CHART_TYPES.Text, + VIS_CHART_TYPES.TreeMap, + VIS_CHART_TYPES.Gauge, + VIS_CHART_TYPES.Histogram, + VIS_CHART_TYPES.Scatter, + VIS_CHART_TYPES.LogsView, ]; //Live tail constants @@ -175,7 +163,7 @@ export interface DefaultChartStylesProps { DefaultModeScatter: string; } -export const DefaultChartStyles: DefaultChartStylesProps = { +export const DEFAULT_CHART_STYLES: DefaultChartStylesProps = { DefaultModeLine: 'lines', Interpolation: 'spline', LineWidth: 2, diff --git a/dashboards-observability/common/query_manager/ast/types/stats.ts b/dashboards-observability/common/query_manager/ast/types/stats.ts index 959ae7847..3127c5c6f 100644 --- a/dashboards-observability/common/query_manager/ast/types/stats.ts +++ b/dashboards-observability/common/query_manager/ast/types/stats.ts @@ -53,7 +53,7 @@ export interface ExpressionChunk { value: string | number; } -export interface DataConfigMetric { +export interface DataConfigSeries { alias: string; label: string; name: string; @@ -61,7 +61,7 @@ export interface DataConfigMetric { } export interface AggregationConfigurations { - metrics: Array; + series: Array; dimensions: Array; span: SpanChunk; } diff --git a/dashboards-observability/common/query_manager/utils/index.ts b/dashboards-observability/common/query_manager/utils/index.ts index eded2c827..5d84ed72e 100644 --- a/dashboards-observability/common/query_manager/utils/index.ts +++ b/dashboards-observability/common/query_manager/utils/index.ts @@ -10,7 +10,7 @@ export const composeAggregations = ( staleStats: PreviouslyParsedStaleStats ) => { return { - aggregations: aggConfig.metrics.map((metric) => ({ + aggregations: aggConfig.series.map((metric) => ({ function_alias: metric.alias, function: { name: metric.aggregation, diff --git a/dashboards-observability/common/types/explorer.ts b/dashboards-observability/common/types/explorer.ts index 2c34b30cd..34087e24b 100644 --- a/dashboards-observability/common/types/explorer.ts +++ b/dashboards-observability/common/types/explorer.ts @@ -16,6 +16,8 @@ import { FINAL_QUERY, SELECTED_TIMESTAMP, SELECTED_DATE_RANGE, + GROUPBY, + AGGREGATIONS, } from '../constants/explorer'; import { CoreStart, @@ -294,8 +296,8 @@ export interface DimensionSpan { } export interface ConfigList { - dimensions?: ConfigListEntry[] | HistogramConfigList[]; - metrics?: ConfigListEntry[]; + [GROUPBY]?: ConfigListEntry[] | HistogramConfigList[]; + [AGGREGATIONS]?: ConfigListEntry[]; breakdowns?: ConfigListEntry[] | HistogramConfigList[]; span?: DimensionSpan; } diff --git a/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap b/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap index ac6bc701d..81c1b660a 100644 --- a/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap +++ b/dashboards-observability/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap @@ -1420,7 +1420,9 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` } } > - + - + - + { + const timeUnitValue = TIME_INTERVAL_OPTIONS.find( + (time_unit) => time_unit.value === groupByToken?.span?.span_expression.time_unit + )?.text; + return groupByToken?.span !== null + ? { + time_field: [ + { + name: groupByToken?.span.span_expression.field, + type: 'timestamp', + label: groupByToken?.span.span_expression.field, + }, + ], + unit: [ + { + text: timeUnitValue, + value: groupByToken?.span.span_expression.time_unit, + label: timeUnitValue, + }, + ], + interval: groupByToken?.span.span_expression.literal_value, + } + : undefined; + }; + + const getUpdatedDataConfig = (statsToken: statsChunk) => { + const groupByToken = statsToken.groupby; + const seriesToken = statsToken.aggregations && statsToken.aggregations[0]; + const span = getSpanValue(groupByToken); + switch (curVisId) { + case VIS_CHART_TYPES.TreeMap: + return { + [GROUPBY]: [ + { + childField: { + ...(groupByToken?.group_fields + ? { + label: groupByToken?.group_fields[0].name ?? '', + name: groupByToken?.group_fields[0].name ?? '', + } + : { label: '', name: '' }), + }, + parentFields: [], + }, + ], + [AGGREGATIONS]: [ + { + valueField: { + ...(seriesToken + ? { + label: `${seriesToken.function?.name}(${seriesToken.function?.value_expression})`, + name: `${seriesToken.function?.name}(${seriesToken.function?.value_expression})`, + } + : { label: '', name: '' }), + }, + }, + ], + }; + case VIS_CHART_TYPES.Histogram: + return { + [GROUPBY]: [{ bucketSize: '', bucketOffset: '' }], + [AGGREGATIONS]: [], + }; + default: + return { + [AGGREGATIONS]: statsToken.aggregations.map((agg) => ({ + label: agg.function?.value_expression, + name: agg.function?.value_expression, + aggregation: agg.function?.name, + })), + [GROUPBY]: groupByToken?.group_fields?.map((agg) => ({ + label: agg.name ?? '', + name: agg.name ?? '', + })), + span, + }; + } + }; + const handleQuerySearch = useCallback( async (availability?: boolean) => { // clear previous selected timestamp when index pattern changes @@ -842,49 +925,15 @@ export const Explorer = ({ if (selectedContentTabId === TAB_CHART_ID) { // parse stats section on every search + const qm = new QueryManager(); const statsTokens = qm.queryParser().parse(tempQuery).getStats(); - const timeUnitValue = TIME_INTERVAL_OPTIONS.find( - (time_unit) => time_unit.value === statsTokens.groupby?.span.span_expression.time_unit - )?.text; - const span = - statsTokens.groupby?.span !== null - ? { - time_field: [ - { - name: statsTokens.groupby?.span.span_expression.field, - type: 'timestamp', - label: statsTokens.groupby?.span.span_expression.field, - }, - ], - unit: [ - { - text: timeUnitValue, - value: statsTokens.groupby?.span.span_expression.time_unit, - label: timeUnitValue, - }, - ], - interval: statsTokens.groupby?.span.span_expression.literal_value, - } - : undefined; + const updatedDataConfig = getUpdatedDataConfig(statsTokens); await dispatch( changeVizConfig({ tabId, vizId: curVisId, - data: { - dataConfig: { - metrics: statsTokens.aggregations.map((agg) => ({ - label: agg.function?.value_expression, - name: agg.function?.value_expression, - aggregation: agg.function?.name, - })), - dimensions: statsTokens.groupby?.group_fields?.map((agg) => ({ - label: agg.name ?? '', - name: agg.name ?? '', - })), - span, - }, - }, + data: { ...updatedDataConfig }, }) ); } diff --git a/dashboards-observability/public/components/event_analytics/explorer/sidebar/field_insights.tsx b/dashboards-observability/public/components/event_analytics/explorer/sidebar/field_insights.tsx index 6a682cc5c..b9414ba70 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/sidebar/field_insights.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/sidebar/field_insights.tsx @@ -48,8 +48,8 @@ export const FieldInsights = ({ field, query }: any) => { format: 'viz', }, ]; - const numericalTypes = ['short', 'integer', 'long', 'float', 'double']; - const isNumericalField = indexOf(numericalTypes, field.type) > 0; + const NUMERICAL_TYPES = ['short', 'integer', 'long', 'float', 'double']; + const isNumericalField = indexOf(NUMERICAL_TYPES, field.type) > 0; const [curReport, setCurReport] = useState({ ...generalReports[0] }); const [reportContent, setReportContent] = useState({}); @@ -137,7 +137,7 @@ export const FieldInsights = ({ field, query }: any) => { ); })} - {indexOf(numericalTypes, field.type) > 0 && + {indexOf(NUMERICAL_TYPES, field.type) > 0 && numericalOnlyReports.map((report) => { return ( diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panel.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panel.tsx index eb3142cdf..db15a837d 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panel.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panel.tsx @@ -25,11 +25,7 @@ import { getDefaultSpec } from '../visualization_specs/default_spec'; import { TabContext } from '../../../hooks'; import { DefaultEditorControls } from './config_panel_footer'; import { getVisType } from '../../../../visualizations/charts/vis_types'; -import { - ENABLED_VIS_TYPES, - ValueOptionsAxes, - visChartTypes, -} from '../../../../../../common/constants/shared'; +import { ENABLED_VIS_TYPES, VIS_CHART_TYPES } from '../../../../../../common/constants/shared'; import { VIZ_CONTAIN_XY_AXIS } from '../../../../../../common/constants/explorer'; const CONFIG_LAYOUT_TEMPLATE = ` @@ -108,7 +104,7 @@ export const ConfigPanel = ({ // To check, If user empty any of the value options const isValidValueOptionConfigSelected = useMemo(() => { const valueOptions = vizConfigs.dataConfig?.valueOptions; - const { TreeMap, Gauge, HeatMap } = visChartTypes; + const { TreeMap, Gauge, HeatMap } = VIS_CHART_TYPES; const isValidValueOptionsXYAxes = VIZ_CONTAIN_XY_AXIS.includes(curVisId) && valueOptions?.xaxis?.length !== 0 && @@ -229,7 +225,7 @@ export const ConfigPanel = ({ const memorizedVisualizationTypes = useMemo( () => ENABLED_VIS_TYPES.map((vs: string) => - vs === visChartTypes.Line || vs === visChartTypes.Scatter + vs === VIS_CHART_TYPES.Line || vs === VIS_CHART_TYPES.Scatter ? getVisType(vs, { type: vs }) : getVisType(vs) ), diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_color_theme.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_color_theme.tsx index 07801e5fe..227fefbfa 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_color_theme.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_color_theme.tsx @@ -18,7 +18,7 @@ import { } from '@elastic/eui'; import { isEmpty } from 'lodash'; import { ADD_BUTTON_TEXT } from '../../../../../../../../common/constants/explorer'; -import { visChartTypes } from '../../../../../../../../common/constants/shared'; +import { VIS_CHART_TYPES } from '../../../../../../../../common/constants/shared'; export const ConfigColorTheme = ({ visualizations, @@ -40,7 +40,7 @@ export const ConfigColorTheme = ({ const options = (dataConfig?.valueOptions?.metrics && dataConfig.valueOptions.metrics.length !== 0 ? dataConfig.valueOptions.metrics - : vis.name === visChartTypes.Histogram + : vis.name === VIS_CHART_TYPES.Histogram ? defaultAxes.yaxis ?? [] : fields ).map((item) => ({ diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_line_chart_styles.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_line_chart_styles.tsx index bd36ea856..b753854a1 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_line_chart_styles.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_line_chart_styles.tsx @@ -7,7 +7,7 @@ import React, { useMemo, useCallback, Fragment } from 'react'; import { EuiAccordion, EuiSpacer } from '@elastic/eui'; import { ButtonGroupItem } from './config_button_group'; import { IConfigPanelOptionSection } from '../../../../../../../../common/types/explorer'; -import { visChartTypes } from '../../../../../../../../common/constants/shared'; +import { VIS_CHART_TYPES } from '../../../../../../../../common/constants/shared'; export const ConfigLineChartStyles = ({ visualizations, @@ -54,7 +54,7 @@ export const ConfigLineChartStyles = ({ (schema: IConfigPanelOptionSection) => schema.mapTo !== 'interpolation' ); } - } else if (visualizations?.vis?.name === visChartTypes.Scatter) { + } else if (visualizations?.vis?.name === VIS_CHART_TYPES.Scatter) { return schemas.filter((schema: IConfigPanelOptionSection) => ['style', 'pointSize'].includes(schema.mapTo) ); diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_panel_option_gauge.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_panel_option_gauge.tsx index 519cd2d73..e04a46343 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_panel_option_gauge.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_panel_option_gauge.tsx @@ -5,7 +5,7 @@ import React, { useState, useEffect } from 'react'; import { EuiFormRow, EuiFieldNumber } from '@elastic/eui'; -import { DefaultGaugeChartParameters } from '../../../../../../../../common/constants/explorer'; +import { DEFAULT_GAUGE_CHART_PARAMETERS } from '../../../../../../../../common/constants/explorer'; const helpText = `Limit number of gauges.`; @@ -20,12 +20,12 @@ export const ConfigPanelOptionGauge = ({ ? dataConfig.valueOptions.dimensions.filter((i) => i.name !== '') : []; const [numberOfGauges, setNumberOfGauges] = useState( - DefaultGaugeChartParameters.DisplayDefaultGauges + DEFAULT_GAUGE_CHART_PARAMETERS.DisplayDefaultGauges ); useEffect(() => { if (!vizState) { - setNumberOfGauges(DefaultGaugeChartParameters.DisplayDefaultGauges); + setNumberOfGauges(DEFAULT_GAUGE_CHART_PARAMETERS.DisplayDefaultGauges); } }, [vizState?.numberOfGauges]); @@ -37,7 +37,7 @@ export const ConfigPanelOptionGauge = ({ setNumberOfGauges(Number(e.target.value)); }} value={numberOfGauges} - min={DefaultGaugeChartParameters.DisplayDefaultGauges} + min={DEFAULT_GAUGE_CHART_PARAMETERS.DisplayDefaultGauges} onBlur={() => { const newPanelOptions = { ...panelOptionsValues, diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_panel_options.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_panel_options.tsx index e6db2acb7..6097073af 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_panel_options.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_panel_options.tsx @@ -5,8 +5,8 @@ import React, { useCallback, useState, useEffect } from 'react'; import { EuiFieldText, EuiForm, EuiFormRow, EuiTextArea, EuiAccordion } from '@elastic/eui'; -import { visChartTypes } from '../../../../../../../../common/constants/shared'; -import { DefaultGaugeChartParameters } from '../../../../../../../../common/constants/explorer'; +import { VIS_CHART_TYPES } from '../../../../../../../../common/constants/shared'; +import { DEFAULT_GAUGE_CHART_PARAMETERS } from '../../../../../../../../common/constants/explorer'; import { ConfigPanelOptionGauge } from './config_panel_option_gauge'; const helpText = 'Name your visualization.'; @@ -24,9 +24,9 @@ export const ConfigPanelOptions = ({ visualizations, handleConfigChange, vizStat setPanelOptionsValues({ title: vizState?.title || '', description: vizState?.description || '', - ...(visualizations?.vis?.name?.toLowerCase() === visChartTypes.Gauge && { + ...(visualizations?.vis?.name?.toLowerCase() === VIS_CHART_TYPES.Gauge && { numberOfGauges: - vizState?.numberOfGauges || DefaultGaugeChartParameters.DisplayDefaultGauges, + vizState?.numberOfGauges || DEFAULT_GAUGE_CHART_PARAMETERS.DisplayDefaultGauges, }), }); }, [name, vizState?.title, vizState?.description, vizState?.numberOfGauges]); @@ -62,7 +62,7 @@ export const ConfigPanelOptions = ({ visualizations, handleConfigChange, vizStat onBlur={() => handleConfigChange(panelOptionsValues)} /> - {visualizations?.vis?.name?.toLowerCase() === visChartTypes.Gauge && ( + {visualizations?.vis?.name?.toLowerCase() === VIS_CHART_TYPES.Gauge && ( { const dispatch = useDispatch(); - const { tabId, handleQuerySearch, handleQueryChange, setTempQuery, fetchData } = useContext( - TabContext - ); + const { + tabId, + handleQuerySearch, + handleQueryChange, + setTempQuery, + fetchData, + changeVisualizationConfig, + curVisId, + } = useContext(TabContext); const { data } = visualizations; const { data: vizData = {}, metadata: { fields = [] } = {} } = data?.rawVizData; const { @@ -116,59 +124,77 @@ export const DataConfigPanelItem = ({ ...configList, [name]: [ ...configList[name], - name === 'metrics' ? initialMetricEntry : initialDimensionEntry, + name === AGGREGATIONS ? initialSeriesEntry : initialDimensionEntry, ], }; setConfigList(list); }; - const updateChart = (updatedConfigList = configList) => { - const statsTokens = qm.queryParser().parse(data.query.rawQuery).getStats(); - const newQuery = qm - .queryBuilder() - .build(data.query.rawQuery, composeAggregations(updatedConfigList, statsTokens)); - - batch(async () => { - await handleQueryChange(newQuery); - await dispatch( - changeQuery({ - tabId, - query: { - ...data.query, - [RAW_QUERY]: newQuery, - }, - }) - ); - await fetchData(); - await dispatch( + const updateChart = (updatedConfigList: ConfigList = configList) => { + if (visualizations.vis.name === VIS_CHART_TYPES.Histogram) { + dispatch( changeVizConfig({ tabId, - vizId: visualizations.vis.name, + vizId: curVisId, data: { + ...userConfigs, dataConfig: { - metrics: updatedConfigList.metrics, - dimensions: updatedConfigList.dimensions, - breakdowns: updatedConfigList.breakdowns, - span: updatedConfigList.span, + ...userConfigs.dataConfig, + [GROUPBY]: configList[GROUPBY], + [AGGREGATIONS]: configList[AGGREGATIONS], }, }, }) ); - }); + } else { + const statsTokens = qm.queryParser().parse(data.query.rawQuery).getStats(); + const newQuery = qm + .queryBuilder() + .build(data.query.rawQuery, composeAggregations(updatedConfigList, statsTokens)); + + batch(async () => { + await handleQueryChange(newQuery); + await dispatch( + changeQuery({ + tabId, + query: { + ...data.query, + [RAW_QUERY]: newQuery, + }, + }) + ); + await fetchData(); + await dispatch( + changeVizConfig({ + tabId, + vizId: visualizations.vis.name, + data: { + dataConfig: { + ...userConfigs.dataConfig, + [GROUPBY]: configList[GROUPBY], + [AGGREGATIONS]: configList[AGGREGATIONS], + breakdowns: updatedConfigList.breakdowns, + span: updatedConfigList.span, + }, + }, + }) + ); + }); + } }; const isPositionButtonVisible = (sectionName: string) => - sectionName === 'metrics' && - (visualizations.vis.name === visChartTypes.Line || - visualizations.vis.name === visChartTypes.Scatter); + sectionName === AGGREGATIONS && + (visualizations.vis.name === VIS_CHART_TYPES.Line || + visualizations.vis.name === VIS_CHART_TYPES.Scatter); const getOptionsAvailable = (sectionName: string) => { const selectedFields = {}; const unselectedFields = fieldOptionList.filter((field) => !selectedFields[field.label]); - return sectionName === 'metrics' + return sectionName === AGGREGATIONS ? unselectedFields - : visualizations.vis.name === visChartTypes.Line || - visualizations.vis.name === visChartTypes.Scatter + : visualizations.vis.name === VIS_CHART_TYPES.Line || + visualizations.vis.name === VIS_CHART_TYPES.Scatter ? unselectedFields.filter((i) => i.type === 'timestamp') : unselectedFields; }; @@ -181,18 +207,17 @@ export const DataConfigPanelItem = ({ <>
- {sectionName === 'dimensions' && - visualizations.vis.name === visChartTypes.HeatMap && ( - -
{index === 0 ? 'X-Axis' : 'Y-Axis'}
-
- )} + {sectionName === GROUPBY && visualizations.vis.name === VIS_CHART_TYPES.HeatMap && ( + +
{index === 0 ? 'X-Axis' : 'Y-Axis'}
+
+ )} - {sectionName === 'metrics' && ( + {sectionName === AGGREGATIONS && ( ))} - {visualizations.vis.name !== visChartTypes.HeatMap && ( + {visualizations.vis.name !== VIS_CHART_TYPES.HeatMap && ( handleServiceAdd(sectionName)} disabled={ sectionName === 'dimensions' && - (visualizations.vis.name === visChartTypes.Line || - visualizations.vis.name === visChartTypes.Scatter) + (visualizations.vis.name === VIS_CHART_TYPES.Line || + visualizations.vis.name === VIS_CHART_TYPES.Scatter) } > Add @@ -310,10 +335,8 @@ export const DataConfigPanelItem = ({ fullWidth placeholder="auto" value={ - configList?.dimensions && - configList?.dimensions.length > 0 && - configList.dimensions[0][type] - ? configList.dimensions[0][type] + configList[GROUPBY] && configList[GROUPBY].length > 0 && configList[GROUPBY][0][type] + ? configList[GROUPBY][0][type] : '' } onChange={(e) => updateHistogramConfig('dimensions', type, e.target.value)} @@ -324,10 +347,10 @@ export const DataConfigPanelItem = ({ ); const getBreakDownFields = useCallback( - (configList) => { - return configList.dimensions; + (configList: ConfigList) => { + return configList[GROUPBY]; }, - [configList.dimensions] + [configList[GROUPBY]] ); const Breakdowns = useMemo(() => { @@ -341,7 +364,7 @@ export const DataConfigPanelItem = ({ aria-label="Accessible screen reader label" placeholder="Select fields" singleSelection={false} - options={configList.dimensions} + options={configList[GROUPBY]} selectedOptions={configList.breakdowns ?? []} onChange={(fields) => { setConfigList((staleState) => { @@ -358,7 +381,7 @@ export const DataConfigPanelItem = ({
); - }, [configList.dimensions, configList.breakdowns]); + }, [configList[GROUPBY], configList.breakdowns]); const DateHistogram = useMemo(() => { return ( @@ -448,19 +471,19 @@ export const DataConfigPanelItem = ({

Data Configurations

- {visualizations.vis.name !== visChartTypes.Histogram ? ( + {visualizations.vis.name !== VIS_CHART_TYPES.Histogram ? ( <>

Series

- {getCommonUI(configList.metrics, 'metrics')} + {getCommonUI(configList[AGGREGATIONS], AGGREGATIONS)}

Dimensions

- {getCommonUI(configList.dimensions, 'dimensions')} + {getCommonUI(configList[GROUPBY], GROUPBY)}

Date Histogram

diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/logs_view_config_panel_item.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/logs_view_config_panel_item.tsx index d5ac25e73..696422006 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/logs_view_config_panel_item.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/logs_view_config_panel_item.tsx @@ -14,10 +14,12 @@ import { EuiIcon, EuiText, } from '@elastic/eui'; -import { batch, useDispatch, useSelector } from 'react-redux'; import { cloneDeep } from 'lodash'; +import { batch, useDispatch, useSelector } from 'react-redux'; import { + AGGREGATIONS, AVAILABLE_FIELDS, + GROUPBY, SELECTED_FIELDS, } from '../../../../../../../../common/constants/explorer'; import { @@ -64,8 +66,8 @@ export const LogsViewConfigPanelItem = ({ useEffect(() => { if (fieldOptionList.length === 0) { setConfigList({ - metrics: [], - dimensions: visualizations?.data?.explorer?.explorerFields?.selectedFields.map((field) => ({ + [AGGREGATIONS]: [], + [GROUPBY]: visualizations?.data?.explorer?.explorerFields?.selectedFields.map((field) => ({ ...field, label: field.name, })), @@ -90,11 +92,11 @@ export const LogsViewConfigPanelItem = ({ }; const updateChart = () => { - if ((configList.dimensions as ConfigListEntry[]).some((field) => field.label === '')) { + if ((configList[GROUPBY] as ConfigListEntry[]).some((field) => field.label === '')) { return; } const nextFields = cloneDeep(explorerFields); - const selectedFields = (configList.dimensions as ConfigListEntry[]).map((field) => ({ + const selectedFields = (configList[GROUPBY] as ConfigListEntry[]).map((field) => ({ name: field.name, type: field.type, })); @@ -129,8 +131,8 @@ export const LogsViewConfigPanelItem = ({ dataConfig: { ...userConfigs.dataConfig, valueOptions: { - dimensions: configList.dimensions, - metrics: configList.metrics, + [GROUPBY]: configList[GROUPBY], + [AGGREGATIONS]: configList[AGGREGATIONS], }, }, }, @@ -142,7 +144,7 @@ export const LogsViewConfigPanelItem = ({ if (fieldOptionList.length !== 0) { return []; } - const dimensionNames = (configList.dimensions as ConfigListEntry[]).map((field) => field.name); + const dimensionNames = (configList[GROUPBY] as ConfigListEntry[]).map((field) => field.name); const availableFields = visualizations?.data?.explorer?.explorerFields?.availableFields.filter( (field) => !dimensionNames.includes(field.name) ); @@ -154,19 +156,19 @@ export const LogsViewConfigPanelItem = ({ const updateLogsViewConfig = (value: string, field: ConfigListEntry) => { const list = { ...configList }; - const index = (list.dimensions as ConfigListEntry[]).findIndex( + const index = (list[GROUPBY] as ConfigListEntry[]).findIndex( (dim) => dim.label === field.label ); const selectedField = visualizations?.data?.explorer?.explorerFields?.availableFields.find( (fld) => fld.name === value ); const newField = { ...selectedField, label: value }; - list.dimensions[index] = newField; + list[GROUPBY][index] = newField; setConfigList(list); }; const getLogsViewUI = () => { - const list = configList.dimensions ? configList.dimensions : []; + const list = configList[GROUPBY] ? configList[GROUPBY] : []; const listUI = list.map((field, index) => ( handleServiceRemove(index, 'dimensions')} + onClick={() => handleServiceRemove(index, GROUPBY)} /> } @@ -203,7 +205,7 @@ export const LogsViewConfigPanelItem = ({ fullWidth iconType="plusInCircleFilled" color="primary" - onClick={() => handleServiceAdd('dimensions')} + onClick={() => handleServiceAdd(GROUPBY)} disabled={fieldOptionList.length !== 0} > Add diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/treemap_config_panel_item.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/treemap_config_panel_item.tsx index e8d68a871..b51756450 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/treemap_config_panel_item.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/treemap_config_panel_item.tsx @@ -13,17 +13,14 @@ import { EuiFormRow, EuiPanel, } from '@elastic/eui'; -import { useDispatch, batch } from 'react-redux'; -import { changeQuery } from '../../../../../redux/slices/query_slice'; -import { - change as changeVizConfig, - selectVisualizationConfig, -} from '../../../../../redux/slices/viualization_config_slice'; +import { useDispatch } from 'react-redux'; import { ConfigTreemapParentFields } from './config_treemap_parents'; -import { numericalTypes, RAW_QUERY } from '../../../../../../../../common/constants/explorer'; +import { + AGGREGATIONS, + GROUPBY, + NUMERICAL_TYPES, +} from '../../../../../../../../common/constants/explorer'; import { TabContext } from '../../../../../hooks'; -import { composeAggregations } from '../../../../../../../../common/query_manager/utils'; -import { DataConfigPanelProps } from '../../../../../../../../common/types/explorer'; export const TreemapConfigPanelItem = ({ fieldOptionList, @@ -31,9 +28,7 @@ export const TreemapConfigPanelItem = ({ qm, }: DataConfigPanelProps) => { const dispatch = useDispatch(); - const { tabId, curVisId, changeVisualizationConfig, fetchData, handleQueryChange } = useContext< - any - >(TabContext); + const { tabId, curVisId, changeVisualizationConfig } = useContext(TabContext); const { data } = visualizations; const { userConfigs } = data; @@ -42,8 +37,8 @@ export const TreemapConfigPanelItem = ({ const newEntry = { label: '', name: '' }; const [configList, setConfigList] = useState({ - dimensions: [{ childField: { ...newEntry }, parentFields: [] }], - metrics: [{ valueField: { ...newEntry } }], + [GROUPBY]: [{ childField: { ...newEntry }, parentFields: [] }], + [AGGREGATIONS]: [{ valueField: { ...newEntry } }], }); useEffect(() => { @@ -71,42 +66,26 @@ export const TreemapConfigPanelItem = ({ setConfigList(newList); }; - const updateChart = (updatedConfigList = configList) => { - const statsTokens = qm.queryParser().parse(data.query.rawQuery).getStats(); - const newQuery = qm - .queryBuilder() - .build(data.query.rawQuery, composeAggregations(updatedConfigList, statsTokens)); - - batch(async () => { - await handleQueryChange(newQuery); - await dispatch( - changeQuery({ - tabId, - query: { - ...data.query, - [RAW_QUERY]: newQuery, - }, - }) - ); - await fetchData(); - await dispatch( - changeVizConfig({ - tabId, - vizId: visualizations.vis.name, - data: { - dataConfig: { - metrics: updatedConfigList.metrics, - dimensions: updatedConfigList.dimensions, - }, + const updateChart = () => { + dispatch( + changeVisualizationConfig({ + tabId, + vizId: curVisId, + data: { + ...userConfigs, + dataConfig: { + ...userConfigs.dataConfig, + [GROUPBY]: configList[GROUPBY], + [AGGREGATIONS]: configList[AGGREGATIONS], }, - }) - ); - }); + }, + }) + ); }; const getOptionsAvailable = (sectionName: string) => { - const { dimensions, metrics } = configList; - const selectedFields = {}; + const { dimensions, series } = configList; + let selectedFields = {}; let allSelectedFields = []; for (const key in configList) { @@ -114,9 +93,9 @@ export const TreemapConfigPanelItem = ({ const [dimElements] = dimensions; const { childField, parentFields } = dimElements; allSelectedFields = [childField, ...parentFields]; - } else if (key === 'metrics') { - const [metricsElement] = metrics; - allSelectedFields = [metricsElement.valueField]; + } else if (key === AGGREGATIONS) { + const [seriesElement] = series; + allSelectedFields = [seriesElement.valueField]; } const allValidSelectedFields = allSelectedFields.filter((item) => item?.label); allValidSelectedFields.length > 0 && @@ -124,8 +103,8 @@ export const TreemapConfigPanelItem = ({ } const unselectedFields = fieldOptionList.filter((field) => !selectedFields[field.label]); - return sectionName === 'metrics' - ? unselectedFields.filter((field) => numericalTypes.includes(field.type)) + return sectionName === AGGREGATIONS + ? unselectedFields.filter((field) => NUMERICAL_TYPES.includes(field.type)) : unselectedFields; }; @@ -143,7 +122,7 @@ export const TreemapConfigPanelItem = ({ - updateList('dimensions', 'childField', val.length > 0 ? val[0].label : '') + updateList(GROUPBY, 'childField', val.length > 0 ? val[0].label : '') } /> ({ + dropdownList={getOptionsAvailable(GROUPBY).map((opt) => ({ label: opt.label, name: opt.label, }))} selectedAxis={configList.dimensions[0]?.parentFields} - onSelectChange={(val) => updateList('dimensions', 'parentFields', val)} + onSelectChange={(val) => updateList(GROUPBY, 'parentFields', val)} /> @@ -176,15 +155,15 @@ export const TreemapConfigPanelItem = ({ - updateList('metrics', 'valueField', val.length > 0 ? val[0].label : '') + updateList(AGGREGATIONS, 'valueField', val.length > 0 ? val[0].label : '') } /> @@ -196,7 +175,7 @@ export const TreemapConfigPanelItem = ({ updateChart()} + onClick={updateChart} size="s" > Update chart diff --git a/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx b/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx index 17d817bc3..55018e3c8 100644 --- a/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx +++ b/dashboards-observability/public/components/event_analytics/explorer/visualizations/index.tsx @@ -22,7 +22,7 @@ import { ConfigPanel } from './config_panel'; import { Sidebar } from '../sidebar'; import { DataConfigPanelItem } from './config_panel/config_panes/config_controls/data_config_panel_item'; import { TabContext } from '../../hooks'; -import { PPL_STATS_REGEX, visChartTypes } from '../../../../../common/constants/shared'; +import { PPL_STATS_REGEX, VIS_CHART_TYPES } from '../../../../../common/constants/shared'; import { TreemapConfigPanelItem } from './config_panel/config_panes/config_controls/treemap_config_panel_item'; import { LogsViewConfigPanelItem } from './config_panel/config_panes/config_controls/logs_view_config_panel_item'; @@ -67,7 +67,7 @@ export const ExplorerVisualizations = ({ const renderDataConfigContainer = () => { switch (curVisId) { - case visChartTypes.TreeMap: + case VIS_CHART_TYPES.TreeMap: return ( ); - case visChartTypes.LogsView: + case VIS_CHART_TYPES.LogsView: return ( handleAddField(field)} handleRemoveField={(field: IField) => handleRemoveField(field)} isFieldToggleButtonDisabled={ - vis.name === visChartTypes.LogsView + vis.name === VIS_CHART_TYPES.LogsView ? isEmpty(explorerData.jsonData) || !isEmpty(query[RAW_QUERY].match(PPL_STATS_REGEX)) : true diff --git a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap index b51fac742..b0f5e3c57 100644 --- a/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap +++ b/dashboards-observability/public/components/visualizations/charts/__tests__/__snapshots__/bar.test.tsx.snap @@ -553,7 +553,9 @@ exports[`Bar component Renders bar component 1`] = ` } } > - + { @@ -34,11 +38,11 @@ export const Bar = ({ visualizations, layout, config }: any) => { if ( isEmpty(queriedVizData) || - !Array.isArray(dataConfig.dimensions) || - !Array.isArray(dataConfig.metrics) || + !Array.isArray(dataConfig[GROUPBY]) || + !Array.isArray(dataConfig[AGGREGATIONS]) || (dataConfig.breakdowns && !Array.isArray(dataConfig.breakdowns)) ) - return ; + return ; /** * determine stylings @@ -58,9 +62,7 @@ export const Bar = ({ visualizations, layout, config }: any) => { dataConfig?.legend?.showLegend && dataConfig.legend.showLegend !== visMetaData.showlegend ); const legendPosition = dataConfig?.legend?.position || visMetaData.legendposition; - visualizations.data?.rawVizData?.dataConfig?.metrics - ? visualizations.data?.rawVizData?.dataConfig?.metrics - : []; + const labelSize = dataConfig?.chartStyles?.labelSize || DEFAULT_LABEL_SIZE; const getSelectedColorTheme = (field: any, index: number) => @@ -78,7 +80,7 @@ export const Bar = ({ visualizations, layout, config }: any) => { // breakdown selections if (dataConfig.breakdowns) { return [ - ...dataConfig.dimensions.filter( + ...dataConfig[GROUPBY].filter( (dimension) => !some(dataConfig.breakdowns, (breakdown) => breakdown.label === dimension.label) ), @@ -88,18 +90,18 @@ export const Bar = ({ visualizations, layout, config }: any) => { // span selection const timestampField = find(fields, (field) => field.type === 'timestamp'); if (dataConfig.span && dataConfig.span.time_field && timestampField) { - return [timestampField, ...dataConfig.dimensions]; + return [timestampField, ...dataConfig[GROUPBY]]; } - return [...dataConfig.dimensions]; - }, [dataConfig.dimensions, dataConfig.breakdowns]); + return [...dataConfig[GROUPBY]]; + }, [dataConfig[GROUPBY], dataConfig.breakdowns]); /** * determine y axis */ const yaxes = useMemo(() => { - return Array.isArray(dataConfig.metrics) ? [...dataConfig.metrics] : []; - }, [dataConfig.metrics]); + return Array.isArray(dataConfig[AGGREGATIONS]) ? [...dataConfig[AGGREGATIONS]] : []; + }, [dataConfig[AGGREGATIONS]]); /** * prepare data for visualization, map x-xais to y-xais diff --git a/dashboards-observability/public/components/visualizations/charts/bar/bar_type.ts b/dashboards-observability/public/components/visualizations/charts/bar/bar_type.ts index 2a0bc0c18..9210850e5 100644 --- a/dashboards-observability/public/components/visualizations/charts/bar/bar_type.ts +++ b/dashboards-observability/public/components/visualizations/charts/bar/bar_type.ts @@ -16,13 +16,13 @@ import { ConfigLegend, InputFieldItem, } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls'; -import { DefaultChartStyles } from '../../../../../common/constants/shared'; +import { DEFAULT_CHART_STYLES } from '../../../../../common/constants/shared'; import { fetchConfigObject } from '../../../../components/event_analytics/utils/utils'; import { ConfigColorTheme } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_color_theme'; const sharedConfigs = getPlotlySharedConfigs(); const VIS_CATEGORY = getPlotlyCategory(); -const { LegendPosition, ShowLegend } = DefaultChartStyles; +const { LegendPosition, ShowLegend } = DEFAULT_CHART_STYLES; export const createBarTypeDefinition = (params: any) => ({ name: 'bar', type: 'bar', diff --git a/dashboards-observability/public/components/visualizations/charts/financial/gauge/gauge.tsx b/dashboards-observability/public/components/visualizations/charts/financial/gauge/gauge.tsx index b9b9f1549..9faad87e1 100644 --- a/dashboards-observability/public/components/visualizations/charts/financial/gauge/gauge.tsx +++ b/dashboards-observability/public/components/visualizations/charts/financial/gauge/gauge.tsx @@ -6,10 +6,15 @@ import React, { useMemo } from 'react'; import Plotly from 'plotly.js-dist'; import { Plt } from '../../../plotly/plot'; -import { PLOTLY_GAUGE_COLUMN_NUMBER } from '../../../../../../common/constants/explorer'; -import { DefaultGaugeChartParameters } from '../../../../../../common/constants/explorer'; +import { + AGGREGATIONS, + GROUPBY, + PLOTLY_GAUGE_COLUMN_NUMBER, +} from '../../../../../../common/constants/explorer'; +import { DEFAULT_GAUGE_CHART_PARAMETERS } from '../../../../../../common/constants/explorer'; import { ThresholdUnitType } from '../../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_thresholds'; import { EmptyPlaceholder } from '../../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder'; +import { IVisualizationContainerProps } from '../../../../../../common/types/explorer'; const { GaugeTitleSize, @@ -17,22 +22,33 @@ const { OrientationDefault, TickLength, LegendPlacement, -} = DefaultGaugeChartParameters; +} = DEFAULT_GAUGE_CHART_PARAMETERS; export const Gauge = ({ visualizations, layout, config }: any) => { const { - data, - metadata: { fields }, - } = visualizations.data.rawVizData; + data: { + defaultAxes, + indexFields, + query, + rawVizData: { + data: queriedVizData, + metadata: { fields }, + }, + userConfigs, + }, + vis: visMetaData, + }: IVisualizationContainerProps = visualizations; // data config parametrs - const { dataConfig = {}, layoutConfig = {} } = visualizations.data.userConfigs; - const dimensions = dataConfig?.dimensions - ? dataConfig.dimensions.filter((item) => item.name !== '') + const { dataConfig = {}, layoutConfig = {} } = userConfigs; + const dimensions = dataConfig[GROUPBY] + ? dataConfig[GROUPBY].filter((item) => item.name !== '') + : []; + const series = dataConfig[AGGREGATIONS] + ? dataConfig[AGGREGATIONS].filter((item) => item.name !== '') : []; - const metrics = dataConfig?.metrics ? dataConfig.metrics.filter((item) => item.name !== '') : []; const dimensionsLength = dimensions.length; - const metricsLength = metrics.length; + const seriesLength = series.length; const numberOfGauges = dataConfig?.panelOptions?.numberOfGauges || DisplayDefaultGauges; // style parameters @@ -44,41 +60,41 @@ export const Gauge = ({ visualizations, layout, config }: any) => { const orientation = dataConfig?.chartStyles?.orientation || OrientationDefault; const legendPlacement = dataConfig?.chartStyles?.legendPlacement || LegendPlacement; - const isEmptyPlot = !metricsLength; + const isEmptyPlot = !seriesLength; - if (isEmptyPlot) return ; + if (isEmptyPlot) return ; const gaugeData: Plotly.Data[] = useMemo(() => { let calculatedGaugeData: Plotly.Data[] = []; - if (dimensionsLength || metricsLength) { + if (dimensionsLength || seriesLength) { // case 1,2: no dimension, single/multiple metrics - if (!dimensionsLength && metricsLength >= 1) { - calculatedGaugeData = metrics.map((metric: any) => { + if (!dimensionsLength && seriesLength >= 1) { + calculatedGaugeData = series.map((seriesItem: any) => { return { - field_name: metric.name, - value: data[metric.name][0], + field_name: seriesItem.name, + value: queriedVizData[seriesItem.name][0], }; }); } // case 3: multiple dimensions and multiple metrics - if (dimensionsLength && metricsLength) { + if (dimensionsLength && seriesLength) { const selectedDimensionsData = dimensions - .map((dimension: any) => data[dimension.name].slice(0, numberOfGauges)) + .map((dimension: any) => queriedVizData[dimension.name].slice(0, numberOfGauges)) .reduce((prev, cur) => { return prev.map((i, j) => `${i}, ${cur[j]}`); }); - const selectedMetricsData = metrics.map((metric: any) => - data[`${metric.aggregation}(${metric.name})`].slice(0, numberOfGauges) + const selectedSeriesData = series.map((seriesItem: any) => + queriedVizData[`${seriesItem.aggregation}(${seriesItem.name})`].slice(0, numberOfGauges) ); - selectedMetricsData.map((metricSlice: any, metricSliceIndex: number) => { + selectedSeriesData.map((seriesSlice: any, seriesSliceIndex: number) => { calculatedGaugeData = [ ...calculatedGaugeData, - ...metricSlice.map((metricSliceData: any, metricSliceDataIndex: number) => { + ...seriesSlice.map((seriesSliceData: any, seriesSliceDataIndex: number) => { return { - field_name: `${selectedDimensionsData[metricSliceDataIndex]}, ${metrics[metricSliceIndex].name}`, - value: metricSliceData, + field_name: `${selectedDimensionsData[seriesSliceDataIndex]}, ${series[seriesSliceIndex].name}`, + value: seriesSliceData, }; }), ]; @@ -140,8 +156,8 @@ export const Gauge = ({ visualizations, layout, config }: any) => { return calculatedGaugeData; }, [ dimensions, - metrics, - data, + series, + queriedVizData, fields, thresholds, showThresholdMarkers, @@ -170,14 +186,7 @@ export const Gauge = ({ visualizations, layout, config }: any) => { ...(layoutConfig.layout && layoutConfig.layout), title: dataConfig?.panelOptions?.title || layoutConfig.layout?.title || '', }; - }, [ - data, - layout, - gaugeData.length, - layoutConfig.layout, - dataConfig?.panelOptions?.title, - orientation, - ]); + }, [layout, gaugeData.length, layoutConfig.layout, dataConfig?.panelOptions?.title, orientation]); const mergedConfigs = { ...config, diff --git a/dashboards-observability/public/components/visualizations/charts/financial/gauge/gauge_type.ts b/dashboards-observability/public/components/visualizations/charts/financial/gauge/gauge_type.ts index 835e75e91..2da38fefd 100644 --- a/dashboards-observability/public/components/visualizations/charts/financial/gauge/gauge_type.ts +++ b/dashboards-observability/public/components/visualizations/charts/financial/gauge/gauge_type.ts @@ -15,11 +15,11 @@ import { ConfigChartOptions, ButtonGroupItem, } from '../../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls'; -import { DefaultGaugeChartParameters } from '../../../../../../common/constants/explorer'; +import { DEFAULT_GAUGE_CHART_PARAMETERS } from '../../../../../../common/constants/explorer'; const sharedConfigs = getPlotlySharedConfigs(); const VIS_CATEGORY = getPlotlyCategory(); -const { ThresholdsMaxLimit } = DefaultGaugeChartParameters; +const { ThresholdsMaxLimit } = DEFAULT_GAUGE_CHART_PARAMETERS; export const createGaugeTypeDefinition = (params: any = {}) => ({ name: 'Gauge', diff --git a/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts b/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts index e23968693..adc7aeabd 100644 --- a/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts +++ b/dashboards-observability/public/components/visualizations/charts/helpers/viz_types.ts @@ -11,9 +11,13 @@ import { IQuery, ExplorerData, } from '../../../../../common/types/explorer'; -import { visChartTypes } from '../../../../../common/constants/shared'; +import { VIS_CHART_TYPES } from '../../../../../common/constants/shared'; import { QueryManager } from '../../../../../common/query_manager'; -import { TIME_INTERVAL_OPTIONS } from '../../../../../common/constants/explorer'; +import { + AGGREGATIONS, + GROUPBY, + TIME_INTERVAL_OPTIONS, +} from '../../../../../common/constants/explorer'; interface IVizContainerProps { vizId: string; appData?: { fromApp: boolean }; @@ -33,7 +37,7 @@ const initialDimensionEntry = { name: '', }; -const initialMetricEntry = { +const initialSeriesEntry = { alias: '', label: '', name: '', @@ -51,15 +55,15 @@ const getDefaultXYAxisLabels = (vizFields: IField[], visName: string) => { const mapXaxis = (): Array<{ [key: string]: string }> => { const xaxis = vizFieldsWithLabel.filter((field) => field.type === 'timestamp'); - return visName === visChartTypes.Line + return visName === VIS_CHART_TYPES.Line ? xaxis.length === 0 ? [initialDimensionEntry] : xaxis : [vizFieldsWithLabel[vizFieldsWithLabel.length - 1]]; }; - const mapYaxis = (): Array<{ [key: string]: string }> => - visName === visChartTypes.Line + const mapYaxis = (): { [key: string]: string }[] => + visName === VIS_CHART_TYPES.Line ? vizFieldsWithLabel.filter((field) => field.type !== 'timestamp') : take( vizFieldsWithLabel, @@ -81,8 +85,8 @@ const defaultUserConfigs = (queryString, visualizationName: string) => { const statsTokens = qm.queryParser().parse(queryString.rawQuery).getStats(); if (!statsTokens) { tempUserConfigs = { - metrics: [], - dimensions: [], + [AGGREGATIONS]: [], + [GROUPBY]: [], }; } else { const fieldInfo = statsTokens.groupby?.span?.span_expression?.field; @@ -104,11 +108,11 @@ const defaultUserConfigs = (queryString, visualizationName: string) => { : [], }, }; - if (visualizationName === visChartTypes.LogsView) { + if (visualizationName === VIS_CHART_TYPES.LogsView) { tempUserConfigs = { ...tempUserConfigs, - metrics: [], - dimensions: statsTokens.aggregations + [AGGREGATIONS]: [], + [GROUPBY]: statsTokens.aggregations .map((agg) => ({ label: agg.name ?? '', name: agg.name ?? '', @@ -120,50 +124,22 @@ const defaultUserConfigs = (queryString, visualizationName: string) => { })) ), }; - } else if (visualizationName === visChartTypes.HeatMap) { + } else if (visualizationName === VIS_CHART_TYPES.HeatMap) { tempUserConfigs = { ...tempUserConfigs, - dimensions: [initialDimensionEntry, initialDimensionEntry], - metrics: [initialMetricEntry], - }; - } else if (visualizationName === visChartTypes.TreeMap) { - tempUserConfigs = { - dimensions: [ - { - childField: { - ...(statsTokens.groupby?.group_fields.length > 0 - ? { - label: statsTokens.groupby?.group_fields[0].name, - name: statsTokens.groupby?.group_fields[0].name, - } - : initialEntryTreemap), - }, - parentFields: [], - }, - ], - metrics: [ - { - valueField: { - ...(statsTokens.aggregations.length > 0 - ? { - label: statsTokens.aggregations[0].function?.value_expression, - name: statsTokens.aggregations[0].function?.value_expression, - } - : initialEntryTreemap), - }, - }, - ], + [GROUPBY]: [initialDimensionEntry, initialDimensionEntry], + [AGGREGATIONS]: [initialSeriesEntry], }; } else { tempUserConfigs = { ...tempUserConfigs, - metrics: statsTokens.aggregations.map((agg) => ({ + [AGGREGATIONS]: statsTokens.aggregations.map((agg) => ({ alias: agg.alias, label: agg.function?.value_expression, name: agg.function?.value_expression, aggregation: agg.function?.name, })), - dimensions: statsTokens.groupby?.group_fields?.map((agg) => ({ + [GROUPBY]: statsTokens.groupby?.group_fields?.map((agg) => ({ label: agg.name ?? '', name: agg.name ?? '', })), @@ -181,9 +157,9 @@ const getUserConfigs = ( ) => { let configOfUser = userSelectedConfigs; const axesData = getDefaultXYAxisLabels(vizFields, visName); - if (!(userSelectedConfigs.dataConfig?.dimensions || userSelectedConfigs.dataConfig?.metrics)) { + if (!(userSelectedConfigs.dataConfig?.GROUPBY || userSelectedConfigs.dataConfig?.AGGREGATIONS)) { switch (visName) { - case visChartTypes.HeatMap: + case VIS_CHART_TYPES.HeatMap: configOfUser = { ...userSelectedConfigs, dataConfig: { @@ -192,28 +168,34 @@ const getUserConfigs = ( }, }; break; - case visChartTypes.TreeMap: + case VIS_CHART_TYPES.TreeMap: configOfUser = { ...userSelectedConfigs, dataConfig: { ...userSelectedConfigs?.dataConfig, - ...defaultUserConfigs(query, visName), + [GROUPBY]: [ + { + childField: { ...(axesData.xaxis ? axesData.xaxis[0] : initialEntryTreemap) }, + parentFields: [], + }, + ], + [AGGREGATIONS]: [ + { valueField: { ...(axesData.yaxis ? axesData.yaxis[0] : initialEntryTreemap) } }, + ], }, }; break; - case visChartTypes.Histogram: + case VIS_CHART_TYPES.Histogram: configOfUser = { ...userSelectedConfigs, dataConfig: { ...userSelectedConfigs?.dataConfig, - valueOptions: { - dimensions: [{ bucketSize: '', bucketOffset: '' }], - metrics: [], - }, + [GROUPBY]: [{ bucketSize: '', bucketOffset: '' }], + [AGGREGATIONS]: [], }, }; break; - case visChartTypes.LogsView: + case VIS_CHART_TYPES.LogsView: configOfUser = { ...userSelectedConfigs, dataConfig: { @@ -246,7 +228,7 @@ export const getVizContainerProps = ({ explorer = { explorerData: { jsonData: [], jsonDataAll: [] } }, }: IVizContainerProps): IVisualizationContainerProps => { const getVisTypeData = () => - vizId === visChartTypes.Line || vizId === visChartTypes.Scatter + vizId === VIS_CHART_TYPES.Line || vizId === VIS_CHART_TYPES.Scatter ? { ...getVisType(vizId, { type: vizId }) } : { ...getVisType(vizId) }; diff --git a/dashboards-observability/public/components/visualizations/charts/histogram/histogram.tsx b/dashboards-observability/public/components/visualizations/charts/histogram/histogram.tsx index 08a1930ee..52af6fb9a 100644 --- a/dashboards-observability/public/components/visualizations/charts/histogram/histogram.tsx +++ b/dashboards-observability/public/components/visualizations/charts/histogram/histogram.tsx @@ -7,21 +7,31 @@ import React, { useMemo } from 'react'; import { take, isEmpty, last } from 'lodash'; import { Plt } from '../../plotly/plot'; import { - DefaultChartStyles, + DEFAULT_CHART_STYLES, PLOTLY_COLOR, FILLOPACITY_DIV_FACTOR, - visChartTypes, + VIS_CHART_TYPES, } from '../../../../../common/constants/shared'; +import { IVisualizationContainerProps } from '../../../../../common/types/explorer'; import { hexToRgb } from '../../../../components/event_analytics/utils/utils'; +import { GROUPBY } from '../../../../../common/constants/explorer'; export const Histogram = ({ visualizations, layout, config }: any) => { - const { LineWidth, FillOpacity, LegendPosition, ShowLegend } = DefaultChartStyles; + const { LineWidth, FillOpacity, LegendPosition, ShowLegend } = DEFAULT_CHART_STYLES; const { - data = {}, - metadata: { fields }, - } = visualizations.data.rawVizData; - const { defaultAxes } = visualizations?.data; - const { dataConfig = {}, layoutConfig = {} } = visualizations?.data?.userConfigs; + data: { + defaultAxes, + indexFields, + query, + rawVizData: { + data: queriedVizData, + metadata: { fields }, + }, + userConfigs, + }, + vis: visMetaData, + }: IVisualizationContainerProps = visualizations; + const { dataConfig = {}, layoutConfig = {} } = userConfigs; const lastIndex = fields.length - 1; const lineWidth = dataConfig?.chartStyles?.lineWidth || LineWidth; const showLegend = @@ -40,11 +50,11 @@ export const Histogram = ({ visualizations, layout, config }: any) => { const valueSeries = defaultAxes?.yaxis || take(fields, lastIndex > 0 ? lastIndex : 1); const xbins: any = {}; - if (dataConfig?.valueOptions?.dimensions[0].bucketSize) { - xbins.size = dataConfig.valueOptions.dimensions[0].bucketSize; + if (dataConfig[GROUPBY] && dataConfig[GROUPBY][0].bucketSize) { + xbins.size = dataConfig[GROUPBY][0].bucketSize; } - if (dataConfig?.valueOptions?.dimensions[0].bucketOffset) { - xbins.start = dataConfig.valueOptions.dimensions[0].bucketOffset; + if (dataConfig[GROUPBY] && dataConfig[GROUPBY][0].bucketOffset) { + xbins.start = dataConfig[GROUPBY][0].bucketOffset; } const selectedColorTheme = (field: any, index: number, opacity?: number) => { @@ -60,8 +70,8 @@ export const Histogram = ({ visualizations, layout, config }: any) => { const hisValues = useMemo( () => valueSeries.map((field: any, index: number) => ({ - x: data[field.name], - type: visChartTypes.Histogram, + x: queriedVizData[field.name], + type: VIS_CHART_TYPES.Histogram, name: field.name, hoverinfo: tooltipMode === 'hidden' ? 'none' : tooltipText, marker: { @@ -73,7 +83,7 @@ export const Histogram = ({ visualizations, layout, config }: any) => { }, xbins: !isEmpty(xbins) ? xbins : undefined, })), - [valueSeries, data, fillOpacity, lineWidth, xbins, selectedColorTheme] + [valueSeries, queriedVizData, fillOpacity, lineWidth, xbins, selectedColorTheme] ); const mergedLayout = { diff --git a/dashboards-observability/public/components/visualizations/charts/histogram/histogram_type.ts b/dashboards-observability/public/components/visualizations/charts/histogram/histogram_type.ts index de9dcf0bc..f87b4920f 100644 --- a/dashboards-observability/public/components/visualizations/charts/histogram/histogram_type.ts +++ b/dashboards-observability/public/components/visualizations/charts/histogram/histogram_type.ts @@ -13,12 +13,12 @@ import { SliderConfig, ConfigColorTheme, } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls'; -import { DefaultChartStyles } from '../../../../../common/constants/shared'; +import { DEFAULT_CHART_STYLES } from '../../../../../common/constants/shared'; import { fetchConfigObject } from '../../../../components/event_analytics/utils/utils'; const sharedConfigs = getPlotlySharedConfigs(); const VIS_CATEGORY = getPlotlyCategory(); -const { LineWidth, FillOpacity, ShowLegend, LegendPosition } = DefaultChartStyles; +const { LineWidth, FillOpacity, ShowLegend, LegendPosition } = DEFAULT_CHART_STYLES; export const createHistogramVisDefinition = (params = {}) => ({ name: 'histogram', diff --git a/dashboards-observability/public/components/visualizations/charts/lines/line.tsx b/dashboards-observability/public/components/visualizations/charts/lines/line.tsx index 7234d4430..b93bd6757 100644 --- a/dashboards-observability/public/components/visualizations/charts/lines/line.tsx +++ b/dashboards-observability/public/components/visualizations/charts/lines/line.tsx @@ -9,13 +9,15 @@ import { Plt } from '../../plotly/plot'; import { AvailabilityUnitType } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability'; import { ThresholdUnitType } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_thresholds'; import { - DefaultChartStyles, + DEFAULT_CHART_STYLES, FILLOPACITY_DIV_FACTOR, PLOTLY_COLOR, - visChartTypes, + VIS_CHART_TYPES, } from '../../../../../common/constants/shared'; import { hexToRgb } from '../../../../components/event_analytics/utils/utils'; import { EmptyPlaceholder } from '../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder'; +import { IVisualizationContainerProps } from '../../../../../common/types/explorer'; +import { AGGREGATIONS, GROUPBY } from '../../../../../common/constants/explorer'; export const Line = ({ visualizations, layout, config }: any) => { const { @@ -28,20 +30,23 @@ export const Line = ({ visualizations, layout, config }: any) => { ShowLegend, DefaultModeScatter, LabelAngle, - } = DefaultChartStyles; + } = DEFAULT_CHART_STYLES; const { - data = {}, - metadata: { fields }, - } = visualizations.data.rawVizData; - const { defaultAxes } = visualizations.data; - const { - dataConfig = {}, - layoutConfig = {}, - availabilityConfig = {}, - } = visualizations?.data?.userConfigs; + data: { + defaultAxes, + indexFields, + query, + rawVizData: { + data: queriedVizData, + metadata: { fields }, + }, + userConfigs, + }, + vis: visMetaData, + }: IVisualizationContainerProps = visualizations; + const { dataConfig = {}, layoutConfig = {}, availabilityConfig = {} } = userConfigs; - // const xaxis = dataConfig?.dimensions ? dataConfig.dimensions.filter((item) => item.label) : []; - const yaxis = dataConfig?.metrics ? dataConfig.metrics.filter((item) => item.label) : []; + const yaxis = dataConfig[AGGREGATIONS] ? dataConfig[AGGREGATIONS].filter((item) => item.label) : []; const tooltipMode = dataConfig?.tooltipOptions?.tooltipMode !== undefined ? dataConfig.tooltipOptions.tooltipMode @@ -53,10 +58,10 @@ export const Line = ({ visualizations, layout, config }: any) => { const lastIndex = fields.length - 1; - const visType: string = visualizations.vis.name; + const visType: string = visMetaData?.name; const mode = dataConfig?.chartStyles?.style || - (visType === visChartTypes.Line ? DefaultModeLine : DefaultModeScatter); + (visType === VIS_CHART_TYPES.Line ? DefaultModeLine : DefaultModeScatter); const lineShape = dataConfig?.chartStyles?.interpolation || Interpolation; const lineWidth = dataConfig?.chartStyles?.lineWidth || LineWidth; const showLegend = !( @@ -81,13 +86,13 @@ export const Line = ({ visualizations, layout, config }: any) => { const timestampField = find(fields, (field) => field.type === 'timestamp'); if (dataConfig.span && dataConfig.span.time_field && timestampField) { - xaxis = [timestampField, ...dataConfig.dimensions]; + xaxis = [timestampField, ...dataConfig[GROUPBY]]; } else { - xaxis = dataConfig.dimensions; + xaxis = dataConfig[GROUPBY]; } if (isEmpty(xaxis) || isEmpty(yaxis)) - return ; + return ; let valueSeries; if (!isEmpty(xaxis) && !isEmpty(yaxis)) { @@ -138,8 +143,8 @@ export const Line = ({ visualizations, layout, config }: any) => { }; return { - x: data[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name], - y: data[`${field.aggregation}(${field.name})`], + x: queriedVizData[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name], + y: queriedVizData[`${field.aggregation}(${field.name})`], type: isBarMode ? 'bar' : 'scatter', name: field.label, mode, @@ -201,15 +206,15 @@ export const Line = ({ visualizations, layout, config }: any) => { const mapToLine = (list: ThresholdUnitType[] | AvailabilityUnitType[], lineStyle: any) => { return list.map((thr: ThresholdUnitType) => { thresholdTraces.x.push( - data[!isEmpty(xaxis) ? xaxis[xaxis.length - 1]?.label : fields[lastIndex].name][0] + queriedVizData[!isEmpty(xaxis) ? xaxis[xaxis.length - 1]?.label : fields[lastIndex].name][0] ); thresholdTraces.y.push(thr.value * (1 + 0.06)); thresholdTraces.text.push(thr.name); return { type: 'line', - x0: data[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name][0], + x0: queriedVizData[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name][0], y0: thr.value, - x1: last(data[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name]), + x1: last(queriedVizData[!isEmpty(xaxis) ? xaxis[0]?.label : fields[lastIndex].name]), y1: thr.value, name: thr.name || '', opacity: 0.7, @@ -229,7 +234,7 @@ export const Line = ({ visualizations, layout, config }: any) => { calculatedLineValues = [...calculatedLineValues, thresholdTraces]; } return [mergedLayout, calculatedLineValues]; - }, [data, fields, lastIndex, layout, layoutConfig, xaxis, yaxis, mode, valueSeries]); + }, [queriedVizData, fields, lastIndex, layout, layoutConfig, xaxis, yaxis, mode, valueSeries]); const mergedConfigs = useMemo( () => ({ @@ -242,6 +247,6 @@ export const Line = ({ visualizations, layout, config }: any) => { return isDimensionTimestamp ? ( ) : ( - + ); }; diff --git a/dashboards-observability/public/components/visualizations/charts/lines/line_type.ts b/dashboards-observability/public/components/visualizations/charts/lines/line_type.ts index 4956075d6..cdcb0fd07 100644 --- a/dashboards-observability/public/components/visualizations/charts/lines/line_type.ts +++ b/dashboards-observability/public/components/visualizations/charts/lines/line_type.ts @@ -17,8 +17,8 @@ import { } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls'; import { ConfigAvailability } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_availability'; import { - DefaultChartStyles, - visChartTypes, + DEFAULT_CHART_STYLES, + VIS_CHART_TYPES, PLOTLY_COLOR, } from '../../../../../common/constants/shared'; import { ButtonGroupItem } from '../../../../../public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group'; @@ -36,14 +36,14 @@ const { LegendPosition, ShowLegend, LabelAngle, -} = DefaultChartStyles; +} = DEFAULT_CHART_STYLES; export const createLineTypeDefinition = (params: any = {}) => ({ name: params.type, type: params.type, id: params.type, - label: params.type === visChartTypes.Line ? 'Time series' : 'Scatter', - fulllabel: params.type === visChartTypes.Line ? 'Time series' : 'Scatter', + label: params.type === VIS_CHART_TYPES.Line ? 'Time series' : 'Scatter', + fulllabel: params.type === VIS_CHART_TYPES.Line ? 'Time series' : 'Scatter', icontype: 'visLine', category: VIS_CATEGORY.BASICS, selection: { @@ -125,7 +125,7 @@ export const createLineTypeDefinition = (params: any = {}) => ({ { name: 'Lines + Markers', id: 'lines+markers' }, ], defaultSelections: [ - params.type === visChartTypes.Line + params.type === VIS_CHART_TYPES.Line ? { name: 'Lines', id: DefaultModeLine } : { name: 'Marker', id: DefaultModeScatter }, ], diff --git a/dashboards-observability/public/components/visualizations/charts/logs_view/logs_view.tsx b/dashboards-observability/public/components/visualizations/charts/logs_view/logs_view.tsx index 13a6516b4..0e8e88f4c 100644 --- a/dashboards-observability/public/components/visualizations/charts/logs_view/logs_view.tsx +++ b/dashboards-observability/public/components/visualizations/charts/logs_view/logs_view.tsx @@ -2,8 +2,8 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ -import { IExplorerFields } from '../../../../../common/types/explorer'; import React from 'react'; +import { IExplorerFields } from '../../../../../common/types/explorer'; import { RAW_QUERY, SELECTED_TIMESTAMP } from '../../../../../common/constants/explorer'; import { DataGrid } from '../../../../components/event_analytics/explorer/events_views/data_grid'; import './logs_view.scss'; diff --git a/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx b/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx index 221ff5556..7b06b2bdc 100644 --- a/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx +++ b/dashboards-observability/public/components/visualizations/charts/maps/heatmap.tsx @@ -16,35 +16,45 @@ import { HEATMAP_SINGLE_COLOR, } from '../../../../../common/constants/colors'; import { hexToRgb, lightenColor } from '../../../../components/event_analytics/utils/utils'; -import { NUMERICAL_FIELDS } from '../../../../../common/constants/shared'; +import { IVisualizationContainerProps } from '../../../../../common/types/explorer'; +import { AGGREGATIONS, GROUPBY } from '../../../../../common/constants/explorer'; export const HeatMap = ({ visualizations, layout, config }: any) => { const { - data, - metadata: { fields }, - } = visualizations.data.rawVizData; - const { dataConfig = {}, layoutConfig = {} } = visualizations?.data?.userConfigs; + data: { + defaultAxes, + indexFields, + query, + rawVizData: { + data: queriedVizData, + metadata: { fields }, + }, + userConfigs, + }, + vis: visMetaData, + }: IVisualizationContainerProps = visualizations; + const { dataConfig = {}, layoutConfig = {} } = userConfigs; - if (fields.length < 3) return ; + if (fields.length < 3) return ; - const xaxisField = dataConfig?.dimensions[0]; - const yaxisField = dataConfig?.dimensions[1]; - const zMetrics = dataConfig?.metrics[0]; + const xaxisField = dataConfig[GROUPBY][0]; + const yaxisField = dataConfig[GROUPBY][1]; + const zMetrics = dataConfig[AGGREGATIONS][0]; if ( isEmpty(xaxisField) || isEmpty(yaxisField) || isEmpty(zMetrics) || - isEmpty(data[xaxisField.label]) || - isEmpty(data[yaxisField.label]) || - isEmpty(data[`${zMetrics.aggregation}(${zMetrics.name})`]) || - dataConfig?.dimensions.length > 2 || - dataConfig?.metrics.length > 1 + isEmpty(queriedVizData[xaxisField.label]) || + isEmpty(queriedVizData[yaxisField.label]) || + isEmpty(queriedVizData[`${zMetrics.aggregation}(${zMetrics.name})`]) || + dataConfig[GROUPBY].length > 2 || + dataConfig[AGGREGATIONS].length > 1 ) - return ; + return ; - const uniqueYaxis = uniq(data[yaxisField.label]); - const uniqueXaxis = uniq(data[xaxisField.label]); + const uniqueYaxis = uniq(queriedVizData[yaxisField.label]); + const uniqueXaxis = uniq(queriedVizData[xaxisField.label]); const uniqueYaxisLength = uniqueYaxis.length; const uniqueXaxisLength = uniqueXaxis.length; const tooltipMode = @@ -79,9 +89,9 @@ export const HeatMap = ({ visualizations, layout, config }: any) => { const buckets = {}; // maps bukcets to metrics - for (let i = 0; i < data[xaxisField.label].length; i++) { - buckets[`${data[xaxisField.label][i]},${data[yaxisField.label][i]}`] = - data[`${zMetrics.aggregation}(${zMetrics.name})`][i]; + for (let i = 0; i < queriedVizData[xaxisField.label].length; i++) { + buckets[`${queriedVizData[xaxisField.label][i]},${queriedVizData[yaxisField.label][i]}`] = + queriedVizData[`${zMetrics.aggregation}(${zMetrics.name})`][i]; } // initialize empty 2 dimensional array, inner loop for each xaxis field, outer loop for yaxis @@ -104,7 +114,7 @@ export const HeatMap = ({ visualizations, layout, config }: any) => { return heapMapZaxis; }, [ - data, + queriedVizData, uniqueYaxis, uniqueXaxis, uniqueYaxisLength, diff --git a/dashboards-observability/public/components/visualizations/charts/maps/treemap_type.ts b/dashboards-observability/public/components/visualizations/charts/maps/treemap_type.ts index 338d39c9c..1a87a428b 100644 --- a/dashboards-observability/public/components/visualizations/charts/maps/treemap_type.ts +++ b/dashboards-observability/public/components/visualizations/charts/maps/treemap_type.ts @@ -16,13 +16,13 @@ import { } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls'; import { DEFAULT_PALETTE, COLOR_PALETTES } from '../../../../../common/constants/colors'; import { ButtonGroupItem } from '../../../../../public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_button_group'; -import { DefaultChartStyles } from '../../../../../common/constants/shared'; +import { DEFAULT_CHART_STYLES } from '../../../../../common/constants/shared'; import { fetchConfigObject } from '../../../../components/event_analytics/utils/utils'; const sharedConfigs = getPlotlySharedConfigs(); const VIS_CATEGORY = getPlotlyCategory(); -const { SortSectors } = DefaultChartStyles; +const { SortSectors } = DEFAULT_CHART_STYLES; export interface BarTypeParams {} diff --git a/dashboards-observability/public/components/visualizations/charts/maps/treemaps.tsx b/dashboards-observability/public/components/visualizations/charts/maps/treemaps.tsx index 47e160bc1..e9e7dd5d7 100644 --- a/dashboards-observability/public/components/visualizations/charts/maps/treemaps.tsx +++ b/dashboards-observability/public/components/visualizations/charts/maps/treemaps.tsx @@ -14,25 +14,35 @@ import { MULTI_COLOR_PALETTE, SINGLE_COLOR_PALETTE, } from '../../../../../common/constants/colors'; -import { DefaultChartStyles } from '../../../../../common/constants/shared'; +import { DEFAULT_CHART_STYLES } from '../../../../../common/constants/shared'; +import { IVisualizationContainerProps } from '../../../../../common/types/explorer'; +import { GROUPBY, AGGREGATIONS } from '../../../../../common/constants/explorer'; export const TreeMap = ({ visualizations, layout, config }: any) => { - const { DefaultSortSectors } = DefaultChartStyles; - + const { DefaultSortSectors } = DEFAULT_CHART_STYLES; const { - data, - metadata: { fields }, - } = visualizations.data.rawVizData; - const { dataConfig = {}, layoutConfig = {} } = visualizations?.data?.userConfigs; + data: { + defaultAxes, + indexFields, + query, + rawVizData: { + data: queriedVizData, + metadata: { fields }, + }, + userConfigs, + }, + vis: visMetaData, + }: IVisualizationContainerProps = visualizations; + const { dataConfig = {}, layoutConfig = {} } = userConfigs; const childField = - dataConfig?.valueOptions?.dimensions && dataConfig.valueOptions.dimensions[0].childField - ? dataConfig.valueOptions.dimensions[0].childField + dataConfig[GROUPBY] && dataConfig[GROUPBY][0].childField + ? dataConfig[GROUPBY][0].childField : fields[fields.length - 1]; const parentFields = - dataConfig?.valueOptions?.dimensions && dataConfig.valueOptions.dimensions[0].parentFields - ? dataConfig.valueOptions.dimensions[0].parentFields + dataConfig[GROUPBY] && dataConfig[GROUPBY][0].parentFields + ? dataConfig[GROUPBY][0].parentFields : []; const tooltipMode = dataConfig?.tooltipOptions?.tooltipMode !== undefined @@ -44,8 +54,8 @@ export const TreeMap = ({ visualizations, layout, config }: any) => { : 'all'; const valueField = - dataConfig?.valueOptions?.metrics && dataConfig.valueOptions.metrics[0].valueField - ? dataConfig.valueOptions.metrics[0].valueField + dataConfig[AGGREGATIONS] && dataConfig[AGGREGATIONS][0].valueField + ? dataConfig[AGGREGATIONS][0].valueField : fields[0]; const colorField = @@ -65,15 +75,16 @@ export const TreeMap = ({ visualizations, layout, config }: any) => { const areParentFieldsInvalid = new Set([...parentFields.map((field) => field.name)]).size !== parentFields.length || - parentFields.some((field) => isEmpty(data[field.name]) || isEqual(childField.name, field.name)); + parentFields.some( + (field) => isEmpty(queriedVizData[field.name]) || isEqual(childField.name, field.name) + ); if ( - isEmpty(data[childField.name]) || - isEmpty(data[valueField.name]) || - indexOf(NUMERICAL_FIELDS, valueField.type) < 0 || + isEmpty(queriedVizData[childField.name]) || + isEmpty(queriedVizData[valueField.name]) || areParentFieldsInvalid ) - return ; + return ; const [treemapData, mergedLayout] = useMemo(() => { let labelsArray: string[] = [], @@ -82,11 +93,13 @@ export const TreeMap = ({ visualizations, layout, config }: any) => { colorsArray: string[] = []; if (parentFields.length === 0) { - labelsArray = [...data[childField.name]]; + labelsArray = [...queriedVizData[childField.name]]; parentsArray = [...Array(labelsArray.length).fill('')]; - valuesArray = [...data[valueField.name]]; + valuesArray = [...queriedVizData[valueField.name]]; if (colorField.name === MULTI_COLOR_PALETTE) { - colorsArray = [...Array(data[childField.name].length).fill(colorField.childColor)]; + colorsArray = [ + ...Array(queriedVizData[childField.name].length).fill(colorField.childColor), + ]; } } else { let currentLevel = parentFields.length - 1; @@ -95,7 +108,7 @@ export const TreeMap = ({ visualizations, layout, config }: any) => { .slice(0) .reverse() .map((field, i) => { - const uniqueParents = uniq(data[field.name]) as string[]; + const uniqueParents = uniq(queriedVizData[field.name]) as string[]; labelsArray = [...labelsArray, ...uniqueParents]; if (i === 0) { parentsArray = [...Array(uniqueParents.length).fill('')]; @@ -110,10 +123,10 @@ export const TreeMap = ({ visualizations, layout, config }: any) => { : []; } else { const currentParentIndices = uniqueParents.map((parent) => - data[field.name].findIndex((index) => index === parent) + queriedVizData[field.name].findIndex((index) => index === parent) ); const lastParents = currentParentIndices.map( - (index) => data[lastParentField.name][index] + (index) => queriedVizData[lastParentField.name][index] ); parentsArray = [...parentsArray, ...lastParents]; valuesArray = [...valuesArray, ...Array(lastParents.length).fill(0)]; @@ -131,12 +144,15 @@ export const TreeMap = ({ visualizations, layout, config }: any) => { lastParentField = field; }); - labelsArray = [...labelsArray, ...data[childField.name]]; - valuesArray = [...valuesArray, ...data[valueField.name]]; - parentsArray = [...parentsArray, ...data[lastParentField.name]]; + labelsArray = [...labelsArray, ...queriedVizData[childField.name]]; + valuesArray = [...valuesArray, ...queriedVizData[valueField.name]]; + parentsArray = [...parentsArray, ...queriedVizData[lastParentField.name]]; colorsArray = colorField.name === MULTI_COLOR_PALETTE - ? [...colorsArray, ...Array(data[childField.name].length).fill(colorField.childColor)] + ? [ + ...colorsArray, + ...Array(queriedVizData[childField.name].length).fill(colorField.childColor), + ] : []; } @@ -184,7 +200,7 @@ export const TreeMap = ({ visualizations, layout, config }: any) => { return [mapData, mapLayout]; }, [ - data, + queriedVizData, childField, valueField, parentFields, diff --git a/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx b/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx index 89d1657fe..13a4e1611 100644 --- a/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx +++ b/dashboards-observability/public/components/visualizations/charts/pie/pie.tsx @@ -8,62 +8,76 @@ import { isEmpty, find } from 'lodash'; import { Plt } from '../../plotly/plot'; import { EmptyPlaceholder } from '../../../event_analytics/explorer/visualizations/shared_components/empty_placeholder'; import { getTooltipHoverInfo } from '../../../event_analytics/utils/utils'; -import { ConfigListEntry } from '../../../../../common/types/explorer'; +import { + ConfigListEntry, + IVisualizationContainerProps, +} from '../../../../../common/types/explorer'; import { DEFAULT_PALETTE, HEX_CONTRAST_COLOR } from '../../../../../common/constants/colors'; import { PLOTLY_PIE_COLUMN_NUMBER, PIE_YAXIS_GAP, PIE_XAXIS_GAP, + AGGREGATIONS, + GROUPBY, } from '../../../../../common/constants/explorer'; export const Pie = ({ visualizations, layout, config }: any) => { - const { vis } = visualizations; const { - data, - metadata: { fields }, - } = visualizations.data.rawVizData; + data: { + defaultAxes, + indexFields, + query, + rawVizData: { + data: queriedVizData, + metadata: { fields }, + }, + userConfigs, + }, + vis: visMetaData, + }: IVisualizationContainerProps = visualizations; + const { dataConfig: { chartStyles = {}, - dimensions = [], - metrics = [], span = {}, legend = {}, panelOptions = {}, tooltipOptions = {}, + [GROUPBY]: dimensions = [], + [AGGREGATIONS]: series = [], }, layoutConfig = {}, } = visualizations?.data?.userConfigs; - const type = chartStyles.mode || vis.mode; + const type = chartStyles.mode || visMetaData.mode; const colorTheme = chartStyles.colorTheme ? chartStyles.colorTheme : { name: DEFAULT_PALETTE }; - const showLegend = legend.showLegend === 'hidden' ? false : vis.showlegend; - const legendSize = legend.size || vis.legendSize; - const labelSize = chartStyles.labelSize || vis.labelSize; + const showLegend = legend.showLegend === 'hidden' ? false : visMetaData.showlegend; + const legendSize = legend.size || visMetaData.legendSize; + const labelSize = chartStyles.labelSize || visMetaData.labelSize; const title = panelOptions.title || layoutConfig.layout?.title || ''; const timestampField = find(fields, (field) => field.type === 'timestamp'); /** * determine x axis */ - let xaxes: ConfigListEntry[]; + let xaxes: ConfigListEntry[] = []; if (span && span.time_field && timestampField) { xaxes = [timestampField, ...dimensions]; } else { xaxes = dimensions; } - if (isEmpty(xaxes) || isEmpty(metrics)) { - return ; + if (isEmpty(xaxes) || isEmpty(series)) { + return ; } const invertHex = (hex: string) => (Number(`0x1${hex}`) ^ HEX_CONTRAST_COLOR).toString(16).substr(1).toUpperCase(); const labelsOfXAxis = xaxes.reduce((prev, cur) => { - if (data[cur.name]) { - if (prev.length === 0) return data[cur.name].flat(); + if (queriedVizData[cur.name]) { + if (prev.length === 0) return queriedVizData[cur.name].flat(); return prev.map( - (item: string | number, index: number) => `${item}, ${data[cur.name][index]}` + (item: string | number, index: number) => `${item}, ${queriedVizData[cur.name][index]}` ); } }, []); @@ -72,13 +86,13 @@ export const Pie = ({ visualizations, layout, config }: any) => { const pies = useMemo( () => - metrics.map((field: any, index: number) => { + series.map((field: any, index: number) => { const fieldName = field.alias ? field.alias : `${field.aggregation}(${field.name})`; const marker = colorTheme.name !== DEFAULT_PALETTE ? { marker: { - colors: [...Array(data[fieldName].length).fill(colorTheme.childColor)], + colors: [...Array(queriedVizData[fieldName].length).fill(colorTheme.childColor)], line: { color: hexColor, width: 1, @@ -88,7 +102,7 @@ export const Pie = ({ visualizations, layout, config }: any) => { : undefined; return { labels: labelsOfXAxis, - values: data[fieldName], + values: queriedVizData[fieldName], type: 'pie', name: fieldName, hole: type === 'pie' ? 0 : 0.5, @@ -111,17 +125,17 @@ export const Pie = ({ visualizations, layout, config }: any) => { }, }; }), - [metrics, data, labelSize, labelsOfXAxis, colorTheme] + [series, queriedVizData, labelSize, labelsOfXAxis, colorTheme] ); const mergedLayout = useMemo(() => { - const isAtleastOneFullRow = Math.floor(metrics.length / PLOTLY_PIE_COLUMN_NUMBER) > 0; + const isAtleastOneFullRow = Math.floor(series.length / PLOTLY_PIE_COLUMN_NUMBER) > 0; return { grid: { xgap: PIE_XAXIS_GAP, ygap: PIE_YAXIS_GAP, - rows: Math.floor(metrics.length / PLOTLY_PIE_COLUMN_NUMBER) + 1, - columns: isAtleastOneFullRow ? PLOTLY_PIE_COLUMN_NUMBER : metrics.length, + rows: Math.floor(series.length / PLOTLY_PIE_COLUMN_NUMBER) + 1, + columns: isAtleastOneFullRow ? PLOTLY_PIE_COLUMN_NUMBER : series.length, pattern: 'independent', }, ...layout, @@ -129,14 +143,14 @@ export const Pie = ({ visualizations, layout, config }: any) => { title, legend: { ...layout.legend, - orientation: legend.position || vis.legendposition, + orientation: legend.position || visMetaData.legendposition, ...(legendSize && { font: { size: legendSize }, }), }, showlegend: showLegend, }; - }, [metrics, layoutConfig.layout, title, layout.legend]); + }, [series, layoutConfig.layout, title, layout.legend]); const mergedConfigs = useMemo( () => ({ diff --git a/dashboards-observability/public/components/visualizations/charts/pie/pie_type.ts b/dashboards-observability/public/components/visualizations/charts/pie/pie_type.ts index c8fea3d51..593d30d38 100644 --- a/dashboards-observability/public/components/visualizations/charts/pie/pie_type.ts +++ b/dashboards-observability/public/components/visualizations/charts/pie/pie_type.ts @@ -17,14 +17,14 @@ import { } from '../../../event_analytics/explorer/visualizations/config_panel/config_panes/config_controls'; import { fetchConfigObject } from '../../../../components/event_analytics/utils/utils'; import { DEFAULT_PALETTE, PIE_PALETTES } from '../../../../../common/constants/colors'; -import { PLOTLY_COLOR, DefaultChartStyles } from '../../../../../common/constants/shared'; -import { DefaultPieChartParameters } from '../../../../../common/constants/explorer'; +import { PLOTLY_COLOR, DEFAULT_CHART_STYLES } from '../../../../../common/constants/shared'; +import { DEFAULT_PIE_CHART_PARAMETERS } from '../../../../../common/constants/explorer'; const sharedConfigs = getPlotlySharedConfigs(); const VIS_CATEGORY = getPlotlyCategory(); -const { ShowLegend, LegendPosition } = DefaultChartStyles; -const { DefaultMode } = DefaultPieChartParameters; +const { ShowLegend, LegendPosition } = DEFAULT_CHART_STYLES; +const { DefaultMode } = DEFAULT_PIE_CHART_PARAMETERS; export const createPieTypeDefinition = (params: any) => ({ name: 'pie', diff --git a/dashboards-observability/public/components/visualizations/visualization.tsx b/dashboards-observability/public/components/visualizations/visualization.tsx index c3c13c4b9..75ba98241 100644 --- a/dashboards-observability/public/components/visualizations/visualization.tsx +++ b/dashboards-observability/public/components/visualizations/visualization.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { isArray } from 'lodash'; import { VisualizationChart } from './visualization_chart'; import { EmptyPlaceholder } from '../event_analytics/explorer/visualizations/shared_components/empty_placeholder'; -import { visChartTypes } from '../../../common/constants/shared'; +import { VIS_CHART_TYPES } from '../../../common/constants/shared'; interface IVisualizationProps {} @@ -24,7 +24,7 @@ export const Visualization = ({ visualizations }: IVisualizationProps) => { return ( <> - {vis?.type === visChartTypes.LogsView || (isVizDataValid && isVizFieldValid) ? ( + {vis?.type === VIS_CHART_TYPES.LogsView || (isVizDataValid && isVizFieldValid) ? ( ) : (