Commit 10fd8a5f authored by Greg Messner's avatar Greg Messner
Browse files

Added support for Pipeline triggers (#341).

parent 065dd386
......@@ -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<Variable> 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
......@@ -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.
* <p>This class provides an entry point to all the GitLab API pipeline related calls.
* For more information on the Pipeline APIs see:</p>
*
* <a href="https://docs.gitlab.com/ee/api/pipelines.html">Pipelines API</a>
* <a href="https://docs.gitlab.com/ee/api/pipeline_schedules.html">Pipeline Schedules API</a>
* <a href="https://docs.gitlab.com/ee/api/pipeline_triggers.html">Pipeline Triggers API</a>
*/
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 {
* <pre><code>GET /projects/:id/pipeline_schedule</code></pre>
*
* @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 {
* <pre><code>GET /projects/:id/pipeline_schedules/:pipeline_schedule_id</code></pre>
*
* @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<PipelineSchedule> 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.
*
* <pre><code>GET /projects/:id/triggers</code></pre>
*
* @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<Trigger> getPipelineTriggers(Object projectIdOrPath) throws GitLabApiException {
return (getPipelineTriggers(projectIdOrPath, getDefaultPerPage()).all());
}
/**
* Get list of project pipeline triggers in the specified page range.
*
* <pre><code>GET /projects/:id/triggers</code></pre>
*
* @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<Trigger> 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<List<Trigger>>() {}));
}
/**
* Get Pager of project pipeline triggers.
*
* <pre><code>GET /projects/:id/pipeline_schedule</code></pre>
*
* @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<Trigger> getPipelineTriggers(Object projectIdOrPath, int itemsPerPage) throws GitLabApiException {
return (new Pager<Trigger>(this, Trigger.class, itemsPerPage, null, "projects", getProjectIdOrPath(projectIdOrPath), "triggers"));
}
/**
* Get a Stream of the project pipeline triggers for the specified project.
*
* <pre><code>GET /projects/:id/pipeline_schedule</code></pre>
*
* @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<Trigger> getPipelineTriggersStream(Object projectIdOrPath) throws GitLabApiException {
return (getPipelineTriggers(projectIdOrPath, getDefaultPerPage()).stream());
}
/**
* Get a specific pipeline schedule for project.
*
* <pre><code>GET /projects/:id/triggers/:trigger_id</code></pre>
*
* @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.
*
* <pre><code>GET /projects/:id/triggers/:trigger_id</code></pre>
*
* @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<Trigger> 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.
*
* <pre><code>POST /projects/:id/triggers</code></pre>
*
* @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.
*
* <pre><code>PUT /projects/:id/triggers/:trigger_id</code></pre>
*
* @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.
*
* <pre><code>DELETE /projects/:id/triggers/:trigger_id</code></pre>
*
* @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.
*
* <pre><code>PUT /projects/:id/triggers/:trigger_id/take_ownership</code></pre>
*
* @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.
*
* <pre><code>POST /projects/:id/trigger/pipeline</code></pre>
*
* @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<Variable> 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.
*
* <pre><code>POST /projects/:id/trigger/pipeline</code></pre>
*
* @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<Variable> 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));
}
}
......@@ -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));
......
......@@ -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");
......
......@@ -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<PipelineSchedule> 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<Trigger> 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<Trigger> 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<String> 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<Trigger> 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());
Stream<Trigger>pipelineTriggers = gitLabApi.getPipelineApi().getPipelineTriggersStream(testProject);
assertNotNull(pipelineTriggers);
assertFalse(pipelineTriggers.map(Trigger::getDescription).collect(toList()).contains(triggerDescription));
}
}
{
"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
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment