Skip to content

Commit

Permalink
move saved object referfence extract and inject into the individual e…
Browse files Browse the repository at this point in the history
…mbeddable factory methods."
  • Loading branch information
ThomThomson committed Feb 9, 2024
1 parent ee34012 commit 6a68f24
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,36 @@
* Side Public License, v 1.
*/

import { Reference } from '@kbn/content-management-utils';
import { CONTROL_GROUP_TYPE, PersistableControlGroupInput } from '@kbn/controls-plugin/common';
import {
EmbeddableInput,
EmbeddableStateWithType,
EmbeddablePersistableStateService,
EmbeddableStateWithType,
} from '@kbn/embeddable-plugin/common';
import { Reference } from '@kbn/content-management-utils';
import { CONTROL_GROUP_TYPE, PersistableControlGroupInput } from '@kbn/controls-plugin/common';

import { DashboardPanelState } from '../types';
import { ParsedDashboardAttributesWithType } from '../../types';

const getPanelStatePrefix = (state: DashboardPanelState) => `${state.explicitInput.id}:`;

const controlGroupReferencePrefix = 'controlGroup_';
const controlGroupId = 'dashboard_control_group';

export const getReferencesForPanelId = (id: string, references: Reference[]): Reference[] => {
const prefix = `${id}:`;
const filteredReferences = references
.filter((reference) => reference.name.indexOf(prefix) === 0)
.map((reference) => ({ ...reference, name: reference.name.replace(prefix, '') }));
return filteredReferences;
};

export const prefixReferencesFromPanel = (id: string, references: Reference[]): Reference[] => {
const prefix = `${id}:`;
return references
.filter((reference) => reference.type !== 'tag') // panel references should never contain tags. If they do, they must be removed
.map((reference) => ({
...reference,
name: `${prefix}${reference.name}`,
}));
};

export const createInject = (
persistableStateService: EmbeddablePersistableStateService
): EmbeddablePersistableStateService['inject'] => {
Expand All @@ -33,47 +47,17 @@ export const createInject = (
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;
}
}
for (const [id, panel] of Object.entries(workingState.panels)) {
workingState.panels[id] = { ...panel };

const panelReferences = getReferencesForPanelId(id, references);

const { type, ...injectedState } = persistableStateService.inject(
{ ...workingState.panels[key].explicitInput, type: workingState.panels[key].type },
{ ...workingState.panels[id].explicitInput, type: workingState.panels[id].type },
panelReferences
);

workingState.panels[key].explicitInput = injectedState as EmbeddableInput;
workingState.panels[id].explicitInput = injectedState as EmbeddableInput;
}
}

Expand Down Expand Up @@ -116,39 +100,14 @@ export const createExtract = (
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;
}

for (const [id, panel] of Object.entries(workingState.panels)) {
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
.filter((reference) => reference.type !== 'tag') // panel references should never contain tags. If they do, they must be removed
.map((reference) => ({
...reference,
name: `${prefix}${reference.name}`,
}));

references.push(...prefixedReferences);

references.push(...prefixReferencesFromPanel(id, panelReferences));
const { type, ...restOfState } = panelState;
workingState.panels[key].explicitInput = restOfState as EmbeddableInput;
workingState.panels[id].explicitInput = restOfState as EmbeddableInput;
}
}

Expand Down
16 changes: 11 additions & 5 deletions src/plugins/discover/common/embeddable/search_inject_extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@
*/

import type { SavedObjectReference } from '@kbn/core-saved-objects-server';
import type { EmbeddableStateWithType } from '@kbn/embeddable-plugin/common';

import {
injectSavedObjectIdRef,
extractSavedObjectIdRef,
type EmbeddableStateWithType,
} from '@kbn/embeddable-plugin/common';
import type { SearchByValueInput } from '@kbn/saved-search-plugin/public';

