import semverGt from 'semver/functions/gt' ; import { SavedObjectAttributes , SavedObjectReference } from '../../../core/types' ; import { DashboardContainerControlGroupInput , DashboardContainerStateWithType , DashboardPanelState , RawControlGroupAttributes , } from './types' ; import { EmbeddablePersistableStateService } from '../../embeddable/common/types' ; import { convertPanelStateToSavedDashboardPanel , convertSavedDashboardPanelToPanelState , } from './embeddable/embeddable_saved_object_converters' ; import { SavedDashboardPanel } from './types' ; import { CONTROL_GROUP_TYPE } from '../../controls/common' ; export interface ExtractDeps { embeddablePersistableStateService : EmbeddablePersistableStateService ; } export interface SavedObjectAttributesAndReferences { attributes : SavedObjectAttributes ; references : SavedObjectReference [ ] ; } const isPre730Panel = ( panel ) : boolean => { return 'version' in panel ? semverGt ( '7.3.0' , panel . version ) : true ; } ; function dashboardAttributesToState ( attributes ) { let inputPanels = [ ] as SavedDashboardPanel [ ] ; if ( typeof attributes . panelsJSON === 'string' ) { inputPanels = JSON . parse ( attributes . panelsJSON ) as SavedDashboardPanel [ ] ; } let controlGroupInput ; if ( attributes . controlGroupInput ) { const rawControlGroupInput = attributes . controlGroupInput as unknown as RawControlGroupAttributes ; if ( rawControlGroupInput . panelsJSON && typeof rawControlGroupInput . panelsJSON === 'string' ) { const controlGroupPanels = JSON . parse ( rawControlGroupInput . panelsJSON ) ; if ( controlGroupPanels && typeof controlGroupPanels === 'object' ) { controlGroupInput = { ... rawControlGroupInput , type : CONTROL_GROUP_TYPE , panels : controlGroupPanels , } ; } } } return { panels : inputPanels , state : { id : attributes . id as string , controlGroupInput , type : 'dashboard' , panels : inputPanels . reduce < Record < string , DashboardPanelState > > ( ( current , panel , index ) => { const panelIndex = panel . panelIndex || `${ index }` ; current [ panelIndex ] = convertSavedDashboardPanelToPanelState ( panel ) ; return current ; } , { } ) , } , } ; } function panelStatesToPanels ( panelStates , originalPanels ) { return Object . entries ( panelStates ) . map ( ( [ id , panelState ] ) => { let originalPanel = originalPanels . find ( ( p ) => p . panelIndex === id ) ; if ( ! originalPanel ) { const numericId = parseInt ( id , 10 ) ; originalPanel = isNaN ( numericId ) ? originalPanel : originalPanels [ numericId ] ; } return convertPanelStateToSavedDashboardPanel ( panelState , originalPanel ?. version ? originalPanel . version : '' ) ; } ) ; } export function extractReferences ( { attributes , references = [ ] } , deps ) { if ( typeof attributes . panelsJSON !== 'string' ) { return { attributes , references } ; } const { panels , state } = dashboardAttributesToState ( attributes ) ; if ( ! Array . isArray ( panels ) ) { return { attributes , references } ; } if ( ( panels as unknown as Array < Record < string , string > > ) . some ( isPre730Panel ) ) { return pre730ExtractReferences ( { attributes , references } , deps ) ; } const missingTypeIndex = panels . findIndex ( ( panel ) => panel . type === undefined ) ; if ( missingTypeIndex >= 0 ) { throw new Error ( `"type" attribute is missing from panel "${ missingTypeIndex }"` ) ; } const { references : extractedReferences , state : rawExtractedState } = deps . embeddablePersistableStateService . extract ( state ) ; const extractedState = rawExtractedState as DashboardContainerStateWithType ; const extractedPanels = panelStatesToPanels ( extractedState . panels , panels ) ; const newAttributes = { ... attributes , panelsJSON : JSON . stringify ( extractedPanels ) , } as SavedObjectAttributes ; if ( extractedState . controlGroupInput ) { newAttributes . controlGroupInput = { ... ( attributes . controlGroupInput as SavedObjectAttributes ) , panelsJSON : JSON . stringify ( extractedState . controlGroupInput . panels ) , } ; } return { references : [ ... references , ... extractedReferences ] , attributes : newAttributes , } ; } export interface InjectDeps { embeddablePersistableStateService : EmbeddablePersistableStateService ; } export function injectReferences ( { attributes , references = [ ] } , deps ) { if ( typeof attributes . panelsJSON !== 'string' ) { return attributes ; } const parsedPanels = JSON . parse ( attributes . panelsJSON ) ; if ( ! Array . isArray ( parsedPanels ) ) { return attributes ; } const { panels , state } = dashboardAttributesToState ( attributes ) ; const injectedState = deps . embeddablePersistableStateService . inject ( state , references ) as DashboardContainerStateWithType ; const injectedPanels = panelStatesToPanels ( injectedState . panels , panels ) ; const newAttributes = { ... attributes , panelsJSON : JSON . stringify ( injectedPanels ) , } as SavedObjectAttributes ; if ( injectedState . controlGroupInput ) { newAttributes . controlGroupInput = { ... ( attributes . controlGroupInput as SavedObjectAttributes ) , panelsJSON : JSON . stringify ( injectedState . controlGroupInput . panels ) , } ; } return newAttributes ; } function pre730ExtractReferences ( { attributes , references = [ ] } , deps ) { if ( typeof attributes . panelsJSON !== 'string' ) { return { attributes , references } ; } const panelReferences = [ ] ; const panels = JSON . parse ( String ( attributes . panelsJSON ) ) ; panels . forEach ( ( panel , i ) => { if ( ! panel . type ) { throw new Error ( `"type" attribute is missing from panel "${ i }"` ) ; } if ( ! panel . id ) { return ; } panel . panelRefName = `panel_${ i }` ; panelReferences . push ( { name : `panel_${ i }` , type : panel . type , id : panel . id , } ) ; delete panel . type ; delete panel . id ; } ) ; return { references : [ ... references , ... panelReferences ] , attributes : { ... attributes , panelsJSON : JSON . stringify ( panels ) , } , } ; }