mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[controls] remove id from explicit input (#211851)
Part of `EmbeddableInput` type removal. PR removes `EmbeddableInput` from controls plugin. Part of this effort is removing `id` key from `controlConfig/explicitInput`. While investigating this PR, I found it odd that `ControlGroupApi.serializeState` returned controls in shape `[ { ...rest } ]` while `ControlGroupFactory.deserializeState` expected to receive controls in the shape `[ { id, ...rest }]`. The only reason this works is that src/platform/plugins/shared/dashboard/server/content_management/v3/transform_utils.ts `controlGroupInputOut` adds `id` to each object in `controls`. This PR also resolves this and updates `ControlGroupApi.serializeState` to return controls in shape `[ { id, ...rest } ]` --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
bee6ba88c9
commit
decf5feba5
9 changed files with 75 additions and 91 deletions
|
@ -51,7 +51,12 @@ export interface ControlGroupSerializedState
|
|||
// In runtime state, we refer to this property as `initialChildControlState`, but in
|
||||
// the serialized state we transform the state object into an array of state objects
|
||||
// to make it easier for API consumers to add new controls without specifying a uuid key.
|
||||
controls: Array<ControlPanelState & { id?: string }>;
|
||||
controls: Array<
|
||||
ControlPanelState & {
|
||||
id?: string;
|
||||
controlConfig?: object;
|
||||
}
|
||||
>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,7 +31,7 @@ export interface DefaultControlState {
|
|||
export interface SerializedControlState<ControlStateType extends object = object>
|
||||
extends DefaultControlState {
|
||||
type: string;
|
||||
explicitInput: { id: string } & ControlStateType;
|
||||
explicitInput: ControlStateType;
|
||||
}
|
||||
|
||||
export interface DefaultDataControlState extends DefaultControlState {
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
} from '@kbn/presentation-publishing';
|
||||
import { BehaviorSubject, first, merge } from 'rxjs';
|
||||
import type {
|
||||
ControlGroupSerializedState,
|
||||
ControlPanelState,
|
||||
ControlPanelsState,
|
||||
ControlWidth,
|
||||
|
@ -151,7 +152,7 @@ export function initControlsManager(
|
|||
serializeControls: () => {
|
||||
const references: Reference[] = [];
|
||||
|
||||
const controls: Array<ControlPanelState & { controlConfig: object }> = [];
|
||||
const controls: ControlGroupSerializedState['controls'] = [];
|
||||
|
||||
controlsInOrder$.getValue().forEach(({ id }, index) => {
|
||||
const controlApi = getControlApi(id);
|
||||
|
@ -160,7 +161,7 @@ export function initControlsManager(
|
|||
}
|
||||
|
||||
const {
|
||||
rawState: { grow, width, ...rest },
|
||||
rawState: { grow, width, ...controlConfig },
|
||||
references: controlReferences,
|
||||
} = controlApi.serializeState();
|
||||
|
||||
|
@ -169,12 +170,13 @@ export function initControlsManager(
|
|||
}
|
||||
|
||||
controls.push({
|
||||
id,
|
||||
grow,
|
||||
order: index,
|
||||
type: controlApi.type,
|
||||
width,
|
||||
/** Re-add the `controlConfig` layer on serialize so control group saved object retains shape */
|
||||
controlConfig: { id, ...rest },
|
||||
controlConfig,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -7,40 +7,40 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { omit } from 'lodash';
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { SerializedPanelState } from '@kbn/presentation-publishing';
|
||||
import type { ControlGroupRuntimeState, ControlGroupSerializedState } from '../../../common';
|
||||
import type {
|
||||
ControlGroupRuntimeState,
|
||||
ControlGroupSerializedState,
|
||||
ControlPanelsState,
|
||||
} from '../../../common';
|
||||
import { parseReferenceName } from '../../controls/data_controls/reference_name_utils';
|
||||
|
||||
export const deserializeControlGroup = (
|
||||
state: SerializedPanelState<ControlGroupSerializedState>
|
||||
): ControlGroupRuntimeState => {
|
||||
const { controls } = state.rawState;
|
||||
const controlsMap = Object.fromEntries(controls.map(({ id, ...rest }) => [id, rest]));
|
||||
const initialChildControlState: ControlPanelsState = {};
|
||||
(state.rawState.controls ?? []).forEach((controlSeriailizedState) => {
|
||||
const { controlConfig, id, ...rest } = controlSeriailizedState;
|
||||
initialChildControlState[id ?? uuidv4()] = {
|
||||
...rest,
|
||||
...(controlConfig ?? {}),
|
||||
};
|
||||
});
|
||||
|
||||
/** Inject data view references into each individual control */
|
||||
// Inject data view references into each individual control
|
||||
// TODO move reference injection into control factory to avoid leaking implemenation details like dataViewId to ControlGroup
|
||||
const references = state.references ?? [];
|
||||
references.forEach((reference) => {
|
||||
const referenceName = reference.name;
|
||||
const { controlId } = parseReferenceName(referenceName);
|
||||
if (controlsMap[controlId]) {
|
||||
controlsMap[controlId].dataViewId = reference.id;
|
||||
if (initialChildControlState[controlId]) {
|
||||
(initialChildControlState[controlId] as { dataViewId?: string }).dataViewId = reference.id;
|
||||
}
|
||||
});
|
||||
|
||||
/** Flatten the state of each control by removing `controlConfig` */
|
||||
const flattenedControls = Object.keys(controlsMap).reduce((prev, controlId) => {
|
||||
const currentControl = controlsMap[controlId];
|
||||
const currentControlExplicitInput = controlsMap[controlId].controlConfig;
|
||||
return {
|
||||
...prev,
|
||||
[controlId]: { ...omit(currentControl, 'controlConfig'), ...currentControlExplicitInput },
|
||||
};
|
||||
}, {});
|
||||
|
||||
return {
|
||||
...state.rawState,
|
||||
initialChildControlState: flattenedControls,
|
||||
initialChildControlState,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,13 +7,12 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { SerializableRecord } from '@kbn/utility-types';
|
||||
import {
|
||||
OPTIONS_LIST_CONTROL,
|
||||
RANGE_SLIDER_CONTROL,
|
||||
type ControlPanelState,
|
||||
type ControlWidth,
|
||||
type DefaultDataControlState,
|
||||
type SerializedControlState,
|
||||
} from '../../common';
|
||||
import { OptionsListControlState } from '../../common/options_list';
|
||||
import { mockDataControlState, mockOptionsListControlState } from '../mocks';
|
||||
|
@ -22,87 +21,67 @@ import { getDefaultControlGroupState } from './control_group_persistence';
|
|||
import type { SerializableControlGroupState } from './types';
|
||||
|
||||
describe('migrate control group', () => {
|
||||
const getOptionsListControl = (
|
||||
order: number,
|
||||
input?: Partial<OptionsListControlState & { id: string }>
|
||||
) => {
|
||||
const getOptionsListControl = (order: number, input?: Partial<OptionsListControlState>) => {
|
||||
return {
|
||||
type: OPTIONS_LIST_CONTROL,
|
||||
order,
|
||||
width: 'small' as ControlWidth,
|
||||
grow: true,
|
||||
explicitInput: { ...mockOptionsListControlState, ...input },
|
||||
} as ControlPanelState<SerializedControlState<OptionsListControlState>>;
|
||||
} as unknown as SerializableRecord;
|
||||
};
|
||||
|
||||
const getRangeSliderControl = (
|
||||
order: number,
|
||||
input?: Partial<DefaultDataControlState & { id: string }>
|
||||
) => {
|
||||
const getRangeSliderControl = (order: number, input?: Partial<DefaultDataControlState>) => {
|
||||
return {
|
||||
type: RANGE_SLIDER_CONTROL,
|
||||
order,
|
||||
width: 'medium' as ControlWidth,
|
||||
grow: false,
|
||||
explicitInput: { ...mockDataControlState, ...input },
|
||||
} as ControlPanelState<SerializedControlState<DefaultDataControlState>>;
|
||||
};
|
||||
|
||||
const getControlGroupState = (
|
||||
panels: Array<ControlPanelState<SerializedControlState>>
|
||||
): SerializableControlGroupState => {
|
||||
const panelsObjects = panels.reduce((acc, panel) => {
|
||||
return { ...acc, [panel.explicitInput.id]: panel };
|
||||
}, {});
|
||||
|
||||
return {
|
||||
...getDefaultControlGroupState(),
|
||||
panels: panelsObjects,
|
||||
};
|
||||
} as unknown as SerializableRecord;
|
||||
};
|
||||
|
||||
describe('remove hideExclude and hideExists', () => {
|
||||
test('should migrate single options list control', () => {
|
||||
const migratedControlGroupState: SerializableControlGroupState =
|
||||
removeHideExcludeAndHideExists(
|
||||
getControlGroupState([getOptionsListControl(0, { id: 'testPanelId', hideExclude: true })])
|
||||
);
|
||||
removeHideExcludeAndHideExists({
|
||||
...getDefaultControlGroupState(),
|
||||
panels: {
|
||||
testPanelId: getOptionsListControl(0, { hideExclude: true }),
|
||||
},
|
||||
});
|
||||
expect(migratedControlGroupState.panels).toEqual({
|
||||
testPanelId: getOptionsListControl(0, { id: 'testPanelId' }),
|
||||
testPanelId: getOptionsListControl(0),
|
||||
});
|
||||
});
|
||||
|
||||
test('should migrate multiple options list controls', () => {
|
||||
const migratedControlGroupInput: SerializableControlGroupState =
|
||||
removeHideExcludeAndHideExists(
|
||||
getControlGroupState([
|
||||
getOptionsListControl(0, { id: 'testPanelId1' }),
|
||||
getOptionsListControl(1, { id: 'testPanelId2', hideExclude: false }),
|
||||
getOptionsListControl(2, { id: 'testPanelId3', hideExists: true }),
|
||||
getOptionsListControl(3, {
|
||||
id: 'testPanelId4',
|
||||
removeHideExcludeAndHideExists({
|
||||
...getDefaultControlGroupState(),
|
||||
panels: {
|
||||
testPanelId1: getOptionsListControl(0),
|
||||
testPanelId2: getOptionsListControl(1, { hideExclude: false }),
|
||||
testPanelId3: getOptionsListControl(2, { hideExists: true }),
|
||||
testPanelId4: getOptionsListControl(3, {
|
||||
hideExclude: true,
|
||||
hideExists: false,
|
||||
}),
|
||||
getOptionsListControl(4, {
|
||||
id: 'testPanelId5',
|
||||
testPanelId5: getOptionsListControl(4, {
|
||||
hideExists: true,
|
||||
hideExclude: false,
|
||||
singleSelect: true,
|
||||
runPastTimeout: true,
|
||||
selectedOptions: ['test'],
|
||||
}),
|
||||
])
|
||||
);
|
||||
},
|
||||
});
|
||||
expect(migratedControlGroupInput.panels).toEqual({
|
||||
testPanelId1: getOptionsListControl(0, { id: 'testPanelId1' }),
|
||||
testPanelId2: getOptionsListControl(1, { id: 'testPanelId2' }),
|
||||
testPanelId3: getOptionsListControl(2, { id: 'testPanelId3' }),
|
||||
testPanelId4: getOptionsListControl(3, {
|
||||
id: 'testPanelId4',
|
||||
}),
|
||||
testPanelId1: getOptionsListControl(0),
|
||||
testPanelId2: getOptionsListControl(1),
|
||||
testPanelId3: getOptionsListControl(2),
|
||||
testPanelId4: getOptionsListControl(3),
|
||||
testPanelId5: getOptionsListControl(4, {
|
||||
id: 'testPanelId5',
|
||||
singleSelect: true,
|
||||
runPastTimeout: true,
|
||||
selectedOptions: ['test'],
|
||||
|
@ -112,20 +91,20 @@ describe('migrate control group', () => {
|
|||
|
||||
test('should migrate multiple different types of controls', () => {
|
||||
const migratedControlGroupInput: SerializableControlGroupState =
|
||||
removeHideExcludeAndHideExists(
|
||||
getControlGroupState([
|
||||
getOptionsListControl(0, {
|
||||
id: 'testPanelId1',
|
||||
removeHideExcludeAndHideExists({
|
||||
...getDefaultControlGroupState(),
|
||||
panels: {
|
||||
testPanelId1: getOptionsListControl(0, {
|
||||
hideExists: true,
|
||||
hideExclude: true,
|
||||
runPastTimeout: true,
|
||||
}),
|
||||
getRangeSliderControl(1, { id: 'testPanelId2' }),
|
||||
])
|
||||
);
|
||||
testPanelId2: getRangeSliderControl(1),
|
||||
},
|
||||
});
|
||||
expect(migratedControlGroupInput.panels).toEqual({
|
||||
testPanelId1: getOptionsListControl(0, { id: 'testPanelId1', runPastTimeout: true }),
|
||||
testPanelId2: getRangeSliderControl(1, { id: 'testPanelId2' }),
|
||||
testPanelId1: getOptionsListControl(0, { runPastTimeout: true }),
|
||||
testPanelId2: getRangeSliderControl(1),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
import { SavedObjectReference } from '@kbn/core/types';
|
||||
import {
|
||||
EmbeddableInput,
|
||||
EmbeddablePersistableStateService,
|
||||
EmbeddableStateWithType,
|
||||
} from '@kbn/embeddable-plugin/common/types';
|
||||
|
@ -22,7 +21,7 @@ import {
|
|||
} from './control_group_migrations';
|
||||
import { SerializableControlGroupState } from './types';
|
||||
|
||||
const getPanelStatePrefix = (state: SerializedControlState) => `${state.explicitInput.id}:`;
|
||||
const getPanelStatePrefix = (panelId: string) => `${panelId}:`;
|
||||
|
||||
export const createControlGroupInject = (
|
||||
persistableStateService: EmbeddablePersistableStateService
|
||||
|
@ -39,7 +38,7 @@ export const createControlGroupInject = (
|
|||
...panel,
|
||||
};
|
||||
// Find the references for this panel
|
||||
const prefix = getPanelStatePrefix(panel);
|
||||
const prefix = getPanelStatePrefix(key);
|
||||
|
||||
const filteredReferences = references
|
||||
.filter((reference) => reference.name.indexOf(prefix) === 0)
|
||||
|
@ -51,7 +50,7 @@ export const createControlGroupInject = (
|
|||
{ ...workingPanels[key].explicitInput, type: workingPanels[key].type },
|
||||
panelReferences
|
||||
);
|
||||
workingPanels[key].explicitInput = injectedState as EmbeddableInput;
|
||||
workingPanels[key].explicitInput = injectedState;
|
||||
}
|
||||
}
|
||||
return { ...workingState, panels: workingPanels } as unknown as EmbeddableStateWithType;
|
||||
|
@ -70,7 +69,7 @@ export const createControlGroupExtract = (
|
|||
|
||||
// Run every panel through the state service to get the nested references
|
||||
for (const [key, panel] of Object.entries(workingState.panels)) {
|
||||
const prefix = getPanelStatePrefix(panel);
|
||||
const prefix = getPanelStatePrefix(key);
|
||||
|
||||
const { state: panelState, references: panelReferences } = persistableStateService.extract({
|
||||
...panel.explicitInput,
|
||||
|
@ -87,7 +86,7 @@ export const createControlGroupExtract = (
|
|||
|
||||
const { type, ...restOfState } = panelState;
|
||||
(workingState.panels as ControlPanelsState<SerializedControlState>)[key].explicitInput =
|
||||
restOfState as EmbeddableInput;
|
||||
restOfState;
|
||||
}
|
||||
}
|
||||
return { state: workingState as EmbeddableStateWithType, references };
|
||||
|
|
|
@ -11,11 +11,10 @@ import type { OptionsListControlState } from '../common/options_list';
|
|||
import type { DefaultDataControlState } from '../common/types';
|
||||
|
||||
export const mockDataControlState = {
|
||||
id: 'id',
|
||||
fieldName: 'sample field',
|
||||
dataViewId: 'sample id',
|
||||
value: ['0', '10'],
|
||||
} as DefaultDataControlState & { id: string };
|
||||
} as DefaultDataControlState;
|
||||
|
||||
export const mockOptionsListControlState = {
|
||||
...mockDataControlState,
|
||||
|
|
|
@ -265,7 +265,7 @@ describe('itemAttrsToSavedObjectAttrs', () => {
|
|||
"chainingSystem": "NONE",
|
||||
"controlStyle": "twoLine",
|
||||
"ignoreParentSettingsJSON": "{\\"ignoreFilters\\":true,\\"ignoreQuery\\":true,\\"ignoreTimerange\\":true,\\"ignoreValidations\\":true}",
|
||||
"panelsJSON": "{\\"foo\\":{\\"grow\\":false,\\"order\\":0,\\"type\\":\\"type1\\",\\"width\\":\\"small\\",\\"explicitInput\\":{\\"anyKey\\":\\"some value\\",\\"id\\":\\"foo\\"}}}",
|
||||
"panelsJSON": "{\\"foo\\":{\\"grow\\":false,\\"order\\":0,\\"type\\":\\"type1\\",\\"width\\":\\"small\\",\\"explicitInput\\":{\\"anyKey\\":\\"some value\\"}}}",
|
||||
"showApplySelections": true,
|
||||
},
|
||||
"description": "description",
|
||||
|
@ -587,7 +587,7 @@ describe('getResultV3ToV2', () => {
|
|||
|
||||
// Check transformed attributes
|
||||
expect(output.item.attributes.controlGroupInput!.panelsJSON).toMatchInlineSnapshot(
|
||||
`"{\\"foo\\":{\\"grow\\":false,\\"order\\":0,\\"type\\":\\"type1\\",\\"width\\":\\"small\\",\\"explicitInput\\":{\\"bizz\\":\\"buzz\\",\\"id\\":\\"foo\\"}}}"`
|
||||
`"{\\"foo\\":{\\"grow\\":false,\\"order\\":0,\\"type\\":\\"type1\\",\\"width\\":\\"small\\",\\"explicitInput\\":{\\"bizz\\":\\"buzz\\"}}}"`
|
||||
);
|
||||
expect(
|
||||
output.item.attributes.controlGroupInput!.ignoreParentSettingsJSON
|
||||
|
|
|
@ -193,7 +193,7 @@ function controlGroupInputIn(
|
|||
controlGroupInput;
|
||||
const updatedControls = Object.fromEntries(
|
||||
controls.map(({ controlConfig, id = uuidv4(), ...restOfControl }) => {
|
||||
return [id, { ...restOfControl, explicitInput: { ...controlConfig, id } }];
|
||||
return [id, { ...restOfControl, explicitInput: controlConfig }];
|
||||
})
|
||||
);
|
||||
return {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue