Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[WiP] [FIX JENKINS-40841] - agent editing for pipelines (#15)
* JENKINS-40841 - agent editing for pipelines
* Update to declarative 0.8.2, update agent handling
  • Loading branch information
kzantow committed Jan 13, 2017
1 parent 5120fa0 commit ebb5bd5
Show file tree
Hide file tree
Showing 22 changed files with 477 additions and 556 deletions.
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -38,7 +38,7 @@
},
"dependencies": {
"@jenkins-cd/blueocean-core-js": "0.0.21",
"@jenkins-cd/design-language": "0.0.89-beta1",
"@jenkins-cd/design-language": "0.0.99",
"@jenkins-cd/js-extensions": "0.0.25",
"@jenkins-cd/js-modules": "0.0.8",
"lodash.debounce": "4.0.8",
Expand Down
21 changes: 11 additions & 10 deletions pom.xml
Expand Up @@ -15,6 +15,16 @@
<version>1.0-alpha-1-SNAPSHOT</version>
<packaging>hpi</packaging>

<properties>
<java.level>7</java.level>
<jackson.version>2.2.3</jackson.version>
<jenkins.version>2.7.1</jenkins.version>
<node.version>5.8.0</node.version>
<npm.version>3.7.3</npm.version>
<blueocean.version>1.0.0-b16</blueocean.version>
<declarative.version>0.8.2</declarative.version>
</properties>

<url>https://wiki.jenkins-ci.org/display/JENKINS/BlueOcean+Pipeline+Editor+Plugin</url>

<licenses>
Expand Down Expand Up @@ -46,16 +56,6 @@
</developer>
</developers>

<properties>
<java.level>7</java.level>
<jackson.version>2.2.3</jackson.version>
<jenkins.version>2.7.1</jenkins.version>
<node.version>5.8.0</node.version>
<npm.version>3.7.3</npm.version>
<blueocean.version>1.0.0-b13-SNAPSHOT</blueocean.version>
<declarative.version>0.8-SNAPSHOT</declarative.version>
</properties>

<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
Expand Down Expand Up @@ -92,6 +92,7 @@
<version>2.23</version>
</dependency>

<!-- test deps for hpi:run -->
<dependency>
<groupId>io.jenkins.blueocean</groupId>
<artifactId>blueocean</artifactId>
Expand Down
Expand Up @@ -36,9 +36,15 @@
@ExportedBean
public class ExportedDescribableModel {
protected final DescribableModel<?> model;
protected final String symbol;

public ExportedDescribableModel(DescribableModel<?> model) {
this(model, null);
}

public ExportedDescribableModel(DescribableModel<?> model, String symbol) {
this.model = model;
this.symbol = symbol;
}

/**
Expand All @@ -49,6 +55,15 @@ public ExportedDescribableModel(DescribableModel<?> model) {
public String getType() {
return model.getType().getName();
}

/**
* Provides the symbol for this describable
* @return
*/
@Exported
public String getSymbol() {
return symbol;
}

/**
* Display Name of the describable class
Expand Down
Expand Up @@ -68,7 +68,7 @@ public ExportedDescribableModel[] doAgentMetadata() {
try {
DescribableModel<? extends DeclarativeAgent> model = new DescribableModel<>(d.clazz);

models.add(new ExportedDescribableModel(model));
models.add(new ExportedDescribableModel(model, symbolForObject(d)));
} catch (NoStaplerConstructorException e) {
// Ignore!
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/js/components/editor/AddStepSelectionDialog.jsx
@@ -1,7 +1,7 @@
// @flow

import React, { Component, PropTypes } from 'react';
import pipelineStepListStore from '../../services/PipelineStepListStore';
import pipelineMetadataService from '../../services/PipelineMetadataService';
import { Dialog } from '@jenkins-cd/design-language';
import { Icon } from "react-material-icons-blue";
import debounce from 'lodash.debounce';
Expand Down Expand Up @@ -38,7 +38,7 @@ export class AddStepSelectionDialog extends Component<DefaultProps, Props, State
}

componentWillMount() {
pipelineStepListStore.getStepListing(data => {
pipelineMetadataService.getStepListing(data => {
this.setState({steps: data});
});
}
Expand Down
134 changes: 134 additions & 0 deletions src/main/js/components/editor/AgentConfiguration.jsx
@@ -0,0 +1,134 @@
// @flow

import React, { Component, PropTypes } from 'react';
import type { PipelineAgent } from '../../services/PipelineSyntaxConverter';
import pipelineMetadataService from '../../services/PipelineMetadataService';
import type { PipelineInfo, StageInfo } from '../../services/PipelineStore';
import { Dropdown } from '@jenkins-cd/design-language';
import { Split } from './Split';

type Props = {
node: PipelineInfo|StageInfo,
onChange: (agent: PipelineAgent) => any,
};

type State = {
agents: ?any,
selectedAgent: PipelineAgent,
};

type DefaultProps = typeof AgentConfiguration.defaultProps;

export class AgentConfiguration extends Component<DefaultProps, Props, State> {
props:Props;
state:State;

constructor(props:Props) {
super(props);
this.state = { agents: null, selectedAgent: props.node.agent };
}

componentWillMount() {
pipelineMetadataService.getAgentListing(data => {
this.setState({agents: data});
});
}

componentDidMount() {
}

componentWillReceiveProps(nextProps: Props) {
this.setState({selectedAgent: nextProps.node.agent});
}

getRealOrEmptyArg(key: string) {
const { selectedAgent } = this.state;
if (selectedAgent.arguments) {
for (const arg of selectedAgent.arguments) {
if (arg.key === key) {
return arg;
}
}
}
return {
key: key,
value: {
isLiteral: true,
value: '',
}
};
return val;
}

setAgentValue(key: string, value: string) {
const { selectedAgent } = this.state;
const val = this.getRealOrEmptyArg(key);
const idx = selectedAgent.arguments.indexOf(val);

// remove any existing values
if (idx !== -1) {
selectedAgent.arguments.splice(idx, 1);
}
// add the value if not empty
if (value) {
selectedAgent.arguments.push({
key: key,
value: {
isLiteral: true,
value: value,
},
});
}
this.props.onChange(selectedAgent);
}

onAgentChanged(agent: any) {
console.log('agent changed', agent);
const selectedAgent = {
type: agent.symbol, // agent is metadata
arguments: [],
};
this.setState({selectedAgent: selectedAgent});
this.props.onChange(selectedAgent);
}

render() {
const { node } = this.props;
const { agents, selectedAgent } = this.state;

if (!agents) {
return null;
}

// find the parameter matching the symbol to determine which agent is selected
let selectedAgentMetadata;
if (selectedAgent) {
for (const agent of agents) {
if (selectedAgent.type === agent.symbol) {
selectedAgentMetadata = agent;
break;
}
}
}

return (<div className="agent-select">
<h4>Agent Configuration</h4>
<Dropdown labelField="symbol" options={agents}
defaultOption={selectedAgentMetadata}
onChange={agent => this.onAgentChanged(agent)} />
<Split>
{selectedAgent && selectedAgentMetadata && <div className="agent-configuration">
{selectedAgentMetadata.parameters.map(param => <div className="agent-param">
<label key={selectedAgent.type + '/' + param.name}>
<div>{param.capitalizedName}</div>
<div>
<input defaultValue={this.getRealOrEmptyArg(param.name).value.value}
onChange={e => this.setAgentValue(param.name, e.target.value)}/>
</div>
</label>
</div>)}
</div>}
</Split>
</div>);
}
}
33 changes: 31 additions & 2 deletions src/main/js/components/editor/EditorMain.jsx
Expand Up @@ -4,6 +4,8 @@ import React, { Component, PropTypes } from 'react';
import { EditorPipelineGraph } from './EditorPipelineGraph';
import { EditorStepList } from './EditorStepList';
import { EditorStepDetails } from './EditorStepDetails';
import { AgentConfiguration } from './AgentConfiguration';
import { EnvironmentConfiguration } from './EnvironmentConfiguration';
import { EmptyStateView } from '@jenkins-cd/design-language';
import { AddStepSelectionDialog } from './AddStepSelectionDialog';
import pipelineStore from '../../services/PipelineStore';
Expand Down Expand Up @@ -44,7 +46,11 @@ export class EditorMain extends Component<DefaultProps, Props, State> {
}

doUpdate() {
this.forceUpdate();
if (!pipelineStore.findParentStage(pipelineStore.pipeline, this.state.selectedStage)) {
this.setState({selectedStage: null});
} else {
this.forceUpdate();
}
}

componentWillReceiveProps(nextProps:Props) {
Expand Down Expand Up @@ -242,8 +248,30 @@ export class EditorMain extends Component<DefaultProps, Props, State> {
</div>
) : null;

// FIXME - agents are defined at the top stage level, this will change
let configurationStage = selectedStage && (pipelineStore.findParentStage(selectedStage) || selectedStage);
if (pipelineStore.pipeline === configurationStage) {
configurationStage = selectedStage;
}
if (!selectedStage) {
configurationStage = pipelineStore.pipeline;
}

const configPanel = pipelineStore.pipeline && (<div className="editor-config-panel" key={selectedStage?selectedStage.id:0}>
<div>
<h4 className="stage-name-edit">
{selectedStage &&
<input defaultValue={title} onChange={e => (selectedStage.name = e.target.value) && this.pipelineUpdated()} />
}
{!selectedStage && 'Pipeline Configuration'}
</h4>
<AgentConfiguration key={'agent'+configurationStage.id} node={configurationStage} onChange={agent => (selectedStage && agent.type == 'none' ? delete configurationStage.agent : configurationStage.agent = agent) && this.pipelineUpdated()} />
<EnvironmentConfiguration key={'env'+configurationStage.id} node={configurationStage} onChange={e => this.pipelineUpdated()} />
</div>
</div>);

return (
<div className="editor-main">
<div className="editor-main" key={pipelineStore.pipeline && pipelineStore.pipeline.id}>
<div className="editor-main-graph">
{pipelineStore.pipeline &&
<EditorPipelineGraph stages={pipelineStore.pipeline.children}
Expand All @@ -253,6 +281,7 @@ export class EditorMain extends Component<DefaultProps, Props, State> {
onCreateParallelStage={(name, parentStage) => this.createParallelStage(name, parentStage)}/>
}
</div>
{configPanel}
{titleBar}
{detailsOrPlaceholder}
{this.state.showSelectStep && <AddStepSelectionDialog
Expand Down
8 changes: 4 additions & 4 deletions src/main/js/components/editor/EditorPage.jsx
Expand Up @@ -6,7 +6,7 @@ import pipelineStore from '../../services/PipelineStore';
import { convertInternalModelToJson, convertJsonToPipeline, convertPipelineToJson, convertJsonToInternalModel } from '../../services/PipelineSyntaxConverter';
import type { PipelineInfo } from '../../services/PipelineStore';
import type { PipelineJsonContainer } from '../../services/PipelineSyntaxConverter';
import pipelineStepListStore from '../../services/PipelineStepListStore';
import pipelineMetadataService from '../../services/PipelineMetadataService';

const PIPELINE_KEY = 'jenkins.pipeline.editor.workingCopy';

Expand Down Expand Up @@ -46,7 +46,7 @@ export class EditorPage extends Component<DefaultProps, Props, State> {
componentWillMount() {
let existingPipeline: any = localStorage.getItem(PIPELINE_KEY);
if (existingPipeline) {
pipelineStepListStore.getStepListing(steps => {
pipelineMetadataService.getStepListing(steps => {
existingPipeline = convertJsonToInternalModel(JSON.parse(existingPipeline));
pipelineStore.setPipeline(existingPipeline);
});
Expand Down Expand Up @@ -109,7 +109,7 @@ export class EditorPage extends Component<DefaultProps, Props, State> {
if (!err) {
this.setState({showPipelineScript: true, pipelineErrors: null, pipelineScript: result});
} else {
this.setState({showPipelineScript: true, pipelineErrors: err});
this.setState({showPipelineScript: true, pipelineErrors: err, pipelineScript: ''});
}
});
} else {
Expand Down Expand Up @@ -145,7 +145,7 @@ export class EditorPage extends Component<DefaultProps, Props, State> {
buttons={<div><button onClick={e => this.updateStateFromPipelineScript(this.refs.pipelineScript.value)}>Update</button></div>}>
{this.state.pipelineErrors &&
<div className="errors">
{this.state.pipelineErrors.map(err => <div className="error">{err}</div>)}
{this.state.pipelineErrors.map(err => <div className="error">{err.location && err.location.join('/')} {err.error}</div>)}
</div>
}
<div className="editor-text-area">
Expand Down
4 changes: 2 additions & 2 deletions src/main/js/components/editor/EditorStepDetails.jsx
@@ -1,7 +1,7 @@
// @flow

import React, {Component, PropTypes} from 'react';
import pipelineStepListStore from '../../services/PipelineStepListStore';
import pipelineMetadataService from '../../services/PipelineMetadataService';
import type {StepInfo} from './common';
import GenericStepEditor from './steps/GenericStepEditor';
import UnknownStepEditor from './steps/UnknownStepEditor';
Expand Down Expand Up @@ -38,7 +38,7 @@ export class EditorStepDetails extends Component {
}

componentWillMount() {
pipelineStepListStore.getStepListing(stepMetadata => {
pipelineMetadataService.getStepListing(stepMetadata => {
this.setState({stepMetadata: stepMetadata});
});
}
Expand Down

0 comments on commit ebb5bd5

Please sign in to comment.