Merge branch 'improvement/JENKINS-36506' into feature/JENKINS-35831-favorite-from-dashboard

This commit is contained in:
Cliff Meyers 2016-07-11 09:16:31 -04:00
commit 5f8d871495
15 changed files with 248 additions and 94 deletions

View File

@ -0,0 +1,58 @@
package io.jenkins.blueocean.service.embedded.rest;
import hudson.model.Item;
import hudson.plugins.favorite.user.FavoriteUserProperty;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueFavorite;
import io.jenkins.blueocean.rest.model.BlueFavoriteContainer;
import io.jenkins.blueocean.service.embedded.util.FavoriteUtil;
import jenkins.model.Jenkins;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author Ivan Meredith
* @author Vivek Pandey
*/
public class FavoriteContainerImpl extends BlueFavoriteContainer {
private final UserImpl user;
private final Link self;
public FavoriteContainerImpl(UserImpl user, Reachable parent) {
this.user = user;
this.self = parent.getLink().rel("favorites");
}
@Override
public BlueFavorite get(final String name) {
if(user.isFavorite(name)){
Item item = Jenkins.getInstance().getItemByFullName(name);
if(FavoriteUtil.isFavorableItem(item)) {
return new FavoriteImpl(item, this);
}
}
return null;
}
@Override
public Iterator<BlueFavorite> iterator() {
FavoriteUserProperty prop = user.getFavoriteProperty();
List<BlueFavorite> pipelines = new ArrayList<>();
Jenkins j = Jenkins.getInstance();
for(final String favorite: prop.getFavorites()){
Item i = j.getItemByFullName(favorite, Item.class);
if(FavoriteUtil.isFavorableItem(i)){
pipelines.add(new FavoriteImpl(i, this));
}
}
return pipelines.iterator();
}
@Override
public Link getLink() {
return self;
}
}

View File

