Skip to content

Commit

Permalink
Issue-315: Processing Queue ID as a long value
Browse files Browse the repository at this point in the history
Queue ID retured from Jenkins can be a Long value. This is due to a recent update which resulted in the queue IDs returning a random long values instead of sequential integers. The jenkins-rest API expects this to be an Integer value. As a result, calls to jobsApi().buildWithParameters and then attempting to retrieve the queue ID will fail/return null.

This PR fixes the JobsApi and QueueApi to deal with the new long values.

Please refer to issue [315](cdancy#315)
  • Loading branch information
Akhil Mehra committed Jul 13, 2024
1 parent 7a7408c commit beaeb45
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,21 @@
import com.google.auto.value.AutoValue;

/**
* Integer response to be returned when an endpoint returns
* an integer.
*
* <p>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
* Long response to be returned when an endpoint returns
* an long.
*
* <p>When the HTTP response code is valid the `value` parameter will
* be set to the long 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<Integer>, ErrorsHolder {
public abstract class LongResponse implements Value<Long>, ErrorsHolder {

@SerializedNames({ "value", "errors" })
public static IntegerResponse create(@Nullable final Integer value,
public static LongResponse create(@Nullable final Long value,
final List<Error> errors) {
return new AutoValue_IntegerResponse(value,

return new AutoValue_LongResponse(value,
JenkinsUtils.nullToEmpty(errors));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import static org.jclouds.http.HttpUtils.returnValueOnCodeOrNull;

import com.cdancy.jenkins.rest.domain.common.IntegerResponse;
import com.cdancy.jenkins.rest.domain.common.LongResponse;
import com.cdancy.jenkins.rest.domain.common.RequestStatus;
import com.cdancy.jenkins.rest.domain.common.Error;
import com.cdancy.jenkins.rest.domain.crumb.Crumb;
Expand Down Expand Up @@ -59,14 +59,14 @@ public Object createOrPropagate(final Throwable throwable) {
}
}

public static final class IntegerResponseOnError implements Fallback<Object> {
public static final class LongResponseOnError implements Fallback<Object> {
@Override
public Object createOrPropagate(final Throwable throwable) {
checkNotNull(throwable, "throwable");
try {
return IntegerResponse.create(null, getErrors(throwable));
return LongResponse.create(null, getErrors(throwable));
} catch (JsonSyntaxException e) {
return IntegerResponse.create(null, getErrors(e));
return LongResponse.create(null, getErrors(e));
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/com/cdancy/jenkins/rest/features/JobsApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,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.LongResponse;
import com.cdancy.jenkins.rest.domain.common.RequestStatus;
import com.cdancy.jenkins.rest.fallbacks.JenkinsFallbacks;
import com.cdancy.jenkins.rest.filters.JenkinsAuthenticationFilter;
Expand Down Expand Up @@ -164,11 +164,11 @@ boolean disable(@Nullable @PathParam("optionalFolderPath") @ParamParser(Optional

@Named("jobs:build")
@Path("{optionalFolderPath}job/{name}/build")
@Fallback(JenkinsFallbacks.IntegerResponseOnError.class)
@Fallback(JenkinsFallbacks.LongResponseOnError.class)
@ResponseParser(LocationToQueueId.class)
@Consumes("application/unknown")
@POST
IntegerResponse build(@Nullable @PathParam("optionalFolderPath") @ParamParser(OptionalFolderPathParser.class) String optionalFolderPath,
LongResponse build(@Nullable @PathParam("optionalFolderPath") @ParamParser(OptionalFolderPathParser.class) String optionalFolderPath,
@PathParam("name") String jobName);

@Named("jobs:stop-build")
Expand Down Expand Up @@ -203,11 +203,11 @@ RequestStatus kill(@Nullable @PathParam("optionalFolderPath") @ParamParser(Optio

@Named("jobs:build-with-params")
@Path("{optionalFolderPath}job/{name}/buildWithParameters")
@Fallback(JenkinsFallbacks.IntegerResponseOnError.class)
@Fallback(JenkinsFallbacks.LongResponseOnError.class)
@ResponseParser(LocationToQueueId.class)
@Consumes("application/unknown")
@POST
IntegerResponse buildWithParameters(@Nullable @PathParam("optionalFolderPath") @ParamParser(OptionalFolderPathParser.class) String optionalFolderPath,
LongResponse buildWithParameters(@Nullable @PathParam("optionalFolderPath") @ParamParser(OptionalFolderPathParser.class) String optionalFolderPath,
@PathParam("name") String jobName,
@Nullable @BinderParam(BindMapToForm.class) Map<String, List<String>> properties);

Expand Down
8 changes: 4 additions & 4 deletions src/main/java/com/cdancy/jenkins/rest/features/QueueApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public interface QueueApi {

/**
* Get a specific queue item.
*
*
* Queue items are builds that have been scheduled to run, but are waiting for a slot.
* You can poll the queueItem that corresponds to a build to detect whether the build is still pending or is executing.
* @param queueId The queue id value as returned by the JobsApi build or buildWithParameters methods.
Expand All @@ -61,11 +61,11 @@ public interface QueueApi {
@Named("queue:item")
@Path("/item/{queueId}/api/json")
@GET
QueueItem queueItem(@PathParam("queueId") int queueId);
QueueItem queueItem(@PathParam("queueId") long queueId);

/**
* Cancel a queue item before it gets built.
*
*
* @param id The queue id value of the queue item to cancel.
* This is the value is returned by the JobsApi build or buildWithParameters methods.
* @return Always returns true due to JENKINS-21311.
Expand All @@ -75,5 +75,5 @@ public interface QueueApi {
@Fallback(JenkinsFallbacks.JENKINS_21311.class)
@ResponseParser(RequestStatusParser.class)
@POST
RequestStatus cancel(@FormParam("id") int id);
RequestStatus cancel(@FormParam("id") long id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@
import com.google.common.collect.Lists;

import com.cdancy.jenkins.rest.domain.common.Error;
import com.cdancy.jenkins.rest.domain.common.IntegerResponse;
import com.cdancy.jenkins.rest.domain.common.LongResponse;

/**
* Created by dancc on 3/11/16.
*/
@Singleton
public class LocationToQueueId implements Function<HttpResponse, IntegerResponse> {
public class LocationToQueueId implements Function<HttpResponse, LongResponse> {

private static final Pattern pattern = Pattern.compile("^.*/queue/item/(\\d+)/$");

public IntegerResponse apply(HttpResponse response) {
public LongResponse apply(HttpResponse response) {
if (response == null) {
throw new RuntimeException("Unexpected NULL HttpResponse object");
}
Expand All @@ -47,12 +47,12 @@ public IntegerResponse apply(HttpResponse response) {
if (url != null) {
Matcher matcher = pattern.matcher(url);
if (matcher.find() && matcher.groupCount() == 1) {
return IntegerResponse.create(Integer.valueOf(matcher.group(1)), null);
return LongResponse.create(Long.valueOf(matcher.group(1)), null);
}
}
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));
return LongResponse.create(null, Lists.newArrayList(error));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ protected Iterable<Module> setupModules() {
* The caller has to check the value of queueItem.executable, and if it is null, the queue item is still pending.
*
*/
protected QueueItem getRunningQueueItem(int queueId) throws InterruptedException {
protected QueueItem getRunningQueueItem(long queueId) throws InterruptedException {
int max = 10;
QueueItem queueItem = api.queueApi().queueItem(queueId);
while (max > 0) {
Expand Down
36 changes: 18 additions & 18 deletions src/test/java/com/cdancy/jenkins/rest/features/JobsApiLiveTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import java.util.Map;

import com.cdancy.jenkins.rest.BaseJenkinsApiLiveTest;
import com.cdancy.jenkins.rest.domain.common.IntegerResponse;
import com.cdancy.jenkins.rest.domain.common.LongResponse;
import com.cdancy.jenkins.rest.domain.common.RequestStatus;
import com.cdancy.jenkins.rest.domain.job.*;
import com.cdancy.jenkins.rest.domain.plugins.Plugin;
Expand All @@ -35,8 +35,8 @@
@Test(groups = "live", testName = "JobsApiLiveTest", singleThreaded = true)
public class JobsApiLiveTest extends BaseJenkinsApiLiveTest {

private IntegerResponse queueId;
private IntegerResponse queueIdForAnotherJob;
private LongResponse queueId;
private LongResponse queueIdForAnotherJob;
private Integer buildNumber;
private static final String FOLDER_PLUGIN_NAME = "cloudbees-folder";
private static final String FOLDER_PLUGIN_VERSION = "latest";
Expand All @@ -58,7 +58,7 @@ public void testStopFreeStyleBuild() throws InterruptedException {
String config = payloadFromResource("/freestyle-project-sleep-10-task.xml");
RequestStatus createStatus = api().create(null, FREESTYLE_JOB_NAME, config);
assertTrue(createStatus.value());
IntegerResponse qId = api().build(null, FREESTYLE_JOB_NAME);
LongResponse qId = api().build(null, FREESTYLE_JOB_NAME);
assertNotNull(qId);
assertTrue(qId.value() > 0);
QueueItem queueItem = getRunningQueueItem(qId.value());
Expand All @@ -73,7 +73,7 @@ public void testStopFreeStyleBuild() throws InterruptedException {

@Test(dependsOnMethods = "testStopFreeStyleBuild")
public void testTermFreeStyleBuild() throws InterruptedException {
IntegerResponse qId = api().build(null, FREESTYLE_JOB_NAME);
LongResponse qId = api().build(null, FREESTYLE_JOB_NAME);
assertNotNull(qId);
assertTrue(qId.value() > 0);
QueueItem queueItem = getRunningQueueItem(qId.value());
Expand All @@ -95,7 +95,7 @@ public void testTermFreeStyleBuild() throws InterruptedException {

@Test(dependsOnMethods = "testTermFreeStyleBuild")
public void testKillFreeStyleBuild() throws InterruptedException {
IntegerResponse qId = api().build(null, FREESTYLE_JOB_NAME);
LongResponse qId = api().build(null, FREESTYLE_JOB_NAME);
assertNotNull(qId);
assertTrue(qId.value() > 0);
QueueItem queueItem = getRunningQueueItem(qId.value());
Expand Down Expand Up @@ -126,7 +126,7 @@ public void testStopPipelineBuild() throws InterruptedException {
String config = payloadFromResource("/pipeline.xml");
RequestStatus createStatus = api().create(null, PIPELINE_JOB_NAME, config);
assertTrue(createStatus.value());
IntegerResponse qId = api().build(null, PIPELINE_JOB_NAME);
LongResponse qId = api().build(null, PIPELINE_JOB_NAME);
assertNotNull(qId);
assertTrue(qId.value() > 0);
QueueItem queueItem = getRunningQueueItem(qId.value());
Expand All @@ -141,7 +141,7 @@ public void testStopPipelineBuild() throws InterruptedException {

@Test(dependsOnMethods = "testStopPipelineBuild")
public void testTermPipelineBuild() throws InterruptedException {
IntegerResponse qId = api().build(null, PIPELINE_JOB_NAME);
LongResponse qId = api().build(null, PIPELINE_JOB_NAME);
assertNotNull(qId);
assertTrue(qId.value() > 0);
QueueItem queueItem = getRunningQueueItem(qId.value());
Expand All @@ -156,7 +156,7 @@ public void testTermPipelineBuild() throws InterruptedException {

@Test(dependsOnMethods = "testTermPipelineBuild")
public void testKillPipelineBuild() throws InterruptedException {
IntegerResponse qId = api().build(null, PIPELINE_JOB_NAME);
LongResponse qId = api().build(null, PIPELINE_JOB_NAME);
assertNotNull(qId);
assertTrue(qId.value() > 0);
QueueItem queueItem = getRunningQueueItem(qId.value());
Expand Down Expand Up @@ -241,7 +241,7 @@ public void testGetBuildInfo() {
BuildInfo output = api().buildInfo(null, "DevTest", buildNumber);
assertNotNull(output);
assertEquals("DevTest #" + buildNumber, output.fullDisplayName());
assertEquals((int) queueId.value(), output.queueId());
assertEquals((long) queueId.value(), output.queueId());
}

@Test(dependsOnMethods = "testGetBuildInfo")
Expand All @@ -255,7 +255,7 @@ public void testBuildInfoActions() throws InterruptedException {
String config = payloadFromResource("/pipeline-with-action.xml");
RequestStatus createStatus = api().create(null, PIPELINE_WITH_ACTION_JOB_NAME, config);
assertTrue(createStatus.value());
IntegerResponse qId = api().build(null, PIPELINE_WITH_ACTION_JOB_NAME);
LongResponse qId = api().build(null, PIPELINE_WITH_ACTION_JOB_NAME);
assertNotNull(qId);
assertTrue(qId.value() > 0);
QueueItem queueItem = getRunningQueueItem(qId.value());
Expand Down Expand Up @@ -319,23 +319,23 @@ public void testUpdateConfig() {
public void testBuildJobWithParameters() {
Map<String, List<String>> params = new HashMap<>();
params.put("SomeKey", Lists.newArrayList("SomeVeryNewValue"));
IntegerResponse output = api().buildWithParameters(null, "DevTest", params);
LongResponse output = api().buildWithParameters(null, "DevTest", params);
assertNotNull(output);
assertTrue(output.value() > 0);
assertEquals(output.errors().size(), 0);
}

@Test(dependsOnMethods = "testBuildJobWithParameters")
public void testBuildJobWithNullParametersMap() {
IntegerResponse output = api().buildWithParameters(null, "DevTest", null);
LongResponse output = api().buildWithParameters(null, "DevTest", null);
assertNotNull(output);
assertTrue(output.value() > 0);
assertEquals(output.errors().size(), 0);
}

@Test(dependsOnMethods = "testBuildJobWithNullParametersMap")
public void testBuildJobWithEmptyParametersMap() {
IntegerResponse output = api().buildWithParameters(null, "DevTest", new HashMap<>());
LongResponse output = api().buildWithParameters(null, "DevTest", new HashMap<>());
assertNotNull(output);
assertNull(output.value());
assertEquals(output.errors().size(), 1);
Expand Down Expand Up @@ -508,7 +508,7 @@ public void testGetBuildInfoOfJobInFolder() {
BuildInfo output = api().buildInfo("test-folder/test-folder-1", "JobInFolder", 1);
assertNotNull(output);
assertTrue(output.fullDisplayName().contains("JobInFolder #1"));
assertEquals((int) queueIdForAnotherJob.value(), output.queueId());
assertEquals((long) queueIdForAnotherJob.value(), output.queueId());
}

@Test(dependsOnMethods = "testGetProgressiveText")
Expand Down Expand Up @@ -549,7 +549,7 @@ public void testBuildWithParametersOfJobForEmptyAndNullParams() throws Interrupt
Map<String, List<String>> params = new HashMap<>();
params.put("SomeKey1", Lists.newArrayList(""));
params.put("SomeKey2", null);
IntegerResponse job1 = api.jobsApi().buildWithParameters(null, "JobForEmptyAndNullParams", params);
LongResponse job1 = api.jobsApi().buildWithParameters(null, "JobForEmptyAndNullParams", params);
assertNotNull(job1);
assertTrue(job1.value() > 0);
assertEquals(job1.errors().size(), 0);
Expand Down Expand Up @@ -639,7 +639,7 @@ public void testGetDescriptionNonExistentJob() {

@Test
public void testBuildNonExistentJob() {
IntegerResponse output = api().build(null, randomString());
LongResponse output = api().build(null, randomString());
assertNotNull(output);
assertNull(output.value());
assertTrue(output.errors().size() > 0);
Expand All @@ -658,7 +658,7 @@ public void testGetBuildInfoNonExistentJob() {
public void testBuildNonExistentJobWithParams() {
Map<String, List<String>> params = new HashMap<>();
params.put("SomeKey", Lists.newArrayList("SomeVeryNewValue"));
IntegerResponse output = api().buildWithParameters(null, randomString(), params);
LongResponse output = api().buildWithParameters(null, randomString(), params);
assertNotNull(output);
assertNull(output.value());
assertTrue(output.errors().size() > 0);
Expand Down
Loading

0 comments on commit beaeb45

Please sign in to comment.