diff --git a/app/scripts/modules/core/src/pipeline/config/stages/common/ExecutionBarLabel.tsx b/app/scripts/modules/core/src/pipeline/config/stages/common/ExecutionBarLabel.tsx index 842e4d1e6f5..ddc7cbc3c53 100644 --- a/app/scripts/modules/core/src/pipeline/config/stages/common/ExecutionBarLabel.tsx +++ b/app/scripts/modules/core/src/pipeline/config/stages/common/ExecutionBarLabel.tsx @@ -27,8 +27,13 @@ export class ExecutionBarLabel extends React.Component { - const { execution, application } = this.props; - if (!execution) { + const { stage, application, execution } = this.props; + const { suspendedStageTypes } = stage; + const suspendedTypesNeedingHydration = ['restrictExecutionDuringTimeWindow', 'waitForCondition']; + const requiresHydration = + stage.labelComponent !== ExecutionBarLabel || + suspendedTypesNeedingHydration.some(s => suspendedStageTypes.has(s)); + if (!requiresHydration || !execution || execution.hydrated) { return; } ReactInjector.executionService.hydrate(application, execution).then(() => { @@ -46,61 +51,50 @@ export class ExecutionBarLabel extends React.Component s.type === 'restrictExecutionDuringTimeWindow'); - const template = ( -
-
- {stage.name} (waiting for execution window) -
- -
- ); - return {this.props.children}; - } else if (suspendedStageTypes.has('waitForCondition') && executionMarker) { - const waitForConditionStage = stage.stages.find(s => s.type === 'waitForCondition'); - const template = ( + private DefaultLabel = () => { + const { stage, application, execution } = this.props; + const LabelComponent = stage.labelComponent; + const tooltip = ( + + + + ); + return ( + + {this.props.children} + + ); + }; + + private getExecutionWindowTemplate = () => { + const { stage, application, execution } = this.props; + const executionWindowStage = stage.stages.find(s => s.type === 'restrictExecutionDuringTimeWindow'); + return ( +
-

- {stage.name} (waiting until conditions are met) -

- Conditions: - + {stage.name} (waiting for execution window)
- ); - return {this.props.children}; - } - if (executionMarker) { - const LabelComponent = stage.labelComponent; - if (LabelComponent !== ExecutionBarLabel && !this.state.hydrated) { - const loadingTooltip = ( - - - - ); - return ( - - - {this.props.children} - - - ); - } - const tooltip = ( - - - - ); - return ( - - {this.props.children} - - ); - } + +
+ ); + }; + + private getWaitForConditionTemplate = () => { + const { stage, application, execution } = this.props; + const waitForConditionStage = stage.stages.find(s => s.type === 'waitForCondition'); + return ( +
+

+ {stage.name} (waiting until conditions are met) +

+ Conditions: + +
+ ); + }; + private getRenderableStageName(): string { + const { stage } = this.props; let stageName = stage.name ? stage.name : stage.type; const params = ReactInjector.$uiRouter.globals.params; if (stage.type === 'group' && stage.groupStages && stage.index === Number(params.stage)) { @@ -112,6 +106,37 @@ export class ExecutionBarLabel extends React.Component{stageName}; + return stageName; + } + + public render() { + const { stage, execution, executionMarker } = this.props; + const { suspendedStageTypes } = stage; + const { getExecutionWindowTemplate, getWaitForConditionTemplate, DefaultLabel } = this; + if (executionMarker) { + const suspendedTypesNeedingHydration = ['restrictExecutionDuringTimeWindow', 'waitForCondition']; + const requiresHydration = + stage.labelComponent !== ExecutionBarLabel || + suspendedTypesNeedingHydration.some(s => suspendedStageTypes.has(s)); + let template: JSX.Element = null; + if (requiresHydration && !execution.hydrated) { + template = ; + } else if (suspendedStageTypes.has('restrictExecutionDuringTimeWindow')) { + template = getExecutionWindowTemplate(); + } else if (suspendedStageTypes.has('waitForCondition')) { + template = getWaitForConditionTemplate(); + } + if (template) { + return ( + + {this.props.children} + + ); + } else { + return ; + } + } + + return {this.getRenderableStageName()}; } } diff --git a/app/scripts/modules/core/src/pipeline/config/stages/concourse/concourseStage.ts b/app/scripts/modules/core/src/pipeline/config/stages/concourse/concourseStage.ts index 06dfb0c1da8..de00931294c 100644 --- a/app/scripts/modules/core/src/pipeline/config/stages/concourse/concourseStage.ts +++ b/app/scripts/modules/core/src/pipeline/config/stages/concourse/concourseStage.ts @@ -12,7 +12,6 @@ Registry.pipeline.registerStage({ }, component: ConcourseStageConfig, executionDetailsSections: [ConcourseExecutionDetails], - useCustomTooltip: true, strategy: true, validators: [ { type: 'requiredField', fieldName: 'master' }, diff --git a/app/scripts/modules/core/src/pipeline/config/stages/entityTags/applyEntityTagsStage.ts b/app/scripts/modules/core/src/pipeline/config/stages/entityTags/applyEntityTagsStage.ts index 89b7c8554d9..e2880dca0a4 100644 --- a/app/scripts/modules/core/src/pipeline/config/stages/entityTags/applyEntityTagsStage.ts +++ b/app/scripts/modules/core/src/pipeline/config/stages/entityTags/applyEntityTagsStage.ts @@ -13,7 +13,6 @@ Registry.pipeline.registerStage({ }, component: ApplyEntityTagsStageConfig, executionDetailsSections: [ApplyEntityTagsExecutionDetails, ExecutionDetailsTasks], - useCustomTooltip: true, strategy: true, validators: [{ type: 'requiredField', fieldName: 'entityRef.entityType' }], }); diff --git a/app/scripts/modules/core/src/pipeline/config/stages/evaluateVariables/evaluateVariablesStage.ts b/app/scripts/modules/core/src/pipeline/config/stages/evaluateVariables/evaluateVariablesStage.ts index c2d7eba4d29..1f090f79410 100644 --- a/app/scripts/modules/core/src/pipeline/config/stages/evaluateVariables/evaluateVariablesStage.ts +++ b/app/scripts/modules/core/src/pipeline/config/stages/evaluateVariables/evaluateVariablesStage.ts @@ -14,7 +14,6 @@ Registry.pipeline.registerStage({ }, component: EvaluateVariablesStageConfig, executionDetailsSections: [EvaluateVariablesExecutionDetails, ExecutionDetailsTasks], - useCustomTooltip: true, strategy: true, validators: [{ type: 'requiredField', fieldName: 'variables' }], }); diff --git a/app/scripts/modules/core/src/pipeline/executions/execution/ExecutionMarker.tsx b/app/scripts/modules/core/src/pipeline/executions/execution/ExecutionMarker.tsx index 64e30686fb8..ce321dc6b47 100644 --- a/app/scripts/modules/core/src/pipeline/executions/execution/ExecutionMarker.tsx +++ b/app/scripts/modules/core/src/pipeline/executions/execution/ExecutionMarker.tsx @@ -1,10 +1,7 @@ import * as React from 'react'; import * as ReactGA from 'react-ga'; -import { OverlayTrigger, Tooltip } from 'react-bootstrap'; import { IExecution, IExecutionStageSummary } from 'core/domain'; -import { ReactInjector } from 'core/reactShims'; -import { Spinner } from 'core/widgets'; import { OrchestratedItemRunningTime } from './OrchestratedItemRunningTime'; import { duration } from 'core/utils/timeFormatters'; @@ -30,7 +27,6 @@ export interface IExecutionMarkerState { export class ExecutionMarker extends React.Component { private runningTime: OrchestratedItemRunningTime; - private mounted = false; constructor(props: IExecutionMarkerProps) { super(props); @@ -43,17 +39,7 @@ export class ExecutionMarker extends React.Component { - const { execution, application } = this.props; - ReactInjector.executionService.hydrate(application, execution).then(() => { - if (this.mounted && !this.state.hydrated) { - this.setState({ hydrated: true }); - } - }); - }; - public componentDidMount() { - this.mounted = true; this.runningTime = new OrchestratedItemRunningTime(this.props.stage, (time: number) => this.setState({ duration: duration(time) }), ); @@ -64,7 +50,6 @@ export class ExecutionMarker extends React.Component @@ -97,32 +82,10 @@ export class ExecutionMarker extends React.Component ); - if (stage.useCustomTooltip) { - if (execution.hydrated) { - return ( - - {stageContents} - - ); - } else { - const loadingTooltip = ( - - - - ); - return ( - - - {stageContents} - - - ); - } - } return ( - + {stageContents} - + ); } } diff --git a/app/scripts/modules/core/src/pipeline/service/execution.service.ts b/app/scripts/modules/core/src/pipeline/service/execution.service.ts index ad8ed94849f..3315b7f0ef8 100644 --- a/app/scripts/modules/core/src/pipeline/service/execution.service.ts +++ b/app/scripts/modules/core/src/pipeline/service/execution.service.ts @@ -480,7 +480,7 @@ export class ExecutionService { * If this method is called multiple times, only the first call performs the fetch; * subsequent calls will return the promise produced by the first call. * - * This is a mutating operation. + * This is a mutating operation - it fills the context, outputs, and tasks on the stages of the unhydrated execution. * @param application the application owning the execution; needed because the stupid * transformExecution requires it. * @param unhydrated the execution to hydrate (which may already be hydrated) @@ -495,7 +495,18 @@ export class ExecutionService { } const executionHydrator = this.getExecution(unhydrated.id).then(hydrated => { this.transformExecution(application, hydrated); - Object.assign(unhydrated, hydrated); + unhydrated.stages.forEach((s, i) => { + // stages *should* be in the same order, so getting the hydrated one by index should be fine. + // worth verifying, though, and, if not, find the stage by id (which makes this an O(n^2) operation instead of O(n)) + const hydratedStage = + hydrated.stages[i].id === s.id ? hydrated.stages[i] : hydrated.stages.find(s2 => s.id === s2.id); + if (hydratedStage) { + s.context = hydratedStage.context; + s.outputs = hydratedStage.outputs; + s.tasks = hydratedStage.tasks; + } + }); + unhydrated.hydrated = true; unhydrated.graphStatusHash = this.calculateGraphStatusHash(unhydrated); return unhydrated; }); diff --git a/app/scripts/modules/core/src/presentation/HoverablePopover.tsx b/app/scripts/modules/core/src/presentation/HoverablePopover.tsx index 143cbaf4efc..55c84c20713 100644 --- a/app/scripts/modules/core/src/presentation/HoverablePopover.tsx +++ b/app/scripts/modules/core/src/presentation/HoverablePopover.tsx @@ -165,6 +165,7 @@ export class HoverablePopover extends React.Component