Skip to content

Commit e438b4c

Browse files
nreeseelasticmachinekibanamachine
authored
[dashboards as code] Make options parameter optional and avoid injecting defaults in REST API response (elastic#238129)
PR does the following: * Updates options schema to make options object optional and option keys optional * Updates server logic to not inject default values for options on write and on read * Updates client typing to consume options with same type as defined in schema. Previously, options where spread into client state. Now options are under the options key just like REST API shape --------- Co-authored-by: Elastic Machine <[email protected]> Co-authored-by: kibanamachine <[email protected]>
1 parent 00ad46b commit e438b4c

File tree

28 files changed

+333
-325
lines changed

28 files changed

+333
-325
lines changed

src/platform/packages/shared/presentation/presentation_publishing/state_manager/state_comparators.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,16 @@ export const runComparator = <StateType extends object = object>(
3535
*/
3636
export const diffComparators = <StateType extends object = object>(
3737
comparators: StateComparators<StateType>,
38-
lastSavedState?: StateType,
39-
latestState?: StateType
38+
lastSavedState?: Partial<StateType>,
39+
latestState?: Partial<StateType>,
40+
defaultState?: Partial<StateType>
4041
): Partial<StateType> => {
4142
return Object.keys(comparators).reduce((acc, key) => {
4243
const comparator = comparators[key as keyof StateType];
43-
const lastSavedValue = lastSavedState?.[key as keyof StateType];
44-
const currentValue = latestState?.[key as keyof StateType];
44+
const lastSavedValue =
45+
lastSavedState?.[key as keyof StateType] ?? defaultState?.[key as keyof StateType];
46+
const currentValue =
47+
latestState?.[key as keyof StateType] ?? defaultState?.[key as keyof StateType];
4548

4649
if (!runComparator(comparator, lastSavedState, latestState, lastSavedValue, currentValue)) {
4750
acc[key as keyof StateType] = currentValue;

src/platform/packages/shared/presentation/presentation_publishing/state_manager/state_manager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type KeyToSubjectMap<StateType extends object> = {
2828
* @param comparators - Optional StateComparators. When provided, subject will only emit when value changes.
2929
*/
3030
export const initializeStateManager = <StateType extends object>(
31-
initialState: StateType,
31+
initialState: Partial<StateType>,
3232
defaultState: WithAllKeys<StateType>,
3333
comparators?: StateComparators<StateType>
3434
): StateManager<StateType> => {

src/platform/plugins/shared/dashboard/common/index.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,7 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10-
export type {
11-
DashboardCapabilities,
12-
DashboardLocatorParams,
13-
DashboardSettings,
14-
DashboardState,
15-
} from './types';
10+
export type { DashboardCapabilities, DashboardLocatorParams, DashboardState } from './types';
1611

1712
export {
1813
getReferencesForPanelId,

src/platform/plugins/shared/dashboard/common/types.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
*/
99

1010
import type { Reference } from '@kbn/content-management-utils';
11-
import type { SerializableRecord, Writable } from '@kbn/utility-types';
11+
import type { SerializableRecord } from '@kbn/utility-types';
1212
import type { ViewMode } from '@kbn/presentation-publishing';
1313
import type { RefreshInterval } from '@kbn/data-plugin/public';
1414
import type { ControlsGroupState } from '@kbn/controls-schemas';
1515

16-
import type { DashboardAttributes, DashboardOptions } from '../server/content_management';
16+
import type { DashboardAttributes } from '../server/content_management';
1717

1818
export interface DashboardCapabilities {
1919
showWriteControls: boolean;
@@ -27,14 +27,12 @@ export interface DashboardAttributesAndReferences {
2727
references: Reference[];
2828
}
2929

30-
export type DashboardSettings = Writable<DashboardOptions> & {
30+
export interface DashboardState {
31+
options?: DashboardAttributes['options'];
3132
description?: DashboardAttributes['description'];
32-
tags: string[];
33+
tags?: DashboardAttributes['tags'];
3334
timeRestore: DashboardAttributes['timeRestore'];
3435
title: DashboardAttributes['description'];
35-
};
36-
37-
export interface DashboardState extends DashboardSettings {
3836
query?: DashboardAttributes['query'];
3937
filters?: DashboardAttributes['filters'];
4038
timeRange?: DashboardAttributes['timeRange'];

src/platform/plugins/shared/dashboard/public/dashboard_api/default_dashboard_state.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,4 @@ export const DEFAULT_DASHBOARD_STATE: DashboardState = {
1717
panels: [],
1818
title: '',
1919
tags: [],
20-
21-
// options
22-
useMargins: true,
23-
syncColors: false,
24-
syncCursor: true,
25-
syncTooltips: false,
26-
hidePanelTitles: false,
2720
};

src/platform/plugins/shared/dashboard/public/dashboard_api/get_dashboard_api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export function getDashboardApi({
120120
const { panels, references: panelReferences } = layoutManager.internalApi.serializeLayout();
121121
const unifiedSearchState = unifiedSearchManager.internalApi.getState();
122122
const dashboardState: DashboardState = {
123-
...settingsManager.api.getSettings(),
123+
...settingsManager.internalApi.serializeSettings(),
124124
...unifiedSearchState,
125125
panels,
126126
};

src/platform/plugins/shared/dashboard/public/dashboard_api/get_serialized_state.ts

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,7 @@ export const getSerializedState = ({
5555
timeRestore,
5656
description,
5757

58-
// Dashboard options
59-
useMargins,
60-
syncColors,
61-
syncCursor,
62-
syncTooltips,
63-
hidePanelTitles,
58+
options,
6459
controlGroupInput,
6560
} = dashboardState;
6661

@@ -76,14 +71,6 @@ export const getSerializedState = ({
7671
//
7772
}
7873

79-
const options = {
80-
useMargins,
81-
syncColors,
82-
syncCursor,
83-
syncTooltips,
84-
hidePanelTitles,
85-
};
86-
8774
/**
8875
* Parse global time filter settings
8976
*/
@@ -115,9 +102,10 @@ export const getSerializedState = ({
115102
// TODO Provide tags as an array of tag names in the attribute. In that case, tag references
116103
// will be extracted by the server.
117104
const savedObjectsTaggingApi = savedObjectsTaggingService?.getTaggingApi();
118-
const references = savedObjectsTaggingApi?.ui.updateTagsReferences
119-
? savedObjectsTaggingApi?.ui.updateTagsReferences([], tags)
120-
: [];
105+
const references =
106+
tags && savedObjectsTaggingApi?.ui.updateTagsReferences
107+
? savedObjectsTaggingApi?.ui.updateTagsReferences([], tags)
108+
: [];
121109

122110
const allReferences = [
123111
...references,
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
import { BehaviorSubject } from 'rxjs';
11+
import { getSampleDashboardState } from '../mocks';
12+
import type { DashboardState } from '../../common';
13+
import { initializeSettingsManager } from './settings_manager';
14+
15+
describe('initializeSettingsManager', () => {
16+
describe('startComparing$', () => {
17+
test('Should return no changes when there are no changes', (done) => {
18+
const lastSavedState$ = new BehaviorSubject<DashboardState>(getSampleDashboardState());
19+
const settingsManager = initializeSettingsManager(lastSavedState$.value);
20+
settingsManager.internalApi.startComparing$(lastSavedState$).subscribe((changes) => {
21+
expect(changes).toMatchInlineSnapshot(`Object {}`);
22+
done();
23+
});
24+
});
25+
26+
test('Should return only changed keys when there are changes', (done) => {
27+
const lastSavedState$ = new BehaviorSubject<DashboardState>({
28+
...getSampleDashboardState(),
29+
timeRestore: true,
30+
});
31+
const settingsManager = initializeSettingsManager(lastSavedState$.value);
32+
settingsManager.internalApi.startComparing$(lastSavedState$).subscribe((changes) => {
33+
expect(changes).toMatchInlineSnapshot(`
34+
Object {
35+
"options": Object {
36+
"hidePanelTitles": true,
37+
"syncColors": false,
38+
"syncCursor": true,
39+
"syncTooltips": false,
40+
"useMargins": true,
41+
},
42+
"timeRestore": false,
43+
"title": "updated title",
44+
}
45+
`);
46+
done();
47+
});
48+
const currentSettings = settingsManager.api.getSettings();
49+
settingsManager.api.setSettings({
50+
...currentSettings,
51+
title: 'updated title',
52+
hidePanelTitles: !currentSettings.hidePanelTitles,
53+
timeRestore: false,
54+
});
55+
});
56+
});
57+
});

0 commit comments

Comments
 (0)