import { get , hasIn } from 'lodash' ; import { i18n } from '@osd/i18n' ; import { OpenSearchDashboardsDatatable , OpenSearchDashboardsDatatableColumn , } from 'src/plugins/expressions/public' ; import { PersistedState } from '../../../../visualizations/public' ; import { Adapters } from '../../../../inspector' ; import { calculateBounds , OpenSearchaggsExpressionFunctionDefinition , Filter , getTime , IIndexPattern , isRangeFilter , Query , TimeRange , } from '../../../common' ; import { getRequestInspectorStats , getResponseInspectorStats , IAggConfigs , ISearchSource , tabifyAggResponse , } from '../../../common' ; import { FilterManager } from '../../query' ; import { getFieldFormats , getIndexPatterns , getQueryService , getSearchService , } from '../../services' ; import { buildTabularInspectorData } from './build_tabular_inspector_data' ; import { serializeAggConfig } from './utils' ; export interface RequestHandlerParams { searchSource : ISearchSource ; aggs : IAggConfigs ; timeRange ? : TimeRange ; timeFields ? : string [ ] ; indexPattern ? : IIndexPattern ; query ? : Query ; filters ? : Filter [ ] ; filterManager : FilterManager ; uiState ? : PersistedState ; partialRows ? : boolean ; inspectorAdapters : Adapters ; metricsAtAllLevels ? : boolean ; visParams ? : any ; abortSignal ? : AbortSignal ; } const name = 'opensearchaggs' ; const handleCourierRequest = async ( { searchSource , aggs , timeRange , timeFields , indexPattern , query , filters , partialRows , metricsAtAllLevels , inspectorAdapters , filterManager , abortSignal , } ) => { const timeFilterSearchSource = searchSource . createChild ( { callParentStartHandlers : true } ) ; const requestSearchSource = timeFilterSearchSource . createChild ( { callParentStartHandlers : true } ) ; aggs . setTimeRange ( timeRange as TimeRange ) ; Object . defineProperty ( requestSearchSource , 'history' , { get ( ) { return searchSource . history ; } , set ( history ) { return ( searchSource . history = history ) ; } , } ) ; requestSearchSource . setField ( 'aggs' , function ( ) { return aggs . toDsl ( metricsAtAllLevels ) ; } ) ; requestSearchSource . onRequestStart ( ( paramSearchSource , options ) => { return aggs . onSearchRequestStart ( paramSearchSource , options ) ; } ) ; const defaultTimeField = indexPattern ?. getTimeField ?. ( ) ; const defaultTimeFields = defaultTimeField ? [ defaultTimeField . name ] : [ ] ; const allTimeFields = timeFields && timeFields . length > 0 ? timeFields : defaultTimeFields ; if ( timeRange && allTimeFields . length > 0 ) { timeFilterSearchSource . setField ( 'filter' , ( ) => { return allTimeFields . map ( ( fieldName ) => getTime ( indexPattern , timeRange , { fieldName } ) ) . filter ( isRangeFilter ) ; } ) ; } requestSearchSource . setField ( 'filter' , filters ) ; requestSearchSource . setField ( 'query' , query ) ; inspectorAdapters . requests . reset ( ) ; const request = inspectorAdapters . requests . start ( i18n . translate ( 'data.functions.opensearchaggs.inspector.dataRequest.title' , { defaultMessage : 'Data' , } ) , { description : i18n . translate ( 'data.functions.opensearchaggs.inspector.dataRequest.description' , { defaultMessage : 'This request queries OpenSearch to fetch the data for the visualization.' , } ) , } ) ; request . stats ( getRequestInspectorStats ( requestSearchSource ) ) ; try { const response = await requestSearchSource . fetch ( { abortSignal } ) ; request . stats ( getResponseInspectorStats ( response , searchSource ) ) . ok ( { json : response } ) ; ( searchSource as any ) . rawResponse = response ; } catch ( e ) { request . error ( { json : e } ) ; throw e ; } finally { requestSearchSource . getSearchRequestBody ( ) . then ( ( req ) => { request . json ( req ) ; } ) ; } let resp = ( searchSource as any ) . rawResponse ; for ( const agg of aggs . aggs ) { if ( hasIn ( agg , 'type.postFlightRequest' ) ) { resp = await agg . type . postFlightRequest ( resp , aggs , agg , requestSearchSource , inspectorAdapters . requests , abortSignal ) ; } } ( searchSource as any ) . finalResponse = resp ; const parsedTimeRange = timeRange ? calculateBounds ( timeRange ) : null ; const tabifyParams = { metricsAtAllLevels , partialRows , timeRange : parsedTimeRange ? { from : parsedTimeRange . min , to : parsedTimeRange . max , timeFields : allTimeFields } : undefined , } ; ( searchSource as any ) . tabifiedResponse = tabifyAggResponse ( aggs , ( searchSource as any ) . finalResponse , tabifyParams ) ; inspectorAdapters . data . setTabularLoader ( ( ) => buildTabularInspectorData ( ( searchSource as any ) . tabifiedResponse , { queryFilter : filterManager , deserializeFieldFormat : getFieldFormats ( ) . deserialize , } ) , { returnsFormattedValues : true } ) ; return ( searchSource as any ) . tabifiedResponse ; } ; export const opensearchaggs = ( ) : OpenSearchaggsExpressionFunctionDefinition => ( { name , type : 'opensearch_dashboards_datatable' , inputTypes : [ 'opensearch_dashboards_context' , 'null' ] , help : i18n . translate ( 'data.functions.opensearchaggs.help' , { defaultMessage : 'Run AggConfig aggregation' , } ) , args : { index : { types : [ 'string' ] , help : '' , } , metricsAtAllLevels : { types : [ 'boolean' ] , default : false , help : '' , } , partialRows : { types : [ 'boolean' ] , default : false , help : '' , } , includeFormatHints : { types : [ 'boolean' ] , default : false , help : '' , } , aggConfigs : { types : [ 'string' ] , default : '""' , help : '' , } , timeFields : { types : [ 'string' ] , help : '' , multi : true , } , } , async fn ( input , args , { inspectorAdapters , abortSignal } ) { const indexPatterns = getIndexPatterns ( ) ; const { filterManager } = getQueryService ( ) ; const searchService = getSearchService ( ) ; const aggConfigsState = JSON . parse ( args . aggConfigs ) ; const indexPattern = await indexPatterns . get ( args . index ) ; const aggs = searchService . aggs . createAggConfigs ( indexPattern , aggConfigsState ) ; const searchSource = await searchService . searchSource . create ( ) ; searchSource . setField ( 'index' , indexPattern ) ; searchSource . setField ( 'size' , 0 ) ; const response = await handleCourierRequest ( { searchSource , aggs , indexPattern , timeRange : get ( input , 'timeRange' , undefined ) , query : get ( input , 'query' , undefined ) as any , filters : get ( input , 'filters' , undefined ) , timeFields : args . timeFields , metricsAtAllLevels : args . metricsAtAllLevels , partialRows : args . partialRows , inspectorAdapters : inspectorAdapters as Adapters , filterManager , abortSignal : ( abortSignal as unknown ) as AbortSignal , } ) ; const table = { type : 'opensearch_dashboards_datatable' , rows : response . rows , columns : response . columns . map ( ( column ) => { const cleanedColumn = { id : column . id , name : column . name , meta : serializeAggConfig ( column . aggConfig ) , } ; if ( args . includeFormatHints ) { cleanedColumn . formatHint = column . aggConfig . toSerializedFieldFormat ( ) ; } return cleanedColumn ; } ) , } ; return table ; } , } ) ;