Merge pull request #293 from jenkinsci/bug/JENKINS-36207

JENKINS-36207# Make BluePipeline extensible
This commit is contained in:
vivek 2016-06-23 22:47:52 -07:00 committed by GitHub
commit 4fc6519c53
9 changed files with 181 additions and 22 deletions

View File

@ -16,9 +16,7 @@ import io.jenkins.blueocean.rest.model.GenericResource;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
@ -176,15 +174,8 @@ public class AbstractRunImpl<T extends Run> extends BlueRun {
return null;
}
public Collection<?> getActions() {
List<BlueActionProxy> actionProxies = new ArrayList<>();
for(Action action:run.getAllActions()){
if(action == null || !action.getClass().isAnnotationPresent(ExportedBean.class)){
continue;
}
actionProxies.add(new ActionProxiesImpl(action, this));
}
return actionProxies;
public Collection<BlueActionProxy> getActions() {
return PipelineImpl.getActionProxies(run.getAllActions(), this);
}
protected static BlueRun getBlueRun(Run r, Link parent){

View File

@ -1,14 +1,19 @@
package io.jenkins.blueocean.service.embedded.rest;
import hudson.Extension;
import hudson.model.Item;
import hudson.model.Job;
import hudson.model.Result;
import hudson.model.Run;
import io.jenkins.blueocean.commons.ServiceException;
import io.jenkins.blueocean.rest.Navigable;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueActionProxy;
import io.jenkins.blueocean.rest.model.BlueMultiBranchPipeline;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BluePipelineContainer;
import io.jenkins.blueocean.rest.model.BluePipelineFactory;
import io.jenkins.blueocean.rest.model.BlueQueueItem;
import io.jenkins.blueocean.rest.model.BlueRun;
import io.jenkins.blueocean.rest.model.BlueRunContainer;
@ -252,8 +257,25 @@ public class MultiBranchPipelineImpl extends BlueMultiBranchPipeline {
};
}
@Override
public Collection<BlueActionProxy> getActions() {
return PipelineImpl.getActionProxies(mbp.getAllActions(), this);
}
@Override
public Link getLink() {
return self;
}
@Extension(ordinal = 2)
public static class PipelineFactoryImpl extends BluePipelineFactory{
@Override
public BluePipeline getPipeline(Item item, Reachable parent) {
if (item instanceof MultiBranchProject) {
return new MultiBranchPipelineImpl((MultiBranchProject) item, parent.getLink());
}
return null;
}
}
}

View File

