Merge pull request #321 from jenkinsci/feature/JENKINS-36286

JENKINS-36286# LinkResolver to resolve link of a jenkins model object…
This commit is contained in:
vivek 2016-07-11 09:55:45 -07:00 committed by GitHub
commit b5a28196b4
16 changed files with 484 additions and 68 deletions

View File

@ -6,6 +6,7 @@ import com.google.inject.Module;
import hudson.Extension;
import hudson.model.RootAction;
import io.jenkins.blueocean.BlueOceanUI;
import io.jenkins.blueocean.rest.hal.LinkResolver;
import org.kohsuke.stapler.StaplerProxy;
/**
@ -48,6 +49,7 @@ public class BlueOceanRootAction implements RootAction, StaplerProxy {
@Override
public void configure(Binder binder) {
binder.bind(BlueOceanUI.class).toInstance(new BlueOceanUI(URL_BASE));
binder.bind(LinkResolver.class).to(LinkResolverImpl.class);
}
}
}

View File

@ -0,0 +1,98 @@
package io.jenkins.blueocean.service.embedded;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.Job;
import hudson.model.Queue;
import hudson.model.Run;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.hal.LinkResolver;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BlueRun;
import io.jenkins.blueocean.rest.model.Resource;
import io.jenkins.blueocean.service.embedded.rest.BluePipelineFactory;
import io.jenkins.blueocean.service.embedded.rest.PipelineNodeUtil;
import org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
/**
* Implementation of {@link LinkResolver}
*
* @author Kohsuke Kawaguchi
* @author Vivek Pandey
*/
public class LinkResolverImpl extends LinkResolver {
private final Logger logger = LoggerFactory.getLogger(LinkResolverImpl.class);
@Override
public Link resolve(Object modelObject) {
if (modelObject instanceof Job) {
Resource resource = resolveJob((Job)modelObject);
if(resource != null){
return resource.getLink();
}
}else if(modelObject instanceof Item && modelObject instanceof ItemGroup){
Resource resource = resolveFolder((Item) modelObject);
if(resource!=null){
return resource.getLink();
}
}else if(modelObject instanceof Run){
Run run = (Run) modelObject;
Resource resource = resolveRun(run);
if(resource != null){
return resource.getLink();
}
}else if(modelObject instanceof FlowNode){
FlowNode flowNode = (FlowNode) modelObject;
BlueRun r = resolveFlowNodeRun(flowNode);
if(PipelineNodeUtil.isParallelBranch(flowNode) || PipelineNodeUtil.isStage(flowNode)){ // its Node
if(r != null){
return r.getLink().rel("nodes/"+flowNode.getId());
}
}else if(flowNode instanceof StepAtomNode && !PipelineNodeUtil.isStage(flowNode)) {
if(r != null){
return r.getLink().rel("steps/"+flowNode.getId());
}
}
}
return null;
}
private Resource resolveJob(Job job){
return BluePipelineFactory.resolve(job);
}
private Resource resolveFolder(Item folder){
return BluePipelineFactory.resolve(folder);
}
private Resource resolveRun(Run run){
Resource resource = resolveJob(run.getParent());
if(resource instanceof BluePipeline){
BluePipeline pipeline = (BluePipeline) resource;
return pipeline.getRuns().get(run.getId());
}
return null;
}
private BlueRun resolveFlowNodeRun(FlowNode flowNode){
try {
Queue.Executable executable = flowNode.getExecution().getOwner().getExecutable();
if(executable!= null && executable instanceof WorkflowRun){
WorkflowRun run = (WorkflowRun) executable;
return (BlueRun) resolveRun(run);
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
return null;
}
return null;
}
}

View File

@ -0,0 +1,80 @@
package io.jenkins.blueocean.service.embedded.rest;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.model.Item;
import hudson.model.ItemGroup;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.Resource;
import jenkins.model.Jenkins;
/**
* 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);
/**
* Finds a blue ocean API model object that pairs up with the given {@code target},
* by looking at the intermediate core model object {@code context} that is an ancestor
* of {@code target}.
*
* If this {@link BluePipelineFactory} understands how to map {@code context} to
* {@link BluePipeline} (as in {@code getPipeline(item,parent)!=null}), then the resolve
* method should also apply the same logic to map {@code context} and then recursively
* resolve {@code target}.
*
* @param context
* This is always an ancestor of target (including target==context)
* @param parent
* The parent object of the blue ocean API model object that pairs up with the 'context' parameter
* @param target
* The core model object that we are trying to map to {@link Resource}
*
* @return
* null if this implementation doesn't know how to map {@code context} to a blue ocean API model object.
* Otherwise return the BO API model object that pairs up with {@code target}
*/
public abstract Resource resolve(Item context, Reachable parent, Item target);
public static ExtensionList<BluePipelineFactory> all(){
return ExtensionList.lookup(BluePipelineFactory.class);
}
/**
* Given a Job in Jenkins, map that to corresponding blue ocean API object,
* for example so that you can get its URL.
*/
public static Resource resolve(Item item) {
Item nextStep = findNextStep(Jenkins.getInstance(), item);
for (BluePipelineFactory f : all()) {
Resource r = f.resolve(nextStep, OrganizationImpl.INSTANCE.getPipelines(), item);
if (r!=null) return r;
}
return null;
}
/**
* Returns the immediate child of 'context' that is also the ancestor of 'target'
*/
protected static Item findNextStep(ItemGroup context, Item target) {
Item i = null;
while (context!=target) {
i = target;
if (target.getParent() instanceof Item) {
target = (Item) target.getParent();
} else {
break;
}
}
return i == null ? target : i;
}
}

