import { i18n } from '@osd/i18n' ; import angular , { auto } from 'angular' ; import { BehaviorSubject } from 'rxjs' ; import { filter , map } from 'rxjs/operators' ; import { AppMountParameters , AppUpdater , CoreSetup , CoreStart , Plugin , PluginInitializerContext , } from 'opensearch-dashboards/public' ; import { UiActionsStart , UiActionsSetup } from 'src/plugins/ui_actions/public' ; import { EmbeddableStart , EmbeddableSetup } from 'src/plugins/embeddable/public' ; import { ChartsPluginStart } from 'src/plugins/charts/public' ; import { NavigationPublicPluginStart as NavigationStart } from 'src/plugins/navigation/public' ; import { SharePluginStart , SharePluginSetup , UrlGeneratorContract } from 'src/plugins/share/public' ; import { VisualizationsStart , VisualizationsSetup } from 'src/plugins/visualizations/public' ; import { OpenSearchDashboardsLegacySetup , OpenSearchDashboardsLegacyStart , } from 'src/plugins/opensearch_dashboards_legacy/public' ; import { UrlForwardingSetup , UrlForwardingStart } from 'src/plugins/url_forwarding/public' ; import { HomePublicPluginSetup } from 'src/plugins/home/public' ; import { Start as InspectorPublicPluginStart } from 'src/plugins/inspector/public' ; import { DataPublicPluginStart , DataPublicPluginSetup , opensearchFilters } from '../../data/public' ; import { SavedObjectLoader } from '../../saved_objects/public' ; import { createOsdUrlTracker } from '../../opensearch_dashboards_utils/public' ; import { DEFAULT_APP_CATEGORIES } from '../../../core/public' ; import { UrlGeneratorState } from '../../share/public' ; import { DocViewInput , DocViewInputFn } from './application/doc_views/doc_views_types' ; import { DocViewsRegistry } from './application/doc_views/doc_views_registry' ; import { DocViewTable } from './application/components/table/table' ; import { JsonCodeBlock } from './application/components/json_code_block/json_code_block' ; import { setDocViewsRegistry , setUrlTracker , setAngularModule , setServices , setHeaderActionMenuMounter , setUiActions , setScopedHistory , getScopedHistory , syncHistoryLocations , getServices , } from './opensearch_dashboards_services' ; import { createSavedSearchesLoader } from './saved_searches' ; import { registerFeature } from './register_feature' ; import { buildServices } from './build_services' ; import { DiscoverUrlGeneratorState , DISCOVER_APP_URL_GENERATOR , DiscoverUrlGenerator , } from './url_generator' ; import { SearchEmbeddableFactory } from './application/embeddable' ; declare module '../../share/public' { export interface UrlGeneratorStateMapping { [ DISCOVER_APP_URL_GENERATOR ] : UrlGeneratorState < DiscoverUrlGeneratorState > ; } } public export interface DiscoverSetup { docViews : { param docViewRaw addDocView ( docViewRaw ) : void ; } ; } export interface DiscoverStart { savedSearchLoader : SavedObjectLoader ; /**
     * `share` plugin URL generator for Discover app. Use it to generate links into
     * Discover application, example:
     *
     * ```ts
     * const url = await plugins.discover.urlGenerator.createUrl({
     *   savedSearchId: '571aaf70-4c88-11e8-b3d7-01146121b73d',
     *   indexPatternId: 'c367b774-a4c2-11ea-bb37-0242ac130002',
     *   timeRange: {
     *     to: 'now',
     *     from: 'now-15m',
     *     mode: 'relative',
     *   },
     * });
     * ```
     */ readonly urlGenerator : undefined | UrlGeneratorContract < 'DISCOVER_APP_URL_GENERATOR' > ; } internal export interface DiscoverSetupPlugins { share ? : SharePluginSetup ; uiActions : UiActionsSetup ; embeddable : EmbeddableSetup ; opensearchDashboardsLegacy : OpenSearchDashboardsLegacySetup ; urlForwarding : UrlForwardingSetup ; home ? : HomePublicPluginSetup ; visualizations : VisualizationsSetup ; data : DataPublicPluginSetup ; } internal export interface DiscoverStartPlugins { uiActions : UiActionsStart ; embeddable : EmbeddableStart ; navigation : NavigationStart ; charts : ChartsPluginStart ; data : DataPublicPluginStart ; share ? : SharePluginStart ; opensearchDashboardsLegacy : OpenSearchDashboardsLegacyStart ; urlForwarding : UrlForwardingStart ; inspector : InspectorPublicPluginStart ; visualizations : VisualizationsStart ; } const innerAngularName = 'app/discover' ; const embeddableAngularName = 'app/discoverEmbeddable' ; /**
 * Contains Discover, one of the oldest parts of OpenSearch Dashboards
 * There are 2 kinds of Angular bootstrapped for rendering, additionally to the main Angular
 * Discover provides embeddables, those contain a slimmer Angular
 */ export class DiscoverPlugin implements Plugin < DiscoverSetup , DiscoverStart , DiscoverSetupPlugins , DiscoverStartPlugins > { constructor ( private readonly initializerContext ) { } private appStateUpdater = new BehaviorSubject < AppUpdater > ( ( ) => ( { } ) ) ; private docViewsRegistry : DocViewsRegistry | null = null ; private embeddableInjector : auto . IInjectorService | null = null ; private stopUrlTracking : ( ( ) => void ) | undefined = undefined ; private servicesInitialized : boolean = false ; private innerAngularInitialized : boolean = false ; private urlGenerator ? : DiscoverStart [ 'urlGenerator' ] ; /**
     * why are those functions public? they are needed for some mocha tests
     * can be removed once all is Jest
     */ public initializeInnerAngular ? : ( ) => void ; public initializeServices ? : ( ) => Promise < { core : CoreStart ; plugins : DiscoverStartPlugins ; } > ; setup ( core , plugins ) { const baseUrl = core . http . basePath . prepend ( '/app/discover' ) ; if ( plugins . share ) { this . urlGenerator = plugins . share . urlGenerators . registerUrlGenerator ( new DiscoverUrlGenerator ( { appBasePath : baseUrl , useHash : core . uiSettings . get ( 'state:storeInSessionStorage' ) , } ) ) ; } this . docViewsRegistry = new DocViewsRegistry ( ) ; setDocViewsRegistry ( this . docViewsRegistry ) ; this . docViewsRegistry . addDocView ( { title : i18n . translate ( 'discover.docViews.table.tableTitle' , { defaultMessage : 'Table' , } ) , order : 10 , component : DocViewTable , } ) ; this . docViewsRegistry . addDocView ( { title : i18n . translate ( 'discover.docViews.json.jsonTitle' , { defaultMessage : 'JSON' , } ) , order : 20 , component : JsonCodeBlock , } ) ; const { appMounted , appUnMounted , stop : stopUrlTracker , setActiveUrl : setTrackedUrl , restorePreviousUrl , } = createOsdUrlTracker ( { getHistory : getScopedHistory , baseUrl , defaultSubUrl : '#/' , storageKey : `lastUrl:${ core . http . basePath . get ( ) }:discover` , navLinkUpdater$ : this . appStateUpdater , toastNotifications : core . notifications . toasts , stateParams : [ { osdUrlKey : '_g' , stateUpdate$ : plugins . data . query . state$ . pipe ( filter ( ( { changes } ) => ! ! ( changes . globalFilters || changes . time || changes . refreshInterval ) ) , map ( ( { state } ) => ( { ... state , filters : state . filters ?. filter ( opensearchFilters . isFilterPinned ) , } ) ) ) , } , ] , } ) ; setUrlTracker ( { setTrackedUrl , restorePreviousUrl } ) ; this . stopUrlTracking = ( ) => { stopUrlTracker ( ) ; } ; this . docViewsRegistry . setAngularInjectorGetter ( this . getEmbeddableInjector ) ; core . application . register ( { id : 'discover' , title : 'Discover' , updater$ : this . appStateUpdater . asObservable ( ) , order : 1000 , euiIconType : 'inputOutput' , defaultPath : '#/' , category : DEFAULT_APP_CATEGORIES . opensearchDashboards , mount : async ( params ) => { if ( ! this . initializeServices ) { throw Error ( 'Discover plugin method initializeServices is undefined' ) ; } if ( ! this . initializeInnerAngular ) { throw Error ( 'Discover plugin method initializeInnerAngular is undefined' ) ; } setScopedHistory ( params . history ) ; setHeaderActionMenuMounter ( params . setHeaderActionMenu ) ; syncHistoryLocations ( ) ; appMounted ( ) ; const { plugins : { data : dataStart } , } = await this . initializeServices ( ) ; await this . initializeInnerAngular ( ) ; await dataStart . indexPatterns . clearCache ( ) ; const { renderApp } = await import ( './application/application' ) ; params . element . classList . add ( 'dscAppWrapper' ) ; const unmount = await renderApp ( innerAngularName , params . element ) ; return ( ) => { params . element . classList . remove ( 'dscAppWrapper' ) ; unmount ( ) ; appUnMounted ( ) ; } ; } , } ) ; plugins . urlForwarding . forwardApp ( 'doc' , 'discover' , ( path ) => { return `#${ path }` ; } ) ; plugins . urlForwarding . forwardApp ( 'context' , 'discover' , ( path ) => { const urlParts = path . split ( '/' ) ; if ( urlParts [ 4 ] ) { const newPath = [ ... urlParts . slice ( 0 , 3 ) , ... urlParts . slice ( 4 ) ] . join ( '/' ) ; return `#${ newPath }` ; } return `#${ path }` ; } ) ; plugins . urlForwarding . forwardApp ( 'discover' , 'discover' , ( path ) => { const [ , id , tail ] = /discover\/([^\?]+)(.*)/ . exec ( path ) || [ ] ; if ( ! id ) { return `#${ path . replace ( '/discover' , '' ) || '/' }` ; } return `#/view/${ id }${ tail || '' }` ; } ) ; if ( plugins . home ) { registerFeature ( plugins . home ) ; } this . registerEmbeddable ( core , plugins ) ; return { docViews : { addDocView : this . docViewsRegistry . addDocView . bind ( this . docViewsRegistry ) , } , } ; } start ( core , plugins ) { this . initializeInnerAngular = async ( ) => { if ( this . innerAngularInitialized ) { return ; } const { getInnerAngularModule } = await import ( './get_inner_angular' ) ; const module = getInnerAngularModule ( innerAngularName , core , plugins , this . initializerContext ) ; setAngularModule ( module ) ; this . innerAngularInitialized = true ; } ; setUiActions ( plugins . uiActions ) ; this . initializeServices = async ( ) => { if ( this . servicesInitialized ) { return { core , plugins } ; } const services = await buildServices ( core , plugins , this . initializerContext , this . getEmbeddableInjector ) ; setServices ( services ) ; this . servicesInitialized = true ; return { core , plugins } ; } ; return { urlGenerator : this . urlGenerator , savedSearchLoader : createSavedSearchesLoader ( { savedObjectsClient : core . savedObjects . client , indexPatterns : plugins . data . indexPatterns , search : plugins . data . search , chrome : core . chrome , overlays : core . overlays , } ) , } ; } stop ( ) { if ( this . stopUrlTracking ) { this . stopUrlTracking ( ) ; } } /**
     * register embeddable with a slimmer embeddable version of inner angular
     */ private registerEmbeddable ( core , plugins ) { if ( ! this . getEmbeddableInjector ) { throw Error ( 'Discover plugin method getEmbeddableInjector is undefined' ) ; } const getStartServices = async ( ) => { const [ coreStart , deps ] = await core . getStartServices ( ) ; return { executeTriggerActions : deps . uiActions . executeTriggerActions , isEditable : ( ) => coreStart . application . capabilities . discover . save as boolean , } ; } ; const factory = new SearchEmbeddableFactory ( getStartServices , this . getEmbeddableInjector ) ; plugins . embeddable . registerEmbeddableFactory ( factory . type , factory ) ; } private getEmbeddableInjector = async ( ) => { if ( ! this . embeddableInjector ) { if ( ! this . initializeServices ) { throw Error ( 'Discover plugin getEmbeddableInjector:  initializeServices is undefined' ) ; } const { core , plugins } = await this . initializeServices ( ) ; getServices ( ) . opensearchDashboardsLegacy . loadFontAwesome ( ) ; const { getInnerAngularModuleEmbeddable } = await import ( './get_inner_angular' ) ; getInnerAngularModuleEmbeddable ( embeddableAngularName , core , plugins ) ; const mountpoint = document . createElement ( 'div' ) ; this . embeddableInjector = angular . bootstrap ( mountpoint , [ embeddableAngularName ] ) ; } return this . embeddableInjector ; } ; }