import Boom from '@hapi/boom' ; import { SavedObject , SavedObjectsClientContract } from '../types' ; import { SavedObjectsImportError , SavedObjectsImportRetry } from './types' ; const REF_TYPES_TO_VLIDATE = [ 'index-pattern' , 'search' ] ; function filterReferencesToValidate ( { type } ) { return REF_TYPES_TO_VLIDATE . includes ( type ) ; } const getObjectsToSkip = ( retries = [ ] ) => retries . reduce ( ( acc , { type , id , ignoreMissingReferences } ) => ignoreMissingReferences ? acc . add ( `${ type }:${ id }` ) : acc , new Set < string > ( ) ) ; export async function getNonExistingReferenceAsKeys ( savedObjects , savedObjectsClient , namespace ? , retries ? ) { const objectsToSkip = getObjectsToSkip ( retries ) ; const collector = new Map ( ) ; for ( const savedObject of savedObjects ) { if ( objectsToSkip . has ( `${ savedObject . type }:${ savedObject . id }` ) ) { continue ; } const filteredReferences = ( savedObject . references || [ ] ) . filter ( filterReferencesToValidate ) ; for ( const { type , id } of filteredReferences ) { collector . set ( `${ type }:${ id }` , { type , id } ) ; } } for ( const savedObject of savedObjects ) { collector . delete ( `${ savedObject . type }:${ savedObject . id }` ) ; } if ( collector . size === 0 ) { return [ ] ; } const bulkGetOpts = Array . from ( collector . values ( ) ) . map ( ( obj ) => ( { ... obj , fields : [ 'id' ] } ) ) ; const bulkGetResponse = await savedObjectsClient . bulkGet ( bulkGetOpts , { namespace } ) ; const erroredObjects = bulkGetResponse . saved_objects . filter ( ( obj ) => obj . error && obj . error . statusCode !== 404 ) ; if ( erroredObjects . length ) { const err = Boom . badRequest ( ) ; err . output . payload . attributes = { objects : erroredObjects , } ; throw err ; } for ( const savedObject of bulkGetResponse . saved_objects ) { if ( savedObject . error ) { continue ; } collector . delete ( `${ savedObject . type }:${ savedObject . id }` ) ; } return [ ... collector . keys ( ) ] ; } export async function validateReferences ( savedObjects , savedObjectsClient , namespace ? , retries ? ) { const objectsToSkip = getObjectsToSkip ( retries ) ; const errorMap = { } ; const nonExistingReferenceKeys = await getNonExistingReferenceAsKeys ( savedObjects , savedObjectsClient , namespace , retries ) ; savedObjects . forEach ( ( { type , id , references , attributes } ) => { if ( objectsToSkip . has ( `${ type }:${ id }` ) ) { return ; } const missingReferences = [ ] ; const enforcedTypeReferences = ( references || [ ] ) . filter ( filterReferencesToValidate ) ; for ( const { type : refType , id : refId } of enforcedTypeReferences ) { if ( nonExistingReferenceKeys . includes ( `${ refType }:${ refId }` ) ) { missingReferences . push ( { type : refType , id : refId } ) ; } } if ( missingReferences . length === 0 ) { return ; } const { title } = attributes ; errorMap [ `${ type }:${ id }` ] = { id , type , title , meta : { title } , error : { type : 'missing_references' , references : missingReferences } , } ; } ) ; return Object . values ( errorMap ) ; }