diff --git a/src/main/java/org/gitlab4j/api/GitLabApiForm.java b/src/main/java/org/gitlab4j/api/GitLabApiForm.java index 6693e320188c9bc13c2b1d9acffe276a9b86b0f6..9072b1bf1489eee1bbbf4eb2012e9aefc6844c19 100644 --- a/src/main/java/org/gitlab4j/api/GitLabApiForm.java +++ b/src/main/java/org/gitlab4j/api/GitLabApiForm.java @@ -9,6 +9,7 @@ import javax.ws.rs.core.Form; import javax.ws.rs.core.MultivaluedHashMap; import org.gitlab4j.api.models.AccessLevel; +import org.gitlab4j.api.models.Variable; import org.gitlab4j.api.utils.ISO8601; /** @@ -194,4 +195,26 @@ public class GitLabApiForm extends Form { this.param(name, stringValue); return (this); } + + /** + * Fluent method for adding a List<Variable> type query and form parameters to a get(), post(), or put() call. + * + * @param variables the List of Variable to add + * @return this GitLabAPiForm instance + */ + public GitLabApiForm withParam(List variables) { + + if (variables == null || variables.isEmpty()) { + return (this); + } + + variables.forEach(v -> { + String value = v.getValue(); + if (value != null) { + this.param("variables[" + v.getKey() + "]", value); + } + }); + + return (this); + } } \ No newline at end of file diff --git a/src/main/java/org/gitlab4j/api/PipelineApi.java b/src/main/java/org/gitlab4j/api/PipelineApi.java index 2770256952e6ece486f8d4dacbbd07d60f44cd8b..b87ae1e54ce3a4c8ab267974ecd13b84e3eee91b 100644 --- a/src/main/java/org/gitlab4j/api/PipelineApi.java +++ b/src/main/java/org/gitlab4j/api/PipelineApi.java @@ -11,10 +11,16 @@ import javax.ws.rs.core.Response; import org.gitlab4j.api.models.Pipeline; import org.gitlab4j.api.models.PipelineSchedule; import org.gitlab4j.api.models.PipelineStatus; +import org.gitlab4j.api.models.Trigger; import org.gitlab4j.api.models.Variable; /** - * This class provides an entry point to all the GitLab API pipeline calls. + *

This class provides an entry point to all the GitLab API pipeline related calls. + * For more information on the Pipeline APIs see:

+ * + * Pipelines API + * Pipeline Schedules API + * Pipeline Triggers API */ public class PipelineApi extends AbstractApi implements Constants { @@ -314,7 +320,7 @@ public class PipelineApi extends AbstractApi implements Constants { * * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required * @param page the page to get - * @param perPage the number of ProjectHook instances per page + * @param perPage the number of PipelineSchedule instances per page * @return a list of project pipeline_schedules for the specified project in the specified page range * @throws GitLabApiException if any exception occurs */ @@ -329,7 +335,7 @@ public class PipelineApi extends AbstractApi implements Constants { *
GET /projects/:id/pipeline_schedule
* * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required - * @param itemsPerPage the number of Project instances that will be fetched per page + * @param itemsPerPage the number of PipelineSchedule instances that will be fetched per page * @return a Pager of project pipeline_schedules for the specified project * @throws GitLabApiException if any exception occurs */ @@ -356,8 +362,8 @@ public class PipelineApi extends AbstractApi implements Constants { *
GET /projects/:id/pipeline_schedules/:pipeline_schedule_id
* * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required - * @param pipelineScheduleId the ID of the hook to get - * @return the project hook for the specified project ID/hook ID pair + * @param pipelineScheduleId the ID of the pipeline schedule to get + * @return the project PipelineSchedule * @throws GitLabApiException if any exception occurs */ public PipelineSchedule getPipelineSchedule(Object projectIdOrPath, Integer pipelineScheduleId) throws GitLabApiException { @@ -372,7 +378,7 @@ public class PipelineApi extends AbstractApi implements Constants { * * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required * @param pipelineScheduleId the ID of the hook to get - * @return the project hook for the specified project ID/hook ID pair as an Optional instance + * @return the project PipelineSchedule as an Optional instance */ public Optional getOptionalPipelineSchedule (Object projectIdOrPath, Integer pipelineScheduleId) { try { @@ -517,4 +523,197 @@ public class PipelineApi extends AbstractApi implements Constants { delete(expectedStatus, null, "projects", getProjectIdOrPath(projectIdOrPath), "pipeline_schedules", pipelineScheduleId, "variables", key); } + + /** + * Get a list of the project pipeline triggers for the specified project. + * + *
GET /projects/:id/triggers
+ * + * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required + * @return a list of pipeline triggers for the specified project + * @throws GitLabApiException if any exception occurs + */ + public List getPipelineTriggers(Object projectIdOrPath) throws GitLabApiException { + return (getPipelineTriggers(projectIdOrPath, getDefaultPerPage()).all()); + } + + /** + * Get list of project pipeline triggers in the specified page range. + * + *
GET /projects/:id/triggers
+ * + * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required + * @param page the page to get + * @param perPage the number of Trigger instances per page + * @return a list of project pipeline triggers for the specified project in the specified page range + * @throws GitLabApiException if any exception occurs + */ + public List getPipelineTriggerss(Object projectIdOrPath, int page, int perPage) throws GitLabApiException { + Response response = get(Response.Status.OK, getPageQueryParams(page, perPage), "projects", getProjectIdOrPath(projectIdOrPath), "triggers"); + return (response.readEntity(new GenericType>() {})); + } + + /** + * Get Pager of project pipeline triggers. + * + *
GET /projects/:id/pipeline_schedule
+ * + * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required + * @param itemsPerPage the number of Project instances that will be fetched per page + * @return a Pager of project pipeline triggers for the specified project + * @throws GitLabApiException if any exception occurs + */ + public Pager getPipelineTriggers(Object projectIdOrPath, int itemsPerPage) throws GitLabApiException { + return (new Pager(this, Trigger.class, itemsPerPage, null, "projects", getProjectIdOrPath(projectIdOrPath), "triggers")); + } + + /** + * Get a Stream of the project pipeline triggers for the specified project. + * + *
GET /projects/:id/pipeline_schedule
+ * + * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required + * @return a Stream of project pipeline triggers for the specified project + * @throws GitLabApiException if any exception occurs + */ + public Stream getPipelineTriggersStream(Object projectIdOrPath) throws GitLabApiException { + return (getPipelineTriggers(projectIdOrPath, getDefaultPerPage()).stream()); + } + + /** + * Get a specific pipeline schedule for project. + * + *
GET /projects/:id/triggers/:trigger_id
+ * + * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required + * @param triggerId the ID of the trigger to get + * @return the project pipeline trigger + * @throws GitLabApiException if any exception occurs + */ + public Trigger getPipelineTrigger(Object projectIdOrPath, Integer triggerId) throws GitLabApiException { + Response response = get(Response.Status.OK, null, "projects", getProjectIdOrPath(projectIdOrPath), "triggers", triggerId); + return (response.readEntity(Trigger.class)); + } + + /** + * Get a specific pipeline trigger for project as an Optional instance. + * + *
GET /projects/:id/triggers/:trigger_id
+ * + * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required + * @param triggerId the ID of the trigger to get + * @return the project pipeline trigger as an Optional instance + */ + public Optional getOptionalPipelineTrigger(Object projectIdOrPath, Integer triggerId) { + try { + return (Optional.ofNullable(getPipelineTrigger(projectIdOrPath, triggerId))); + } catch (GitLabApiException glae) { + return (GitLabApi.createOptionalFromException(glae)); + } + } + + /** + * Create a pipeline trigger for a project. + * + *
POST /projects/:id/triggers
+ * + * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required + * @param description the trigger description + * @return the created Trigger instance + * @throws GitLabApiException if any exception occurs + */ + public Trigger createPipelineTrigger(Object projectIdOrPath, String description) throws GitLabApiException { + GitLabApiForm formData = new GitLabApiForm().withParam("description", description, true); + Response response = post(Response.Status.CREATED, formData, "projects", getProjectIdOrPath(projectIdOrPath), "triggers"); + return (response.readEntity(Trigger.class)); + } + + /** + * Updates a pipeline trigger for project. + * + *
PUT /projects/:id/triggers/:trigger_id
+ * + * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required + * @param triggerId the trigger ID to update + * @param description the new trigger description + * @return the updated Trigger instance + * @throws GitLabApiException if any exception occurs + */ + public Trigger updatePipelineTrigger(Object projectIdOrPath, Integer triggerId, String description) throws GitLabApiException { + GitLabApiForm formData = new GitLabApiForm().withParam("description", description, false); + Response response = put(Response.Status.OK, formData.asMap(), "projects", getProjectIdOrPath(projectIdOrPath), "triggers", triggerId); + return (response.readEntity(Trigger.class)); + } + + /** + * Deletes a pipeline trigger from the project. + * + *
DELETE /projects/:id/triggers/:trigger_id
+ * + * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required + * @param triggerId the project trigger ID to delete + * @throws GitLabApiException if any exception occurs + */ + public void deletePipelineTrigger(Object projectIdOrPath, Integer triggerId) throws GitLabApiException { + delete(Response.Status.NO_CONTENT, null, "projects", getProjectIdOrPath(projectIdOrPath), "triggers", triggerId); + } + + /** + * Take ownership of a pipeline trigger for project. + * + *
PUT /projects/:id/triggers/:trigger_id/take_ownership
+ * + * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required + * @param triggerId the trigger ID to take opwnership of + * @return the updated Trigger instance + * @throws GitLabApiException if any exception occurs + */ + public Trigger takeOwnewrshipOfPipelineTrigger(Object projectIdOrPath, Integer triggerId) throws GitLabApiException { + Response response = put(Response.Status.OK, null, + "projects", getProjectIdOrPath(projectIdOrPath), "triggers", triggerId, "take_ownership"); + return (response.readEntity(Trigger.class)); + } + + /** + * Trigger a pipeline for a project. + * + *
POST /projects/:id/trigger/pipeline
+ * + * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required + * @param trigger the Trigger instance holding the trigger token + * @param ref the ref that the pipeline is to be triggered for + * @param variables a List of variables to be passed with the trigger + * @return a Pipeline instance holding information on the triggered pipeline + * @throws GitLabApiException if any exception occurs + */ + public Pipeline triggerPipeline(Object projectIdOrPath, Trigger trigger, String ref, List variables) throws GitLabApiException { + + if (trigger == null) { + throw new GitLabApiException("trigger cannot be null"); + } + + return (triggerPipeline(projectIdOrPath, trigger.getToken(), ref, variables)); + } + + /** + * Trigger a pipeline for a project. + * + *
POST /projects/:id/trigger/pipeline
+ * + * @param projectIdOrPath projectIdOrPath the project in the form of an Integer(ID), String(path), or Project instance, required + * @param token the trigger token + * @param ref the ref that the pipeline is to be triggered for + * @param variables a List of variables to be passed with the trigger + * @return a Pipeline instance holding information on the triggered pipeline + * @throws GitLabApiException if any exception occurs + */ + public Pipeline triggerPipeline(Object projectIdOrPath, String token, String ref, List variables) throws GitLabApiException { + GitLabApiForm formData = new GitLabApiForm() + .withParam("token", token, true) + .withParam("ref", ref, true) + .withParam(variables); + Response response = post(Response.Status.CREATED, formData, + "projects", getProjectIdOrPath(projectIdOrPath), "trigger", "pipeline"); + return (response.readEntity(Pipeline.class)); + } } diff --git a/src/main/java/org/gitlab4j/api/models/Pipeline.java b/src/main/java/org/gitlab4j/api/models/Pipeline.java index bae7b3e2dd0f6c72b848095ebcebcd548d079324..84ff648bf194c740128b8629d0fb475562a04e8d 100644 --- a/src/main/java/org/gitlab4j/api/models/Pipeline.java +++ b/src/main/java/org/gitlab4j/api/models/Pipeline.java @@ -21,6 +21,8 @@ public class Pipeline { private Date committed_at; private String coverage; private Integer duration; + private String webUrl; + private DetailedStatus detailedStatus; public Integer getId() { return id; @@ -142,6 +144,22 @@ public class Pipeline { this.duration = duration; } + public String getWebUrl() { + return webUrl; + } + + public void setWebUrl(String webUrl) { + this.webUrl = webUrl; + } + + public DetailedStatus getDetailedStatus() { + return detailedStatus; + } + + public void setDetailedStatus(DetailedStatus detailedStatus) { + this.detailedStatus = detailedStatus; + } + @Override public String toString() { return (JacksonJson.toJsonString(this)); diff --git a/src/test/java/org/gitlab4j/api/TestGitLabApiBeans.java b/src/test/java/org/gitlab4j/api/TestGitLabApiBeans.java index fbf23f830e83628ce69cab16a204f79e7927399d..10416223ea61215353cb54a96c664b3a4f971d7d 100644 --- a/src/test/java/org/gitlab4j/api/TestGitLabApiBeans.java +++ b/src/test/java/org/gitlab4j/api/TestGitLabApiBeans.java @@ -80,6 +80,7 @@ import org.gitlab4j.api.models.SshKey; import org.gitlab4j.api.models.SystemHook; import org.gitlab4j.api.models.Tag; import org.gitlab4j.api.models.TreeItem; +import org.gitlab4j.api.models.Trigger; import org.gitlab4j.api.models.User; import org.gitlab4j.api.models.Variable; import org.gitlab4j.api.services.JiraService; @@ -434,6 +435,12 @@ public class TestGitLabApiBeans { assertTrue(compareJson(tree, "tree.json")); } + @Test + public void testTrigger() throws Exception { + Trigger trigger = unmarshalResource(Trigger.class, "trigger.json"); + assertTrue(compareJson(trigger, "trigger.json")); + } + @Test public void testUser() throws Exception { User user = unmarshalResource(User.class, "user.json"); diff --git a/src/test/java/org/gitlab4j/api/TestPipelineApi.java b/src/test/java/org/gitlab4j/api/TestPipelineApi.java index cdf3e6f1ece969a08b5b7617b32e3769bad0b339..fde62191dc8a3255554014e5224520262384977f 100644 --- a/src/test/java/org/gitlab4j/api/TestPipelineApi.java +++ b/src/test/java/org/gitlab4j/api/TestPipelineApi.java @@ -7,9 +7,12 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeNotNull; import java.util.List; +import java.util.stream.Stream; +import org.gitlab4j.api.models.Pipeline; import org.gitlab4j.api.models.PipelineSchedule; import org.gitlab4j.api.models.Project; +import org.gitlab4j.api.models.Trigger; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -20,6 +23,7 @@ import org.junit.experimental.categories.Category; public class TestPipelineApi extends AbstractIntegrationTest { private static final String SCHEDULE_DESCRIPTION = "Test pipeline schedule - DELETE AFTER TEST"; + private static final String TRIGGER_DESCRIPTION = "Test pipeline trigger - DELETE AFTER TEST"; private static GitLabApi gitLabApi; private static Project testProject; @@ -28,7 +32,7 @@ public class TestPipelineApi extends AbstractIntegrationTest { super(); } - private static void deleteTestSchedules() { + private static void deleteTestResources() { if (testProject == null) { return; @@ -37,18 +41,24 @@ public class TestPipelineApi extends AbstractIntegrationTest { try { List pipelineSchedules = gitLabApi.getPipelineApi().getPipelineSchedules(testProject); - if (pipelineSchedules == null || pipelineSchedules.isEmpty()) { - return; - } - for (PipelineSchedule schedule : pipelineSchedules) { if (schedule.getDescription().startsWith(SCHEDULE_DESCRIPTION)) { gitLabApi.getPipelineApi().deletePipelineSchedule(testProject, schedule.getId()); } } - } catch (Exception ignore) { - } + } catch (Exception ignore) {} + + try { + + List triggers = gitLabApi.getPipelineApi().getPipelineTriggers(testProject); + for (Trigger trigger : triggers) { + if (trigger.getDescription().startsWith(TRIGGER_DESCRIPTION)) { + gitLabApi.getPipelineApi().deletePipelineTrigger(testProject, trigger.getId()); + } + } + + } catch (Exception ignore) {} } @BeforeClass @@ -60,7 +70,7 @@ public class TestPipelineApi extends AbstractIntegrationTest { @AfterClass public static void teardown() { - deleteTestSchedules(); + deleteTestResources(); } @Before @@ -121,4 +131,70 @@ public class TestPipelineApi extends AbstractIntegrationTest { assertNotNull(pipelineSchedules); assertFalse(pipelineSchedules.stream().map(PipelineSchedule::getDescription).collect(toList()).contains(scheduleDescription)); } + + @Test + public void testCreateAndUpdatePipelineTrigger() throws GitLabApiException { + + assertNotNull(testProject); + + String triggerDescription = TRIGGER_DESCRIPTION + " - test updatePipelineTrigger()"; + Trigger createdTrigger = gitLabApi.getPipelineApi().createPipelineTrigger(testProject, triggerDescription); + assertNotNull(createdTrigger); + + // Make sure the created trigger is present before updating + List pipelineTriggers = gitLabApi.getPipelineApi().getPipelineTriggers(testProject); + assertNotNull(pipelineTriggers); + assertTrue(pipelineTriggers.stream().map(Trigger::getDescription).collect(toList()).contains(triggerDescription)); + + String newTriggerDescription = triggerDescription + " - updated"; + gitLabApi.getPipelineApi().updatePipelineTrigger(testProject, createdTrigger.getId(), newTriggerDescription); + + pipelineTriggers = gitLabApi.getPipelineApi().getPipelineTriggers(testProject); + assertNotNull(pipelineTriggers); + + List triggerDecriptions = pipelineTriggers.stream().map(Trigger::getDescription).collect(toList()); + assertFalse(triggerDecriptions.contains(triggerDescription)); + assertTrue(triggerDecriptions.contains(newTriggerDescription)); + } + + @Test + public void testDeletePipelineTrigger() throws GitLabApiException { + + assertNotNull(testProject); + + String triggerDescription = TRIGGER_DESCRIPTION + " - test deletePipelineTrigger()"; + Trigger createdTrigger = gitLabApi.getPipelineApi().createPipelineTrigger(testProject, triggerDescription); + assertNotNull(createdTrigger); + + // Make sure the created trigger is present before deleting + List pipelineTriggers = gitLabApi.getPipelineApi().getPipelineTriggers(testProject); + assertNotNull(pipelineTriggers); + assertTrue(pipelineTriggers.stream().map(Trigger::getDescription).collect(toList()).contains(triggerDescription)); + + gitLabApi.getPipelineApi().deletePipelineTrigger(testProject, createdTrigger.getId()); + pipelineTriggers = gitLabApi.getPipelineApi().getPipelineTriggers(testProject); + assertNotNull(pipelineTriggers); + assertFalse(pipelineTriggers.stream().map(Trigger::getDescription).collect(toList()).contains(triggerDescription)); + } + + @Test + public void testTriggerAndCancelPipeline() throws GitLabApiException { + + assertNotNull(testProject); + + String triggerDescription = TRIGGER_DESCRIPTION + " - test triggerPipeline() - " + HelperUtils.getRandomInt(1000); + Trigger createdTrigger = gitLabApi.getPipelineApi().createPipelineTrigger(testProject, triggerDescription); + assertNotNull(createdTrigger); + + Pipeline pipeline = gitLabApi.getPipelineApi().triggerPipeline(testProject, createdTrigger, "master", null); + assertNotNull(pipeline); + + Pipeline canceledPipeline = gitLabApi.getPipelineApi().cancelPipelineJobs(testProject, pipeline.getId()); + assertNotNull(canceledPipeline); + + gitLabApi.getPipelineApi().deletePipelineTrigger(testProject, createdTrigger.getId()); + StreampipelineTriggers = gitLabApi.getPipelineApi().getPipelineTriggersStream(testProject); + assertNotNull(pipelineTriggers); + assertFalse(pipelineTriggers.map(Trigger::getDescription).collect(toList()).contains(triggerDescription)); + } } diff --git a/src/test/resources/org/gitlab4j/api/pipeline.json b/src/test/resources/org/gitlab4j/api/pipeline.json index e14a026c62b64ff4410e62a82e6a7e1c1abbd3a9..cfbcf81a372c53f7eaa18967330d729e6494dc47 100644 --- a/src/test/resources/org/gitlab4j/api/pipeline.json +++ b/src/test/resources/org/gitlab4j/api/pipeline.json @@ -1,6 +1,7 @@ { "id": 46, "status": "success", + "web_url": "http://3cb4fb3163d4/gitlab4j/test-project/pipelines/66", "ref": "master", "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a", "before_sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a", @@ -16,5 +17,15 @@ "created_at": "2016-08-11T11:28:34.085Z", "updated_at": "2016-08-11T11:32:35.169Z", "finished_at": "2016-08-11T11:32:35.145Z", - "coverage": "30.0" + "coverage": "30.0", + "detailed_status": { + "icon": "status_pending", + "text": "pending", + "label": "pending", + "group": "pending", + "tooltip": "pending", + "has_details": true, + "details_path": "/gitlab4j/test-project/pipelines/66", + "favicon": "/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png" + } } \ No newline at end of file