import expect from '@kbn/expect' ; import { ProvidedType } from '@kbn/test' ; import { savedSearches , dashboards } from './test_resources_data' ; import { COMMON_REQUEST_HEADERS } from './common_api' ; import { FtrProviderContext } from '../../ftr_provider_context' ; import { JobType } from '../../../../plugins/ml/common/types/saved_objects' ; export enum SavedObjectType { CONFIG = 'config' , DASHBOARD = 'dashboard' , INDEX_PATTERN = 'index-pattern' , SEARCH = 'search' , VISUALIZATION = 'visualization' , ML_JOB = 'ml-job' } export type MlTestResourcesi = ProvidedType < typeof MachineLearningTestResourcesProvider > ; export function MachineLearningTestResourcesProvider ( { getService } ) { const kibanaServer = getService ( 'kibanaServer' ) ; const log = getService ( 'log' ) ; const supertest = getService ( 'supertest' ) ; const retry = getService ( 'retry' ) ; return { async setKibanaTimeZoneToUTC ( ) { await kibanaServer . uiSettings . update ( { 'dateFormat:tz' : 'UTC' , } ) ; } , async resetKibanaTimeZone ( ) { await kibanaServer . uiSettings . unset ( 'dateFormat:tz' ) ; } , async savedObjectExistsById ( id , objectType ) { const response = await supertest . get ( `/api/saved_objects/${ objectType }/${ id }` ) ; return response . status === 200 ; } , async savedObjectExistsByTitle ( title , objectType ) { const id = await this . getSavedObjectIdByTitle ( title , objectType ) ; if ( id ) { return await this . savedObjectExistsById ( id , objectType ) ; } else { return false ; } } , async getSavedObjectIdByTitle ( title , objectType ) { log . debug ( `Searching for '${ objectType }' with title '${ title }'...` ) ; const findResponse = await supertest . get ( `/api/saved_objects/_find?type=${ objectType }&per_page=10000` ) . set ( COMMON_REQUEST_HEADERS ) . expect ( 200 ) . then ( ( res ) => res . body ) ; for ( const savedObject of findResponse . saved_objects ) { const objectTitle = savedObject . attributes . title ; if ( objectTitle === title ) { log . debug ( ` > Found '${ savedObject . id }'` ) ; return savedObject . id ; } } log . debug ( ` > Not found` ) ; } , async getSavedObjectIdsByType ( objectType ) { const savedObjectIds = [ ] ; log . debug ( `Searching for '${ objectType }' ...` ) ; const findResponse = await supertest . get ( `/api/saved_objects/_find?type=${ objectType }&per_page=10000` ) . set ( COMMON_REQUEST_HEADERS ) . expect ( 200 ) . then ( ( res ) => res . body ) ; findResponse . saved_objects . forEach ( ( element ) => { savedObjectIds . push ( element . id ) ; } ) ; return savedObjectIds ; } , async getIndexPatternId ( title ) { return this . getSavedObjectIdByTitle ( title , SavedObjectType . INDEX_PATTERN ) ; } , async getSavedSearchId ( title ) { return this . getSavedObjectIdByTitle ( title , SavedObjectType . SEARCH ) ; } , async getVisualizationId ( title ) { return this . getSavedObjectIdByTitle ( title , SavedObjectType . VISUALIZATION ) ; } , async getDashboardId ( title ) { return this . getSavedObjectIdByTitle ( title , SavedObjectType . DASHBOARD ) ; } , async createIndexPattern ( title , timeFieldName ? ) { log . debug ( `Creating index pattern with title '${ title }'${ timeFieldName !== undefined ? ` and time field '${ timeFieldName }'` : '' }` ) ; const createResponse = await supertest . post ( `/api/saved_objects/${ SavedObjectType . INDEX_PATTERN }` ) . set ( COMMON_REQUEST_HEADERS ) . send ( { attributes : { title , timeFieldName } } ) . expect ( 200 ) . then ( ( res ) => res . body ) ; await this . assertIndexPatternExistByTitle ( title ) ; log . debug ( ` > Created with id '${ createResponse . id }'` ) ; return createResponse . id ; } , async createBulkSavedObjects ( body ) { log . debug ( `Creating bulk saved objects'` ) ; const createResponse = await supertest . post ( `/api/saved_objects/_bulk_create` ) . set ( COMMON_REQUEST_HEADERS ) . send ( body ) . expect ( 200 ) . then ( ( res ) => res . body ) ; log . debug ( ` > Created bulk saved objects'` ) ; return createResponse ; } , async createIndexPatternIfNeeded ( title , timeFieldName ? ) { const indexPatternId = await this . getIndexPatternId ( title ) ; if ( indexPatternId !== undefined ) { log . debug ( `Index pattern with title '${ title }' already exists. Nothing to create.` ) ; return indexPatternId ; } else { return await this . createIndexPattern ( title , timeFieldName ) ; } } , async assertIndexPatternNotExist ( title ) { await this . assertSavedObjectNotExistsByTitle ( title , SavedObjectType . INDEX_PATTERN ) ; } , async createSavedSearch ( title , body ) { log . debug ( `Creating saved search with title '${ title }'` ) ; const createResponse = await supertest . post ( `/api/saved_objects/${ SavedObjectType . SEARCH }` ) . set ( COMMON_REQUEST_HEADERS ) . send ( body ) . expect ( 200 ) . then ( ( res ) => res . body ) ; await this . assertSavedSearchExistByTitle ( title ) ; log . debug ( ` > Created with id '${ createResponse . id }'` ) ; return createResponse . id ; } , async createDashboard ( title , body ) { log . debug ( `Creating dashboard with title '${ title }'` ) ; const createResponse = await supertest . post ( `/api/saved_objects/${ SavedObjectType . DASHBOARD }` ) . set ( COMMON_REQUEST_HEADERS ) . send ( body ) . expect ( 200 ) . then ( ( res ) => res . body ) ; log . debug ( ` > Created with id '${ createResponse . id }'` ) ; return createResponse . id ; } , async createSavedSearchIfNeeded ( savedSearch ) { const title = savedSearch . requestBody . attributes . title ; const savedSearchId = await this . getSavedSearchId ( title ) ; if ( savedSearchId !== undefined ) { log . debug ( `Saved search with title '${ title }' already exists. Nothing to create.` ) ; return savedSearchId ; } else { const body = await this . updateSavedSearchRequestBody ( savedSearch . requestBody , savedSearch . indexPatternTitle ) ; return await this . createSavedSearch ( title , body ) ; } } , async updateSavedSearchRequestBody ( body , indexPatternTitle ) { const indexPatternId = await this . getIndexPatternId ( indexPatternTitle ) ; if ( indexPatternId === undefined ) { throw new Error ( `Index pattern '${ indexPatternTitle }' to base saved search on does not exist. ` ) ; } const updatedBody = JSON . parse ( JSON . stringify ( body ) , ( _key , value ) => { if ( value === 'INDEX_PATTERN_ID_PLACEHOLDER' ) { return indexPatternId ; } else { return value ; } } ) ; const searchSourceJsonNode = updatedBody . attributes . kibanaSavedObjectMeta . searchSourceJSON ; const searchSourceJsonString = JSON . stringify ( searchSourceJsonNode ) ; updatedBody . attributes . kibanaSavedObjectMeta . searchSourceJSON = searchSourceJsonString ; return updatedBody ; } , async createSavedSearchFarequoteFilterIfNeeded ( ) { await this . createSavedSearchIfNeeded ( savedSearches . farequoteFilter ) ; } , async createMLTestDashboardIfNeeded ( ) { await this . createDashboardIfNeeded ( dashboards . mlTestDashboard ) ; } , async deleteMLTestDashboard ( ) { await this . deleteDashboardByTitle ( dashboards . mlTestDashboard . requestBody . attributes . title ) ; } , async createDashboardIfNeeded ( dashboard ) { const title = dashboard . requestBody . attributes . title ; const dashboardId = await this . getDashboardId ( title ) ; if ( dashboardId !== undefined ) { log . debug ( `Dashboard with title '${ title }' already exists. Nothing to create.` ) ; return dashboardId ; } else { return await this . createDashboard ( title , dashboard . requestBody ) ; } } , async createSavedSearchFarequoteLuceneIfNeeded ( ) { await this . createSavedSearchIfNeeded ( savedSearches . farequoteLucene ) ; } , async createSavedSearchFarequoteKueryIfNeeded ( ) { await this . createSavedSearchIfNeeded ( savedSearches . farequoteKuery ) ; } , async createSavedSearchFarequoteFilterAndLuceneIfNeeded ( ) { await this . createSavedSearchIfNeeded ( savedSearches . farequoteFilterAndLucene ) ; } , async createSavedSearchFarequoteFilterAndKueryIfNeeded ( ) { await this . createSavedSearchIfNeeded ( savedSearches . farequoteFilterAndKuery ) ; } , async deleteSavedObjectById ( id , objectType , force = false ) { log . debug ( `Deleting ${ objectType } with id '${ id }'...` ) ; if ( ( await this . savedObjectExistsById ( id , objectType ) ) === false ) { log . debug ( `${ objectType } with id '${ id }' does not exists. Nothing to delete.` ) ; return ; } else { await supertest . delete ( `/api/saved_objects/${ objectType }/${ id }` ) . set ( COMMON_REQUEST_HEADERS ) . query ( { force } ) . expect ( 200 ) ; await this . assertSavedObjectNotExistsById ( id , objectType ) ; log . debug ( ` > Deleted ${ objectType } with id '${ id }'` ) ; } } , async deleteIndexPatternByTitle ( title ) { log . debug ( `Deleting index pattern with title '${ title }'...` ) ; const indexPatternId = await this . getIndexPatternId ( title ) ; if ( indexPatternId === undefined ) { log . debug ( `Index pattern with title '${ title }' does not exists. Nothing to delete.` ) ; return ; } else { await this . deleteIndexPatternById ( indexPatternId ) ; } } , async deleteIndexPatternById ( id ) { await this . deleteSavedObjectById ( id , SavedObjectType . INDEX_PATTERN ) ; } , async deleteSavedSearchByTitle ( title ) { log . debug ( `Deleting saved search with title '${ title }'...` ) ; const savedSearchId = await this . getSavedSearchId ( title ) ; if ( savedSearchId === undefined ) { log . debug ( `Saved search with title '${ title }' does not exists. Nothing to delete.` ) ; return ; } else { await this . deleteSavedSearchById ( savedSearchId ) ; } } , async deleteSavedSearchById ( id ) { await this . deleteSavedObjectById ( id , SavedObjectType . SEARCH ) ; } , async deleteVisualizationByTitle ( title ) { log . debug ( `Deleting visualization with title '${ title }'...` ) ; const visualizationId = await this . getVisualizationId ( title ) ; if ( visualizationId === undefined ) { log . debug ( `Visualization with title '${ title }' does not exists. Nothing to delete.` ) ; return ; } else { await this . deleteVisualizationById ( visualizationId ) ; } } , async deleteVisualizationById ( id ) { await this . deleteSavedObjectById ( id , SavedObjectType . VISUALIZATION ) ; } , async deleteDashboardByTitle ( title ) { log . debug ( `Deleting dashboard with title '${ title }'...` ) ; const dashboardId = await this . getDashboardId ( title ) ; if ( dashboardId === undefined ) { log . debug ( `Dashboard with title '${ title }' does not exists. Nothing to delete.` ) ; return ; } else { await this . deleteDashboardById ( dashboardId ) ; } } , async deleteDashboardById ( id ) { await this . deleteSavedObjectById ( id , SavedObjectType . DASHBOARD ) ; } , async deleteSavedSearches ( ) { for ( const search of Object . values ( savedSearches ) ) { await this . deleteSavedSearchByTitle ( search . requestBody . attributes . title ) ; } } , async deleteDashboards ( ) { for ( const dashboard of Object . values ( dashboards ) ) { await this . deleteDashboardByTitle ( dashboard . requestBody . attributes . title ) ; } } , async assertSavedObjectExistsByTitle ( title , objectType ) { await retry . waitForWithTimeout ( `${ objectType } with title '${ title }' to exist` , 5 * 1000 , async ( ) => { if ( ( await this . savedObjectExistsByTitle ( title , objectType ) ) === true ) { return true ; } else { throw new Error ( `${ objectType } with title '${ title }' should exist.` ) ; } } ) ; } , async assertSavedObjectNotExistsByTitle ( title , objectType ) { await retry . waitForWithTimeout ( `${ objectType } with title '${ title }' not to exist` , 5 * 1000 , async ( ) => { if ( ( await this . savedObjectExistsByTitle ( title , objectType ) ) === false ) { return true ; } else { throw new Error ( `${ objectType } with title '${ title }' should not exist.` ) ; } } ) ; } , async assertSavedObjectExistsById ( id , objectType ) { await retry . waitForWithTimeout ( `${ objectType } with id '${ id }' to exist` , 5 * 1000 , async ( ) => { if ( ( await this . savedObjectExistsById ( id , objectType ) ) === true ) { return true ; } else { throw new Error ( `${ objectType } with id '${ id }' should exist.` ) ; } } ) ; } , async assertSavedObjectNotExistsById ( id , objectType ) { await retry . waitForWithTimeout ( `${ objectType } with id '${ id }' not to exist` , 5 * 1000 , async ( ) => { if ( ( await this . savedObjectExistsById ( id , objectType ) ) === false ) { return true ; } else { throw new Error ( `${ objectType } with id '${ id }' should not exist.` ) ; } } ) ; } , async assertIndexPatternExistByTitle ( title ) { await this . assertSavedObjectExistsByTitle ( title , SavedObjectType . INDEX_PATTERN ) ; } , async assertIndexPatternExistById ( id ) { await this . assertSavedObjectExistsById ( id , SavedObjectType . INDEX_PATTERN ) ; } , async assertSavedSearchExistByTitle ( title ) { await this . assertSavedObjectExistsByTitle ( title , SavedObjectType . SEARCH ) ; } , async assertSavedSearchExistById ( id ) { await this . assertSavedObjectExistsById ( id , SavedObjectType . SEARCH ) ; } , async assertVisualizationExistByTitle ( title ) { await this . assertSavedObjectExistsByTitle ( title , SavedObjectType . VISUALIZATION ) ; } , async assertVisualizationExistById ( id ) { await this . assertSavedObjectExistsById ( id , SavedObjectType . VISUALIZATION ) ; } , async assertDashboardExistByTitle ( title ) { await this . assertSavedObjectExistsByTitle ( title , SavedObjectType . DASHBOARD ) ; } , async assertDashboardExistById ( id ) { await this . assertSavedObjectExistsById ( id , SavedObjectType . DASHBOARD ) ; } , async deleteMlSavedObjectByJobId ( jobId , jobType ) { const savedObjectId = `${ jobType }-${ jobId }` ; await this . deleteSavedObjectById ( savedObjectId , SavedObjectType . ML_JOB , true ) ; } , async cleanMLSavedObjects ( ) { log . debug ( 'Deleting ML saved objects ...' ) ; const savedObjectIds = await this . getSavedObjectIdsByType ( SavedObjectType . ML_JOB ) ; for ( const id of savedObjectIds ) { await this . deleteSavedObjectById ( id , SavedObjectType . ML_JOB , true ) ; } log . debug ( '> ML saved objects deleted.' ) ; } , async setupFleet ( ) { log . debug ( `Setting up Fleet` ) ; await retry . tryForTime ( 2 * 60 * 1000 , async ( ) => { await supertest . post ( `/api/fleet/setup` ) . set ( COMMON_REQUEST_HEADERS ) . expect ( 200 ) ; } ) ; log . debug ( ` > Setup done` ) ; } , async installFleetPackage ( packageName ) { log . debug ( `Installing Fleet package '${ packageName }'` ) ; const version = await this . getFleetPackageVersion ( packageName ) ; await retry . tryForTime ( 30 * 1000 , async ( ) => { await supertest . post ( `/api/fleet/epm/packages/${ packageName }/${ version }` ) . set ( COMMON_REQUEST_HEADERS ) . expect ( 200 ) ; } ) ; log . debug ( ` > Installed` ) ; return version ; } , async removeFleetPackage ( packageName , version ) { log . debug ( `Removing Fleet package '${ packageName }-${ version }'` ) ; await retry . tryForTime ( 30 * 1000 , async ( ) => { await supertest . delete ( `/api/fleet/epm/packages/${ packageName }/${ version }` ) . set ( COMMON_REQUEST_HEADERS ) . expect ( 200 ) ; } ) ; log . debug ( ` > Removed` ) ; } , async getFleetPackageVersion ( packageName ) { log . debug ( `Fetching version for Fleet package '${ packageName }'` ) ; let packageVersion = '' ; await retry . tryForTime ( 10 * 1000 , async ( ) => { const { body } = await supertest . get ( `/api/fleet/epm/packages?experimental=true` ) . set ( COMMON_REQUEST_HEADERS ) . expect ( 200 ) ; packageVersion = body . response . find ( ( { name , version } ) => name === packageName && version ) ?. version ?? '' ; expect ( packageVersion ) . to . not . eql ( '' , `Fleet package definition for '${ packageName }' should exist and have a version` ) ; } ) ; log . debug ( ` > found version '${ packageVersion }'` ) ; return packageVersion ; } , async setAdvancedSettingProperty ( propertyName , propertyValue ) { await kibanaServer . uiSettings . update ( { [ propertyName ] : propertyValue , } ) ; } , async clearAdvancedSettingProperty ( propertyName ) { await kibanaServer . uiSettings . unset ( propertyName ) ; } , async installKibanaSampleData ( sampleDataId ) { log . debug ( `Installing Kibana sample data '${ sampleDataId }'` ) ; await supertest . post ( `/api/sample_data/${ sampleDataId }` ) . set ( COMMON_REQUEST_HEADERS ) . expect ( 200 ) ; log . debug ( ` > Installed` ) ; } , async removeKibanaSampleData ( sampleDataId ) { log . debug ( `Removing Kibana sample data '${ sampleDataId }'` ) ; await supertest . delete ( `/api/sample_data/${ sampleDataId }` ) . set ( COMMON_REQUEST_HEADERS ) . expect ( 204 ) ; log . debug ( ` > Removed` ) ; } , async installAllKibanaSampleData ( ) { await this . installKibanaSampleData ( 'ecommerce' ) ; await this . installKibanaSampleData ( 'flights' ) ; await this . installKibanaSampleData ( 'logs' ) ; } , async removeAllKibanaSampleData ( ) { await this . removeKibanaSampleData ( 'ecommerce' ) ; await this . removeKibanaSampleData ( 'flights' ) ; await this . removeKibanaSampleData ( 'logs' ) ; } , } ; }