From 7fdad6c889a6cca52a00e953081346b18910f2cf Mon Sep 17 00:00:00 2001 From: Wei Yuan Cho Date: Mon, 20 Jan 2020 13:47:09 +0000 Subject: [PATCH] API to get list of Jenkins Jobs --- .../cdancy/jenkins/rest/domain/job/Job.java | 24 ++++++++++ .../jenkins/rest/domain/job/JobList.java | 44 +++++++++++++++++ .../cdancy/jenkins/rest/features/JobsApi.java | 14 +++--- .../rest/parsers/FolderPathParser.java | 37 +++++++++++++++ .../rest/features/JobsApiLiveTest.java | 30 ++++++++---- .../rest/features/JobsApiMockTest.java | 47 ++++++++++++++++--- src/test/resources/jobsInJenkinsFolder.json | 10 ++++ src/test/resources/jobsInRootFolder.json | 36 ++++++++++++++ 8 files changed, 220 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/cdancy/jenkins/rest/domain/job/Job.java create mode 100644 src/main/java/com/cdancy/jenkins/rest/domain/job/JobList.java create mode 100644 src/main/java/com/cdancy/jenkins/rest/parsers/FolderPathParser.java create mode 100644 src/test/resources/jobsInJenkinsFolder.json create mode 100644 src/test/resources/jobsInRootFolder.json diff --git a/src/main/java/com/cdancy/jenkins/rest/domain/job/Job.java b/src/main/java/com/cdancy/jenkins/rest/domain/job/Job.java new file mode 100644 index 00000000..a291c80b --- /dev/null +++ b/src/main/java/com/cdancy/jenkins/rest/domain/job/Job.java @@ -0,0 +1,24 @@ +package com.cdancy.jenkins.rest.domain.job; + +import com.google.auto.value.AutoValue; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; + +@AutoValue +public abstract class Job { + + @Nullable + public abstract String clazz(); + + public abstract String name(); + + public abstract String url(); + + Job() { + } + + @SerializedNames({"_class", "name", "url"}) + public static Job create(final String clazz, final String name, final String url) { + return new AutoValue_Job(clazz, name, url); + } +} diff --git a/src/main/java/com/cdancy/jenkins/rest/domain/job/JobList.java b/src/main/java/com/cdancy/jenkins/rest/domain/job/JobList.java new file mode 100644 index 00000000..a5cbedc7 --- /dev/null +++ b/src/main/java/com/cdancy/jenkins/rest/domain/job/JobList.java @@ -0,0 +1,44 @@ +/* + * 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.job; + +import com.google.auto.value.AutoValue; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; + +import java.util.List; + +@AutoValue +public abstract class JobList { + + @Nullable + public abstract String clazz(); + + public abstract List jobs(); + + @Nullable + public abstract String url(); + + JobList() { + } + + @SerializedNames({"_class", "jobs", "url"}) + public static JobList create(final String clazz, final List jobs, final String url) { + return new AutoValue_JobList(clazz, jobs, url); + } +} 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 90d5affc..b3bf05f4 100644 --- a/src/main/java/com/cdancy/jenkins/rest/features/JobsApi.java +++ b/src/main/java/com/cdancy/jenkins/rest/features/JobsApi.java @@ -32,7 +32,7 @@ import javax.ws.rs.core.MediaType; import com.cdancy.jenkins.rest.domain.job.*; -import com.google.gson.JsonObject; +import com.cdancy.jenkins.rest.parsers.*; import org.jclouds.Fallbacks; import org.jclouds.javax.annotation.Nullable; import org.jclouds.rest.annotations.BinderParam; @@ -48,16 +48,18 @@ import com.cdancy.jenkins.rest.domain.common.RequestStatus; import com.cdancy.jenkins.rest.fallbacks.JenkinsFallbacks; import com.cdancy.jenkins.rest.filters.JenkinsAuthenticationFilter; -import com.cdancy.jenkins.rest.parsers.BuildNumberToInteger; -import com.cdancy.jenkins.rest.parsers.LocationToQueueId; -import com.cdancy.jenkins.rest.parsers.OutputToProgressiveText; -import com.cdancy.jenkins.rest.parsers.RequestStatusParser; -import com.cdancy.jenkins.rest.parsers.OptionalFolderPathParser; @RequestFilters(JenkinsAuthenticationFilter.class) @Path("/") public interface JobsApi { + @Named("jobs:get-jobs") + @Path("{folderPath}api/json") + @Fallback(Fallbacks.NullOnNotFoundOr404.class) + @Consumes(MediaType.APPLICATION_JSON) + @GET + JobList jobList(@PathParam("folderPath") @ParamParser(FolderPathParser.class) String folderPath); + @Named("jobs:job-info") @Path("{optionalFolderPath}job/{name}/api/json") @Fallback(Fallbacks.NullOnNotFoundOr404.class) diff --git a/src/main/java/com/cdancy/jenkins/rest/parsers/FolderPathParser.java b/src/main/java/com/cdancy/jenkins/rest/parsers/FolderPathParser.java new file mode 100644 index 00000000..87d7018a --- /dev/null +++ b/src/main/java/com/cdancy/jenkins/rest/parsers/FolderPathParser.java @@ -0,0 +1,37 @@ +package com.cdancy.jenkins.rest.parsers; + +import com.google.common.base.Function; + +import javax.inject.Singleton; + +/* + * Turn the optionalFolderPath param to jenkins URL style + */ +@Singleton +public class FolderPathParser implements Function { + + public static final String EMPTY_STRING = ""; + public static final String FOLDER_NAME_PREFIX = "job/"; + public static final Character FOLDER_NAME_SEPARATOR = '/'; + + @Override + public String apply(Object folderPath) { + final StringBuilder path = new StringBuilder((String) folderPath); + if (path.length() == 0) { + return EMPTY_STRING; + } + + if(path.charAt(0) == FOLDER_NAME_SEPARATOR){ + path.deleteCharAt(0); + } + if(path.charAt(path.length() - 1) == FOLDER_NAME_SEPARATOR) { + path.deleteCharAt(path.length() - 1); + } + final String[] folders = path.toString().split(Character.toString(FOLDER_NAME_SEPARATOR)); + path.setLength(0); + for(final String folder : folders) { + path.append(FOLDER_NAME_PREFIX).append(folder).append(FOLDER_NAME_SEPARATOR); + } + return path.toString(); + } +} 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 4f13b2ee..8a9166e8 100644 --- a/src/test/java/com/cdancy/jenkins/rest/features/JobsApiLiveTest.java +++ b/src/test/java/com/cdancy/jenkins/rest/features/JobsApiLiveTest.java @@ -16,17 +16,11 @@ */ package com.cdancy.jenkins.rest.features; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; - import java.util.HashMap; import java.util.List; import java.util.Map; -import com.cdancy.jenkins.rest.domain.job.Cause; -import com.cdancy.jenkins.rest.domain.job.Parameter; +import com.cdancy.jenkins.rest.domain.job.*; import com.cdancy.jenkins.rest.domain.plugins.Plugin; import com.cdancy.jenkins.rest.domain.plugins.Plugins; import com.cdancy.jenkins.rest.domain.queue.QueueItem; @@ -35,12 +29,11 @@ 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; -import com.cdancy.jenkins.rest.domain.job.ProgressiveText; import com.google.common.collect.Lists; +import static org.testng.Assert.*; + @Test(groups = "live", testName = "JobsApiLiveTest", singleThreaded = true) public class JobsApiLiveTest extends BaseJenkinsApiLiveTest { @@ -57,6 +50,14 @@ public void testCreateJob() { assertTrue(success.value()); } + @Test(dependsOnMethods = "testCreateJob") + public void testGetJobListFromRoot() { + JobList output = api().jobList(""); + assertNotNull(output); + assertFalse(output.jobs().isEmpty()); + assertEquals(output.jobs().size(), 2); + } + @Test(dependsOnMethods = "testCreateJob") public void testGetJobInfo() { JobInfo output = api().jobInfo(null, "DevTest"); @@ -274,6 +275,15 @@ public void testCreateJobWithIncorrectFolderPath() { assertFalse(success.value()); } + @Test(dependsOnMethods = "testCreateJobInFolder") + public void testGetJobListInFolder() { + JobList output = api().jobList("test-folder/test-folder-1"); + assertNotNull(output); + assertFalse(output.jobs().isEmpty()); + assertEquals(output.jobs().size(), 1); + assertEquals(output.jobs().get(0), Job.create("hudson.model.FreeStyleProject", "JobInFolder", "http://127.0.0.1:8080/job/test-folder/job/test-folder-1/job/JobInFolder/")); + } + @Test(dependsOnMethods = "testCreateJobInFolder") public void testUpdateJobConfigInFolder() { String config = payloadFromResource("/freestyle-project.xml"); 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 a88a4fde..8a719714 100644 --- a/src/test/java/com/cdancy/jenkins/rest/features/JobsApiMockTest.java +++ b/src/test/java/com/cdancy/jenkins/rest/features/JobsApiMockTest.java @@ -16,12 +16,6 @@ */ package com.cdancy.jenkins.rest.features; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; - -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -41,12 +35,53 @@ import com.squareup.okhttp.mockwebserver.MockResponse; import com.squareup.okhttp.mockwebserver.MockWebServer; +import static org.testng.Assert.*; + /** * Mock tests for the {@link com.cdancy.jenkins.rest.features.JobsApi} class. */ @Test(groups = "unit", testName = "JobsApiMockTest") public class JobsApiMockTest extends BaseJenkinsMockTest { + public void testGetInnerFolderJobList() throws Exception { + MockWebServer server = mockWebServer(); + + String body = payloadFromResource("/jobsInJenkinsFolder.json"); + server.enqueue(new MockResponse().setBody(body).setResponseCode(200)); + JenkinsApi jenkinsApi = api(server.url("/").url()); + JobsApi api = jenkinsApi.jobsApi(); + try { + JobList output = api.jobList("Folder1/Folder 2"); + assertNotNull(output); + assertNotNull(output.jobs()); + assertEquals(output.jobs().size(), 1); + assertEquals(output.jobs().get(0), Job.create("hudson.model.FreeStyleProject", "Test Project", "http://localhost:8080/job/username")); + assertSent(server, "GET", "/job/Folder1/job/Folder%202/api/json"); + } finally { + jenkinsApi.close(); + server.shutdown(); + } + } + + public void testGetRootFolderJobList() throws Exception { + MockWebServer server = mockWebServer(); + + String body = payloadFromResource("/jobsInRootFolder.json"); + server.enqueue(new MockResponse().setBody(body).setResponseCode(200)); + JenkinsApi jenkinsApi = api(server.url("/").url()); + JobsApi api = jenkinsApi.jobsApi(); + try { + JobList output = api.jobList(""); + assertNotNull(output); + assertNotNull(output.jobs()); + assertEquals(output.jobs().size(), 6); + assertSent(server, "GET", "/api/json"); + } finally { + jenkinsApi.close(); + server.shutdown(); + } + } + public void testGetJobInfo() throws Exception { MockWebServer server = mockWebServer(); diff --git a/src/test/resources/jobsInJenkinsFolder.json b/src/test/resources/jobsInJenkinsFolder.json new file mode 100644 index 00000000..ad9d77d9 --- /dev/null +++ b/src/test/resources/jobsInJenkinsFolder.json @@ -0,0 +1,10 @@ +{ + "_class" : "com.cloudbees.hudson.plugins.folder.Folder", + "jobs" : [ + { + "_class" : "hudson.model.FreeStyleProject", + "name" : "Test Project", + "url": "http://localhost:8080/job/username" + } + ] +} diff --git a/src/test/resources/jobsInRootFolder.json b/src/test/resources/jobsInRootFolder.json new file mode 100644 index 00000000..d148674b --- /dev/null +++ b/src/test/resources/jobsInRootFolder.json @@ -0,0 +1,36 @@ +{ + "_class" : "hudson.model.Hudson", + "jobs" : [ + { + "_class" : "hudson.maven.MavenModuleSet", + "name" : "Test Tool", + "url": "http://localhost:32769/job/fish-test/" + }, + { + "_class" : "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject", + "name" : "Test MultiBranch Project", + "url": "http://localhost:32769/job/fish-multibranch/" + }, + { + "_class" : "com.cloudbees.hudson.plugins.folder.Folder", + "name" : "Test Folder", + "url": "http://localhost:32769/job/fish-folder/" + }, + { + "_class" : "com.cloudbees.tiger.plugins.palace.templates.DockerSlaveTemplate", + "name" : "Test Docker Slave", + "url":"http://localhost:32769/job/fish-agent/" + }, + { + "_class" : "hudson.model.FreeStyleProject", + "name" : "Test Free Style", + "url": "http://localhost:32769/job/fish-project/", + "color": "red" + }, + { + "_class" : "org.jenkinsci.plugins.workflow.job.WorkflowJob", + "name" : "Test Workflow Job", + "url": "http://localhost:32769/job/fish-workflow/" + } + ] +}