import { Observable , timer , merge , throwError , EMPTY } from 'rxjs' ; import { map , takeUntil , catchError } from 'rxjs/operators' ; import { uniq } from 'lodash' ; import { i18n } from '@kbn/i18n' ; import { KibanaRequest , CoreStart , IBasePath } from 'src/core/server' ; import { GlobalSearchProviderResult , GlobalSearchBatchedResults , GlobalSearchFindParams , } from '../../common/types' ; import { GlobalSearchFindError } from '../../common/errors' ; import { takeInArray } from '../../common/operators' ; import { defaultMaxProviderResults } from '../../common/constants' ; import { ILicenseChecker } from '../../common/license_checker' ; import { processProviderResult } from '../../common/process_result' ; import { GlobalSearchConfigType } from '../config' ; import { getContextFactory , GlobalSearchContextFactory } from './context' ; import { GlobalSearchResultProvider , GlobalSearchFindOptions } from '../types' ; import { getRequestBasePath } from './utils' ; public export interface SearchServiceSetup { example registerResultProvider ( provider ) : void ; } public export interface SearchServiceStart { /**
     * Perform a search for given `term` and  GlobalSearchFindOptions .
     *
     *  example remarks find ( params , options , request ) : Observable < GlobalSearchBatchedResults > ; /**
     * Returns all the searchable types registered by the underlying result providers.
     */ getSearchableTypes ( request ) : Promise < string [ ] > ; } interface SetupDeps { basePath : IBasePath ; config : GlobalSearchConfigType ; maxProviderResults ? : number ; } interface StartDeps { core : CoreStart ; licenseChecker : ILicenseChecker ; } const mapToUndefined = ( ) => undefined ; internal export class SearchService { private readonly providers = new Map < string , GlobalSearchResultProvider > ( ) ; private basePath ? : IBasePath ; private config ? : GlobalSearchConfigType ; private contextFactory ? : GlobalSearchContextFactory ; private licenseChecker ? : ILicenseChecker ; private maxProviderResults = defaultMaxProviderResults ; setup ( { basePath , config , maxProviderResults = defaultMaxProviderResults , } ) { this . basePath = basePath ; this . config = config ; this . maxProviderResults = maxProviderResults ; return { registerResultProvider : ( provider ) => { if ( this . providers . has ( provider . id ) ) { throw new Error ( `trying to register duplicate provider: ${ provider . id }` ) ; } this . providers . set ( provider . id , provider ) ; } , } ; } start ( { core , licenseChecker } ) { this . licenseChecker = licenseChecker ; this . contextFactory = getContextFactory ( core ) ; return { find : ( params , options , request ) => this . performFind ( params , options , request ) , getSearchableTypes : ( request ) => this . getSearchableTypes ( request ) , } ; } private async getSearchableTypes ( request ) { const context = this . contextFactory ! ( request ) ; const allTypes = ( await Promise . all ( [ ... this . providers . values ( ) ] . map ( ( provider ) => provider . getSearchableTypes ( context ) ) ) ) . flat ( ) ; return uniq ( allTypes ) ; } private performFind ( params , options , request ) { const licenseState = this . licenseChecker ! . getState ( ) ; if ( ! licenseState . valid ) { return throwError ( GlobalSearchFindError . invalidLicense ( i18n . translate ( 'xpack.globalSearch.find.invalidLicenseError' , { defaultMessage : `GlobalSearch API is disabled because of invalid license state: {errorMessage}` , values : { errorMessage : licenseState . message } , } ) ) ) ; } const context = this . contextFactory ! ( request ) ; const basePath = getRequestBasePath ( request , this . basePath ! ) ; const timeout$ = timer ( this . config ! . search_timeout . asMilliseconds ( ) ) . pipe ( map ( mapToUndefined ) ) ; const aborted$ = options . aborted$ ? merge ( options . aborted$ , timeout$ ) : timeout$ ; const findOptions = { ... options , preference : options . preference ?? 'default' , maxResults : this . maxProviderResults , aborted$ , } ; const processResult = ( result ) => processProviderResult ( result , basePath ) ; const providersResults$ = [ ... this . providers . values ( ) ] . map ( ( provider ) => provider . find ( params , findOptions , context ) . pipe ( catchError ( ( ) => EMPTY ) , takeInArray ( this . maxProviderResults ) , takeUntil ( aborted$ ) , map ( ( results ) => results . map ( ( r ) => processResult ( r ) ) ) ) ) ; return merge ( ... providersResults$ ) . pipe ( map ( ( results ) => ( { results , } ) ) ) ; } }