diff --git a/src/main/java/com/cdancy/jenkins/rest/domain/common/IntegerResponse.java b/src/main/java/com/cdancy/jenkins/rest/domain/common/IntegerResponse.java
new file mode 100644
index 00000000..a8fe6367
--- /dev/null
+++ b/src/main/java/com/cdancy/jenkins/rest/domain/common/IntegerResponse.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 com.cdancy.jenkins.rest.domain.common;
+
+import java.util.List;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.cdancy.jenkins.rest.JenkinsUtils;
+import com.google.auto.value.AutoValue;
+
+/**
+ * Integer response to be returned when an endpoint returns
+ * an integer.
+ *
+ *
When the HTTP response code is valid the `value` parameter will
+ * be set to the integer value while a non-valid response has the `value` set to
+ * null along with any potential `error` objects returned from Jenkins.
+ */
+@AutoValue
+public abstract class IntegerResponse implements Value, ErrorsHolder {
+
+ @SerializedNames({ "value", "errors" })
+ public static IntegerResponse create(@Nullable final Integer value,
+ final List errors) {
+
+ return new AutoValue_IntegerResponse(value,
+ JenkinsUtils.nullToEmpty(errors));
+ }
+}
diff --git a/src/main/java/com/cdancy/jenkins/rest/fallbacks/JenkinsFallbacks.java b/src/main/java/com/cdancy/jenkins/rest/fallbacks/JenkinsFallbacks.java
index 08d88027..dfc8e874 100644
--- a/src/main/java/com/cdancy/jenkins/rest/fallbacks/JenkinsFallbacks.java
+++ b/src/main/java/com/cdancy/jenkins/rest/fallbacks/JenkinsFallbacks.java
@@ -22,6 +22,7 @@
import static org.jclouds.http.HttpUtils.returnValueOnCodeOrNull;
+import com.cdancy.jenkins.rest.domain.common.IntegerResponse;
import com.cdancy.jenkins.rest.domain.common.RequestStatus;
import com.cdancy.jenkins.rest.domain.common.Error;
import com.cdancy.jenkins.rest.domain.crumb.Crumb;
@@ -80,6 +81,20 @@ public Object createOrPropagate(final Throwable throwable) throws Exception {
}
}
+ public static final class IntegerResponseOnError implements Fallback {
+ @Override
+ public Object createOrPropagate(final Throwable throwable) throws Exception {
+ if (checkNotNull(throwable, "throwable") != null) {
+ try {
+ return IntegerResponse.create(null, getErrors(throwable));
+ } catch (JsonSyntaxException e) {
+ return IntegerResponse.create(null, getErrors(e));
+ }
+ }
+ throw propagate(throwable);
+ }
+ }
+
public static final class CrumbOnError implements Fallback {
@Override
public Object createOrPropagate(final Throwable throwable) throws Exception {
diff --git a/src/main/java/com/cdancy/jenkins/rest/features/JobsApi.java b/src/main/java/com/cdancy/jenkins/rest/features/JobsApi.java
index a909c99d..08053525 100644
--- a/src/main/java/com/cdancy/jenkins/rest/features/JobsApi.java
+++ b/src/main/java/com/cdancy/jenkins/rest/features/JobsApi.java
@@ -40,6 +40,7 @@
import org.jclouds.rest.annotations.ResponseParser;
import com.cdancy.jenkins.rest.binders.BindMapToForm;
+import com.cdancy.jenkins.rest.domain.common.IntegerResponse;
import com.cdancy.jenkins.rest.domain.common.RequestStatus;
import com.cdancy.jenkins.rest.domain.job.BuildInfo;
import com.cdancy.jenkins.rest.domain.job.JobInfo;
@@ -137,19 +138,19 @@ boolean description(@PathParam("name") String jobName,
@Named("jobs:build")
@Path("/job/{name}/build")
- @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+ @Fallback(JenkinsFallbacks.IntegerResponseOnError.class)
@ResponseParser(LocationToQueueId.class)
@Consumes("application/unknown")
@POST
- Integer build(@PathParam("name") String jobName);
+ IntegerResponse build(@PathParam("name") String jobName);
@Named("jobs:build-with-params")
@Path("/job/{name}/buildWithParameters")
- @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+ @Fallback(JenkinsFallbacks.IntegerResponseOnError.class)
@ResponseParser(LocationToQueueId.class)
@Consumes("application/unknown")
@POST
- Integer buildWithParameters(@PathParam("name") String jobName,
+ IntegerResponse buildWithParameters(@PathParam("name") String jobName,
@BinderParam(BindMapToForm.class) Map> properties);
@Named("jobs:last-build-number")
diff --git a/src/main/java/com/cdancy/jenkins/rest/parsers/LocationToQueueId.java b/src/main/java/com/cdancy/jenkins/rest/parsers/LocationToQueueId.java
index e163f5a5..6e80159e 100644
--- a/src/main/java/com/cdancy/jenkins/rest/parsers/LocationToQueueId.java
+++ b/src/main/java/com/cdancy/jenkins/rest/parsers/LocationToQueueId.java
@@ -17,6 +17,7 @@
package com.cdancy.jenkins.rest.parsers;
+import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -25,25 +26,31 @@
import org.jclouds.http.HttpResponse;
import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+
+import com.cdancy.jenkins.rest.domain.common.Error;
+import com.cdancy.jenkins.rest.domain.common.IntegerResponse;
/**
* Created by dancc on 3/11/16.
*/
@Singleton
-public class LocationToQueueId implements Function {
+public class LocationToQueueId implements Function {
private static final Pattern pattern = Pattern.compile("^.*/queue/item/(\\d+)/$");
- public Integer apply(HttpResponse response) {
+ public IntegerResponse apply(HttpResponse response) {
String url = response.getFirstHeaderOrNull("Location");
if (url != null) {
Matcher matcher = pattern.matcher(url);
if (matcher.find() && matcher.groupCount() == 1) {
- return Integer.valueOf(matcher.group(1));
+ return IntegerResponse.create(Integer.valueOf(matcher.group(1)), null);
}
}
-
- return 0;
+ final Error error = Error.create(null,
+ "No queue item Location header could be found despite getting a valid HTTP response.",
+ NumberFormatException.class.getCanonicalName());
+ return IntegerResponse.create(null, Lists.newArrayList(error));
}
}
diff --git a/src/test/java/com/cdancy/jenkins/rest/features/JobsApiLiveTest.java b/src/test/java/com/cdancy/jenkins/rest/features/JobsApiLiveTest.java
index 99e34f1c..9df342ba 100644
--- a/src/test/java/com/cdancy/jenkins/rest/features/JobsApiLiveTest.java
+++ b/src/test/java/com/cdancy/jenkins/rest/features/JobsApiLiveTest.java
@@ -28,6 +28,7 @@
import org.testng.annotations.Test;
import com.cdancy.jenkins.rest.BaseJenkinsApiLiveTest;
+import com.cdancy.jenkins.rest.domain.common.IntegerResponse;
import com.cdancy.jenkins.rest.domain.common.RequestStatus;
import com.cdancy.jenkins.rest.domain.job.BuildInfo;
import com.cdancy.jenkins.rest.domain.job.JobInfo;
@@ -38,7 +39,7 @@
@Test(groups = "live", testName = "SystemApiLiveTest", singleThreaded = true)
public class JobsApiLiveTest extends BaseJenkinsApiLiveTest {
- private Integer queueId;
+ private IntegerResponse queueId;
private Integer buildNumber;
@Test
@@ -74,7 +75,8 @@ public void testLastBuildTimestampOnJobWithNoBuilds() {
public void testBuildJob() {
queueId = api().build("DevTest");
assertNotNull(queueId);
- assertTrue(queueId > 0);
+ assertTrue(queueId.value() > 0);
+ assertTrue(queueId.errors().size() == 0);
}
@Test(dependsOnMethods = "testBuildJob")
@@ -108,7 +110,7 @@ public void testGetBuildInfo() {
BuildInfo output = api().buildInfo("DevTest", buildNumber);
assertNotNull(output);
assertTrue(output.fullDisplayName().equals("DevTest #" + buildNumber));
- assertTrue(output.queueId() == queueId);
+ assertTrue(output.queueId() == queueId.value());
}
@Test(dependsOnMethods = "testGetBuildInfo")
@@ -147,9 +149,10 @@ public void testUpdateConfig() {
public void testBuildJobWithParameters() {
Map> params = new HashMap<>();
params.put("SomeKey", Lists.newArrayList("SomeVeryNewValue"));
- Integer output = api().buildWithParameters("DevTest", params);
+ IntegerResponse output = api().buildWithParameters("DevTest", params);
assertNotNull(output);
- assertTrue(output > 0);
+ assertTrue(output.value() > 0);
+ assertTrue(output.errors().size() == 0);
}
@Test(dependsOnMethods = "testBuildJobWithParameters")
@@ -216,8 +219,13 @@ public void testGetDescriptionNonExistentJob() {
@Test
public void testBuildNonExistentJob() {
- Integer output = api().build(randomString());
- assertNull(output);
+ IntegerResponse output = api().build(randomString());
+ assertNotNull(output);
+ assertNull(output.value());
+ assertTrue(output.errors().size() > 0);
+ assertNotNull(output.errors().get(0).context());
+ assertNotNull(output.errors().get(0).message());
+ assertTrue(output.errors().get(0).exceptionName().equals("org.jclouds.rest.ResourceNotFoundException"));
}
@Test
@@ -230,8 +238,13 @@ public void testGetBuildInfoNonExistentJob() {
public void testBuildNonExistentJobWithParams() {
Map> params = new HashMap<>();
params.put("SomeKey", Lists.newArrayList("SomeVeryNewValue"));
- Integer output = api().buildWithParameters(randomString(), params);
- assertNull(output);
+ IntegerResponse output = api().buildWithParameters(randomString(), params);
+ assertNotNull(output);
+ assertNull(output.value());
+ assertTrue(output.errors().size() > 0);
+ assertNotNull(output.errors().get(0).context());
+ assertNotNull(output.errors().get(0).message());
+ assertTrue(output.errors().get(0).exceptionName().equals("org.jclouds.rest.ResourceNotFoundException"));
}
private JobsApi api() {
diff --git a/src/test/java/com/cdancy/jenkins/rest/features/JobsApiMockTest.java b/src/test/java/com/cdancy/jenkins/rest/features/JobsApiMockTest.java
index fb8258aa..69a30485 100644
--- a/src/test/java/com/cdancy/jenkins/rest/features/JobsApiMockTest.java
+++ b/src/test/java/com/cdancy/jenkins/rest/features/JobsApiMockTest.java
@@ -34,6 +34,7 @@
import com.cdancy.jenkins.rest.domain.job.JobInfo;
import com.cdancy.jenkins.rest.domain.job.ProgressiveText;
import com.cdancy.jenkins.rest.BaseJenkinsMockTest;
+import com.cdancy.jenkins.rest.domain.common.IntegerResponse;
import com.cdancy.jenkins.rest.domain.common.RequestStatus;
import com.google.common.collect.Lists;
@@ -399,9 +400,10 @@ public void testBuildJob() throws Exception {
JenkinsApi jenkinsApi = api(server.getUrl("/"));
JobsApi api = jenkinsApi.jobsApi();
try {
- Integer output = api.build("DevTest");
+ IntegerResponse output = api.build("DevTest");
assertNotNull(output);
- assertTrue(output == 1);
+ assertTrue(output.value() == 1);
+ assertTrue(output.errors().size() == 0);
assertSentAccept(server, "POST", "/job/DevTest/build", "application/unknown");
} finally {
jenkinsApi.close();
@@ -417,9 +419,13 @@ public void testBuildJobWithNoLocationReturned() throws Exception {
JenkinsApi jenkinsApi = api(server.getUrl("/"));
JobsApi api = jenkinsApi.jobsApi();
try {
- Integer output = api.build("DevTest");
+ IntegerResponse output = api.build("DevTest");
assertNotNull(output);
- assertTrue(output == 0);
+ assertNull(output.value());
+ assertTrue(output.errors().size() == 1);
+ assertNull(output.errors().get(0).context());
+ assertTrue(output.errors().get(0).message().equals("No queue item Location header could be found despite getting a valid HTTP response."));
+ assertTrue(output.errors().get(0).exceptionName().equals(NumberFormatException.class.getCanonicalName()));
assertSentAccept(server, "POST", "/job/DevTest/build", "application/unknown");
} finally {
jenkinsApi.close();
@@ -434,8 +440,13 @@ public void testBuildJobNonExistentJob() throws Exception {
JenkinsApi jenkinsApi = api(server.getUrl("/"));
JobsApi api = jenkinsApi.jobsApi();
try {
- Integer output = api.build("DevTest");
- assertNull(output);
+ IntegerResponse output = api.build("DevTest");
+ assertNotNull(output);
+ assertNull(output.value());
+ assertTrue(output.errors().size() == 1);
+ assertTrue(output.errors().get(0).message().equals(""));
+ assertTrue(output.errors().get(0).exceptionName().equals("org.jclouds.rest.ResourceNotFoundException"));
+ assertNotNull(output.errors().get(0).context());
assertSentAccept(server, "POST", "/job/DevTest/build", "application/unknown");
} finally {
jenkinsApi.close();
@@ -453,9 +464,10 @@ public void testBuildJobWithParams() throws Exception {
try {
Map> params = new HashMap<>();
params.put("SomeKey", Lists.newArrayList("SomeVeryNewValue"));
- Integer output = api.buildWithParameters("DevTest", params);
+ IntegerResponse output = api.buildWithParameters("DevTest", params);
assertNotNull(output);
- assertTrue(output == 1);
+ assertTrue(output.value() == 1);
+ assertTrue(output.errors().size() == 0);
assertSentAccept(server, "POST", "/job/DevTest/buildWithParameters", "application/unknown");
} finally {
jenkinsApi.close();
@@ -472,8 +484,13 @@ public void testBuildJobWithParamsNonExistentJob() throws Exception {
try {
Map> params = new HashMap<>();
params.put("SomeKey", Lists.newArrayList("SomeVeryNewValue"));
- Integer output = api.buildWithParameters("DevTest", params);
- assertNull(output);
+ IntegerResponse output = api.buildWithParameters("DevTest", params);
+ assertNotNull(output);
+ assertNull(output.value());
+ assertTrue(output.errors().size() == 1);
+ assertTrue(output.errors().get(0).message().equals(""));
+ assertTrue(output.errors().get(0).exceptionName().equals("org.jclouds.rest.ResourceNotFoundException"));
+ assertNotNull(output.errors().get(0).context());
assertSentAccept(server, "POST", "/job/DevTest/buildWithParameters", "application/unknown");
} finally {
jenkinsApi.close();
diff --git a/src/test/java/com/cdancy/jenkins/rest/features/QueueApiLiveTest.java b/src/test/java/com/cdancy/jenkins/rest/features/QueueApiLiveTest.java
index 3aea7c83..713edc88 100644
--- a/src/test/java/com/cdancy/jenkins/rest/features/QueueApiLiveTest.java
+++ b/src/test/java/com/cdancy/jenkins/rest/features/QueueApiLiveTest.java
@@ -31,6 +31,7 @@
import org.testng.annotations.Test;
import com.cdancy.jenkins.rest.BaseJenkinsApiLiveTest;
+import com.cdancy.jenkins.rest.domain.common.IntegerResponse;
import com.cdancy.jenkins.rest.domain.common.RequestStatus;
import com.cdancy.jenkins.rest.domain.queue.QueueItem;
@@ -57,15 +58,17 @@ public void init() {
@Test
public void testGetQueue() {
- Integer job1 = api.jobsApi().build("QueueTest");
+ IntegerResponse job1 = api.jobsApi().build("QueueTest");
assertNotNull(job1);
- Integer job2 = api.jobsApi().build("QueueTest");
+ assertTrue(job1.errors().size() == 0);
+ IntegerResponse job2 = api.jobsApi().build("QueueTest");
assertNotNull(job2);
+ assertTrue(job2.errors().size() == 0);
List queueItems = api().queue();
assertTrue(queueItems.size() > 0);
boolean foundLastKickedJob = false;
for (QueueItem item : queueItems) {
- if (item.id() == job2) {
+ if (item.id() == job2.value()) {
foundLastKickedJob = true;
break;
}
@@ -75,13 +78,15 @@ public void testGetQueue() {
@Test
public void testGetPendingQueueItem() {
- Integer job1 = api.jobsApi().build("QueueTest");
+ IntegerResponse job1 = api.jobsApi().build("QueueTest");
assertNotNull(job1);
- Integer job2 = api.jobsApi().build("QueueTest");
+ assertTrue(job1.errors().size() == 0);
+ IntegerResponse job2 = api.jobsApi().build("QueueTest");
assertNotNull(job2);
+ assertTrue(job2.errors().size() == 0);
// job2 is queue after job1, so while job1 runs, job2 is pending in the queue
- QueueItem queueItem = api().queueItem(job2);
+ QueueItem queueItem = api().queueItem(job2.value());
assertFalse(queueItem.cancelled());
assertNotNull(queueItem.why());
assertNull(queueItem.executable());
@@ -89,13 +94,15 @@ public void testGetPendingQueueItem() {
@Test
public void testGetRunningQueueItem() throws InterruptedException {
- Integer job1 = api.jobsApi().build("QueueTest");
+ IntegerResponse job1 = api.jobsApi().build("QueueTest");
assertNotNull(job1);
- Integer job2 = api.jobsApi().build("QueueTest");
+ assertTrue(job1.errors().size() == 0);
+ IntegerResponse job2 = api.jobsApi().build("QueueTest");
assertNotNull(job2);
+ assertTrue(job2.errors().size() == 0);
// job1 runs first, so we get its queueItem
- QueueItem queueItem = getRunningQueueItem(job1);
+ QueueItem queueItem = getRunningQueueItem(job1.value());
// If null, it means the queueItem has been cancelled, which would not be normal in this test
assertNotNull(queueItem);
@@ -114,17 +121,21 @@ public void testGetRunningQueueItem() throws InterruptedException {
public void testQueueItemSingleParameters() throws InterruptedException {
Map> params = new HashMap<>();
params.put("SomeKey", Lists.newArrayList("SomeVeryNewValue1"));
- Integer job1 = api.jobsApi().buildWithParameters("QueueTestSingleParam", params);
+ IntegerResponse job1 = api.jobsApi().buildWithParameters("QueueTestSingleParam", params);
assertNotNull(job1);
+ assertTrue(job1.value() > 0);
+ assertTrue(job1.errors().size() == 0);
// Jenkins will reject two consecutive build requests when the build parameter values are the same
// So we must set some different parameter values
params = new HashMap<>();
params.put("SomeKey", Lists.newArrayList("SomeVeryNewValue2"));
- Integer job2 = api.jobsApi().buildWithParameters("QueueTestSingleParam", params);
+ IntegerResponse job2 = api.jobsApi().buildWithParameters("QueueTestSingleParam", params);
assertNotNull(job2);
+ assertTrue(job2.value() > 0);
+ assertTrue(job2.errors().size() == 0);
- QueueItem queueItem = getRunningQueueItem(job1);
+ QueueItem queueItem = getRunningQueueItem(job1.value());
assertNotNull(queueItem);
assertFalse(queueItem.cancelled());
@@ -137,17 +148,21 @@ public void testQueueItemSingleParameters() throws InterruptedException {
public void testQueueItemMultipleParameters() throws InterruptedException {
Map> params = new HashMap<>();
params.put("SomeKey1", Lists.newArrayList("SomeVeryNewValue1"));
- Integer job1 = api.jobsApi().buildWithParameters("QueueTestMultipleParams",params);
+ IntegerResponse job1 = api.jobsApi().buildWithParameters("QueueTestMultipleParams",params);
assertNotNull(job1);
+ assertTrue(job1.value() > 0);
+ assertTrue(job1.errors().size() == 0);
// Jenkins will reject two consecutive build requests when the build parameter values are the same
// So we must set some different parameter values
params = new HashMap<>();
params.put("SomeKey1", Lists.newArrayList("SomeVeryNewValue2"));
- Integer job2 = api.jobsApi().buildWithParameters("QueueTestMultipleParams", params);
+ IntegerResponse job2 = api.jobsApi().buildWithParameters("QueueTestMultipleParams", params);
assertNotNull(job2);
+ assertTrue(job2.value() > 0);
+ assertTrue(job2.errors().size() == 0);
- QueueItem queueItem = getRunningQueueItem(job1);
+ QueueItem queueItem = getRunningQueueItem(job1.value());
assertNotNull(queueItem);
assertFalse(queueItem.cancelled());
@@ -160,17 +175,19 @@ public void testQueueItemMultipleParameters() throws InterruptedException {
@Test
public void testGetCancelledQueueItem() throws InterruptedException {
- Integer job1 = api.jobsApi().build("QueueTest");
+ IntegerResponse job1 = api.jobsApi().build("QueueTest");
assertNotNull(job1);
- Integer job2 = api.jobsApi().build("QueueTest");
+ assertTrue(job1.errors().size() == 0);
+ IntegerResponse job2 = api.jobsApi().build("QueueTest");
assertNotNull(job2);
+ assertTrue(job2.errors().size() == 0);
- RequestStatus success = api().cancel(job2);
+ RequestStatus success = api().cancel(job2.value());
assertNotNull(success);
assertTrue(success.value());
assertTrue(success.errors().isEmpty());
- QueueItem queueItem = api().queueItem(job2);
+ QueueItem queueItem = api().queueItem(job2.value());
assertTrue(queueItem.cancelled());
assertNull(queueItem.why());
assertNull(queueItem.executable());