import { schema , TypeOf } from '@kbn/config-schema' ; import type { SnapshotDetailsEs } from '../../../common/types' ; import { deserializeSnapshotDetails } from '../../../common/lib' ; import type { RouteDependencies } from '../../types' ; import { getManagedRepositoryName } from '../../lib' ; import { addBasePath } from '../helpers' ; import { snapshotListSchema } from './validate_schemas' ; import { getSnapshotSearchWildcard } from '../../lib/get_snapshot_search_wildcard' ; const sortFieldToESParams = { snapshot : 'name' , repository : 'repository' , indices : 'index_count' , startTimeInMillis : 'start_time' , durationInMillis : 'duration' , 'shards.total' : 'shard_count' , 'shards.failed' : 'failed_shard_count' , } ; const isSearchingForNonExistentRepository = ( repositories , value , match ? , operator ? ) : boolean => { if ( match === 'must' && operator === 'exact' ) { return ! ( repositories || [ ] ) . includes ( value ) ; } return false ; } ; export function registerSnapshotsRoutes ( { router , license , lib : { wrapEsError , handleEsError } , } ) { router . get ( { path : addBasePath ( 'snapshots' ) , validate : { query : snapshotListSchema } } , license . guardApiRoute ( async ( ctx , req , res ) => { const { client : clusterClient } = ctx . core . elasticsearch ; const sortField = sortFieldToESParams [ ( req . query as TypeOf < typeof snapshotListSchema > ) . sortField ] ; const sortDirection = ( req . query as TypeOf < typeof snapshotListSchema > ) . sortDirection ; const pageIndex = ( req . query as TypeOf < typeof snapshotListSchema > ) . pageIndex ; const pageSize = ( req . query as TypeOf < typeof snapshotListSchema > ) . pageSize ; const searchField = ( req . query as TypeOf < typeof snapshotListSchema > ) . searchField ; const searchValue = ( req . query as TypeOf < typeof snapshotListSchema > ) . searchValue ; const searchMatch = ( req . query as TypeOf < typeof snapshotListSchema > ) . searchMatch ; const searchOperator = ( req . query as TypeOf < typeof snapshotListSchema > ) . searchOperator ; const managedRepository = await getManagedRepositoryName ( clusterClient . asCurrentUser ) ; let policies = [ ] ; try { const { body : policiesByName } = await clusterClient . asCurrentUser . slm . getLifecycle ( ) ; policies = Object . keys ( policiesByName ) ; } catch ( e ) { } let repositories = [ ] ; try { const { body : repositoriesByName } = await clusterClient . asCurrentUser . snapshot . getRepository ( { name : '_all' , } ) ; repositories = Object . keys ( repositoriesByName ) ; if ( repositories . length === 0 ) { return res . ok ( { body : { snapshots : [ ] , repositories : [ ] , policies } , } ) ; } } catch ( e ) { return handleEsError ( { error : e , response : res } ) ; } if ( searchField === 'repository' && isSearchingForNonExistentRepository ( repositories , searchValue ! , searchMatch , searchOperator ) ) { return res . ok ( { body : { snapshots : [ ] , policies , repositories , errors : [ ] , total : 0 , } , } ) ; } try { const { body : fetchedSnapshots } = await clusterClient . asCurrentUser . snapshot . get ( { repository : searchField === 'repository' ? getSnapshotSearchWildcard ( { field : searchField , value : searchValue ! , match : searchMatch , operator : searchOperator , } ) : '_all' , ignore_unavailable : true , snapshot : searchField === 'snapshot' ? getSnapshotSearchWildcard ( { field : searchField , value : searchValue ! , match : searchMatch , operator : searchOperator , } ) : '_all' , slm_policy_filter : searchField === 'policyName' ? getSnapshotSearchWildcard ( { field : searchField , value : searchValue ! , match : searchMatch , operator : searchOperator , } ) : '*,_none' , order : sortDirection , sort : sortField , size : pageSize , offset : pageIndex * pageSize , } ) ; const snapshots = fetchedSnapshots ?. snapshots ?. map ( ( snapshot ) => { return deserializeSnapshotDetails ( snapshot as SnapshotDetailsEs , managedRepository ) ; } ) ; return res . ok ( { body : { snapshots : snapshots || [ ] , policies , repositories , errors : fetchedSnapshots ?. failures , total : fetchedSnapshots ?. total , } , } ) ; } catch ( e ) { return handleEsError ( { error : e , response : res } ) ; } } ) ) ; const getOneParamsSchema = schema . object ( { repository : schema . string ( ) , snapshot : schema . string ( ) , } ) ; router . get ( { path : addBasePath ( 'snapshots/{repository}/{snapshot}' ) , validate : { params : getOneParamsSchema } , } , license . guardApiRoute ( async ( ctx , req , res ) => { const { client : clusterClient } = ctx . core . elasticsearch ; const { repository , snapshot } = req . params as TypeOf < typeof getOneParamsSchema > ; const managedRepository = await getManagedRepositoryName ( clusterClient . asCurrentUser ) ; try { const response = await clusterClient . asCurrentUser . snapshot . get ( { repository , snapshot : '_all' , ignore_unavailable : true , } ) ; const { snapshots : snapshotsList } = response . body ; if ( ! snapshotsList || snapshotsList . length === 0 ) { return res . notFound ( { body : 'Snapshot not found' } ) ; } const selectedSnapshot = snapshotsList . find ( ( { snapshot : snapshotName } ) => snapshot === snapshotName ) as SnapshotDetailsEs ; if ( ! selectedSnapshot ) { return res . notFound ( { body : 'Snapshot not found' } ) ; } const successfulSnapshots = snapshotsList . filter ( ( { state } ) => state === 'SUCCESS' ) . sort ( ( a , b ) => { return + new Date ( b . end_time ! ) - + new Date ( a . end_time ! ) ; } ) as SnapshotDetailsEs [ ] ; return res . ok ( { body : deserializeSnapshotDetails ( selectedSnapshot , managedRepository , successfulSnapshots ) , } ) ; } catch ( e ) { return handleEsError ( { error : e , response : res } ) ; } } ) ) ; const deleteSchema = schema . arrayOf ( schema . object ( { repository : schema . string ( ) , snapshot : schema . string ( ) , } ) ) ; router . post ( { path : addBasePath ( 'snapshots/bulk_delete' ) , validate : { body : deleteSchema } } , license . guardApiRoute ( async ( ctx , req , res ) => { const { client : clusterClient } = ctx . core . elasticsearch ; const response = { itemsDeleted : [ ] , errors : [ ] , } ; const snapshots = req . body ; try { for ( let i = 0 ; i < snapshots . length ; i ++ ) { const { snapshot , repository } = snapshots [ i ] ; await clusterClient . asCurrentUser . snapshot . delete ( { snapshot , repository } ) . then ( ( ) => response . itemsDeleted . push ( { snapshot , repository } ) ) . catch ( ( e ) => response . errors . push ( { id : { snapshot , repository } , error : wrapEsError ( e ) , } ) ) ; } return res . ok ( { body : response } ) ; } catch ( e ) { return handleEsError ( { error : e , response : res } ) ; } } ) ) ; }