export const inject = (
state: EmbeddableStateWithType,
_state: EmbeddableStateWithType,
injectedReferences: SavedObjectReference[]
): EmbeddableStateWithType => {
let state = injectSavedObjectIdRef(_state, injectedReferences);
if (hasAttributes(state)) {
// Filter out references that are not in the state
// https://github.com/elastic/kibana/pull/119079
Expand All @@ -36,12 +42,12 @@ export const inject = (
};

export const extract = (
state: EmbeddableStateWithType
_state: EmbeddableStateWithType
): { state: EmbeddableStateWithType; references: SavedObjectReference[] } => {
let references: SavedObjectReference[] = [];
const { state, references } = extractSavedObjectIdRef(_state, []);

if (hasAttributes(state)) {
references = state.attributes.references;
references.push(...state.attributes.references);
}

return { state, references };
Expand Down
16 changes: 10 additions & 6 deletions src/plugins/embeddable/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@
* Side Public License, v 1.
*/

export {
extractSavedObjectIdRef,
injectSavedObjectIdRef,
isSavedObjectEmbeddableInput,
} from './lib';
export type { SavedObjectEmbeddableInput } from './lib';
export { ViewMode } from './types';
export type {
EmbeddableInput,
CommonEmbeddableStartContract,
EmbeddableStateWithType,
PanelState,
EmbeddableInput,
EmbeddablePersistableStateService,
EmbeddableRegistryDefinition,
EmbeddableStateWithType,
PanelState,
} from './types';
export { ViewMode } from './types';
export type { SavedObjectEmbeddableInput } from './lib';
export { isSavedObjectEmbeddableInput } from './lib';
7 changes: 4 additions & 3 deletions src/plugins/embeddable/common/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

export { getExtractFunction } from './extract';
export { getInjectFunction } from './inject';
export type { MigrateFunction } from './migrate';
export { getMigrateFunction } from './migrate';
export { getTelemetryFunction } from './telemetry';
export type { SavedObjectEmbeddableInput } from './saved_object_embeddable';
export type { MigrateFunction } from './migrate';
export { isSavedObjectEmbeddableInput } from './saved_object_embeddable';
export type { SavedObjectEmbeddableInput } from './saved_object_embeddable';
export { extractSavedObjectIdRef, injectSavedObjectIdRef } from './saved_object_id_references';
export { getTelemetryFunction } from './telemetry';
52 changes: 52 additions & 0 deletions src/plugins/embeddable/common/lib/saved_object_id_references.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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 { Reference } from '@kbn/content-management-utils';
import { EmbeddableStateWithType } from '../types';

type EmbeddableStateWithTypeAndSavedObjectId = EmbeddableStateWithType & { savedObjectId?: string };
const stateHasSavedObjectId = (
state: EmbeddableStateWithType
): state is EmbeddableStateWithTypeAndSavedObjectId => {
return Boolean((state as EmbeddableStateWithTypeAndSavedObjectId).savedObjectId);
};

const panelSignifier = 'panel_';

export const injectSavedObjectIdRef = (
state: EmbeddableStateWithType,
references: Reference[]
): EmbeddableStateWithType => {
const savedObjectReference = references.find(
(reference) => reference.name.indexOf(`${panelSignifier}${state.id}`) === 0
);
if (!savedObjectReference) {
return state;
}

const nextState: EmbeddableStateWithTypeAndSavedObjectId = {
...state,
savedObjectId: savedObjectReference.id,
};
return nextState;
};

export const extractSavedObjectIdRef = (
state: EmbeddableStateWithType,
references: Reference[]
): { state: EmbeddableStateWithType; references: Reference[] } => {
if (!stateHasSavedObjectId(state) || !state.savedObjectId) return { state, references };
references.push({
name: `${panelSignifier}${state.id}`,
type: state.type,
id: state.savedObjectId,
});

delete state.savedObjectId;
return { state, references };
};
14 changes: 9 additions & 5 deletions src/plugins/links/common/embeddable/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@
* Side Public License, v 1.
*/

import { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/common';
import {
EmbeddableRegistryDefinition,
extractSavedObjectIdRef,
} from '@kbn/embeddable-plugin/common';
import type { LinksAttributes } from '../content_management';
import { extractReferences } from '../persistable_state';
import { LinksPersistableState } from './types';

export const extract: EmbeddableRegistryDefinition['extract'] = (state) => {
const typedState = state as LinksPersistableState;
export const extract: EmbeddableRegistryDefinition['extract'] = (_state) => {
const typedState = _state as LinksPersistableState;

// by-reference embeddable
if (!('attributes' in typedState) || typedState.attributes === undefined) {
const { state, references } = extractSavedObjectIdRef(_state, []);
// No references to extract for by-reference embeddable since all references are stored with by-reference saved object
return { state, references: [] };
return { state, references };
}

// by-value embeddable
Expand All @@ -27,7 +31,7 @@ export const extract: EmbeddableRegistryDefinition['extract'] = (state) => {

return {
state: {
...state,
..._state,
attributes,
},
references,
Expand Down
7 changes: 5 additions & 2 deletions src/plugins/links/common/embeddable/inject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
* Side Public License, v 1.
*/

import { EmbeddableRegistryDefinition } from '@kbn/embeddable-plugin/common';
import {
EmbeddableRegistryDefinition,
injectSavedObjectIdRef,
} from '@kbn/embeddable-plugin/common';
import { LinksAttributes } from '../content_management';
import { injectReferences } from '../persistable_state';
import { LinksPersistableState } from './types';
Expand All @@ -16,7 +19,7 @@ export const inject: EmbeddableRegistryDefinition['inject'] = (state, references

// by-reference embeddable
if (!('attributes' in typedState) || typedState.attributes === undefined) {
return typedState;
return injectSavedObjectIdRef(state, references);
}

// by-value embeddable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import { i18n } from '@kbn/i18n';
import { first } from 'rxjs/operators';
import type { OnSaveProps } from '@kbn/saved-objects-plugin/public';
import type { SavedObjectMetaData } from '@kbn/saved-objects-finder-plugin/public';
import type { EmbeddableStateWithType } from '@kbn/embeddable-plugin/common';
import {
EmbeddableStateWithType,
injectSavedObjectIdRef,
extractSavedObjectIdRef,
} from '@kbn/embeddable-plugin/common';

import {
injectSearchSourceReferences,
Expand Down Expand Up @@ -277,7 +281,8 @@ export class VisualizeEmbeddableFactory
}

public inject(_state: EmbeddableStateWithType, references: SavedObjectReference[]) {
let state = _state as unknown as VisualizeInput;
const stateWithSORef = injectSavedObjectIdRef(_state, references);
let state = stateWithSORef as unknown as VisualizeInput;

const { type, params } = state.savedVis ?? {};

Expand Down Expand Up @@ -317,8 +322,8 @@ export class VisualizeEmbeddableFactory
}

public extract(_state: EmbeddableStateWithType) {
let state = _state as unknown as VisualizeInput;
const references = [];
const { state: extractedSOIdState, references } = extractSavedObjectIdRef(_state, []);
let state = extractedSOIdState as unknown as VisualizeInput;

if (state.savedVis?.data.savedSearchId) {
references.push({
Expand Down
12 changes: 9 additions & 3 deletions x-pack/plugins/lens/common/embeddable_factory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@

import type { SerializableRecord, Serializable } from '@kbn/utility-types';
import type { SavedObjectReference } from '@kbn/core/types';
import type {
import {
EmbeddableStateWithType,
EmbeddableRegistryDefinition,
extractSavedObjectIdRef,
injectSavedObjectIdRef,
} from '@kbn/embeddable-plugin/common';

export type LensEmbeddablePersistableState = EmbeddableStateWithType & {
Expand All @@ -36,7 +38,8 @@ export const inject: EmbeddableRegistryDefinition['inject'] = (state, references
typedState.attributes.references = matchedReferences as unknown as Serializable[];
}

return typedState;
const stateWithSOId = injectSavedObjectIdRef(typedState, references);
return stateWithSOId;
};

export const extract: EmbeddableRegistryDefinition['extract'] = (state) => {
Expand All @@ -47,5 +50,8 @@ export const extract: EmbeddableRegistryDefinition['extract'] = (state) => {
references = typedState.attributes.references as unknown as SavedObjectReference[];
}

return { state, references };
const { references: referencesWithExtractedSOId, state: stateWithoutSOId } =
extractSavedObjectIdRef(typedState, references);

return { state: stateWithoutSOId, references: referencesWithExtractedSOId };
};
Loading

0 comments on commit 6a68f24

Please sign in to comment.