Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(core/delivery): Convert waitExecutionDetails to react #4297

Merged
merged 4 commits into from
Oct 21, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 25 additions & 21 deletions app/scripts/modules/core/src/delivery/details/StageDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -15,8 +15,9 @@ export interface IStageDetailsProps {
}

export interface IStageDetailsState {
sourceUrl?: string;
configSections?: string[];
ReactComponent?: React.ComponentClass<IExecutionDetailsComponentProps>;
sourceUrl?: string;
}

@BindAll()
Expand All @@ -28,18 +29,23 @@ export class StageDetails extends React.Component<IStageDetailsProps, IStageDeta

private getState(): IStageDetailsState {
let configSections: string[] = [];
let sourceUrl = require('./defaultExecutionDetails.html');
let sourceUrl: string;
let ReactComponent: React.ComponentClass<IExecutionDetailsComponentProps>;

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() {
Expand All @@ -48,23 +54,21 @@ export class StageDetails extends React.Component<IStageDetailsProps, IStageDeta

public render(): React.ReactElement<StageDetails> {
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 (
<div className="stage-details">
<div className="stage-details-heading">
<h5>
<StatusGlyph item={stage} />
{robotToHuman(stage.name || stage.type)}
</h5>
</div>
<StageDetailsWrapper application={application} execution={execution} sourceUrl={sourceUrl} configSections={configSections} stage={stage} />
return (
<div className="stage-details">
<div className="stage-details-heading">
<h5>
<StatusGlyph item={stage} />
{robotToHuman(stage.name || stage.type)}
</h5>
</div>
);
}
return null;
{sourceUrl && <StageDetailsWrapper {...detailsProps} sourceUrl={sourceUrl} />}
{ReactComponent && <ReactComponent {...detailsProps} />}
</div>
);
}
}
Original file line number Diff line number Diff line change
@@ -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<IExecutionDetailsComponentProps>): React.ComponentClass<IExecutionDetailsComponentProps> {
return class extends React.Component<IExecutionDetailsComponentProps, IExecutionDetailsComponentState> {
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 (
<WrappedStageExecutionDetails detailsSection={this.state.detailsSection} {...this.props} />
);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<string>(props.stage, 'context.execution.logs');
if (!logs) { return null; }

return (
<div className="row">
<div className="col-md-12">
<div className="well alert alert-info">
<a target="_blank" href={logs}>
View Execution Logs
</a>
</div>
</div>
</div>
);
}
4 changes: 4 additions & 0 deletions app/scripts/modules/core/src/delivery/details/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './ExecutionDetailsSectionNav';
export * from './StageExecutionDetails';
export * from './StageExecutionLogs';
export * from './StageFailureMessage';
12 changes: 12 additions & 0 deletions app/scripts/modules/core/src/domain/IExecutionStage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down
3 changes: 2 additions & 1 deletion app/scripts/modules/core/src/domain/IStageTypeConfig.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -12,6 +12,7 @@ export interface IStageTypeConfig extends IStageOrTriggerTypeConfig {
configuration?: any;
defaultTimeoutMs?: number;
executionConfigSections?: string[];
executionDetailsComponent?: React.ComponentClass<IExecutionDetailsComponentProps>;
executionDetailsUrl?: string;
executionLabelComponent?: React.ComponentClass<IExecutionStageLabelComponentProps>;
executionStepLabelUrl?: string;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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<IExecutionDetailsComponentProps> {
public render() {
const { application, configSections, detailsSection, execution, stage } = this.props;
return (
<div>
<ExecutionDetailsSectionNav sections={configSections} />
{detailsSection === 'waitConfig' && (
<div className="step-section-details">
<SkipWait application={application} execution={execution} stage={stage} />
<StageFailureMessage stage={stage} message={stage.failureMessage} />
<StageExecutionLogs stage={stage} />
</div>
)}

{detailsSection === 'taskStatus' && (
<div className="step-section-details">
<div className="row">
<ExecutionStepDetails item={stage} />
</div>
</div>
)}
</div>
);
}
}

export const WaitExecutionDetails = stageExecutionDetails(ExecutionDetails);

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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,
]);
Original file line number Diff line number Diff line change
@@ -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);
3 changes: 2 additions & 1 deletion app/scripts/modules/core/src/pipeline/pipelines.module.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions app/scripts/modules/core/src/reactShims/react.injector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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; }
Expand Down