import { UISref } from '@uirouter/react'; import React from 'react'; import { Modal } from 'react-bootstrap'; import { PipelineGraph } from '../../config/graph/PipelineGraph'; import type { IExecution, IExecutionStageSummary } from '../../../domain'; import { ExecutionInformationService } from './executionInformation.service'; import { ParametersAndArtifacts } from '../../status/ParametersAndArtifacts'; import { duration, relativeTime, timestamp } from '../../../utils'; import { Spinner } from '../../../widgets/spinners/Spinner'; import './executionMarkerInformationModal.less'; interface IExecutionErrorLocatorProps { executionId: string; stageId: string; onClose: Function; } interface IFailedStageExecutionLink { application: string; executionId: string; stageIndex: number; } interface IExecutionLocatorState { executionDetails: IExecution; failedInApplication: string; link: IFailedStageExecutionLink; showPipelineGraph: boolean; stageDetails: any; } export class ExecutionMarkerInformationModal extends React.PureComponent< IExecutionErrorLocatorProps, IExecutionLocatorState > { allExecutions: any[]; childExecution: any; childPipelineConfig: any; childTerminalPipelineStage: any; informationService: ExecutionInformationService; constructor(props: IExecutionErrorLocatorProps) { super(props); this.state = { executionDetails: null, failedInApplication: null, link: null, showPipelineGraph: false, stageDetails: null, }; this.allExecutions = []; this.informationService = new ExecutionInformationService(); } public componentDidMount() { this.getPipelineLink(this.props.stageId, this.props.executionId); } private getPipelineLink = async (stageId: string, executionId: string): Promise => { // get the current execution id is from ExecutionMarker.tsx try { const currentExecution = await this.informationService.getExecution(executionId); let stageIndex; // get the current stage in the exeuction index is from ExecutionMarker.tsx const currentStage = currentExecution.stageSummaries.find((stage: IExecutionStageSummary, index: number) => { if (stage.id === stageId) { // store the index for our pipeline graph stageIndex = index; return stage; } return null; }); // save this for rendering pipelines this.allExecutions.push({ execution: currentExecution, stageId, stageIndex, }); // get the child execution aka clicking View Pipeline Details const childExecution = await this.informationService.getExecution(currentStage.masterStage.context.executionId); const childTerminalStage = childExecution.stageSummaries.find((stage: IExecutionStageSummary, index: number) => { if (stage.status.toLocaleLowerCase() === 'terminal') stageIndex = index; return stage.status.toLowerCase() === 'terminal'; }); const childTerminalPipelineStage = childExecution.stageSummaries.find( (stage: IExecutionStageSummary) => stage.status.toLowerCase() === 'terminal' && stage.type === 'pipeline', ); // get the current configuration for this execution const childPipelineConfig = await this.informationService.getPipelineConfig( childExecution.application, childExecution.pipelineConfigId, ); if (childExecution && !childTerminalPipelineStage) { this.childExecution = childExecution; this.childPipelineConfig = childPipelineConfig; // save this for rendering pipelines this.allExecutions.push({ execution: childExecution, stageId, stageIndex, }); } if (childTerminalPipelineStage) { this.getPipelineLink(childTerminalPipelineStage.id, childExecution.id); this.childTerminalPipelineStage = childTerminalPipelineStage; } else { // now that we are complete let's fix up the allExecutions array // we are using allExecutions as a breadcrumb so reverse them then pop the first one since there user is already at the first one this.allExecutions.reverse(); this.allExecutions.pop(); this.setState({ executionDetails: this.childExecution || currentExecution, failedInApplication: currentStage.masterStage.context.application, link: { application: currentStage.masterStage.context.application, executionId: currentStage.masterStage.context.executionId, stageIndex: this.allExecutions[0].stageIndex, }, stageDetails: childTerminalStage || this.childTerminalPipelineStage || currentStage, }); } } catch (err) { if (console) { console.error('Error retrieving pipeline execution data.'); this.props.onClose(); } } }; private showPipelineGraph = () => { this.setState({ showPipelineGraph: true }); }; private hidePipelineGraph = () => { this.setState({ showPipelineGraph: false }); }; public render(): React.ReactElement { const { executionDetails, failedInApplication, link, stageDetails } = this.state; const content = this.state.showPipelineGraph ? (
{this.allExecutions.map((item) => { return (
{item.execution.application} - {item.execution.name} {}} viewState={{ activeStageId: item.stageIndex, activeSubStageId: null, canTriggerPipelineManually: false, canConfigure: false, }} />
); })}
) : (
{!executionDetails ? (
) : (
{executionDetails.name}
PIPELINE
{`[${executionDetails.authentication.user}] ${ executionDetails.user ? `(${executionDetails.user})` : '' }`}
{relativeTime(executionDetails.startTime || executionDetails.buildTime)}
Status: {executionDetails.status} by parent pipeline{' '} {timestamp(executionDetails.trigger.parentExecution.buildTime)}
{stageDetails && (
STAGE DETAILS
Name: {stageDetails.name}
Duration: {duration(stageDetails.endTime - stageDetails.startTime)}
Exception: {stageDetails.getErrorMessage || 'No message available'}
Pipeline Execution History (Decending order) {this.allExecutions.map((item: any, index: number) => { return ( ); })}
APPLICATION PIPELINE NAME STAGE STATUS DURATION
{index === 0 && } {item.execution.application} {item.execution.name} {item.execution.stageSummaries[item.stageIndex].name} {item.execution.stageSummaries[item.stageIndex].status} {duration(item.execution.endTime - item.execution.startTime)}
)}
)} {link && (
Link to the Last Failed Execution Stage
)}
); const title = this.state.showPipelineGraph ? ( { this.hidePipelineGraph(); }} > Pipeline Execution History Graphs (Decending order) ) : ( {failedInApplication ? failedInApplication : '-'} ); return ( {title} {content} ); } }