@ -8,6 +8,7 @@ import io.jenkins.blueocean.commons.ServiceException;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BluePipelineContainer;
import io.jenkins.blueocean.rest.model.BluePipelineFactory;
import jenkins.branch.MultiBranchProject;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
@ -49,16 +50,12 @@ public class PipelineContainerImpl extends BluePipelineContainer {
}
public BluePipeline get(Item item){
if (item instanceof BuildableItem) {
if (item instanceof MultiBranchProject) {
return new MultiBranchPipelineImpl((MultiBranchProject) item, getLink());
} else if (item instanceof Job) {
return new PipelineImpl((Job) item, getLink());
for(BluePipelineFactory factory:BluePipelineFactory.all()){
BluePipeline pipeline = factory.getPipeline(item, this);
if( pipeline!= null){
return pipeline;
}
} else if (item instanceof ItemGroup) {
return new PipelineFolderImpl((ItemGroup) item, getLink());
}
// TODO: I'm going to turn this into a decorator annotation
throw new ServiceException.NotFoundException(String.format("Pipeline %s not found", item.getName()));
}

View File

@ -1,14 +1,22 @@
package io.jenkins.blueocean.service.embedded.rest;
import hudson.Extension;
import hudson.model.Item;
import hudson.model.ItemGroup;
import io.jenkins.blueocean.commons.ServiceException;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueActionProxy;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BluePipelineContainer;
import io.jenkins.blueocean.rest.model.BluePipelineFactory;
import io.jenkins.blueocean.rest.model.BluePipelineFolder;
import io.jenkins.blueocean.service.embedded.util.FavoriteUtil;
import org.kohsuke.stapler.json.JsonBody;
import java.util.Collection;
import java.util.Collections;
import static io.jenkins.blueocean.service.embedded.rest.PipelineImpl.getRecursivePathFromFullName;
/**
@ -44,6 +52,11 @@ public class PipelineFolderImpl extends BluePipelineFolder {
return folder.getFullName();
}
@Override
public Collection<BlueActionProxy> getActions() {
return Collections.emptyList();
}
@Override
public BluePipelineContainer getPipelines() {
return new PipelineContainerImpl(folder);
@ -86,4 +99,15 @@ public class PipelineFolderImpl extends BluePipelineFolder {
return OrganizationImpl.INSTANCE.getLink().rel("pipelines").rel(getRecursivePathFromFullName(this));
}
@Extension(ordinal = 0)
public static class PipelineFactoryImpl extends BluePipelineFactory{
@Override
public BluePipeline getPipeline(Item item, Reachable parent) {
if (item instanceof ItemGroup) {
return new PipelineFolderImpl((ItemGroup) item, parent.getLink());
}
return null;
}
}
}

View File

@ -1,13 +1,18 @@
package io.jenkins.blueocean.service.embedded.rest;
import hudson.Extension;
import hudson.model.Action;
import hudson.model.BuildableItem;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.Job;
import io.jenkins.blueocean.commons.ServiceException;
import io.jenkins.blueocean.rest.Navigable;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueActionProxy;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BluePipelineFactory;
import io.jenkins.blueocean.rest.model.BlueQueueContainer;
import io.jenkins.blueocean.rest.model.BlueRun;
import io.jenkins.blueocean.rest.model.BlueRunContainer;
@ -15,16 +20,21 @@ import io.jenkins.blueocean.service.embedded.util.FavoriteUtil;
import jenkins.branch.MultiBranchProject;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.WebMethod;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.json.JsonBody;
import org.kohsuke.stapler.verb.DELETE;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static io.jenkins.blueocean.service.embedded.rest.PipelineContainerImpl.isMultiBranchProjectJob;
/**
* @author Kohsuke Kawaguchi
*/
public class PipelineImpl extends BluePipeline {
/*package*/ final Job job;
@ -93,6 +103,11 @@ public class PipelineImpl extends BluePipeline {
return new RunContainerImpl(this, job);
}
@Override
public Collection<BlueActionProxy> getActions() {
return getActionProxies(job.getAllActions(), this);
}
@Override
@Navigable
public BlueQueueContainer getQueue() {
@ -140,8 +155,6 @@ public class PipelineImpl extends BluePipeline {
@Override
public Link getLink() {
// Link parentLink = (parent == null) ? OrganizationImpl.INSTANCE.getLink().rel("pipelines") : parent;
return OrganizationImpl.INSTANCE.getLink().rel("pipelines").rel(getRecursivePathFromFullName(this));
}
@ -164,4 +177,28 @@ public class PipelineImpl extends BluePipeline {
return pipelinePath.toString();
}
@Extension(ordinal = 1)
public static class PipelineFactoryImpl extends BluePipelineFactory {
@Override
public BluePipeline getPipeline(Item item, Reachable parent) {
if (item instanceof Job) {
return new PipelineImpl((Job) item, parent.getLink());
}
return null;
}
}
public static Collection<BlueActionProxy> getActionProxies(List<? extends Action> actions, Reachable parent){
List<BlueActionProxy> actionProxies = new ArrayList<>();
for(Action action:actions){
if(action == null || !action.getClass().isAnnotationPresent(ExportedBean.class)){
continue;
}
actionProxies.add(new ActionProxiesImpl(action, parent));
}
return actionProxies;
}
}

View File

@ -2,6 +2,7 @@ package io.jenkins.blueocean.service.embedded;
import com.google.common.collect.ImmutableMap;
import com.mashape.unirest.http.HttpResponse;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.AbstractBuild;
@ -10,6 +11,9 @@ import hudson.model.Cause;
import hudson.model.CauseAction;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.Job;
import hudson.model.ParametersAction;
import hudson.model.ParametersDefinitionProperty;
import hudson.model.Project;
@ -21,6 +25,11 @@ import hudson.tasks.ArtifactArchiver;
import hudson.tasks.Shell;
import hudson.tasks.junit.JUnitResultArchiver;
import hudson.tasks.junit.TestResultAction;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BluePipelineFactory;
import io.jenkins.blueocean.service.embedded.rest.PipelineImpl;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
@ -30,6 +39,7 @@ import org.junit.Test;
import org.jvnet.hudson.test.MockFolder;
import org.jvnet.hudson.test.TestBuilder;
import org.kohsuke.stapler.AcceptHeader;
import org.kohsuke.stapler.export.Exported;
import java.io.IOException;
import java.util.List;
@ -532,4 +542,40 @@ public class PipelineApiTest extends BaseTest {
Assert.assertNotNull(p3.getQueueItem());
Assert.assertEquals(Long.toString(p3.getQueueItem().getId()), r.get("id"));
}
@Test
public void getPipelinesExtensionTest() throws Exception {
Project p = j.createFreeStyleProject("pipeline1");
Map<String,Object> response = get("/organizations/jenkins/pipelines/pipeline1");
validatePipeline(p, response);
Assert.assertEquals("hello world!", response.get("hello"));
}
@Extension(ordinal = 3)
public static class PipelineFactoryTestImpl extends BluePipelineFactory {
@Override
public BluePipeline getPipeline(Item item, Reachable parent) {
if(item instanceof Job){
return new TestPipelineImpl(null, (Job)item, parent.getLink());
}
return null;
}
}
public static class TestPipelineImpl extends PipelineImpl {
public TestPipelineImpl(ItemGroup folder, Job job, Link parent) {
super(folder, job, parent);
}
@Exported(name = "hello")
public String getHello(){
return "hello world!";
}
}
}

View File

@ -6,6 +6,8 @@ import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.json.JsonBody;
import org.kohsuke.stapler.verb.PUT;
import java.util.Collection;
/**
* Defines pipeline state and its routing
*
@ -20,6 +22,7 @@ public abstract class BluePipeline extends Resource {
public static final String LATEST_RUN = "latestRun";
public static final String ESTIMATED_DURATION = "estimatedDurationInMillis";
public static final String LAST_SUCCESSFUL_RUN = "lastSuccessfulRun";
public static final String ACTIONS = "actions";
/**
* @return name of the organization
@ -74,6 +77,15 @@ public abstract class BluePipeline extends Resource {
@Navigable
public abstract BlueRunContainer getRuns();
/**
*
* @return Gives Actions associated with this Run
*/
@Navigable
@Exported(name = ACTIONS, inline = true)
public abstract Collection<BlueActionProxy> getActions();
/**
* @return Gives {@link BlueQueueContainer}
*/

View File

@ -0,0 +1,23 @@
package io.jenkins.blueocean.rest.model;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.model.Item;
import io.jenkins.blueocean.rest.Reachable;
/**
* Factory that gives instance of {@link BluePipeline}
*
* It's useful for example in cases where a plugin that has custom project and they want to serve
* extra meta-data thru BluePipeline, would provide implementation of their BluePipeline and and implementation
* of BluePipelineFactory.
*
* @author Vivek Pandey
*/
public abstract class BluePipelineFactory implements ExtensionPoint {
public abstract BluePipeline getPipeline(Item item, Reachable parent);
public static ExtensionList<BluePipelineFactory> all(){
return ExtensionList.lookup(BluePipelineFactory.class);
}
}

View File

@ -162,13 +162,19 @@ public abstract class BlueRun extends Resource {
@Navigable
public abstract BluePipelineNodeContainer getNodes();
/**
*
* @return Gives Actions associated with this Run
*/
@Navigable
@Exported(name = ACTIONS, inline = true)
public abstract Collection<?> getActions();
public abstract Collection<BlueActionProxy> getActions();
/**
* @return Gives steps from pipeline. The list of steps must not include stages, this is because stage could be
* interpreted as step as its StepAtomNode and implementation of this API must ensure not to include it.
*/
@Navigable
@Exported(name = STEPS)
public abstract BluePipelineStepContainer getSteps();
@ -182,6 +188,7 @@ public abstract class BlueRun extends Resource {
* X-TEXT-SIZE header value with *start* query parameter.</li>
* </ul>
*/
@Navigable
public abstract Object getLog();
public enum BlueRunState {