import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey' ; import { withTimeout } from '@kbn/std' ; import * as Either from 'fp-ts/lib/Either' ; import * as TaskEither from 'fp-ts/lib/TaskEither' ; import { RetryableEsClientError } from '.' ; import { ElasticsearchClient } from '../../../elasticsearch' ; import { SavedObjectTypeExcludeFromUpgradeFilterHook } from '../../types' ; import { catchRetryableEsClientErrors } from './catch_retryable_es_client_errors' ; export interface CalculateExcludeFiltersParams { client : ElasticsearchClient ; excludeFromUpgradeFilterHooks : Record < string , SavedObjectTypeExcludeFromUpgradeFilterHook > ; hookTimeoutMs ? : number ; } export interface CalculatedExcludeFilter { /** Composite filter of all calculated filters */ excludeFilter : estypes . QueryDslQueryContainer ; /** Any errors that were encountered during filter calculation, keyed by the type name */ errorsByType : Record < string , Error > ; } export const calculateExcludeFilters = ( { client , excludeFromUpgradeFilterHooks , hookTimeoutMs = 30000 , } ) : TaskEither . TaskEither < RetryableEsClientError , CalculatedExcludeFilter > => ( ) => { return Promise . all < Either . Right < estypes . QueryDslQueryContainer > | Either . Left < { soType : string ; error : Error | RetryableEsClientError ; } > > ( Object . entries ( excludeFromUpgradeFilterHooks ) . map ( ( [ soType , hook ] ) => withTimeout ( { promise : Promise . resolve ( hook ( { readonlyEsClient : { search : client . search . bind ( client ) } } ) ) , timeoutMs : hookTimeoutMs , } ) . then ( ( result ) => result . timedout ? Either . left ( { soType , error : new Error ( `excludeFromUpgrade hook timed out after ${ hookTimeoutMs / 1000 } seconds.` ) , } ) : Either . right ( result . value ) ) . catch ( ( error ) => { const retryableError = catchRetryableEsClientErrors ( error ) ; if ( Either . isLeft ( retryableError ) ) { return Either . left ( { soType , error : retryableError . left } ) ; } else { return Either . left ( { soType , error : new Error ( `Unexpected return value from catchRetryableEsClientErrors: "${ retryableError . toString ( ) }"` ) , } ) ; } } ) . catch ( ( error ) => Either . left ( { soType , error } ) ) ) ) . then ( ( results ) => { const retryableError = results . find ( ( r ) => Either . isLeft ( r ) && ! ( r . left . error instanceof Error ) && r . left . error . type === 'retryable_es_client_error' ) as Either . Left < { soType : string ; error : RetryableEsClientError ; } > | undefined ; if ( retryableError ) { return Either . left ( retryableError . left . error ) ; } const errorsByType = [ ] ; const filters = [ ] ; results . forEach ( ( r ) => Either . isRight ( r ) ? filters . push ( r . right ) : Either . isLeft ( r ) && errorsByType . push ( [ r . left . soType , r . left . error as Error ] ) ) ; const excludeFilter = { bool : { must_not : filters } , } ; return Either . right ( { excludeFilter , errorsByType : Object . fromEntries ( errorsByType ) , } ) ; } ) ; } ;