-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Dashboard] Move all dashboard extract/inject into persistable state (#…
…96095) * Move all dashboard inject/extract to be part of embeddable persistable state * Fixes typescript errors * Remove comments * Fixes test * API Doc changes * Fix integration tests * Fix functional testS * Fix unit tests * Update Dashboard plugin API to get dashboard embeddable renderer * Fix Types Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
- Loading branch information
1 parent
f544d8d
commit b645fec
Showing
21 changed files
with
964 additions
and
345 deletions.
There are no files selected for viewing
11 changes: 11 additions & 0 deletions
11
...ns/embeddable/server/kibana-plugin-plugins-embeddable-server.embeddablestart.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<!-- Do not edit this file. It is automatically generated by API Documenter. --> | ||
|
||
[Home](./index.md) > [kibana-plugin-plugins-embeddable-server](./kibana-plugin-plugins-embeddable-server.md) > [EmbeddableStart](./kibana-plugin-plugins-embeddable-server.embeddablestart.md) | ||
|
||
## EmbeddableStart type | ||
|
||
<b>Signature:</b> | ||
|
||
```typescript | ||
export declare type EmbeddableStart = PersistableStateService<EmbeddableStateWithType>; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
158 changes: 158 additions & 0 deletions
158
src/plugins/dashboard/common/embeddable/dashboard_container_persistable_state.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
import { createExtract, createInject } from './dashboard_container_persistable_state'; | ||
import { createEmbeddablePersistableStateServiceMock } from '../../../embeddable/common/mocks'; | ||
import { DashboardContainerStateWithType } from '../types'; | ||
|
||
const persistableStateService = createEmbeddablePersistableStateServiceMock(); | ||
|
||
const dashboardWithExtractedPanel: DashboardContainerStateWithType = { | ||
id: 'id', | ||
type: 'dashboard', | ||
panels: { | ||
panel_1: { | ||
type: 'panel_type', | ||
gridData: { w: 0, h: 0, x: 0, y: 0, i: '0' }, | ||
panelRefName: 'panel_panel_1', | ||
explicitInput: { | ||
id: 'panel_1', | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const extractedSavedObjectPanelRef = { | ||
name: 'panel_1:panel_panel_1', | ||
type: 'panel_type', | ||
id: 'object-id', | ||
}; | ||
|
||
const unextractedDashboardState: DashboardContainerStateWithType = { | ||
id: 'id', | ||
type: 'dashboard', | ||
panels: { | ||
panel_1: { | ||
type: 'panel_type', | ||
gridData: { w: 0, h: 0, x: 0, y: 0, i: '0' }, | ||
explicitInput: { | ||
id: 'panel_1', | ||
savedObjectId: 'object-id', | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
describe('inject/extract by reference panel', () => { | ||
it('should inject the extracted saved object panel', () => { | ||
const inject = createInject(persistableStateService); | ||
const references = [extractedSavedObjectPanelRef]; | ||
|
||
const injected = inject( | ||
dashboardWithExtractedPanel, | ||
references | ||
) as DashboardContainerStateWithType; | ||
|
||
expect(injected).toEqual(unextractedDashboardState); | ||
}); | ||
|
||
it('should extract the saved object panel', () => { | ||
const extract = createExtract(persistableStateService); | ||
const { state: extractedState, references: extractedReferences } = extract( | ||
unextractedDashboardState | ||
); | ||
|
||
expect(extractedState).toEqual(dashboardWithExtractedPanel); | ||
expect(extractedReferences[0]).toEqual(extractedSavedObjectPanelRef); | ||
}); | ||
}); | ||
|
||
const dashboardWithExtractedByValuePanel: DashboardContainerStateWithType = { | ||
id: 'id', | ||
type: 'dashboard', | ||
panels: { | ||
panel_1: { | ||
type: 'panel_type', | ||
gridData: { w: 0, h: 0, x: 0, y: 0, i: '0' }, | ||
explicitInput: { | ||
id: 'panel_1', | ||
extracted_reference: 'ref', | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const extractedByValueRef = { | ||
id: 'id', | ||
name: 'panel_1:ref', | ||
type: 'panel_type', | ||
}; | ||
|
||
const unextractedDashboardByValueState: DashboardContainerStateWithType = { | ||
id: 'id', | ||
type: 'dashboard', | ||
panels: { | ||
panel_1: { | ||
type: 'panel_type', | ||
gridData: { w: 0, h: 0, x: 0, y: 0, i: '0' }, | ||
explicitInput: { | ||
id: 'panel_1', | ||
value: 'id', | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
describe('inject/extract by value panels', () => { | ||
it('should inject the extracted references', () => { | ||
const inject = createInject(persistableStateService); | ||
|
||
persistableStateService.inject.mockImplementationOnce((state, references) => { | ||
const ref = references.find((r) => r.name === 'ref'); | ||
if (!ref) { | ||
return state; | ||
} | ||
|
||
if (('extracted_reference' in state) as any) { | ||
(state as any).value = ref.id; | ||
delete (state as any).extracted_reference; | ||
} | ||
|
||
return state; | ||
}); | ||
|
||
const injectedState = inject(dashboardWithExtractedByValuePanel, [extractedByValueRef]); | ||
|
||
expect(injectedState).toEqual(unextractedDashboardByValueState); | ||
}); | ||
|
||
it('should extract references using persistable state', () => { | ||
const extract = createExtract(persistableStateService); | ||
|
||
persistableStateService.extract.mockImplementationOnce((state) => { | ||
if ((state as any).value === 'id') { | ||
delete (state as any).value; | ||
(state as any).extracted_reference = 'ref'; | ||
|
||
return { | ||
state, | ||
references: [{ id: extractedByValueRef.id, name: 'ref', type: extractedByValueRef.type }], | ||
}; | ||
} | ||
|
||
return { state, references: [] }; | ||
}); | ||
|
||
const { state: extractedState, references: extractedReferences } = extract( | ||
unextractedDashboardByValueState | ||
); | ||
|
||
expect(extractedState).toEqual(dashboardWithExtractedByValuePanel); | ||
expect(extractedReferences).toEqual([extractedByValueRef]); | ||
}); | ||
}); |
125 changes: 125 additions & 0 deletions
125
src/plugins/dashboard/common/embeddable/dashboard_container_persistable_state.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
import { | ||
EmbeddableInput, | ||
EmbeddablePersistableStateService, | ||
EmbeddableStateWithType, | ||
} from '../../../embeddable/common'; | ||
import { SavedObjectReference } from '../../../../core/types'; | ||
import { DashboardContainerStateWithType, DashboardPanelState } from '../types'; | ||
|
||
const getPanelStatePrefix = (state: DashboardPanelState) => `${state.explicitInput.id}:`; | ||
|
||
export const createInject = ( | ||
persistableStateService: EmbeddablePersistableStateService | ||
): EmbeddablePersistableStateService['inject'] => { | ||
return (state: EmbeddableStateWithType, references: SavedObjectReference[]) => { | ||
const workingState = { ...state } as EmbeddableStateWithType | DashboardContainerStateWithType; | ||
|
||
if ('panels' in workingState) { | ||
workingState.panels = { ...workingState.panels }; | ||
|
||
for (const [key, panel] of Object.entries(workingState.panels)) { | ||
workingState.panels[key] = { ...panel }; | ||
// Find the references for this panel | ||
const prefix = getPanelStatePrefix(panel); | ||
|
||
const filteredReferences = references | ||
.filter((reference) => reference.name.indexOf(prefix) === 0) | ||
.map((reference) => ({ ...reference, name: reference.name.replace(prefix, '') })); | ||
|
||
const panelReferences = filteredReferences.length === 0 ? references : filteredReferences; | ||
|
||
// Inject dashboard references back in | ||
if (panel.panelRefName !== undefined) { | ||
const matchingReference = panelReferences.find( | ||
(reference) => reference.name === panel.panelRefName | ||
); | ||
|
||
if (!matchingReference) { | ||
throw new Error(`Could not find reference "${panel.panelRefName}"`); | ||
} | ||
|
||
if (matchingReference !== undefined) { | ||
workingState.panels[key] = { | ||
...panel, | ||
type: matchingReference.type, | ||
explicitInput: { | ||
...workingState.panels[key].explicitInput, | ||
savedObjectId: matchingReference.id, | ||
}, | ||
}; | ||
|
||
delete workingState.panels[key].panelRefName; | ||
} | ||
} | ||
|
||
const { type, ...injectedState } = persistableStateService.inject( | ||
{ ...workingState.panels[key].explicitInput, type: workingState.panels[key].type }, | ||
panelReferences | ||
); | ||
|
||
workingState.panels[key].explicitInput = injectedState as EmbeddableInput; | ||
} | ||
} | ||
|
||
return workingState as EmbeddableStateWithType; | ||
}; | ||
}; | ||
|
||
export const createExtract = ( | ||
persistableStateService: EmbeddablePersistableStateService | ||
): EmbeddablePersistableStateService['extract'] => { | ||
return (state: EmbeddableStateWithType) => { | ||
const workingState = { ...state } as EmbeddableStateWithType | DashboardContainerStateWithType; | ||
|
||
const references: SavedObjectReference[] = []; | ||
|
||
if ('panels' in workingState) { | ||
workingState.panels = { ...workingState.panels }; | ||
|
||
// 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); | ||
|
||
// If the panel is a saved object, then we will make the reference for that saved object and change the explicit input | ||
if (panel.explicitInput.savedObjectId) { | ||
panel.panelRefName = `panel_${key}`; | ||
|
||
references.push({ | ||
name: `${prefix}panel_${key}`, | ||
type: panel.type, | ||
id: panel.explicitInput.savedObjectId as string, | ||
}); | ||
|
||
delete panel.explicitInput.savedObjectId; | ||
delete panel.explicitInput.type; | ||
} | ||
|
||
const { state: panelState, references: panelReferences } = persistableStateService.extract({ | ||
...panel.explicitInput, | ||
type: panel.type, | ||
}); | ||
|
||
// We're going to prefix the names of the references so that we don't end up with dupes (from visualizations for instance) | ||
const prefixedReferences = panelReferences.map((reference) => ({ | ||
...reference, | ||
name: `${prefix}${reference.name}`, | ||
})); | ||
|
||
references.push(...prefixedReferences); | ||
|
||
const { type, ...restOfState } = panelState; | ||
workingState.panels[key].explicitInput = restOfState as EmbeddableInput; | ||
} | ||
} | ||
|
||
return { state: workingState as EmbeddableStateWithType, references }; | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.