diff --git a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/JenkinsAsyncClient.java b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/JenkinsAsyncClient.java index 57d5ecd29c..eec53e9615 100644 --- a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/JenkinsAsyncClient.java +++ b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/JenkinsAsyncClient.java @@ -18,9 +18,21 @@ */ package org.jclouds.jenkins.v1; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.core.MediaType; + +import org.jclouds.jenkins.v1.domain.Node; import org.jclouds.jenkins.v1.features.ComputerAsyncClient; import org.jclouds.jenkins.v1.features.JobAsyncClient; +import org.jclouds.jenkins.v1.filters.BasicAuthenticationUnlessAnonymous; import org.jclouds.rest.annotations.Delegate; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; /** * Provides asynchronous access to Jenkins via their REST API. @@ -30,8 +42,18 @@ import org.jclouds.rest.annotations.Delegate; * @see api doc * @author Adrian Cole */ +@RequestFilters(BasicAuthenticationUnlessAnonymous.class) public interface JenkinsAsyncClient { - + + /** + * @see JenkinsClient#getMaster + */ + @GET + @Path("/api/json") + @Consumes(MediaType.APPLICATION_JSON) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + ListenableFuture getMaster(); + /** * Provides asynchronous access to Computer features. */ diff --git a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/JenkinsClient.java b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/JenkinsClient.java index 38bff221c0..93db464829 100644 --- a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/JenkinsClient.java +++ b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/JenkinsClient.java @@ -21,6 +21,7 @@ package org.jclouds.jenkins.v1; import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; +import org.jclouds.jenkins.v1.domain.Node; import org.jclouds.jenkins.v1.features.ComputerClient; import org.jclouds.jenkins.v1.features.JobClient; import org.jclouds.rest.annotations.Delegate; @@ -35,6 +36,10 @@ import org.jclouds.rest.annotations.Delegate; */ @Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) public interface JenkinsClient { + /** + * @return the master computer + */ + Node getMaster(); /** * Provides synchronous access to Computer features. diff --git a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/domain/Job.java b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/domain/Job.java new file mode 100644 index 0000000000..b003617fe0 --- /dev/null +++ b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/domain/Job.java @@ -0,0 +1,157 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.jenkins.v1.domain; + +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.net.URI; + +import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; + +/** + * Minimal info about a Job + * + * @author Adrian Cole + */ +public class Job { + + public static Builder builder() { + return new ConcreteBuilder(); + } + + public Builder toBuilder() { + return builder().fromJob(this); + } + + private static class ConcreteBuilder extends Builder { + } + + public static abstract class Builder> { + private String name; + private URI url; + private String color; + + @SuppressWarnings("unchecked") + protected B self() { + return (B) this; + } + + /** + * @see Job#getName() + */ + public B name(String name) { + this.name = name; + return self(); + } + + /** + * @see Job#getUrl() + */ + public B url(URI url) { + this.url = url; + return self(); + } + + /** + * @see Job#getColor() + */ + public B color(String color) { + this.color = color; + return self(); + } + + public Job build() { + return new Job(this); + } + + protected B fromJob(Job in) { + return name(in.getName()).color(in.getColor()).url(in.getUrl()); + } + } + + private final String name; + private final String color; + private final URI url; + + protected Job(Builder builder) { + this.name = checkNotNull(builder.name, "name"); + this.color = checkNotNull(builder.color, "color"); + this.url = checkNotNull(builder.url, "url"); + } + + /** + * name of the job + */ + public String getName() { + return name; + } + + /** + * + * color of the job + */ + public String getColor() { + return color; + } + + /** + * + * url of the job + */ + public URI getUrl() { + return url; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Job that = Job.class.cast(o); + return equal(this.name, that.name) && equal(this.url, that.url) + && equal(this.color, that.color); + } + + public boolean clone(Object o) { + if (this == o) + return false; + if (o == null || getClass() != o.getClass()) + return false; + Job that = Job.class.cast(o); + return equal(this.color, that.color); + } + + @Override + public int hashCode() { + return Objects.hashCode(name, url, color); + } + + @Override + public String toString() { + return string().toString(); + } + + protected ToStringHelper string() { + return Objects.toStringHelper("").add("name", name).add("url", url).add("color", + color); + } +} diff --git a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/domain/JobDetails.java b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/domain/JobDetails.java new file mode 100644 index 0000000000..09519baae2 --- /dev/null +++ b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/domain/JobDetails.java @@ -0,0 +1,62 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.jenkins.v1.domain; + +import com.google.common.base.Objects.ToStringHelper; + +/** + * + * @author Adrian Cole + */ +public class JobDetails extends Job { + + public static Builder builder() { + return new ConcreteBuilder(); + } + + @Override + public Builder toBuilder() { + return builder().fromJobDetails(this); + } + + public static class Builder> extends Job.Builder { + + @Override + public JobDetails build() { + return new JobDetails(this); + } + + public B fromJobDetails(JobDetails in) { + return fromJob(in); + } + } + + private static class ConcreteBuilder extends Builder { + } + + protected JobDetails(Builder builder) { + super(builder); + } + + @Override + public ToStringHelper string() { + return super.string(); // .add("field", field); + } + +} diff --git a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/domain/Node.java b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/domain/Node.java new file mode 100644 index 0000000000..ab4f15cd6c --- /dev/null +++ b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/domain/Node.java @@ -0,0 +1,147 @@ +package org.jclouds.jenkins.v1.domain; + +import static com.google.common.base.Objects.equal; +import static com.google.common.base.Objects.toStringHelper; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Set; + +import com.google.common.base.Objects; +import com.google.common.base.Objects.ToStringHelper; +import com.google.common.collect.ImmutableSet; +import com.google.gson.annotations.SerializedName; + +/** + * @author Adrian Cole + * @see api + * doc + */ +public class Node implements Comparable { + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return builder().fromNodeMetadata(this); + } + + public static class Builder { + protected String name; + protected String description; + protected Set jobs = ImmutableSet.of(); + + /** + * @see Node#getName() + */ + public Builder name(String name) { + this.name = checkNotNull(name, "name"); + return this; + } + + /** + * @see Node#getDescription() + */ + public Builder description(String description) { + this.description = description; + return this; + } + + /** + * @see Node#getJobs() + */ + public Builder jobs(Job... jobs) { + return jobs(ImmutableSet.copyOf(checkNotNull(jobs, "jobs"))); + } + + /** + * @see Node#getJobs() + */ + public Builder jobs(Set jobs) { + this.jobs = ImmutableSet.copyOf(checkNotNull(jobs, "jobs")); + return this; + } + + public Node build() { + return new Node(name, description, jobs); + } + + public Builder fromNodeMetadata(Node from) { + return name(from.getName()).description(from.getDescription()).jobs(from.getJobs()); + } + } + + @SerializedName("nodeName") + protected final String name; + @SerializedName("nodeDescription") + protected final String description; + protected final Set jobs; + + public Node(String name, String description, Set jobs) { + this.name = checkNotNull(name, "name"); + this.description = description; + this.jobs = ImmutableSet.copyOf(checkNotNull(jobs, "jobs")); + } + + /** + * + * @return the name of the node + */ + public String getName() { + return name; + } + + /** + * + * @return the description of the node + */ + public String getDescription() { + return description; + } + + /** + * @return the jobs on this node + */ + public Set getJobs() { + return jobs; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Node) { + final Node other = Node.class.cast(object); + return equal(getName(), other.getName()) && equal(getDescription(), other.getDescription()) + && equal(getJobs(), other.getJobs()); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hashCode(getName(), getDescription(), getJobs()); + } + + @Override + public String toString() { + return string().toString(); + } + + protected ToStringHelper string() { + return toStringHelper("").add("name", getName()).add("description", getDescription()).add("jobs", getJobs()); + } + + @Override + public int compareTo(Node that) { + if (that == null) + return 1; + if (this == that) + return 0; + return this.getName().compareTo(that.getName()); + } + +} diff --git a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/ComputerAsyncClient.java b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/ComputerAsyncClient.java index 1e86e9b340..30953985a4 100644 --- a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/ComputerAsyncClient.java +++ b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/ComputerAsyncClient.java @@ -61,4 +61,5 @@ public interface ComputerAsyncClient { @Consumes(MediaType.APPLICATION_JSON) @ExceptionParser(ReturnNullOnNotFoundOr404.class) ListenableFuture get(@PathParam("displayName") String displayName); + } diff --git a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/ComputerClient.java b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/ComputerClient.java index 120fba7015..25beb3fcf5 100644 --- a/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/ComputerClient.java +++ b/labs/jenkins/src/main/java/org/jclouds/jenkins/v1/features/ComputerClient.java @@ -45,4 +45,5 @@ public interface ComputerClient { * @return computer or null if not found */ Computer get(String displayName); + } diff --git a/labs/jenkins/src/test/java/org/jclouds/jenkins/v1/JenkinsClientExpectTest.java b/labs/jenkins/src/test/java/org/jclouds/jenkins/v1/JenkinsClientExpectTest.java new file mode 100644 index 0000000000..77ae552d59 --- /dev/null +++ b/labs/jenkins/src/test/java/org/jclouds/jenkins/v1/JenkinsClientExpectTest.java @@ -0,0 +1,58 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.jenkins.v1; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.jenkins.v1.internal.BaseJenkinsClientExpectTest; +import org.jclouds.jenkins.v1.parse.ParseNodeTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "JenkinsClientExpectTest") +public class JenkinsClientExpectTest extends BaseJenkinsClientExpectTest { + + public void testGetMasterWhenResponseIs2xx() { + HttpRequest getMaster = HttpRequest + .builder() + .method("GET") + .endpoint(URI.create("http://localhost:8080/api/json")) + .headers( + ImmutableMultimap. builder() + .put("Accept", "application/json") + .put("Authorization", "Basic aWRlbnRpdHk6Y3JlZGVudGlhbA==").build()).build(); + + HttpResponse getMasterResponse = HttpResponse.builder().statusCode(200) + .payload(payloadFromResource("/master.json")).build(); + + JenkinsClient clientWhenMasterExists = requestSendsResponse(getMaster, getMasterResponse); + + assertEquals(clientWhenMasterExists.getMaster().toString(), + new ParseNodeTest().expected().toString()); + } +} diff --git a/labs/jenkins/src/test/java/org/jclouds/jenkins/v1/JenkinsClientLiveTest.java b/labs/jenkins/src/test/java/org/jclouds/jenkins/v1/JenkinsClientLiveTest.java new file mode 100644 index 0000000000..ae1b83c3b7 --- /dev/null +++ b/labs/jenkins/src/test/java/org/jclouds/jenkins/v1/JenkinsClientLiveTest.java @@ -0,0 +1,41 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.jenkins.v1; + +import static org.testng.Assert.assertNotNull; + +import org.jclouds.jenkins.v1.domain.Node; +import org.jclouds.jenkins.v1.internal.BaseJenkinsClientLiveTest; +import org.testng.annotations.Test; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "live", testName = "JenkinsClientLiveTest") +public class JenkinsClientLiveTest extends BaseJenkinsClientLiveTest { + + public void testGetMaster(){ + Node master = context.getApi().getMaster(); + assertNotNull(master); + assertNotNull(master.getName()); + assertNotNull(master.getJobs()); + } + +} \ No newline at end of file diff --git a/labs/jenkins/src/test/java/org/jclouds/jenkins/v1/parse/ParseNodeTest.java b/labs/jenkins/src/test/java/org/jclouds/jenkins/v1/parse/ParseNodeTest.java new file mode 100644 index 0000000000..d2ec328d32 --- /dev/null +++ b/labs/jenkins/src/test/java/org/jclouds/jenkins/v1/parse/ParseNodeTest.java @@ -0,0 +1,55 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.jenkins.v1.parse; + +import java.net.URI; + +import javax.ws.rs.Consumes; +import javax.ws.rs.core.MediaType; + +import org.jclouds.jenkins.v1.domain.Job; +import org.jclouds.jenkins.v1.domain.Node; +import org.jclouds.json.BaseItemParserTest; +import org.testng.annotations.Test; + +/** + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "ParseNodeTest") +public class ParseNodeTest extends BaseItemParserTest { + + @Override + public String resource() { + return "/master.json"; + } + + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Node expected() { + return Node.builder() + .name("") + .description("the master Jenkins node") + .jobs(Job.builder() + .name("ddd") + .url(URI.create("http://localhost:8080/job/ddd/")) + .color("grey").build()) + .build(); + } +} diff --git a/labs/jenkins/src/test/resources/job.json b/labs/jenkins/src/test/resources/job.json new file mode 100644 index 0000000000..ebf43f35fb --- /dev/null +++ b/labs/jenkins/src/test/resources/job.json @@ -0,0 +1,29 @@ +{ + "actions": [], + "description": "", + "displayName": "ddd", + "displayNameOrNull": null, + "name": "ddd", + "url": "http://localhost:8080/job/ddd/", + "buildable": true, + "builds": [], + "color": "grey", + "firstBuild": null, + "healthReport": [], + "inQueue": false, + "keepDependencies": false, + "lastBuild": null, + "lastCompletedBuild": null, + "lastFailedBuild": null, + "lastStableBuild": null, + "lastSuccessfulBuild": null, + "lastUnstableBuild": null, + "lastUnsuccessfulBuild": null, + "nextBuildNumber": 1, + "property": [], + "queueItem": null, + "concurrentBuild": false, + "downstreamProjects": [], + "scm": {}, + "upstreamProjects": [] +} \ No newline at end of file diff --git a/labs/jenkins/src/test/resources/master.json b/labs/jenkins/src/test/resources/master.json new file mode 100644 index 0000000000..8e5c622b95 --- /dev/null +++ b/labs/jenkins/src/test/resources/master.json @@ -0,0 +1,26 @@ +{ + "assignedLabels": [{}], + "mode": "NORMAL", + "nodeDescription": "the master Jenkins node", + "nodeName": "", + "numExecutors": 2, + "description": null, + "jobs": [{ + "name": "ddd", + "url": "http://localhost:8080/job/ddd/", + "color": "grey" + }], + "overallLoad": {}, + "primaryView": { + "name": "All", + "url": "http://localhost:8080/" + }, + "quietingDown": false, + "slaveAgentPort": 0, + "useCrumbs": false, + "useSecurity": false, + "views": [{ + "name": "All", + "url": "http://localhost:8080/" + }] +} \ No newline at end of file