import { Subject , Observable } from 'rxjs' ; import { first , filter , take , switchMap } from 'rxjs/operators' ; import { CoreService } from '../../types' ; import { SavedObjectsClient , SavedObjectsClientProvider , SavedObjectsClientProviderOptions , } from './' ; import { OpenSearchDashboardsMigrator , IOpenSearchDashboardsMigrator } from './migrations' ; import { CoreContext } from '../core_context' ; import { OpenSearchClient , IClusterClient , InternalOpenSearchServiceSetup , InternalOpenSearchServiceStart , } from '../opensearch' ; import { OpenSearchDashboardsConfigType } from '../opensearch_dashboards_config' ; import { SavedObjectsConfigType , SavedObjectsMigrationConfigType , SavedObjectConfig , } from './saved_objects_config' ; import { OpenSearchDashboardsRequest , InternalHttpServiceSetup } from '../http' ; import { SavedObjectsClientContract , SavedObjectsType , SavedObjectStatusMeta } from './types' ; import { ISavedObjectsRepository , SavedObjectsRepository } from './service/lib/repository' ; import { SavedObjectsClientFactoryProvider , SavedObjectsClientWrapperFactory , } from './service/lib/scoped_client_provider' ; import { Logger } from '../logging' ; import { SavedObjectTypeRegistry , ISavedObjectTypeRegistry } from './saved_objects_type_registry' ; import { SavedObjectsSerializer } from './serialization' ; import { registerRoutes } from './routes' ; import { ServiceStatus } from '../status' ; import { calculateStatus$ } from './status' ; import { createMigrationOpenSearchClient } from './migrations/core/' ; remarks example example public export interface SavedObjectsServiceSetup { /**
     * Set the default  SavedObjectsClientFactoryProvider for creating Saved Objects clients.
     * Only one provider can be set, subsequent calls to this method will fail.
      setClientFactoryProvider : ( clientFactoryProvider : SavedObjectsClientFactoryProvider ) => void ; /**
     * Add a  SavedObjectsClientWrapperFactory with the given priority.
      addClientWrapper : ( priority : number , id : string , factory : SavedObjectsClientWrapperFactory ) => void ; /**
     * Register a  SavedObjectsType definition.
     *
     * See the  SavedObjectsTypeMappingDefinition and
     *  SavedObjectMigrationMap for more details about these.
     *
     *  example registerType : ( type : SavedObjectsType ) => void ; /**
     * Returns the maximum number of objects allowed for import or export operations.
     */ getImportExportObjectLimit : ( ) => number ; } internal export interface InternalSavedObjectsServiceSetup extends SavedObjectsServiceSetup { status$ : Observable < ServiceStatus < SavedObjectStatusMeta > > ; } public export interface SavedObjectsServiceStart { /**
     * Creates a  SavedObjectsClientContract that
     * uses the credentials from the passed in request to authenticate with
     * OpenSearch. If other plugins have registered Saved Objects client
     * wrappers, these will be applied to extend the functionality of the client.
     *
     * A client that is already scoped to the incoming request is also exposed
     * from the route handler context see  RequestHandlerContext .
      getScopedClient : ( req : OpenSearchDashboardsRequest , options ? : SavedObjectsClientProviderOptions ) => SavedObjectsClientContract ; /**
     * Creates a  ISavedObjectsRepository that
     * uses the credentials from the passed in request to authenticate with
     * OpenSearch.
     *
     *  param req param includedHiddenTypes remarks Prefer using `getScopedClient`. This should only be used when using methods
     * not exposed on  SavedObjectsClientContract createScopedRepository : ( req : OpenSearchDashboardsRequest , includedHiddenTypes ? : string [ ] ) => ISavedObjectsRepository ; /**
     * Creates a  ISavedObjectsRepository that
     * uses the internal OpenSearch Dashboards user for authenticating with OpenSearch.
     *
     *  param includedHiddenTypes createInternalRepository : ( includedHiddenTypes ? : string [ ] ) => ISavedObjectsRepository ; /**
     * Creates a  SavedObjectsSerializer that is aware of all registered types.
      createSerializer : ( ) => SavedObjectsSerializer ; /**
     * Returns the  ISavedObjectTypeRegistry containing all registered
     *  SavedObjectsType getTypeRegistry : ( ) => ISavedObjectTypeRegistry ; } export type InternalSavedObjectsServiceStart = SavedObjectsServiceStart ; /**
 * Factory provided when invoking a  SavedObjectsClientFactoryProvider * See  SavedObjectsServiceSetup . setClientFactoryProvider public export interface SavedObjectsRepositoryFactory { /**
     * Creates a  ISavedObjectsRepository that
     * uses the credentials from the passed in request to authenticate with
     * OpenSearch.
     *
     *  param includedHiddenTypes createScopedRepository : ( req : OpenSearchDashboardsRequest , includedHiddenTypes ? : string [ ] ) => ISavedObjectsRepository ; /**
     * Creates a  ISavedObjectsRepository that
     * uses the internal OpenSearch Dashboards user for authenticating with OpenSearch.
     *
     *  param includedHiddenTypes createInternalRepository : ( includedHiddenTypes ? : string [ ] ) => ISavedObjectsRepository ; } internal export interface SavedObjectsSetupDeps { http : InternalHttpServiceSetup ; opensearch : InternalOpenSearchServiceSetup ; } interface WrappedClientFactoryWrapper { priority : number ; id : string ; factory : SavedObjectsClientWrapperFactory ; } internal export interface SavedObjectsStartDeps { opensearch : InternalOpenSearchServiceStart ; pluginsInitialized ? : boolean ; } export class SavedObjectsService implements CoreService < InternalSavedObjectsServiceSetup , InternalSavedObjectsServiceStart > { private logger : Logger ; private setupDeps ? : SavedObjectsSetupDeps ; private config ? : SavedObjectConfig ; private clientFactoryProvider ? : SavedObjectsClientFactoryProvider ; private clientFactoryWrappers : WrappedClientFactoryWrapper [ ] = [ ] ; private migrator$ = new Subject < IOpenSearchDashboardsMigrator > ( ) ; private typeRegistry = new SavedObjectTypeRegistry ( ) ; private started = false ; constructor ( private readonly coreContext ) { this . logger = coreContext . logger . get ( 'savedobjects-service' ) ; } public async setup ( setupDeps ) { this . logger . debug ( 'Setting up SavedObjects service' ) ; this . setupDeps = setupDeps ; const savedObjectsConfig = await this . coreContext . configService . atPath < SavedObjectsConfigType > ( 'savedObjects' ) . pipe ( first ( ) ) . toPromise ( ) ; const savedObjectsMigrationConfig = await this . coreContext . configService . atPath < SavedObjectsMigrationConfigType > ( 'migrations' ) . pipe ( first ( ) ) . toPromise ( ) ; this . config = new SavedObjectConfig ( savedObjectsConfig , savedObjectsMigrationConfig ) ; registerRoutes ( { http : setupDeps . http , logger : this . logger , config : this . config , migratorPromise : this . migrator$ . pipe ( first ( ) ) . toPromise ( ) , } ) ; return { status$ : calculateStatus$ ( this . migrator$ . pipe ( switchMap ( ( migrator ) => migrator . getStatus$ ( ) ) ) , setupDeps . opensearch . status$ ) , setClientFactoryProvider : ( provider ) => { if ( this . started ) { throw new Error ( 'cannot call `setClientFactoryProvider` after service startup.' ) ; } if ( this . clientFactoryProvider ) { throw new Error ( 'custom client factory is already set, and can only be set once' ) ; } this . clientFactoryProvider = provider ; } , addClientWrapper : ( priority , id , factory ) => { if ( this . started ) { throw new Error ( 'cannot call `addClientWrapper` after service startup.' ) ; } this . clientFactoryWrappers . push ( { priority , id , factory , } ) ; } , registerType : ( type ) => { if ( this . started ) { throw new Error ( 'cannot call `registerType` after service startup.' ) ; } this . typeRegistry . registerType ( type ) ; } , getImportExportObjectLimit : ( ) => this . config ! . maxImportExportSize , } ; } public async start ( { opensearch , pluginsInitialized = true } , migrationsRetryDelay ? ) { if ( ! this . setupDeps || ! this . config ) { throw new Error ( '#setup() needs to be run first' ) ; } this . logger . debug ( 'Starting SavedObjects service' ) ; const opensearchDashboardsConfig = await this . coreContext . configService . atPath < OpenSearchDashboardsConfigType > ( 'opensearchDashboards' ) . pipe ( first ( ) ) . toPromise ( ) ; const client = opensearch . client ; const migrator = this . createMigrator ( opensearchDashboardsConfig , this . config . migration , opensearch . client , migrationsRetryDelay ) ; this . migrator$ . next ( migrator ) ; /**
         * Note: We want to ensure that migrations have completed before
         * continuing with further Core start steps that might use SavedObjects
         * such as running the legacy server, legacy plugins and allowing incoming
         * HTTP requests.
         *
         * However, our build system optimize step and some tests depend on the
         * HTTP server running without an OpenSearch server being available.
         * So, when the `migrations.skip` is true, we skip migrations altogether.
         *
         * We also cannot safely run migrations if plugins are not initialized since
         * not plugin migrations won't be registered.
         */ const skipMigrations = this . config . migration . skip || ! pluginsInitialized ; if ( skipMigrations ) { this . logger . warn ( 'Skipping Saved Object migrations on startup. Note: Individual documents will still be migrated when read or written.' ) ; } else { this . logger . info ( 'Waiting until all OpenSearch nodes are compatible with OpenSearch Dashboards before starting saved objects migrations...' ) ; this . setupDeps ! . opensearch . opensearchNodesCompatibility$ . subscribe ( ( { isCompatible , message } ) => { if ( ! isCompatible && message ) { this . logger . error ( message ) ; } } ) ; await this . setupDeps ! . opensearch . opensearchNodesCompatibility$ . pipe ( filter ( ( nodes ) => nodes . isCompatible ) , take ( 1 ) ) . toPromise ( ) ; this . logger . info ( 'Starting saved objects migrations' ) ; await migrator . runMigrations ( ) ; } const createRepository = ( opensearchClient , includedHiddenTypes = [ ] ) => { return SavedObjectsRepository . createRepository ( migrator , this . typeRegistry , opensearchDashboardsConfig . index , opensearchClient , includedHiddenTypes ) ; } ; const repositoryFactory = { createInternalRepository : ( includedHiddenTypes ? ) => createRepository ( client . asInternalUser , includedHiddenTypes ) , createScopedRepository : ( req , includedHiddenTypes ? ) => createRepository ( client . asScoped ( req ) . asCurrentUser , includedHiddenTypes ) , } ; const clientProvider = new SavedObjectsClientProvider ( { defaultClientFactory ( { request , includedHiddenTypes } ) { const repository = repositoryFactory . createScopedRepository ( request , includedHiddenTypes ) ; return new SavedObjectsClient ( repository ) ; } , typeRegistry : this . typeRegistry , } ) ; if ( this . clientFactoryProvider ) { const clientFactory = this . clientFactoryProvider ( repositoryFactory ) ; clientProvider . setClientFactory ( clientFactory ) ; } this . clientFactoryWrappers . forEach ( ( { id , factory , priority } ) => { clientProvider . addClientWrapperFactory ( priority , id , factory ) ; } ) ; this . started = true ; return { getScopedClient : clientProvider . getClient . bind ( clientProvider ) , createScopedRepository : repositoryFactory . createScopedRepository , createInternalRepository : repositoryFactory . createInternalRepository , createSerializer : ( ) => new SavedObjectsSerializer ( this . typeRegistry ) , getTypeRegistry : ( ) => this . typeRegistry , } ; } public async stop ( ) { } private createMigrator ( opensearchDashboardsConfig , savedObjectsConfig , client , migrationsRetryDelay ? ) { return new OpenSearchDashboardsMigrator ( { typeRegistry : this . typeRegistry , logger : this . logger , opensearchDashboardsVersion : this . coreContext . env . packageInfo . version , savedObjectsConfig , opensearchDashboardsConfig , client : createMigrationOpenSearchClient ( client . asInternalUser , this . logger , migrationsRetryDelay ) , } ) ; } }