View File

@ -7,7 +7,6 @@ import hudson.model.Job;
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.rest.model.Resource;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.actions.ChangeRequestAction;
@ -55,6 +54,14 @@ public class BranchImpl extends PipelineImpl {
}
return null;
}
@Override
public Resource resolve(Item context, Reachable parent, Item target) {
if (context==target.getParent()) {
return getPipeline(context,parent);
}
return null;
}
}
public static class PullRequest extends Resource {

View File

@ -14,11 +14,11 @@ import io.jenkins.blueocean.rest.model.BlueFavoriteAction;
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.BlueQueueContainer;
import io.jenkins.blueocean.rest.model.BlueQueueItem;
import io.jenkins.blueocean.rest.model.BlueRun;
import io.jenkins.blueocean.rest.model.BlueRunContainer;
import io.jenkins.blueocean.rest.model.Resource;
import io.jenkins.blueocean.service.embedded.util.FavoriteUtil;
import jenkins.branch.MultiBranchProject;
import jenkins.scm.api.SCMHead;
@ -293,11 +293,25 @@ public class MultiBranchPipelineImpl extends BlueMultiBranchPipeline {
public static class PipelineFactoryImpl extends BluePipelineFactory{
@Override
public BluePipeline getPipeline(Item item, Reachable parent) {
public MultiBranchPipelineImpl getPipeline(Item item, Reachable parent) {
if (item instanceof MultiBranchProject) {
return new MultiBranchPipelineImpl((MultiBranchProject) item);
}
return null;
}
@Override
public Resource resolve(Item context, Reachable parent, Item target) {
if (context instanceof MultiBranchProject) {
if (context==target)
return getPipeline(context,parent);
if (context==target.getParent()) {
// target is a branch
return getPipeline(context,parent).getBranches().get(target.getName());
}
}
return null;
}
}
}

View File

@ -7,7 +7,6 @@ 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.BluePipelineContainer;
import io.jenkins.blueocean.rest.model.BluePipelineFactory;
import jenkins.model.Jenkins;
import javax.annotation.Nonnull;

View File

@ -10,8 +10,8 @@ import io.jenkins.blueocean.rest.model.BlueActionProxy;
import io.jenkins.blueocean.rest.model.BlueFavoriteAction;
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.rest.model.Resource;
import io.jenkins.blueocean.service.embedded.util.FavoriteUtil;
import org.kohsuke.stapler.json.JsonBody;
@ -104,11 +104,28 @@ public class PipelineFolderImpl extends BluePipelineFolder {
public static class PipelineFactoryImpl extends BluePipelineFactory{
@Override
public BluePipeline getPipeline(Item item, Reachable parent) {
public PipelineFolderImpl getPipeline(Item item, Reachable parent) {
if (item instanceof ItemGroup) {
return new PipelineFolderImpl((ItemGroup) item, parent.getLink());
}
return null;
}
@Override
public Resource resolve(Item context, Reachable parent, Item target) {
PipelineFolderImpl folder = getPipeline(context, parent);
if (folder!=null) {
if(context == target){
return folder;
}
Item nextChild = findNextStep(folder.folder,target);
for (BluePipelineFactory f : all()) {
Resource answer = f.resolve(nextChild, folder, target);
if (answer!=null)
return answer;
}
}
return null;
}
}
}

View File

@ -11,10 +11,10 @@ import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueActionProxy;
import io.jenkins.blueocean.rest.model.BlueFavoriteAction;
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;
import io.jenkins.blueocean.rest.model.Resource;
import io.jenkins.blueocean.service.embedded.util.FavoriteUtil;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.WebMethod;
@ -150,6 +150,14 @@ public class PipelineImpl extends BluePipeline {
}
return null;
}
@Override
public Resource resolve(Item context, Reachable parent, Item target) {
if(context == target.getParent()){
return getPipeline(target,parent);
}
return null;
}
}
public static Collection<BlueActionProxy> getActionProxies(List<? extends Action> actions, Reachable parent){

View File

@ -12,11 +12,13 @@ import hudson.Util;
import hudson.model.Job;
import hudson.model.Run;
import io.jenkins.blueocean.commons.JsonConverter;
import io.jenkins.blueocean.service.embedded.rest.PipelineNodeUtil;
import jenkins.branch.MultiBranchProject;
import org.jenkinsci.plugins.workflow.actions.ThreadNameAction;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject;
import org.jenkinsci.plugins.workflow.support.visualization.table.FlowGraphTable;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
@ -27,7 +29,9 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.logging.LogManager;
@ -304,6 +308,29 @@ public abstract class BaseTest {
return baseUrl + path;
}
protected List<FlowNode> getStages(FlowGraphTable nodeGraphTable){
List<FlowNode> nodes = new ArrayList<>();
for(FlowGraphTable.Row row: nodeGraphTable.getRows()){
if(PipelineNodeUtil.isStage(row.getNode()) ||
PipelineNodeUtil.isParallelBranch(row.getNode())){
nodes.add(row.getNode());
}
}
return nodes;
}
protected List<FlowNode> getParallelNodes(FlowGraphTable nodeGraphTable){
List<FlowNode> parallelNodes = new ArrayList<>();
for(FlowGraphTable.Row row: nodeGraphTable.getRows()){
if(PipelineNodeUtil.isParallelBranch(row.getNode())){
parallelNodes.add(row.getNode());
}
}
return parallelNodes;
}
public RequestBuilder request() {
return new RequestBuilder(baseUrl);
}

View File

@ -0,0 +1,149 @@
package io.jenkins.blueocean.service.embedded;
import com.google.inject.Inject;
import hudson.model.FreeStyleProject;
import hudson.model.Project;
import hudson.model.Run;
import io.jenkins.blueocean.rest.hal.LinkResolver;
import io.jenkins.blueocean.service.embedded.rest.PipelineNodeGraphBuilder;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.support.visualization.table.FlowGraphTable;
import org.junit.Assert;
import org.junit.Test;
import org.jvnet.hudson.test.MockFolder;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
/**
* @author Vivek Pandey
*/
public class LinkResolverTest extends BaseTest {
@Inject
private LinkResolver linkResolver;
@Override
public void setup() throws Exception {
super.setup();
j.jenkins.getInjector().injectMembers(this);
}
@Test
public void nestedFolderJobLinkResolveTest() throws IOException {
Project f = j.createFreeStyleProject("fstyle1");
MockFolder folder1 = j.createFolder("folder1");
Project p1 = folder1.createProject(FreeStyleProject.class, "test1");
MockFolder folder2 = folder1.createProject(MockFolder.class, "folder2");
MockFolder folder3 = folder2.createProject(MockFolder.class, "folder3");
Project p2 = folder2.createProject(FreeStyleProject.class, "test2");
Project p3 = folder3.createProject(FreeStyleProject.class, "test3");
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/fstyle1/",linkResolver.resolve(f).getHref());
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/folder1/",linkResolver.resolve(folder1).getHref());
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/folder1/pipelines/test1/",linkResolver.resolve(p1).getHref());
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/folder1/pipelines/folder2/",linkResolver.resolve(folder2).getHref());
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/folder1/pipelines/folder2/pipelines/test2/",linkResolver.resolve(p2).getHref());
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/folder1/pipelines/folder2/pipelines/folder3/",linkResolver.resolve(folder3).getHref());
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/folder1/pipelines/folder2/pipelines/folder3/pipelines/test3/",linkResolver.resolve(p3).getHref());
}
@Test
public void runLinkResolveTest() throws IOException, ExecutionException, InterruptedException {
Project f = j.createFreeStyleProject("fstyle1");
MockFolder folder1 = j.createFolder("folder1");
Project p1 = folder1.createProject(FreeStyleProject.class, "test1");
MockFolder folder2 = folder1.createProject(MockFolder.class, "folder2");
MockFolder folder3 = folder2.createProject(MockFolder.class, "folder3");
Project p2 = folder2.createProject(FreeStyleProject.class, "test2");
Project p3 = folder3.createProject(FreeStyleProject.class, "test3");
Run r = (Run) f.scheduleBuild2(0).get();
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/fstyle1/runs/"+r.getId()+"/",linkResolver.resolve(r).getHref());
r = (Run) p1.scheduleBuild2(0).get();
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/folder1/pipelines/test1/runs/"+r.getId()+"/",linkResolver.resolve(r).getHref());
r = (Run) p2.scheduleBuild2(0).get();
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/folder1/pipelines/folder2/pipelines/test2/runs/"+r.getId()+"/",linkResolver.resolve(r).getHref());
r = (Run) p3.scheduleBuild2(0).get();
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/folder1/pipelines/folder2/pipelines/folder3/pipelines/test3/runs/"+r.getId()+"/",linkResolver.resolve(r).getHref());
}
@Test
public void resolveNodeLink() throws Exception {
{
WorkflowJob job1 = j.jenkins.createProject(WorkflowJob.class, "pipeline1");
job1.setDefinition(new CpsFlowDefinition("stage \"Build\"\n" +
" node {\n" +
" sh \"echo here\"\n" +
" }\n" +
"\n" +
"stage \"Test\"\n" +
" parallel (\n" +
" \"Firefox\" : {\n" +
" node {\n" +
" sh \"echo ffox\"\n" +
" }\n" +
" },\n" +
" \"Chrome\" : {\n" +
" node {\n" +
" sh \"echo chrome\"\n" +
" }\n" +
" }\n" +
" )\n" +
"\n" +
"stage \"CrashyMcgee\"\n" +
" parallel (\n" +
" \"SlowButSuccess\" : {\n" +
" node {\n" +
" echo 'This is time well spent.'\n" +
" }\n" +
" },\n" +
" \"DelayThenFail\" : {\n" +
" node {\n" +
" echo 'Not yet.'\n" +
" }\n" +
" }\n" +
" )\n" +
"\n" +
"\n" +
"stage \"Deploy\"\n" +
" node {\n" +
" sh \"echo deploying\"\n" +
" }"));
WorkflowRun b1 = job1.scheduleBuild2(0).get();
j.assertBuildStatusSuccess(b1);
FlowGraphTable nodeGraphTable = new FlowGraphTable(b1.getExecution());
nodeGraphTable.build();
List<FlowNode> nodes = getStages(nodeGraphTable);
List<FlowNode> parallelNodes = getParallelNodes(nodeGraphTable);
Assert.assertEquals(String.format("/blue/rest/organizations/jenkins/pipelines/pipeline1/runs/%s/nodes/%s/",
b1.getId(),nodes.get(0).getId()),
linkResolver.resolve(nodes.get(0)).getHref());
Assert.assertEquals(String.format("/blue/rest/organizations/jenkins/pipelines/pipeline1/runs/%s/nodes/%s/",
b1.getId(),parallelNodes.get(0).getId()),
linkResolver.resolve(parallelNodes.get(0)).getHref());
PipelineNodeGraphBuilder graphBuilder = new PipelineNodeGraphBuilder(b1);
List<FlowNode> steps = graphBuilder.getAllSteps();
Assert.assertEquals(String.format("/blue/rest/organizations/jenkins/pipelines/pipeline1/runs/%s/steps/%s/",
b1.getId(),steps.get(0).getId()),
linkResolver.resolve(steps.get(0)).getHref());
}
}
}

View File

@ -1,10 +1,12 @@
package io.jenkins.blueocean.service.embedded;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
import hudson.Util;
import hudson.model.FreeStyleProject;
import hudson.plugins.git.util.BuildData;
import hudson.scm.ChangeLogSet;
import io.jenkins.blueocean.rest.hal.LinkResolver;
import io.jenkins.blueocean.service.embedded.scm.GitSampleRepoRule;
import jenkins.branch.BranchProperty;
import jenkins.branch.BranchSource;
@ -43,6 +45,9 @@ public class MultiBranchTest extends BaseTest{
@Rule
public GitSampleRepoRule sampleRepo = new GitSampleRepoRule();
@Inject
private LinkResolver linkResolver;
private final String[] branches={"master", "feature%2Fux-1", "feature2"};
@ -62,6 +67,29 @@ public class MultiBranchTest extends BaseTest{
}
@Test
public void resolveMbpLink() throws Exception {
j.jenkins.getInjector().injectMembers(this);
WorkflowMultiBranchProject mp = j.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
FreeStyleProject f = j.jenkins.createProject(FreeStyleProject.class, "f");
mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", false),
new DefaultBranchPropertyStrategy(new BranchProperty[0])));
for (SCMSource source : mp.getSCMSources()) {
assertEquals(mp, source.getOwner());
}
mp.scheduleBuild2(0).getFuture().get();
j.waitUntilNoActivity();
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/p/",linkResolver.resolve(mp).getHref());
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/p/branches/master/",linkResolver.resolve(mp.getBranch("master")).getHref());
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/p/branches/feature%252Fux-1/",linkResolver.resolve(mp.getBranch("feature%2Fux-1")).getHref());
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/p/branches/feature2/",linkResolver.resolve(mp.getBranch("feature2")).getHref());
Assert.assertEquals("/blue/rest/organizations/jenkins/pipelines/f/",linkResolver.resolve(f).getHref());
}
@Test
public void getMultiBranchPipelines() throws IOException, ExecutionException, InterruptedException {
Assume.assumeTrue(runAllTests());

View File

@ -27,7 +27,8 @@ import hudson.tasks.junit.TestResultAction;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.annotation.Capability;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BluePipelineFactory;
import io.jenkins.blueocean.rest.model.Resource;
import io.jenkins.blueocean.service.embedded.rest.BluePipelineFactory;
import io.jenkins.blueocean.service.embedded.rest.PipelineImpl;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
@ -563,6 +564,14 @@ public class PipelineApiTest extends BaseTest {
}
return null;
}
@Override
public Resource resolve(Item context, Reachable parent, Item target) {
if(context == target && target instanceof Job) {
return new TestPipelineImpl((Job) target);
}
return null;
}
}
@Capability({"io.jenkins.blueocean.rest.annotation.test.TestPipeline", "io.jenkins.blueocean.rest.annotation.test.TestPipelineExample"})

