From 2416cea54d192958c544a5d8fcd35141d8c5970a Mon Sep 17 00:00:00 2001 From: Justin Reynolds Date: Fri, 20 Oct 2017 10:26:05 -0700 Subject: [PATCH] refactor(core/delivery): Convert waitExecutionDetails to react --- .../src/delivery/details/StageDetails.tsx | 46 ++++++++++--------- .../details/StageExecutionDetails.tsx | 43 +++++++++++++++++ .../delivery/details/StageExecutionLogs.tsx | 21 +++++++++ .../core/src/delivery/details/index.ts | 4 ++ .../core/src/domain/IExecutionStage.ts | 12 +++++ .../core/src/domain/IStageTypeConfig.ts | 3 +- .../stages/group/waitExecutionDetails.html | 24 ---------- .../stages/wait/WaitExecutionDetails.tsx | 35 ++++++++++++++ .../stages/wait/waitExecutionDetails.html | 24 ---------- .../pipeline/config/stages/wait/waitStage.js | 31 ------------- .../config/stages/wait/waitStage.module.js | 7 --- .../config/stages/wait/waitStage.module.ts | 8 ++++ .../pipeline/config/stages/wait/waitStage.ts | 32 +++++++++++++ .../core/src/pipeline/pipelines.module.js | 3 +- .../core/src/reactShims/react.injector.ts | 2 + 15 files changed, 186 insertions(+), 109 deletions(-) create mode 100644 app/scripts/modules/core/src/delivery/details/StageExecutionDetails.tsx create mode 100644 app/scripts/modules/core/src/delivery/details/StageExecutionLogs.tsx create mode 100644 app/scripts/modules/core/src/delivery/details/index.ts delete mode 100644 app/scripts/modules/core/src/pipeline/config/stages/group/waitExecutionDetails.html create mode 100644 app/scripts/modules/core/src/pipeline/config/stages/wait/WaitExecutionDetails.tsx delete mode 100644 app/scripts/modules/core/src/pipeline/config/stages/wait/waitExecutionDetails.html delete mode 100644 app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.js delete mode 100644 app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.module.js create mode 100644 app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.module.ts create mode 100644 app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.ts diff --git a/app/scripts/modules/core/src/delivery/details/StageDetails.tsx b/app/scripts/modules/core/src/delivery/details/StageDetails.tsx index ee66ba5d018..285a2ae0fd0 100644 --- a/app/scripts/modules/core/src/delivery/details/StageDetails.tsx +++ b/app/scripts/modules/core/src/delivery/details/StageDetails.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { BindAll } from 'lodash-decorators'; import { Application } from 'core/application'; -import { IExecution, IExecutionStage, IStageTypeConfig } from 'core/domain'; +import { IExecution, IExecutionDetailsComponentProps, IExecutionStage, IStageTypeConfig } from 'core/domain'; import { NgReact } from 'core/reactShims'; import { StatusGlyph } from 'core/task/StatusGlyph'; import { robotToHuman } from 'core/presentation/robotToHumanFilter/robotToHuman.filter'; @@ -15,8 +15,9 @@ export interface IStageDetailsProps { } export interface IStageDetailsState { - sourceUrl?: string; configSections?: string[]; + ReactComponent?: React.ComponentClass; + sourceUrl?: string; } @BindAll() @@ -28,18 +29,23 @@ export class StageDetails extends React.Component; const stageConfig = this.props.config; if (stageConfig) { if (stageConfig.executionConfigSections) { configSections = stageConfig.executionConfigSections; } - if (stageConfig.executionDetailsUrl) { - sourceUrl = stageConfig.executionDetailsUrl; + if (stageConfig.executionDetailsComponent) { + // React execution details + ReactComponent = stageConfig.executionDetailsComponent; + } else { + // Angular execution details + sourceUrl = stageConfig.executionDetailsUrl || require('./defaultExecutionDetails.html'); } } - return { configSections, sourceUrl }; + return { configSections, ReactComponent, sourceUrl }; } public componentWillReceiveProps() { @@ -48,23 +54,21 @@ export class StageDetails extends React.Component { const { application, execution, stage } = this.props; - const { sourceUrl, configSections } = this.state; - + const { ReactComponent, sourceUrl, configSections } = this.state; const { StageDetailsWrapper } = NgReact; + const detailsProps = { application, execution, stage, configSections }; - if ( sourceUrl ) { - return ( -
-
-
- - {robotToHuman(stage.name || stage.type)} -
-
- + return ( +
+
+
+ + {robotToHuman(stage.name || stage.type)} +
- ); - } - return null; + {sourceUrl && } + {ReactComponent && } +
+ ); } } diff --git a/app/scripts/modules/core/src/delivery/details/StageExecutionDetails.tsx b/app/scripts/modules/core/src/delivery/details/StageExecutionDetails.tsx new file mode 100644 index 00000000000..b804353861e --- /dev/null +++ b/app/scripts/modules/core/src/delivery/details/StageExecutionDetails.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { isEqual } from 'lodash'; + +import { IExecutionDetailsComponentProps, IExecutionDetailsComponentState } from 'core/domain'; +import { ReactInjector } from 'core/reactShims'; + +export function stageExecutionDetails(WrappedStageExecutionDetails: React.ComponentClass): React.ComponentClass { + return class extends React.Component { + constructor(props: IExecutionDetailsComponentProps) { + super(props); + this.state = { + detailsSection: null, + } + } + + public updateDetailsSection(): void { + const detailsSection = ReactInjector.$stateParams.details; + if (this.state.detailsSection !== detailsSection) { + this.setState({detailsSection}); + } + } + + public syncDetails(props: IExecutionDetailsComponentProps): void { + ReactInjector.executionDetailsSectionService.synchronizeSection(props.configSections, () => this.updateDetailsSection()); + } + + public componentDidMount(): void { + this.syncDetails(this.props); + } + + public componentWillReceiveProps(nextProps: IExecutionDetailsComponentProps): void { + if (!isEqual(nextProps.configSections, this.props.configSections)) { + this.syncDetails(nextProps); + } + } + + public render() { + return ( + + ); + } + } +} diff --git a/app/scripts/modules/core/src/delivery/details/StageExecutionLogs.tsx b/app/scripts/modules/core/src/delivery/details/StageExecutionLogs.tsx new file mode 100644 index 00000000000..87b48bfe489 --- /dev/null +++ b/app/scripts/modules/core/src/delivery/details/StageExecutionLogs.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { get } from 'lodash'; + +import { IStage } from 'core/domain'; + +export const StageExecutionLogs = (props: { stage: IStage }): JSX.Element => { + const logs = get(props.stage, 'context.execution.logs'); + if (!logs) { return null; } + + return ( + + ); +} diff --git a/app/scripts/modules/core/src/delivery/details/index.ts b/app/scripts/modules/core/src/delivery/details/index.ts new file mode 100644 index 00000000000..e189b3392cc --- /dev/null +++ b/app/scripts/modules/core/src/delivery/details/index.ts @@ -0,0 +1,4 @@ +export * from './ExecutionDetailsSectionNav'; +export * from './StageExecutionDetails'; +export * from './StageExecutionLogs'; +export * from './StageFailureMessage'; diff --git a/app/scripts/modules/core/src/domain/IExecutionStage.ts b/app/scripts/modules/core/src/domain/IExecutionStage.ts index c286c621084..904c3c016e0 100644 --- a/app/scripts/modules/core/src/domain/IExecutionStage.ts +++ b/app/scripts/modules/core/src/domain/IExecutionStage.ts @@ -34,6 +34,18 @@ export interface IExecutionStageLabelComponentProps { stage: IExecutionStageSummary; } +export interface IExecutionDetailsComponentProps { + application: Application; + configSections: string[]; + detailsSection?: string; + execution: IExecution; + stage: IExecutionStage; +} + +export interface IExecutionDetailsComponentState { + detailsSection: string; +} + export interface IExecutionStageSummary extends IOrchestratedItem { activeStageType?: string, after: IExecutionStage[]; diff --git a/app/scripts/modules/core/src/domain/IStageTypeConfig.ts b/app/scripts/modules/core/src/domain/IStageTypeConfig.ts index a52e7b93d3d..a94cfd8f807 100644 --- a/app/scripts/modules/core/src/domain/IStageTypeConfig.ts +++ b/app/scripts/modules/core/src/domain/IStageTypeConfig.ts @@ -1,5 +1,5 @@ import { IStage } from './IStage'; -import { IExecutionStageLabelComponentProps, IExecutionStageSummary } from './IExecutionStage'; +import { IExecutionDetailsComponentProps, IExecutionStageLabelComponentProps, IExecutionStageSummary } from './IExecutionStage'; import { IStageOrTriggerTypeConfig } from './IStageOrTriggerTypeConfig'; export interface IStageTypeConfig extends IStageOrTriggerTypeConfig { @@ -12,6 +12,7 @@ export interface IStageTypeConfig extends IStageOrTriggerTypeConfig { configuration?: any; defaultTimeoutMs?: number; executionConfigSections?: string[]; + executionDetailsComponent?: React.ComponentClass; executionDetailsUrl?: string; executionLabelComponent?: React.ComponentClass; executionStepLabelUrl?: string; diff --git a/app/scripts/modules/core/src/pipeline/config/stages/group/waitExecutionDetails.html b/app/scripts/modules/core/src/pipeline/config/stages/group/waitExecutionDetails.html deleted file mode 100644 index c46da209a42..00000000000 --- a/app/scripts/modules/core/src/pipeline/config/stages/group/waitExecutionDetails.html +++ /dev/null @@ -1,24 +0,0 @@ -
- -
- - - - -
- -
-
- -
-
-
diff --git a/app/scripts/modules/core/src/pipeline/config/stages/wait/WaitExecutionDetails.tsx b/app/scripts/modules/core/src/pipeline/config/stages/wait/WaitExecutionDetails.tsx new file mode 100644 index 00000000000..f4c0460f07c --- /dev/null +++ b/app/scripts/modules/core/src/pipeline/config/stages/wait/WaitExecutionDetails.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; + +import { IExecutionDetailsComponentProps } from 'core/domain'; +import { ExecutionDetailsSectionNav, StageExecutionLogs, StageFailureMessage } from 'core/delivery/details'; +import { ExecutionStepDetails } from 'core/pipeline/config/stages/core/ExecutionStepDetails'; +import { SkipWait } from './SkipWait'; +import { stageExecutionDetails } from 'core/delivery/details'; + +class ExecutionDetails extends React.Component { + public render() { + const { application, configSections, detailsSection, execution, stage } = this.props; + return ( +
+ + {detailsSection === 'waitConfig' && ( +
+ + + +
+ )} + + {detailsSection === 'taskStatus' && ( +
+
+ +
+
+ )} +
+ ); + } +} + +export const WaitExecutionDetails = stageExecutionDetails(ExecutionDetails); diff --git a/app/scripts/modules/core/src/pipeline/config/stages/wait/waitExecutionDetails.html b/app/scripts/modules/core/src/pipeline/config/stages/wait/waitExecutionDetails.html deleted file mode 100644 index c46da209a42..00000000000 --- a/app/scripts/modules/core/src/pipeline/config/stages/wait/waitExecutionDetails.html +++ /dev/null @@ -1,24 +0,0 @@ -
- -
- - - - -
- -
-
- -
-
-
diff --git a/app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.js b/app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.js deleted file mode 100644 index 10ccadf60a3..00000000000 --- a/app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -const angular = require('angular'); - -import {SKIP_WAIT_COMPONENT} from './skipWait.component'; -import {WaitExecutionLabel} from './WaitExecutionLabel'; - -module.exports = angular.module('spinnaker.core.pipeline.stage.waitStage', [ - SKIP_WAIT_COMPONENT, -]) - .config(function(pipelineConfigProvider) { - pipelineConfigProvider.registerStage({ - label: 'Wait', - description: 'Waits a specified period of time', - key: 'wait', - templateUrl: require('./waitStage.html'), - executionDetailsUrl: require('./waitExecutionDetails.html'), - executionConfigSections: ['waitConfig', 'taskStatus'], - executionLabelComponent: WaitExecutionLabel, - useCustomTooltip: true, - strategy: true, - controller: 'WaitStageCtrl', - validators: [ - { type: 'requiredField', fieldName: 'waitTime' }, - ], - }); - }).controller('WaitStageCtrl', function ($scope, stage) { - if (!stage.waitTime) { - stage.waitTime = 30; - } - }); diff --git a/app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.module.js b/app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.module.js deleted file mode 100644 index a288fba0a32..00000000000 --- a/app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.module.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -const angular = require('angular'); - -module.exports = angular.module('spinnaker.core.pipeline.stage.wait', [ - require('./waitStage.js').name, -]); diff --git a/app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.module.ts b/app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.module.ts new file mode 100644 index 00000000000..f5440dbb6a5 --- /dev/null +++ b/app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.module.ts @@ -0,0 +1,8 @@ +import { module } from 'angular'; + +import { WAIT_STAGE } from './waitStage'; + +export const WAIT_STAGE_MODULE = 'spinnaker.core.pipeline.stage.wait'; +module(WAIT_STAGE_MODULE, [ + WAIT_STAGE, +]); diff --git a/app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.ts b/app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.ts new file mode 100644 index 00000000000..9b118bad7d2 --- /dev/null +++ b/app/scripts/modules/core/src/pipeline/config/stages/wait/waitStage.ts @@ -0,0 +1,32 @@ +import { module } from 'angular'; + +import { IStage } from 'core/domain'; +import { PIPELINE_CONFIG_PROVIDER, PipelineConfigProvider } from 'core/pipeline/config/pipelineConfigProvider'; + +import { SKIP_WAIT_COMPONENT } from './skipWait.component'; +import { WaitExecutionDetails } from './WaitExecutionDetails'; +import { WaitExecutionLabel } from './WaitExecutionLabel'; + +export const WAIT_STAGE = 'spinnaker.core.pipeline.stage.waitStage'; + +module(WAIT_STAGE, [ + PIPELINE_CONFIG_PROVIDER, + SKIP_WAIT_COMPONENT, +]) + .config((pipelineConfigProvider: PipelineConfigProvider) => { + pipelineConfigProvider.registerStage({ + label: 'Wait', + description: 'Waits a specified period of time', + key: 'wait', + templateUrl: require('./waitStage.html'), + executionDetailsComponent: WaitExecutionDetails, + executionConfigSections: ['waitConfig', 'taskStatus'], + executionLabelComponent: WaitExecutionLabel, + useCustomTooltip: true, + strategy: true, + controller: 'WaitStageCtrl', + validators: [ + { type: 'requiredField', fieldName: 'waitTime' }, + ], + }); + }).controller('WaitStageCtrl', (stage: IStage) => stage.waitTime = stage.waitTime || 30); diff --git a/app/scripts/modules/core/src/pipeline/pipelines.module.js b/app/scripts/modules/core/src/pipeline/pipelines.module.js index 48c0b5f84fc..710bd940017 100644 --- a/app/scripts/modules/core/src/pipeline/pipelines.module.js +++ b/app/scripts/modules/core/src/pipeline/pipelines.module.js @@ -14,6 +14,7 @@ import { GROUP_STAGE_MODULE } from './config/stages/group/groupStage.module'; import { STAGE_CORE_MODULE } from './config/stages/core/stage.core.module'; import { TRAVIS_STAGE_MODULE } from './config/stages/travis/travisStage.module'; import { UNMATCHED_STAGE_TYPE_STAGE } from './config/stages/unmatchedStageTypeStage/unmatchedStageTypeStage'; +import { WAIT_STAGE_MODULE } from './config/stages/wait/waitStage.module'; import { WEBHOOK_STAGE_MODULE } from './config/stages/webhook/webhookStage.module'; import './pipelines.less'; @@ -48,7 +49,7 @@ module.exports = angular.module('spinnaker.core.pipeline', [ require('./config/stages/scaleDownCluster/scaleDownClusterStage.module').name, require('./config/stages/script/scriptStage.module').name, require('./config/stages/shrinkCluster/shrinkClusterStage.module').name, - require('./config/stages/wait/waitStage.module').name, + WAIT_STAGE_MODULE, require('./config/stages/waitForParentTasks/waitForParentTasks').name, CREATE_LOAD_BALANCER_STAGE, APPLY_SOURCE_SERVER_GROUP_CAPACITY_STAGE, diff --git a/app/scripts/modules/core/src/reactShims/react.injector.ts b/app/scripts/modules/core/src/reactShims/react.injector.ts index 5b3b5122249..d29a5e1c6ce 100644 --- a/app/scripts/modules/core/src/reactShims/react.injector.ts +++ b/app/scripts/modules/core/src/reactShims/react.injector.ts @@ -16,6 +16,7 @@ import { ClusterFilterService } from '../cluster/filter/clusterFilter.service'; import { CollapsibleSectionStateCache } from '../cache/collapsibleSectionStateCache'; import { ConfirmationModalService } from '../confirmationModal/confirmationModal.service'; import { EntityTagWriter} from '../entityTag'; +import { ExecutionDetailsSectionService } from 'core/delivery/details/executionDetailsSection.service'; import { ExecutionFilterModel } from '../delivery/filter/executionFilter.model'; import { ExecutionFilterService } from '../delivery/filter/executionFilter.service'; import { ExecutionService } from '../delivery/service/execution.service'; @@ -80,6 +81,7 @@ export class CoreReactInject extends ReactInject { public get collapsibleSectionStateCache() { return this.$injector.get('collapsibleSectionStateCache') as CollapsibleSectionStateCache; } public get confirmationModalService() { return this.$injector.get('confirmationModalService') as ConfirmationModalService; } public get entityTagWriter() { return this.$injector.get('entityTagWriter') as EntityTagWriter; } + public get executionDetailsSectionService() { return this.$injector.get('executionDetailsSectionService') as ExecutionDetailsSectionService; } public get executionFilterModel() { return this.$injector.get('executionFilterModel') as ExecutionFilterModel; } public get executionFilterService() { return this.$injector.get('executionFilterService') as ExecutionFilterService; } public get executionService() { return this.$injector.get('executionService') as ExecutionService; }