diff --git a/src/platform/plugins/private/links/common/index.ts b/src/platform/plugins/private/links/common/index.ts index 92a4de7696241..550018cb63fac 100644 --- a/src/platform/plugins/private/links/common/index.ts +++ b/src/platform/plugins/private/links/common/index.ts @@ -8,3 +8,4 @@ */ export { APP_ICON, APP_NAME, CONTENT_ID, DISPLAY_NAME, LATEST_VERSION } from './constants'; +export { extractReferences, injectReferences } from './persistable_state'; diff --git a/src/platform/plugins/private/links/common/persistable_state/references.ts b/src/platform/plugins/private/links/common/persistable_state/references.ts index 44f47f6f4dd3f..53f7966ccdb2a 100644 --- a/src/platform/plugins/private/links/common/persistable_state/references.ts +++ b/src/platform/plugins/private/links/common/persistable_state/references.ts @@ -67,18 +67,19 @@ export function injectReferences({ } const { links } = attributes; - links.forEach((link) => { + const newLinks = links.map((link) => { if (link.type === DASHBOARD_LINK_TYPE && link.destinationRefName) { const reference = findReference(link.destinationRefName, references); - link.destination = reference.id; - delete link.destinationRefName; + const { destinationRefName, ...rest } = link; + return { ...rest, destination: reference.id }; } + return link; }); return { attributes: { ...attributes, - links, + links: newLinks, }, }; } diff --git a/src/platform/plugins/private/links/common/types.ts b/src/platform/plugins/private/links/common/types.ts index 922d6c5ae8ca4..fea7450836d26 100644 --- a/src/platform/plugins/private/links/common/types.ts +++ b/src/platform/plugins/private/links/common/types.ts @@ -8,7 +8,10 @@ */ import type { SavedObjectsResolveResponse } from '@kbn/core-saved-objects-api-server'; +import type { SerializedTitles } from '@kbn/presentation-publishing'; +import type { DynamicActionsSerializedState } from '@kbn/embeddable-enhanced-plugin/public/plugin'; import { CONTENT_ID } from './constants'; +import { LinksAttributes } from './content_management'; export type LinksContentType = typeof CONTENT_ID; @@ -18,3 +21,15 @@ export interface SharingSavedObjectProps { aliasPurpose?: SavedObjectsResolveResponse['alias_purpose']; sourceId?: string; } + +export interface LinksByReferenceSerializedState { + savedObjectId: string; +} + +export interface LinksByValueSerializedState { + attributes: LinksAttributes; +} + +export type LinksSerializedState = SerializedTitles & + Partial & + (LinksByReferenceSerializedState | LinksByValueSerializedState); diff --git a/src/platform/plugins/private/links/public/embeddable/links_embeddable.tsx b/src/platform/plugins/private/links/public/embeddable/links_embeddable.tsx index 43d50920fb9a0..e9c1deeb1c249 100644 --- a/src/platform/plugins/private/links/public/embeddable/links_embeddable.tsx +++ b/src/platform/plugins/private/links/public/embeddable/links_embeddable.tsx @@ -8,7 +8,6 @@ */ import React, { createContext, useMemo } from 'react'; -import { cloneDeep } from 'lodash'; import { BehaviorSubject } from 'rxjs'; import { EuiListGroup, EuiPanel } from '@elastic/eui'; @@ -29,17 +28,13 @@ import { } from '../../common/content_management'; import { DashboardLinkComponent } from '../components/dashboard_link/dashboard_link_component'; import { ExternalLinkComponent } from '../components/external_link/external_link_component'; +import { LinksApi, LinksParentApi, LinksRuntimeState, ResolvedLink } from '../types'; +import { DISPLAY_NAME } from '../../common'; import { - LinksApi, LinksByReferenceSerializedState, LinksByValueSerializedState, - LinksParentApi, - LinksRuntimeState, LinksSerializedState, - ResolvedLink, -} from '../types'; -import { DISPLAY_NAME } from '../../common'; -import { injectReferences } from '../../common/persistable_state'; +} from '../../common/types'; import '../components/links_component.scss'; import { checkForDuplicateTitle, linksClient } from '../content_management'; @@ -61,8 +56,7 @@ export const getLinksEmbeddableFactory = () => { > = { type: CONTENT_ID, deserializeState: async (serializedState) => { - // Clone the state to avoid an object not extensible error when injecting references - const state = cloneDeep(serializedState.rawState); + const state = serializedState.rawState; const { title, description, hidePanelTitles } = serializedState.rawState; if (linksSerializeStateIsByReference(state)) { @@ -76,21 +70,16 @@ export const getLinksEmbeddableFactory = () => { }; } - const { attributes: attributesWithInjectedIds } = injectReferences({ - attributes: state.attributes, - references: serializedState.references ?? [], - }); - - const resolvedLinks = await resolveLinks(attributesWithInjectedIds.links ?? []); + const resolvedLinks = await resolveLinks(state.attributes.links ?? []); return { title, description, hidePanelTitles, links: resolvedLinks, - layout: attributesWithInjectedIds.layout, - defaultPanelTitle: attributesWithInjectedIds.title, - defaultPanelDescription: attributesWithInjectedIds.description, + layout: state.attributes.layout, + defaultPanelTitle: state.attributes.title, + defaultPanelDescription: state.attributes.description, }; }, buildEmbeddable: async (state, buildApi, uuid, parentApi) => { diff --git a/src/platform/plugins/private/links/public/lib/deserialize_from_library.ts b/src/platform/plugins/private/links/public/lib/deserialize_from_library.ts index 725227d3a9f74..e08ecabd9f7e4 100644 --- a/src/platform/plugins/private/links/public/lib/deserialize_from_library.ts +++ b/src/platform/plugins/private/links/public/lib/deserialize_from_library.ts @@ -10,8 +10,9 @@ import type { SOWithMetadata } from '@kbn/content-management-utils'; import { LinksAttributes } from '../../common/content_management'; import { injectReferences } from '../../common/persistable_state'; -import { LinksByReferenceSerializedState, LinksRuntimeState, LinksSerializedState } from '../types'; +import { LinksRuntimeState } from '../types'; import { resolveLinks } from './resolve_links'; +import { LinksByReferenceSerializedState, LinksSerializedState } from '../../common/types'; export const linksSerializeStateIsByReference = ( state?: LinksSerializedState diff --git a/src/platform/plugins/private/links/public/types.ts b/src/platform/plugins/private/links/public/types.ts index aaa5e34faa236..17225af95ba56 100644 --- a/src/platform/plugins/private/links/public/types.ts +++ b/src/platform/plugins/private/links/public/types.ts @@ -18,14 +18,14 @@ import { SerializedTitles, } from '@kbn/presentation-publishing'; import { DefaultEmbeddableApi } from '@kbn/embeddable-plugin/public'; -import { DynamicActionsSerializedState } from '@kbn/embeddable-enhanced-plugin/public/plugin'; import { HasSerializedChildState, PresentationContainer } from '@kbn/presentation-containers'; import { LocatorPublic } from '@kbn/share-plugin/common'; import { DashboardLocatorParams, DASHBOARD_API_TYPE } from '@kbn/dashboard-plugin/public'; import type { DashboardAttributes } from '@kbn/dashboard-plugin/server'; import { CONTENT_ID } from '../common'; -import { Link, LinksAttributes, LinksLayoutType } from '../common/content_management'; +import { Link, LinksLayoutType } from '../common/content_management'; +import { LinksByReferenceSerializedState, LinksSerializedState } from '../common/types'; export type LinksParentApi = PresentationContainer & HasType & @@ -42,18 +42,6 @@ export type LinksApi = HasType & HasEditCapabilities & HasLibraryTransforms; -export interface LinksByReferenceSerializedState { - savedObjectId: string; -} - -export interface LinksByValueSerializedState { - attributes: LinksAttributes; -} - -export type LinksSerializedState = SerializedTitles & - Partial & - (LinksByReferenceSerializedState | LinksByValueSerializedState); - export interface LinksRuntimeState extends Partial, SerializedTitles { diff --git a/src/platform/plugins/private/links/server/plugin.ts b/src/platform/plugins/private/links/server/plugin.ts index c2d374f8d1e3c..20d71a0568b15 100644 --- a/src/platform/plugins/private/links/server/plugin.ts +++ b/src/platform/plugins/private/links/server/plugin.ts @@ -8,11 +8,15 @@ */ import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from '@kbn/core/server'; +import type { EmbeddableSetup } from '@kbn/embeddable-plugin/server'; import type { ContentManagementServerSetup } from '@kbn/content-management-plugin/server'; -import { CONTENT_ID, LATEST_VERSION } from '../common'; +import { EmbeddableStateWithType } from '@kbn/embeddable-plugin/common'; +import { CONTENT_ID, LATEST_VERSION, extractReferences, injectReferences } from '../common'; import { LinksAttributes } from '../common/content_management'; import { LinksStorage } from './content_management'; import { linksSavedObjectType } from './saved_objects'; +import { LinksSerializedState } from '../common/types'; +import { LinksByValueSerializedState } from '../common/types'; export class LinksServerPlugin implements Plugin { private readonly logger: Logger; @@ -25,6 +29,7 @@ export class LinksServerPlugin implements Plugin { core: CoreSetup, plugins: { contentManagement: ContentManagementServerSetup; + embeddable: EmbeddableSetup; } ) { plugins.contentManagement.register({ @@ -38,6 +43,39 @@ export class LinksServerPlugin implements Plugin { }, }); + plugins.embeddable.registerEmbeddableFactory({ + id: CONTENT_ID, + extract: (state) => { + const typedState = state as EmbeddableStateWithType & LinksSerializedState; + if (!('attributes' in typedState) || typedState.attributes === undefined) { + return { state, references: [] }; + } + + const { attributes, references } = extractReferences({ + attributes: typedState.attributes, + } as LinksByValueSerializedState); + return { + state: { ...state, attributes }, + references, + }; + }, + inject: (state, references) => { + const typedState = state as EmbeddableStateWithType & LinksSerializedState; + if (!('attributes' in typedState) || typedState.attributes === undefined) { + return typedState; + } + + const { attributes } = injectReferences({ + attributes: typedState.attributes, + references, + }); + return { + ...typedState, + attributes, + }; + }, + }); + core.savedObjects.registerType(linksSavedObjectType); return {}; diff --git a/src/platform/plugins/shared/controls/common/constants.ts b/src/platform/plugins/shared/controls/common/constants.ts index d1f88567a3c6e..096ba4c3c4573 100644 --- a/src/platform/plugins/shared/controls/common/constants.ts +++ b/src/platform/plugins/shared/controls/common/constants.ts @@ -27,6 +27,7 @@ export const DEFAULT_IGNORE_PARENT_SETTINGS = { } as const; export const DEFAULT_AUTO_APPLY_SELECTIONS = true; +export const REFERENCE_NAME_PREFIX = 'controlGroup_'; export const TIME_SLIDER_CONTROL = 'timeSlider'; export const RANGE_SLIDER_CONTROL = 'rangeSliderControl'; export const OPTIONS_LIST_CONTROL = 'optionsListControl'; diff --git a/src/platform/plugins/shared/controls/common/index.ts b/src/platform/plugins/shared/controls/common/index.ts index 1fd51fd92be9b..386072f52b7a5 100644 --- a/src/platform/plugins/shared/controls/common/index.ts +++ b/src/platform/plugins/shared/controls/common/index.ts @@ -26,6 +26,7 @@ export { CONTROL_WIDTH_OPTIONS, CONTROL_CHAINING_OPTIONS, CONTROL_LABEL_POSITION_OPTIONS, + REFERENCE_NAME_PREFIX, OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL, TIME_SLIDER_CONTROL, diff --git a/src/platform/plugins/shared/controls/public/controls/data_controls/reference_name_utils.ts b/src/platform/plugins/shared/controls/public/controls/data_controls/reference_name_utils.ts index 43dc37610f66c..f1323975e81ba 100644 --- a/src/platform/plugins/shared/controls/public/controls/data_controls/reference_name_utils.ts +++ b/src/platform/plugins/shared/controls/public/controls/data_controls/reference_name_utils.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -const REFERENCE_NAME_PREFIX = 'controlGroup_'; +import { REFERENCE_NAME_PREFIX } from '../../../common'; export function getReferenceName(controlId: string, referenceNameSuffix: string) { return `${REFERENCE_NAME_PREFIX}${controlId}:${referenceNameSuffix}`; diff --git a/src/platform/plugins/shared/dashboard/common/dashboard_container/persistable_state/dashboard_container_references.ts b/src/platform/plugins/shared/dashboard/common/dashboard_container/persistable_state/dashboard_container_references.ts index 0a331c8c253fd..94110f307d4d3 100644 --- a/src/platform/plugins/shared/dashboard/common/dashboard_container/persistable_state/dashboard_container_references.ts +++ b/src/platform/plugins/shared/dashboard/common/dashboard_container/persistable_state/dashboard_container_references.ts @@ -13,6 +13,7 @@ import { EmbeddablePersistableStateService, EmbeddableStateWithType, } from '@kbn/embeddable-plugin/common'; +import { REFERENCE_NAME_PREFIX } from '@kbn/controls-plugin/common'; import { ParsedDashboardAttributesWithType } from '../../types'; export const getReferencesForPanelId = (id: string, references: Reference[]): Reference[] => { @@ -24,7 +25,7 @@ export const getReferencesForPanelId = (id: string, references: Reference[]): Re }; export const getReferencesForControls = (references: Reference[]): Reference[] => { - return references.filter((reference) => reference.name.startsWith(controlGroupReferencePrefix)); + return references.filter((reference) => reference.name.startsWith(REFERENCE_NAME_PREFIX)); }; export const prefixReferencesFromPanel = (id: string, references: Reference[]): Reference[] => { @@ -37,8 +38,6 @@ export const prefixReferencesFromPanel = (id: string, references: Reference[]): })); }; -const controlGroupReferencePrefix = 'controlGroup_'; - export const createInject = ( persistableStateService: EmbeddablePersistableStateService ): EmbeddablePersistableStateService['inject'] => { diff --git a/src/platform/plugins/shared/dashboard/common/index.ts b/src/platform/plugins/shared/dashboard/common/index.ts index c8c988d5c461e..137f29e6a3a07 100644 --- a/src/platform/plugins/shared/dashboard/common/index.ts +++ b/src/platform/plugins/shared/dashboard/common/index.ts @@ -27,7 +27,11 @@ export { createExtract, } from './dashboard_container/persistable_state/dashboard_container_references'; -export { prefixReferencesFromPanel } from './dashboard_container/persistable_state/dashboard_container_references'; +export { + prefixReferencesFromPanel, + getReferencesForControls, + getReferencesForPanelId, +} from './dashboard_container/persistable_state/dashboard_container_references'; export { convertPanelsArrayToPanelMap, diff --git a/src/platform/plugins/shared/dashboard/server/content_management/dashboard_storage.ts b/src/platform/plugins/shared/dashboard/server/content_management/dashboard_storage.ts index d113d509f5e89..8338c893be9c8 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/dashboard_storage.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/dashboard_storage.ts @@ -17,10 +17,11 @@ import type { Logger } from '@kbn/logging'; import { CreateResult, DeleteResult, SearchQuery } from '@kbn/content-management-plugin/common'; import { StorageContext } from '@kbn/content-management-plugin/server'; +import { EmbeddableStart } from '@kbn/embeddable-plugin/server'; import { DASHBOARD_SAVED_OBJECT_TYPE } from '../dashboard_saved_object'; import { cmServicesDefinition } from './cm_services'; import { DashboardSavedObjectAttributes } from '../dashboard_saved_object'; -import { itemAttrsToSavedObjectAttrs, savedObjectToItem } from './latest'; +import { itemAttrsToSavedObject, savedObjectToItem } from './latest'; import type { DashboardAttributes, DashboardItem, @@ -62,10 +63,13 @@ export class DashboardStorage { constructor({ logger, throwOnResultValidationError, + embeddable, }: { logger: Logger; throwOnResultValidationError: boolean; + embeddable: EmbeddableStart; }) { + this.embeddable = embeddable; this.logger = logger; this.throwOnResultValidationError = throwOnResultValidationError ?? false; this.mSearch = { @@ -76,6 +80,7 @@ export class DashboardStorage { const { item, error: itemError } = savedObjectToItem( savedObject as SavedObjectsFindResult, + this.embeddable, false ); if (itemError) { @@ -110,6 +115,7 @@ export class DashboardStorage { }; } + private embeddable: EmbeddableStart; private logger: Logger; private throwOnResultValidationError: boolean; @@ -131,7 +137,7 @@ export class DashboardStorage { outcome, } = await soClient.resolve(DASHBOARD_SAVED_OBJECT_TYPE, id); - const { item, error: itemError } = savedObjectToItem(savedObject, false); + const { item, error: itemError } = savedObjectToItem(savedObject, this.embeddable, false); if (itemError) { throw Boom.badRequest(`Invalid response. ${itemError.message}`); } @@ -194,8 +200,11 @@ export class DashboardStorage { throw Boom.badRequest(`Invalid options. ${optionsError.message}`); } - const { attributes: soAttributes, error: attributesError } = - itemAttrsToSavedObjectAttrs(dataToLatest); + const { + attributes: soAttributes, + references: soReferences, + error: attributesError, + } = itemAttrsToSavedObject(dataToLatest, this.embeddable, options.references); if (attributesError) { throw Boom.badRequest(`Invalid data. ${attributesError.message}`); } @@ -204,10 +213,10 @@ export class DashboardStorage { const savedObject = await soClient.create( DASHBOARD_SAVED_OBJECT_TYPE, soAttributes, - optionsToLatest + { ...optionsToLatest, references: soReferences } ); - const { item, error: itemError } = savedObjectToItem(savedObject, false); + const { item, error: itemError } = savedObjectToItem(savedObject, this.embeddable, false); if (itemError) { throw Boom.badRequest(`Invalid response. ${itemError.message}`); } @@ -263,8 +272,11 @@ export class DashboardStorage { throw Boom.badRequest(`Invalid options. ${optionsError.message}`); } - const { attributes: soAttributes, error: attributesError } = - itemAttrsToSavedObjectAttrs(dataToLatest); + const { + attributes: soAttributes, + references: soReferences, + error: attributesError, + } = itemAttrsToSavedObject(dataToLatest, this.embeddable, options.references); if (attributesError) { throw Boom.badRequest(`Invalid data. ${attributesError.message}`); } @@ -274,10 +286,10 @@ export class DashboardStorage { DASHBOARD_SAVED_OBJECT_TYPE, id, soAttributes, - optionsToLatest + { ...optionsToLatest, references: soReferences } ); - const { item, error: itemError } = savedObjectToItem(partialSavedObject, true); + const { item, error: itemError } = savedObjectToItem(partialSavedObject, this.embeddable, true); if (itemError) { throw Boom.badRequest(`Invalid response. ${itemError.message}`); } @@ -341,7 +353,7 @@ export class DashboardStorage { const soResponse = await soClient.find(soQuery); const hits = soResponse.saved_objects .map((so) => { - const { item } = savedObjectToItem(so, false, { + const { item } = savedObjectToItem(so, this.embeddable, false, { allowedAttributes: soQuery.fields, allowedReferences: optionsToLatest?.includeReferences, }); diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v3/index.ts b/src/platform/plugins/shared/dashboard/server/content_management/v3/index.ts index 7be9313c3210e..32a5469fcf72c 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v3/index.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v3/index.ts @@ -37,6 +37,6 @@ export { } from './cm_services'; export { dashboardAttributesOut, - itemAttrsToSavedObjectAttrs, + itemAttrsToSavedObject, savedObjectToItem, } from './transform_utils'; diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v3/transform_utils.ts b/src/platform/plugins/shared/dashboard/server/content_management/v3/transform_utils.ts index 18c9085df4ec0..48676b36125da 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v3/transform_utils.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v3/transform_utils.ts @@ -25,14 +25,16 @@ import { } from '@kbn/controls-plugin/common'; import { SerializedSearchSourceFields, parseSearchSourceJSON } from '@kbn/data-plugin/common'; +import { EmbeddableStart } from '@kbn/embeddable-plugin/server'; import type { SavedObject, SavedObjectReference } from '@kbn/core-saved-objects-api-server'; +import { getReferencesForPanelId, prefixReferencesFromPanel } from '../../../common'; import type { ControlGroupAttributes, DashboardAttributes, DashboardGetOut, DashboardItem, DashboardOptions, - ItemAttrsToSavedObjectAttrsReturn, + ItemAttrsToSavedObjectReturn, PartialDashboardItem, SavedObjectToItemReturn, } from './types'; @@ -131,24 +133,37 @@ function optionsOut(optionsJSON: string): DashboardAttributes['options'] { }; } -function panelsOut(panelsJSON: string): DashboardAttributes['panels'] { +function panelsOut( + panelsJSON: string, + references: SavedObjectReference[], + embeddable: EmbeddableStart +): DashboardAttributes['panels'] { const panels = JSON.parse(panelsJSON) as SavedDashboardPanel[]; return panels.map( - ({ embeddableConfig, gridData, id, panelIndex, panelRefName, title, type, version }) => ({ - gridData, - id, - panelConfig: embeddableConfig, - panelIndex, - panelRefName, - title, - type, - version, - }) + ({ embeddableConfig, gridData, id, panelIndex, panelRefName, title, type, version }) => { + const panelReferences = getReferencesForPanelId(panelIndex, references); + const { type: embeddableType, ...injectedAttributes } = embeddable.inject( + { type, ...embeddableConfig }, + panelReferences + ); + return { + gridData, + id, + panelConfig: injectedAttributes, + panelIndex, + panelRefName, + title, + type, + version, + }; + } ); } export function dashboardAttributesOut( - attributes: DashboardSavedObjectAttributes | Partial + attributes: DashboardSavedObjectAttributes | Partial, + references: SavedObjectReference[], + embeddable: EmbeddableStart ): DashboardAttributes | Partial { const { controlGroupInput, @@ -171,7 +186,7 @@ export function dashboardAttributesOut( kibanaSavedObjectMeta: kibanaSavedObjectMetaOut(kibanaSavedObjectMeta), }), ...(optionsJSON && { options: optionsOut(optionsJSON) }), - ...(panelsJSON && { panels: panelsOut(panelsJSON) }), + ...(panelsJSON && { panels: panelsOut(panelsJSON, references, embeddable) }), ...(refreshInterval && { refreshInterval: { pause: refreshInterval.pause, value: refreshInterval.value }, }), @@ -206,13 +221,31 @@ function controlGroupInputIn( } function panelsIn( - panels: DashboardAttributes['panels'] + panels: DashboardAttributes['panels'], + embeddable: EmbeddableStart, + references: SavedObjectReference[], + incomingReferences?: SavedObjectReference[] ): DashboardSavedObjectAttributes['panelsJSON'] { - const updatedPanels = panels.map(({ panelIndex, gridData, panelConfig, ...restPanel }) => { + const updatedPanels = panels.map(({ panelIndex, gridData, type, panelConfig, ...restPanel }) => { const idx = panelIndex ?? uuidv4(); + const updatedPanelConfig = incomingReferences + ? embeddable.inject( + { + type, + ...panelConfig, + }, + getReferencesForPanelId(idx, incomingReferences) + ) + : panelConfig; + const { state: extractedPanelConfig, references: panelReferences } = embeddable.extract({ + type, + ...updatedPanelConfig, + }); + references.push(...prefixReferencesFromPanel(idx, panelReferences)); return { ...restPanel, - embeddableConfig: panelConfig, + type, + embeddableConfig: extractedPanelConfig, panelIndex: idx, gridData: { ...gridData, @@ -274,9 +307,12 @@ export const getResultV3ToV2 = (result: DashboardGetOut): DashboardCrudTypesV2[' }; }; -export const itemAttrsToSavedObjectAttrs = ( - attributes: DashboardAttributes -): ItemAttrsToSavedObjectAttrsReturn => { +export const itemAttrsToSavedObject = ( + attributes: DashboardAttributes, + embeddable: EmbeddableStart, + incomingReferences?: SavedObjectReference[] +): ItemAttrsToSavedObjectReturn => { + const soReferences: SavedObjectReference[] = []; try { const { controlGroupInput, kibanaSavedObjectMeta, options, panels, ...rest } = attributes; const soAttributes = { @@ -288,15 +324,15 @@ export const itemAttrsToSavedObjectAttrs = ( optionsJSON: JSON.stringify(options), }), ...(panels && { - panelsJSON: panelsIn(panels), + panelsJSON: panelsIn(panels, embeddable, soReferences, incomingReferences), }), ...(kibanaSavedObjectMeta && { kibanaSavedObjectMeta: kibanaSavedObjectMetaIn(kibanaSavedObjectMeta), }), }; - return { attributes: soAttributes, error: null }; + return { attributes: soAttributes, references: soReferences, error: null }; } catch (e) { - return { attributes: null, error: e }; + return { attributes: null, references: null, error: e }; } }; @@ -317,12 +353,14 @@ export interface SavedObjectToItemOptions { export function savedObjectToItem( savedObject: SavedObject, + embeddable: EmbeddableStart, partial: false, opts?: SavedObjectToItemOptions ): SavedObjectToItemReturn; export function savedObjectToItem( savedObject: PartialSavedObject, + embeddable: EmbeddableStart, partial: true, opts?: SavedObjectToItemOptions ): SavedObjectToItemReturn; @@ -331,6 +369,7 @@ export function savedObjectToItem( savedObject: | SavedObject | PartialSavedObject, + embeddable: EmbeddableStart, partial: boolean /* partial arg is used to enforce the correct savedObject type */, { allowedAttributes, allowedReferences }: SavedObjectToItemOptions = {} ): SavedObjectToItemReturn { @@ -344,15 +383,15 @@ export function savedObjectToItem( attributes, error, namespaces, - references, + references = [], version, managed, } = savedObject; try { const attributesOut = allowedAttributes - ? pick(dashboardAttributesOut(attributes), allowedAttributes) - : dashboardAttributesOut(attributes); + ? pick(dashboardAttributesOut(attributes, references, embeddable), allowedAttributes) + : dashboardAttributesOut(attributes, references, embeddable); // if includeReferences is provided, only include references of those types const referencesOut = allowedReferences diff --git a/src/platform/plugins/shared/dashboard/server/content_management/v3/types.ts b/src/platform/plugins/shared/dashboard/server/content_management/v3/types.ts index 0c7144569aba2..87ba378bcd296 100644 --- a/src/platform/plugins/shared/dashboard/server/content_management/v3/types.ts +++ b/src/platform/plugins/shared/dashboard/server/content_management/v3/types.ts @@ -81,12 +81,14 @@ export type SavedObjectToItemReturn = error: Error; }; -export type ItemAttrsToSavedObjectAttrsReturn = +export type ItemAttrsToSavedObjectReturn = | { attributes: DashboardSavedObjectAttributes; + references: SavedObjectReference[]; error: null; } | { attributes: null; + references: null; error: Error; }; diff --git a/src/platform/plugins/shared/dashboard/server/plugin.ts b/src/platform/plugins/shared/dashboard/server/plugin.ts index a102fb39eaf34..56c85ce021009 100644 --- a/src/platform/plugins/shared/dashboard/server/plugin.ts +++ b/src/platform/plugins/shared/dashboard/server/plugin.ts @@ -11,7 +11,7 @@ import { TaskManagerSetupContract, TaskManagerStartContract, } from '@kbn/task-manager-plugin/server'; -import { EmbeddableSetup } from '@kbn/embeddable-plugin/server'; +import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/server'; import { UsageCollectionSetup, UsageCollectionStart } from '@kbn/usage-collection-plugin/server'; import { ContentManagementServerSetup } from '@kbn/content-management-plugin/server'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from '@kbn/core/server'; @@ -42,6 +42,7 @@ interface SetupDeps { interface StartDeps { taskManager: TaskManagerStartContract; usageCollection?: UsageCollectionStart; + embeddable: EmbeddableStart; } export class DashboardPlugin @@ -69,6 +70,7 @@ export class DashboardPlugin storage: new DashboardStorage({ throwOnResultValidationError: this.initializerContext.env.mode.dev, logger: this.logger.get('storage'), + embeddable: plugins.embeddable, }), version: { latest: LATEST_VERSION,