View File

@ -11,7 +11,6 @@ import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
@ -1007,27 +1006,4 @@ public class PipelineNodeTest extends BaseTest {
Assert.assertNotNull(output);
System.out.println(output);
}
private List<FlowNode> getStages(FlowGraphTable nodeGraphTable){
List<FlowNode> nodes = new ArrayList<>();
for(FlowGraphTable.Row row: nodeGraphTable.getRows()){
if(PipelineNodeUtil.isStage(row.getNode()) ||
PipelineNodeUtil.isParallelBranch(row.getNode())){
nodes.add(row.getNode());
}
}
return nodes;
}
private List<FlowNode> getParallelNodes(FlowGraphTable nodeGraphTable){
List<FlowNode> parallelNodes = new ArrayList<>();
for(FlowGraphTable.Row row: nodeGraphTable.getRows()){
if(PipelineNodeUtil.isParallelBranch(row.getNode())){
parallelNodes.add(row.getNode());
}
}
return parallelNodes;
}
}

View File

@ -1,14 +1,15 @@
package io.jenkins.blueocean.rest;
import com.google.inject.Inject;
import hudson.Extension;
import hudson.ExtensionList;
import io.jenkins.blueocean.BlueOceanUI;
import io.jenkins.blueocean.RootRoutable;
import io.jenkins.blueocean.commons.ServiceException;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.pageable.Pageable;
import io.jenkins.blueocean.rest.pageable.Pageables;
import io.jenkins.blueocean.rest.pageable.PagedResponse;
import org.kohsuke.stapler.Ancestor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
@ -26,6 +27,9 @@ import java.util.Map;
@Extension
public final class ApiHead implements RootRoutable, Reachable {
@Inject
private BlueOceanUI blueOceanUI;
private final Map<String,ApiRoutable> apis = new HashMap<>();
public ApiHead() {
@ -81,20 +85,19 @@ public final class ApiHead implements RootRoutable, Reachable {
@Override
public Link getLink() {
Ancestor apiHead = Stapler.getCurrentRequest().findAncestor(ApiHead.class);
String contextPath = Stapler.getCurrentRequest().getContextPath();
String parentUrl = apiHead.getUrl();
if(!contextPath.isEmpty() && !contextPath.equals("/")){
int i = parentUrl.indexOf(contextPath);
if(i>=0 && i+contextPath.length() < parentUrl.length()){
parentUrl = parentUrl.substring(i+contextPath.length());
}
}
return new Link(parentUrl);
return new Link("/"+blueOceanUI.getUrlBase()).rel(getUrlName());
}
/**
* Gives instance of ApiHead by looking in to Extensions. In some cases it might be null, such as when jenkins is
* booting up.
*/
public static ApiHead INSTANCE(){
return (ApiHead) Stapler.getCurrentRequest().findAncestor(ApiHead.class).getObject();
ExtensionList<ApiHead> extensionList = ExtensionList.lookup(ApiHead.class);
if(!extensionList.isEmpty()){
return extensionList.get(0); //ApiHead is singleton
}
return null;
}
}

View File

@ -0,0 +1,22 @@
package io.jenkins.blueocean.rest.hal;
/**
*
* Resolves a {@link Link} for a given model object
*
* @author Kohsuke Kawaguchi
* @author Vivek Pandey
*/
public abstract class LinkResolver {
/**
*
* @param modelObject
* a model object to map to corresponding BlueOcean API model object
*
* @return
* {@link Link} to BlueOcean API model object. null if there is no BlueOcean API model object found that maps
* to the given model object.
*/
public abstract Link resolve(Object modelObject);
}

View File

@ -1,23 +0,0 @@
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);
}
}