import { i18n } from '@osd/i18n' ; import { SavedObjectMetaData , OnSaveProps } from 'src/plugins/saved_objects/public' ; import { first } from 'rxjs/operators' ; import { SavedObjectAttributes } from '../../../../core/public' ; import { EmbeddableFactoryDefinition , EmbeddableOutput , ErrorEmbeddable , IContainer , } from '../../../embeddable/public' ; import { DisabledLabEmbeddable } from './disabled_lab_embeddable' ; import { VisualizeByReferenceInput , VisualizeByValueInput , VisualizeEmbeddable , VisualizeInput , VisualizeOutput , VisualizeSavedObjectAttributes , } from './visualize_embeddable' ; import { VISUALIZE_EMBEDDABLE_TYPE } from './constants' ; import { SerializedVis , Vis } from '../vis' ; import { getCapabilities , getTypes , getUISettings , getSavedVisualizationsLoader , } from '../services' ; import { showNewVisModal } from '../wizard' ; import { convertToSerializedVis } from '../saved_visualizations/_saved_vis' ; import { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object' ; import { StartServicesGetter } from '../../../opensearch_dashboards_utils/public' ; import { VisualizationsStartDeps } from '../plugin' ; import { VISUALIZE_ENABLE_LABS_SETTING } from '../../common/constants' ; import { AttributeService } from '../../../dashboard/public' ; import { checkForDuplicateTitle } from '../../../saved_objects/public' ; interface VisualizationAttributes extends SavedObjectAttributes { visState : string ; } export interface VisualizeEmbeddableFactoryDeps { start : StartServicesGetter < Pick < VisualizationsStartDeps , 'inspector' | 'embeddable' | 'dashboard' | 'savedObjectsClient' > > ; } export class VisualizeEmbeddableFactory implements EmbeddableFactoryDefinition < VisualizeInput , VisualizeOutput | EmbeddableOutput , VisualizeEmbeddable | DisabledLabEmbeddable , VisualizationAttributes > { public readonly type = VISUALIZE_EMBEDDABLE_TYPE ; private attributeService ? : AttributeService < VisualizeSavedObjectAttributes , VisualizeByValueInput , VisualizeByReferenceInput > ; public readonly savedObjectMetaData : SavedObjectMetaData < VisualizationAttributes > = { name : i18n . translate ( 'visualizations.savedObjectName' , { defaultMessage : 'Visualization' } ) , includeFields : [ 'visState' ] , type : 'visualization' , getIconForSavedObject : ( savedObject ) => { return ( getTypes ( ) . get ( JSON . parse ( savedObject . attributes . visState ) . type ) . icon || 'visualizeApp' ) ; } , getTooltipForSavedObject : ( savedObject ) => { return `${ savedObject . attributes . title } (${ getTypes ( ) . get ( JSON . parse ( savedObject . attributes . visState ) . type ) . title })` ; } , showSavedObject : ( savedObject ) => { const typeName = JSON . parse ( savedObject . attributes . visState ) . type ; const visType = getTypes ( ) . get ( typeName ) ; if ( ! visType ) { return false ; } if ( getUISettings ( ) . get ( VISUALIZE_ENABLE_LABS_SETTING ) ) { return true ; } return visType . stage !== 'experimental' ; } , } ; constructor ( private readonly deps ) { } public async isEditable ( ) { return getCapabilities ( ) . visualize . save as boolean ; } public getDisplayName ( ) { return i18n . translate ( 'visualizations.displayName' , { defaultMessage : 'visualization' , } ) ; } public async getCurrentAppId ( ) { return await this . deps . start ( ) . core . application . currentAppId$ . pipe ( first ( ) ) . toPromise ( ) ; } private async getAttributeService ( ) { if ( ! this . attributeService ) { this . attributeService = await this . deps . start ( ) . plugins . dashboard . getAttributeService < VisualizeSavedObjectAttributes , VisualizeByValueInput , VisualizeByReferenceInput > ( this . type , { saveMethod : this . saveMethod . bind ( this ) , checkForDuplicateTitle : this . checkTitle . bind ( this ) , } ) ; } return this . attributeService ! ; } public async createFromSavedObject ( savedObjectId , input , parent ? ) { const savedVisualizations = getSavedVisualizationsLoader ( ) ; try { const savedObject = await savedVisualizations . get ( savedObjectId ) ; const visState = convertToSerializedVis ( savedObject ) ; const vis = new Vis ( savedObject . visState . type , visState ) ; await vis . setState ( visState ) ; return createVisEmbeddableFromObject ( this . deps ) ( vis , input , savedVisualizations , await this . getAttributeService ( ) , parent ) ; } catch ( e ) { console . error ( e ) ; return new ErrorEmbeddable ( e , input , parent ) ; } } public async create ( input , parent ? ) { if ( input . savedVis ) { const visState = input . savedVis ; const vis = new Vis ( visState . type , visState ) ; await vis . setState ( visState ) ; const savedVisualizations = getSavedVisualizationsLoader ( ) ; return createVisEmbeddableFromObject ( this . deps ) ( vis , input , savedVisualizations , await this . getAttributeService ( ) , parent ) ; } else { showNewVisModal ( { originatingApp : await this . getCurrentAppId ( ) , outsideVisualizeApp : true , } ) ; return undefined ; } } private async saveMethod ( attributes ) { try { const { title , savedVis } = attributes ; const visObj = attributes . vis ; if ( ! savedVis ) { throw new Error ( 'No Saved Vis' ) ; } const saveOptions = { confirmOverwrite : false , returnToOrigin : true , } ; savedVis . title = title ; savedVis . copyOnSave = false ; savedVis . description = '' ; savedVis . searchSourceFields = visObj ?. data . searchSource ?. getSerializedFields ( ) ; const serializedVis = ( ( visObj as unknown ) as Vis ) . serialize ( ) ; const { params , data } = serializedVis ; savedVis . visState = { title , type : serializedVis . type , params , aggs : data . aggs , } ; if ( visObj ) { savedVis . uiStateJSON = visObj ?. uiState . toString ( ) ; } const id = await savedVis . save ( saveOptions ) ; if ( ! id || id === '' ) { throw new Error ( i18n . translate ( 'visualizations.savingVisualizationFailed.errorMsg' , { defaultMessage : 'Saving a visualization failed' , } ) ) ; } return { id } ; } catch ( error ) { throw error ; } } public async checkTitle ( props ) { const savedObjectsClient = await this . deps . start ( ) . core . savedObjects . client ; const overlays = await this . deps . start ( ) . core . overlays ; return checkForDuplicateTitle ( { title : props . newTitle , copyOnSave : false , lastSavedTitle : '' , getOpenSearchType : ( ) => this . type , getDisplayName : this . getDisplayName || ( ( ) => this . type ) , } , props . isTitleDuplicateConfirmed , props . onTitleDuplicate , { savedObjectsClient , overlays , } ) ; } }