Skip to content

Commit af8aaab

Browse files
[Vis Builder] Bug fixes for datasource picker and auto time interval (#2632) (#2765)
* Fixes auto time interval Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> * Fixes change datasource while editing agg Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> * Misc fixes Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> * Updates Changelog Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> * Correctly sets timerange Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> * Fixes rebase Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> (cherry picked from commit 7a41d94) Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Signed-off-by: Ashwin P Chandran <ashwinpc@amazon.com> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 19656d6 commit af8aaab

File tree

18 files changed

+165
-64
lines changed

18 files changed

+165
-64
lines changed

src/core/public/saved_objects/simple_saved_object.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ export class SimpleSavedObject<T = unknown> {
6363
error,
6464
references,
6565
migrationVersion,
66-
// eslint-disable-next-line @typescript-eslint/naming-convention
67-
updated_at,
66+
updated_at: updateAt,
6867
}: SavedObjectType<T>
6968
) {
7069
this.id = id;
@@ -73,7 +72,7 @@ export class SimpleSavedObject<T = unknown> {
7372
this.references = references || [];
7473
this._version = version;
7574
this.migrationVersion = migrationVersion;
76-
this.updated_at = updated_at;
75+
this.updated_at = updateAt;
7776
if (error) {
7877
this.error = error;
7978
}

src/plugins/vis_builder/public/application/components/data_tab/use/use_dropbox.tsx

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ import { cloneDeep } from 'lodash';
88
import { BucketAggType, IndexPatternField, propFilter } from '../../../../../../data/common';
99
import { Schema } from '../../../../../../vis_default_editor/public';
1010
import { COUNT_FIELD, FieldDragDataType } from '../../../utils/drag_drop/types';
11-
import { useTypedDispatch, useTypedSelector } from '../../../utils/state_management';
11+
import { useTypedDispatch } from '../../../utils/state_management';
1212
import { DropboxDisplay, DropboxProps } from '../dropbox';
1313
import { useDrop } from '../../../utils/drag_drop';
1414
import {
1515
editDraftAgg,
1616
reorderAgg,
1717
updateAggConfigParams,
1818
} from '../../../utils/state_management/visualization_slice';
19-
import { useIndexPatterns } from '../../../utils/use/use_index_pattern';
2019
import { useOpenSearchDashboards } from '../../../../../../opensearch_dashboards_react/public';
2120
import { VisBuilderServices } from '../../../../types';
21+
import { useAggs } from '../../../utils/use';
2222

2323
const filterByName = propFilter('name');
2424
const filterByType = propFilter('type');
@@ -30,36 +30,34 @@ export interface UseDropboxProps extends Pick<DropboxProps, 'id' | 'label'> {
3030
export const useDropbox = (props: UseDropboxProps): DropboxProps => {
3131
const { id: dropboxId, label, schema } = props;
3232
const [validAggTypes, setValidAggTypes] = useState<string[]>([]);
33+
const { aggConfigs, indexPattern, aggs, timeRange } = useAggs();
3334
const dispatch = useTypedDispatch();
34-
const indexPattern = useIndexPatterns().selected;
3535
const {
3636
services: {
3737
data: {
3838
search: { aggs: aggService },
3939
},
4040
},
4141
} = useOpenSearchDashboards<VisBuilderServices>();
42-
const aggConfigParams = useTypedSelector(
43-
(state) => state.visualization.activeVisualization?.aggConfigParams
44-
);
45-
46-
const aggConfigs = useMemo(() => {
47-
return indexPattern && aggService.createAggConfigs(indexPattern, cloneDeep(aggConfigParams));
48-
}, [aggConfigParams, aggService, indexPattern]);
4942

50-
const aggs = useMemo(() => aggConfigs?.aggs ?? [], [aggConfigs?.aggs]);
51-
52-
const dropboxAggs = aggs.filter((agg) => agg.schema === schema.name);
43+
const dropboxAggs = useMemo(() => aggs.filter((agg) => agg.schema === schema.name), [
44+
aggs,
45+
schema.name,
46+
]);
5347

5448
const displayFields: DropboxDisplay[] = useMemo(
5549
() =>
5650
dropboxAggs?.map(
57-
(agg): DropboxDisplay => ({
58-
id: agg.id,
59-
label: agg.makeLabel(),
60-
})
61-
) || [],
62-
[dropboxAggs]
51+
(agg): DropboxDisplay => {
52+
// For timeseries aggregations that have timeinterval set as auto, the current timerange is required to calculate the label accurately
53+
agg.aggConfigs.setTimeRange(timeRange);
54+
return {
55+
id: agg.id,
56+
label: agg.makeLabel(),
57+
};
58+
}
59+
) ?? [],
60+
[dropboxAggs, timeRange]
6361
);
6462

6563
// Event handlers for each dropbox action type

src/plugins/vis_builder/public/application/components/option.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const Option: FC<Props> = ({ title, children, initialIsOpen = false }) =>
2020
buttonContent={title}
2121
className="vbOption"
2222
initialIsOpen={initialIsOpen}
23+
data-test-subj={`vbOption-${title.replace(/\s+/g, '-')}`}
2324
>
2425
<EuiSpacer size="s" />
2526
<EuiPanel color="subdued" className="vbOption__panel">

src/plugins/vis_builder/public/application/components/workspace.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export const Workspace: FC = ({ children }) => {
4141
useEffect(() => {
4242
async function loadExpression() {
4343
const schemas = ui.containerConfig.data.schemas;
44-
const [valid, errorMsg] = validateSchemaState(schemas, rootState);
44+
const [valid, errorMsg] = validateSchemaState(schemas, rootState.visualization);
4545

4646
if (!valid) {
4747
if (errorMsg) {
@@ -50,12 +50,13 @@ export const Workspace: FC = ({ children }) => {
5050
setExpression(undefined);
5151
return;
5252
}
53-
const exp = await toExpression(rootState);
53+
54+
const exp = await toExpression(rootState, searchContext);
5455
setExpression(exp);
5556
}
5657

5758
loadExpression();
58-
}, [rootState, toExpression, toasts, ui.containerConfig.data.schemas]);
59+
}, [rootState, toExpression, toasts, ui.containerConfig.data.schemas, searchContext]);
5960

6061
useLayoutEffect(() => {
6162
const subscription = data.query.state$.subscribe(({ state }) => {

src/plugins/vis_builder/public/application/utils/state_management/store.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export const getPreloadedStore = async (services: VisBuilderServices) => {
7272

7373
// Infer the `RootState` and `AppDispatch` types from the store itself
7474
export type RootState = ReturnType<typeof rootReducer>;
75+
export type RenderState = Omit<RootState, 'metadata'>; // Remaining state after auxillary states are removed
7576
type Store = ReturnType<typeof configurePreloadedStore>;
7677
export type AppDispatch = Store['dispatch'];
7778

src/plugins/vis_builder/public/application/utils/state_management/visualization_slice.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export const slice = createSlice({
5050
setIndexPattern: (state, action: PayloadAction<string>) => {
5151
state.indexPattern = action.payload;
5252
state.activeVisualization!.aggConfigParams = [];
53+
state.activeVisualization!.draftAgg = undefined;
5354
},
5455
setSearchField: (state, action: PayloadAction<string>) => {
5556
state.searchField = action.payload;

src/plugins/vis_builder/public/application/utils/use/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6+
export { useAggs } from './use_aggs';
67
export { useVisualizationType } from './use_visualization_type';
78
export { useIndexPatterns } from './use_index_pattern';
89
export { useSavedVisBuilderVis } from './use_saved_vis_builder_vis';
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { cloneDeep } from 'lodash';
7+
import { useLayoutEffect, useMemo, useState } from 'react';
8+
import { useOpenSearchDashboards } from '../../../../../opensearch_dashboards_react/public';
9+
import { VisBuilderServices } from '../../../types';
10+
import { useTypedSelector, useTypedDispatch } from '../state_management';
11+
import { useIndexPatterns } from './use_index_pattern';
12+
13+
/**
14+
* Returns common agg parameters from the store and app context
15+
* @returns { indexPattern, aggConfigs, aggs, timeRange }
16+
*/
17+
export const useAggs = () => {
18+
const {
19+
services: {
20+
data: {
21+
search: { aggs: aggService },
22+
query: {
23+
timefilter: { timefilter },
24+
},
25+
},
26+
},
27+
} = useOpenSearchDashboards<VisBuilderServices>();
28+
const indexPattern = useIndexPatterns().selected;
29+
const [timeRange, setTimeRange] = useState(timefilter.getTime());
30+
const aggConfigParams = useTypedSelector(
31+
(state) => state.visualization.activeVisualization?.aggConfigParams
32+
);
33+
const dispatch = useTypedDispatch();
34+
35+
const aggConfigs = useMemo(() => {
36+
const configs =
37+
indexPattern && aggService.createAggConfigs(indexPattern, cloneDeep(aggConfigParams));
38+
return configs;
39+
}, [aggConfigParams, aggService, indexPattern]);
40+
41+
useLayoutEffect(() => {
42+
const subscription = timefilter.getTimeUpdate$().subscribe(() => {
43+
setTimeRange(timefilter.getTime());
44+
});
45+
46+
return () => {
47+
subscription.unsubscribe();
48+
};
49+
}, [dispatch, timefilter]);
50+
51+
return {
52+
indexPattern,
53+
aggConfigs,
54+
aggs: aggConfigs?.aggs ?? [],
55+
timeRange,
56+
};
57+
};

src/plugins/vis_builder/public/application/utils/validate_schema_state.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,19 @@
55

66
import { countBy } from 'lodash';
77
import { Schemas } from '../../../../vis_default_editor/public';
8-
import { RootState } from './state_management';
8+
import { VisualizationState } from './state_management';
99

10-
export const validateSchemaState = (schemas: Schemas, state: RootState): [boolean, string?] => {
11-
const activeViz = state.visualization.activeVisualization;
10+
/**
11+
* Validate if the visualization state fits the vis type schema criteria
12+
* @param schemas Visualization type config Schema objects
13+
* @param state visualization state
14+
* @returns [Validity, 'Message']
15+
*/
16+
export const validateSchemaState = (
17+
schemas: Schemas,
18+
state: VisualizationState
19+
): [boolean, string?] => {
20+
const activeViz = state.activeVisualization;
1221
const vizName = activeViz?.name;
1322
const aggs = activeViz?.aggConfigParams;
1423

src/plugins/vis_builder/public/embeddable/vis_builder_embeddable.tsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
import { validateSchemaState } from '../application/utils/validate_schema_state';
3131
import { getExpressionLoader, getTypeService } from '../plugin_services';
3232
import { PersistedState } from '../../../visualizations/public';
33+
import { RenderState, VisualizationState } from '../application/utils/state_management';
3334

3435
// Apparently this needs to match the saved object type for the clone and replace panel actions to work
3536
export const VISBUILDER_EMBEDDABLE = VISBUILDER_SAVED_OBJECT;
@@ -121,24 +122,24 @@ export class VisBuilderEmbeddable extends Embeddable<SavedObjectEmbeddableInput,
121122
const { visualization, style } = this.serializedState;
122123

123124
const vizStateWithoutIndex = JSON.parse(visualization);
124-
const visualizationState = {
125+
const visualizationState: VisualizationState = {
125126
searchField: vizStateWithoutIndex.searchField,
126127
activeVisualization: vizStateWithoutIndex.activeVisualization,
127128
indexPattern: this.savedVisBuilder?.searchSourceFields?.index,
128129
};
129-
const rootState = {
130+
const renderState: RenderState = {
130131
visualization: visualizationState,
131132
style: JSON.parse(style),
132133
};
133-
const visualizationName = rootState.visualization?.activeVisualization?.name ?? '';
134+
const visualizationName = renderState.visualization?.activeVisualization?.name ?? '';
134135
const visualizationType = getTypeService().get(visualizationName);
135136
if (!visualizationType) {
136137
this.onContainerError(new Error(`Invalid visualization type ${visualizationName}`));
137138
return;
138139
}
139140
const { toExpression, ui } = visualizationType;
140141
const schemas = ui.containerConfig.data.schemas;
141-
const [valid, errorMsg] = validateSchemaState(schemas, rootState);
142+
const [valid, errorMsg] = validateSchemaState(schemas, visualizationState);
142143

143144
if (!valid) {
144145
if (errorMsg) {
@@ -147,7 +148,11 @@ export class VisBuilderEmbeddable extends Embeddable<SavedObjectEmbeddableInput,
147148
}
148149
} else {
149150
// TODO: handle error in Expression creation
150-
const exp = await toExpression(rootState);
151+
const exp = await toExpression(renderState, {
152+
filters: this.filters,
153+
query: this.query,
154+
timeRange: this.timeRange,
155+
});
151156
return exp;
152157
}
153158
};
@@ -268,12 +273,15 @@ export class VisBuilderEmbeddable extends Embeddable<SavedObjectEmbeddableInput,
268273
// Check if rootState has changed
269274
if (!isEqual(this.getSerializedState(), this.serializedState)) {
270275
this.serializedState = this.getSerializedState();
271-
this.expression = (await this.getExpression()) ?? '';
272276
dirty = true;
273277
}
274278

275-
if (this.handler && dirty) {
276-
this.updateHandler();
279+
if (dirty) {
280+
this.expression = (await this.getExpression()) ?? '';
281+
282+
if (this.handler) {
283+
this.updateHandler();
284+
}
277285
}
278286
}
279287

0 commit comments

Comments
 (0)