@ -1,36 +1,52 @@
package io.jenkins.blueocean.service.embedded.rest;
import hudson.Util;
import hudson.model.Item;
import hudson.model.Job;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueFavorite;
import io.jenkins.blueocean.rest.model.BlueFavoriteContainer;
import org.kohsuke.stapler.WebMethod;
import org.kohsuke.stapler.verb.GET;
import java.util.Iterator;
import jenkins.branch.MultiBranchProject;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
/**
* @author Ivan Meredith
* @author Vivek Pandey
*/
public class FavoriteImpl extends BlueFavoriteContainer {
private final UserImpl user;
public FavoriteImpl(UserImpl user) {
this.user = user;
public class FavoriteImpl extends BlueFavorite {
private final Object item;
private final Link self;
public FavoriteImpl(Item item, Reachable parent) {
this.self = parent.getLink().rel(Util.rawEncode(item.getFullName()));
// TODO: MBP nested inside folder won't work nor would a favorited folder is going to work
//TODO: Needs https://issues.jenkins-ci.org/browse/JENKINS-36286 to be fixed,
// it should use LinkResolver for it to work in all cases
Object obj = null;
if(item instanceof WorkflowJob){
if(item.getParent() instanceof MultiBranchProject){
Link s = OrganizationImpl.INSTANCE.getLink().rel(String.format("pipelines/%s/branches/",
((MultiBranchProject) item.getParent()).getName()));
obj = new BranchImpl((Job) item, s);
}
}
if(obj == null){
this.item = new PipelineImpl((Job) item);
}else{
this.item = obj;
}
}
@Override
public BlueFavorite get(final String name) {
return user.getFavorite(name);
}
@Override
@GET
@WebMethod(name="")
public Iterator<BlueFavorite> iterator() {
return user.getFavouriteIterator();
public Object getItem() {
return item;
}
@Override
public Link getLink() {
return user.getLink().rel("favorites");
return self;
}
}

View File

@ -10,6 +10,7 @@ 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.BlueFavoriteAction;
import io.jenkins.blueocean.rest.model.BlueMultiBranchPipeline;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BluePipelineContainer;
@ -51,14 +52,14 @@ public class MultiBranchPipelineImpl extends BlueMultiBranchPipeline {
@Override
public void favorite(@JsonBody FavoriteAction favoriteAction) {
public void favorite(@JsonBody BlueFavoriteAction favoriteAction) {
if(favoriteAction == null) {
throw new ServiceException.BadRequestExpception("Must provide pipeline name");
}
Job job = mbp.getBranch("master");
if(job == null) {
throw new ServiceException.UnexpectedErrorException("no master branch to favorite");
throw new ServiceException.BadRequestExpception("no master branch to favorite");
}
FavoriteUtil.favoriteJob(job.getFullName(), favoriteAction.isFavorite());

View File

@ -7,6 +7,7 @@ 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.BlueFavoriteAction;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BluePipelineContainer;
import io.jenkins.blueocean.rest.model.BluePipelineFactory;
@ -86,7 +87,7 @@ public class PipelineFolderImpl extends BluePipelineFolder {
@Override
public void favorite(@JsonBody FavoriteAction favoriteAction) {
public void favorite(@JsonBody BlueFavoriteAction favoriteAction) {
if(favoriteAction == null) {
throw new ServiceException.BadRequestExpception("Must provide pipeline name");
}

View File

@ -9,6 +9,7 @@ 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.BlueFavoriteAction;
import io.jenkins.blueocean.rest.model.BluePipeline;
import io.jenkins.blueocean.rest.model.BluePipelineFactory;
import io.jenkins.blueocean.rest.model.BlueQueueContainer;
@ -102,7 +103,7 @@ public class PipelineImpl extends BluePipeline {
@Override
public void favorite(@JsonBody FavoriteAction favoriteAction) {
public void favorite(@JsonBody BlueFavoriteAction favoriteAction) {
if(favoriteAction == null) {
throw new ServiceException.BadRequestExpception("Must provide pipeline name");
}

View File

@ -1,6 +1,5 @@
package io.jenkins.blueocean.service.embedded.rest;
import hudson.model.Item;
import hudson.model.User;
import hudson.plugins.favorite.user.FavoriteUserProperty;
import hudson.tasks.Mailer;
@ -8,16 +7,10 @@ import io.jenkins.blueocean.commons.ServiceException;
import io.jenkins.blueocean.rest.ApiHead;
import io.jenkins.blueocean.rest.Reachable;
import io.jenkins.blueocean.rest.hal.Link;
import io.jenkins.blueocean.rest.model.BlueFavorite;
import io.jenkins.blueocean.rest.model.BlueFavoriteContainer;
import io.jenkins.blueocean.rest.model.BlueUser;
import io.jenkins.blueocean.service.embedded.util.FavoriteUtil;
import jenkins.model.Jenkins;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* {@link BlueUser} implementation backed by in-memory {@link User}
*
@ -56,44 +49,23 @@ public class UserImpl extends BlueUser {
return p != null ? p.getAddress() : null;
}
public BlueFavorite getFavorite(final String name){
protected boolean isFavorite(final String name) {
FavoriteUserProperty prop = user.getProperty(FavoriteUserProperty.class);
if(prop == null) {
return null;
}
if(prop.isJobFavorite(name)) {
return new BlueFavorite(name);
}
return null;
return prop != null && prop.isJobFavorite(name);
}
public Iterator<BlueFavorite> getFavouriteIterator(){
FavoriteUserProperty prop = user.getProperty(FavoriteUserProperty.class);
List<BlueFavorite> pipelines = new ArrayList<BlueFavorite>();
Jenkins j = Jenkins.getInstance();
String org = j.getDisplayName().toLowerCase();
for(final String favorite: prop.getFavorites()){
Item i = j.getItem(favorite,j);
if(i == null) {
continue;
}
pipelines.add(new BlueFavorite(FavoriteUtil.generateBlueUrl(org,i)));
}
return pipelines.iterator();
protected FavoriteUserProperty getFavoriteProperty(){
return user.getProperty(FavoriteUserProperty.class);
}
@Override
public BlueFavoriteContainer getFavorites() {
String name = Jenkins.getAuthentication().getName();
if(!user.getId().equals(name)) {
throw new ServiceException.ForbiddenException("You do not have access to this resource.");
}
return new FavoriteImpl(this);
return new FavoriteContainerImpl(this, this);
}
@Override

View File

@ -2,6 +2,7 @@ package io.jenkins.blueocean.service.embedded.util;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.Job;
import hudson.model.User;
import hudson.plugins.favorite.FavoritePlugin;
import hudson.plugins.favorite.user.FavoriteUserProperty;
@ -56,4 +57,8 @@ public class FavoriteUtil {
}
return url;
}
public static boolean isFavorableItem(Item i){
return i!= null && (i instanceof Job || i instanceof ItemGroup);
}
}

View File

@ -8,11 +8,14 @@ import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import com.mashape.unirest.request.HttpRequest;
import com.mashape.unirest.request.HttpRequestWithBody;
import hudson.Util;
import hudson.model.Job;
import hudson.model.Run;
import io.jenkins.blueocean.commons.JsonConverter;
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.junit.Assert;
import org.junit.Before;
@ -257,8 +260,13 @@ public abstract class BaseTest {
Assert.assertEquals(p.getBuildHealth().getScore(), resp.get("weatherScore"));
if(p.getLastSuccessfulBuild() != null){
Run b = p.getLastSuccessfulBuild();
Assert.assertEquals(baseUrl + "/organizations/jenkins/pipelines/" +
p.getName() + "/runs/" + b.getId()+"/", resp.get("lastSuccessfulRun"));
String s = baseUrl + "/organizations/jenkins/pipelines/" +
p.getName() + "/runs/" + b.getId()+"/";
if(p instanceof WorkflowJob && p.getParent() instanceof MultiBranchProject){
s = baseUrl + "/organizations/jenkins/pipelines/" +
((MultiBranchProject) p.getParent()).getName() +"/branches/"+ Util.rawEncode(p.getName())+"/runs/" + b.getId()+"/";
}
Assert.assertEquals(s, resp.get("lastSuccessfulRun"));
}else{
Assert.assertNull(resp.get("lastSuccessfulRun"));

View File

@ -55,7 +55,7 @@ public class MultiBranchTest extends BaseTest{
/**
* Some of these tests can be problematic until:
* https://issues.jenkins-ci.org/browse/JENKINS-36290 is resolved
* Set an env var to any value to get these to run.
* Set an env var to any value to get these to run.
*/
private boolean runAllTests() {
return System.getenv("RUN_MULTIBRANCH_TESTS") != null;
@ -412,14 +412,15 @@ public class MultiBranchTest extends BaseTest{
.build(List.class);
Assert.assertEquals(l.size(), 1);
Assert.assertEquals(((Map)l.get(0)).get("pipeline"),"/organizations/jenkins/pipelines/p/branches/master");
Map branch = (Map)((Map)l.get(0)).get("item");
validatePipeline(p, branch);
new RequestBuilder(baseUrl)
.get("/users/"+user.getId()+"/favorites/")
.auth("bob","bob")
.status(403)
.build(String.class);
}
@ -450,7 +451,11 @@ public class MultiBranchTest extends BaseTest{
.build(List.class);
Assert.assertEquals(l.size(), 1);
Assert.assertEquals(((Map)l.get(0)).get("pipeline"),"/organizations/jenkins/pipelines/p/branches/feature2");
WorkflowJob p1 = scheduleAndFindBranchProject(mp, "feature2");
Map branch = (Map)((Map)l.get(0)).get("item");
validatePipeline(p1, branch);
new RequestBuilder(baseUrl)
.get("/users/"+user.getId()+"/favorites/")

View File

@ -105,7 +105,9 @@ public class ProfileApiTest extends BaseTest{
.build(List.class);
Assert.assertEquals(l.size(), 1);
Assert.assertEquals(((Map)l.get(0)).get("pipeline"),"/organizations/jenkins/pipelines/pipeline1");
Map pipeline = (Map)((Map)l.get(0)).get("item");
validatePipeline(p, pipeline);
new RequestBuilder(baseUrl)
.get("/users/"+user.getId()+"/favorites/")

View File

@ -1124,9 +1124,86 @@ Must be authenticated.
curl -u bob:bob http://localhost:8080/jenkins/blue/rest/users/bob/favorites/
[{
"pipeline":"/organizations/jenkins/pipelines/pipeline1"
}]
[ {
"_class" : "io.jenkins.blueocean.service.embedded.rest.FavoriteImpl",
"_links" : {
"self" : {
"_class" : "io.jenkins.blueocean.rest.hal.Link",
"href" : "/blue/rest/users/alice/favorites/p%2Fmaster/"
}
},
"item" : {
"_class" : "io.jenkins.blueocean.service.embedded.rest.BranchImpl",
"_links" : {
"self" : {
"_class" : "io.jenkins.blueocean.rest.hal.Link",
"href" : "/blue/rest/organizations/jenkins/pipelines/p/branches/master/"
},
"actions" : {
"_class" : "io.jenkins.blueocean.rest.hal.Link",
"href" : "/blue/rest/organizations/jenkins/pipelines/p/branches/master/actions/"
},
"runs" : {
"_class" : "io.jenkins.blueocean.rest.hal.Link",
"href" : "/blue/rest/organizations/jenkins/pipelines/p/branches/master/runs/"
},
"queue" : {
"_class" : "io.jenkins.blueocean.rest.hal.Link",
"href" : "/blue/rest/organizations/jenkins/pipelines/p/branches/master/queue/"
}
},
"actions" : [ ],
"displayName" : "master",
"estimatedDurationInMillis" : 953,
"fullName" : "p/master",
"lastSuccessfulRun" : "http://localhost:49669/jenkins/blue/rest/organizations/jenkins/pipelines/p/branches/master/runs/1/",
"latestRun" : {
"_class" : "io.jenkins.blueocean.service.embedded.rest.PipelineRunImpl",
"_links" : {
"nodes" : {
"_class" : "io.jenkins.blueocean.rest.hal.Link",
"href" : "/blue/rest/organizations/jenkins/pipelines/p/branches/master/runs/1/nodes/"
},
"log" : {
"_class" : "io.jenkins.blueocean.rest.hal.Link",
"href" : "/blue/rest/organizations/jenkins/pipelines/p/branches/master/runs/1/log/"
},
"self" : {
"_class" : "io.jenkins.blueocean.rest.hal.Link",
"href" : "/blue/rest/organizations/jenkins/pipelines/p/branches/master/runs/1/"
},
"actions" : {
"_class" : "io.jenkins.blueocean.rest.hal.Link",
"href" : "/blue/rest/organizations/jenkins/pipelines/p/branches/master/runs/1/actions/"
},
"steps" : {
"_class" : "io.jenkins.blueocean.rest.hal.Link",
"href" : "/blue/rest/organizations/jenkins/pipelines/p/branches/master/runs/1/steps/"
}
},
"actions" : [ ],
"artifacts" : [ ],
"changeSet" : [ ],
"durationInMillis" : 953,
"enQueueTime" : "2016-07-08T13:27:15.250-0700",
"endTime" : "2016-07-08T13:27:16.204-0700",
"estimatedDurationInMillis" : 953,
"id" : "1",
"organization" : "jenkins",
"pipeline" : "master",
"result" : "SUCCESS",
"runSummary" : "stable",
"startTime" : "2016-07-08T13:27:15.251-0700",
"state" : "FINISHED",
"type" : "WorkflowRun",
"commitId" : "0cd84cc9a1a62fbe636e5d1197ef7a5cc4c56b63"
},
"name" : "master",
"organization" : "jenkins",
"weatherScore" : 100,
"pullRequest" : null
}
} ]
## Stop a build
Note it takes a while to stop, so you may get a state of RUNNING or QUEUED.

View File

@ -1,26 +1,24 @@
package io.jenkins.blueocean.rest.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.jenkins.blueocean.rest.annotation.Capability;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
/**
* A favorite item. The item itself must be JSON serializable bean
*
* @author Ivan Meredith
* @author Vivek Pandey
*/
@ExportedBean
@Capability("io.jenkins.blueocean.rest.model.BlueFavorite")
public class BlueFavorite {
private static final String PIPELINE = "pipeline";
private final String pipeline;
public abstract class BlueFavorite extends Resource{
private static final String ITEM = "item";
public BlueFavorite(String pipeline) {
this.pipeline = pipeline;
}
@Exported(name = PIPELINE)
@JsonProperty(PIPELINE)
public String getPipeline(){
return pipeline;
}
/**
* @return Gives favorite item
*/
@Exported(name = ITEM, inline = true)
public abstract Object getItem();
}

View File

@ -0,0 +1,24 @@
package io.jenkins.blueocean.rest.model;
/**
* Describes requested state of a favorited item
*
* @author Ivan Meredith
* @author Vivek Pandey
*
* @see io.jenkins.blueocean.rest.model.BluePipeline#favorite(BlueFavoriteAction)
*/
public class BlueFavoriteAction {
private Boolean favorite;
public void setFavorite(boolean favorite) {
this.favorite = favorite;
}
/**
* @return true favorite, false un-favorite, null toggle
*/
public Boolean isFavorite() {
return favorite;
}
}

View File

@ -5,6 +5,4 @@ package io.jenkins.blueocean.rest.model;
*/
public abstract class BlueFavoriteContainer extends Container<BlueFavorite> {
public abstract BlueFavorite get(String name);
}

View File

@ -96,17 +96,5 @@ public abstract class BluePipeline extends Resource {
@PUT
@WebMethod(name="favorite")
public abstract void favorite(@JsonBody FavoriteAction favoriteAction);
public static class FavoriteAction {
private boolean favorite;
public void setFavorite(boolean favorite) {
this.favorite = favorite;
}
public boolean isFavorite() {
return favorite;
}
}
public abstract void favorite(@JsonBody BlueFavoriteAction favoriteAction);
}