import { Stream } from 'stream' ; import contentDisposition from 'content-disposition' ; import { CSV_JOB_TYPE , CSV_JOB_TYPE_DEPRECATED } from '../../../common/constants' ; import { ReportApiJSON } from '../../../common/types' ; import { ReportingCore } from '../../' ; import { getContentStream , statuses } from '../../lib' ; import { ExportTypeDefinition } from '../../types' ; import { jobsQueryFactory } from './jobs_query' ; export interface ErrorFromPayload { message : string ; } interface Payload { statusCode : number ; content : string | Stream | ErrorFromPayload ; contentType : string | null ; headers : Record < string , string | number > ; } type TaskRunResult = Required < ReportApiJSON > [ 'output' ] ; const DEFAULT_TITLE = 'report' ; const getTitle = ( exportType , title ? ) : string => `${ title || DEFAULT_TITLE }.${ exportType . jobContentExtension }` ; const getReportingHeaders = ( output , exportType ) => { const metaDataHeaders = { } ; if ( exportType . jobType === CSV_JOB_TYPE || exportType . jobType === CSV_JOB_TYPE_DEPRECATED ) { const csvContainsFormulas = output . csv_contains_formulas ?? false ; const maxSizedReach = output . max_size_reached ?? false ; metaDataHeaders [ 'kbn-csv-contains-formulas' ] = csvContainsFormulas ; metaDataHeaders [ 'kbn-max-size-reached' ] = maxSizedReach ; } return metaDataHeaders ; } ; export function getDocumentPayloadFactory ( reporting ) { const exportTypesRegistry = reporting . getExportTypesRegistry ( ) ; async function getCompleted ( { id , index , output , jobtype : jobType , payload : { title } , } ) { const exportType = exportTypesRegistry . get ( ( item ) => item . jobType === jobType ) ; const encoding = exportType . jobContentEncoding === 'base64' ? 'base64' : 'raw' ; const content = await getContentStream ( reporting , { id , index } , { encoding } ) ; const filename = getTitle ( exportType , title ) ; const headers = getReportingHeaders ( output , exportType ) ; return { content , statusCode : 200 , contentType : output . content_type , headers : { ... headers , 'Content-Disposition' : contentDisposition ( filename , { type : 'inline' } ) , 'Content-Length' : output . size , } , } ; } async function getFailure ( { id } ) { const jobsQuery = jobsQueryFactory ( reporting ) ; const error = await jobsQuery . getError ( id ) ; return { statusCode : 500 , content : { message : `Reporting generation failed: ${ error }` , } , contentType : 'application/json' , headers : { } , } ; } function getIncomplete ( { status } ) { return { statusCode : 503 , content : status , contentType : 'text/plain' , headers : { 'retry-after' : 30 } , } ; } return async function getDocumentPayload ( report ) : Promise < Payload > { if ( report . output ) { if ( [ statuses . JOB_STATUS_COMPLETED , statuses . JOB_STATUS_WARNINGS ] . includes ( report . status ) ) { return getCompleted ( report as Required < ReportApiJSON > ) ; } if ( statuses . JOB_STATUS_FAILED === report . status ) { return getFailure ( report ) ; } } return getIncomplete ( report ) ; } ; }