Skip to content

Commit 8b75961

Browse files
committed
Added support for merge request approvals.
1 parent 133e6e1 commit 8b75961

File tree

4 files changed

+216
-7
lines changed

4 files changed

+216
-7
lines changed

src/main/java/org/gitlab4j/api/MergeRequestApi.java

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ public MergeRequest acceptMergeRequest(Integer projectId, Integer mergeRequestId
265265
* @return the updated merge request
266266
* @throws GitLabApiException if any exception occurs
267267
*/
268-
public MergeRequest acceptMergeRequest(Integer projectId, Integer mergeRequestId) throws GitLabApiException {
268+
public MergeRequest cancelMergeRequest(Integer projectId, Integer mergeRequestId) throws GitLabApiException {
269269

270270
if (projectId == null) {
271271
throw new RuntimeException("projectId cannot be null");
@@ -278,4 +278,84 @@ public MergeRequest acceptMergeRequest(Integer projectId, Integer mergeRequestId
278278
Response response = put(Response.Status.OK, null, "projects", projectId, "merge_requests", mergeRequestId, "cancel_merge_when_pipeline_succeeds");
279279
return (response.readEntity(MergeRequest.class));
280280
}
281+
282+
/**
283+
* Get the merge request with approval information.
284+
*
285+
* Note: This API endpoint is only available on 8.9 EE and above.
286+
*
287+
* GET /projects/:id/merge_requests/:merge_request_iid/approvals
288+
*
289+
* @param projectId the project ID of the merge request
290+
* @param mergeRequestId the internal ID of the merge request
291+
* @return a MergeRequest instance with approval information included
292+
* @throws GitLabApiException if any exception occurs
293+
*/
294+
public MergeRequest getMergeRequestApprovals(Integer projectId, Integer mergeRequestId) throws GitLabApiException {
295+
296+
if (projectId == null) {
297+
throw new RuntimeException("projectId cannot be null");
298+
}
299+
300+
if (mergeRequestId == null) {
301+
throw new RuntimeException("mergeRequestId cannot be null");
302+
}
303+
304+
Response response = get(Response.Status.OK, null, "projects", projectId, "merge_requests", mergeRequestId, "approvals");
305+
return (response.readEntity(MergeRequest.class));
306+
}
307+
308+
/**
309+
* Approve a merge request.
310+
*
311+
* Note: This API endpoint is only available on 8.9 EE and above.
312+
*
313+
* POST /projects/:id/merge_requests/:merge_request_iid/approve
314+
*
315+
* @param projectId the project ID of the merge request
316+
* @param mergeRequestId the internal ID of the merge request
317+
* @param sha the HEAD of the merge request, optional
318+
* @return a MergeRequest instance with approval information included
319+
* @throws GitLabApiException if any exception occurs
320+
*/
321+
public MergeRequest approveMergeRequest(Integer projectId, Integer mergeRequestId, String sha) throws GitLabApiException {
322+
323+
if (projectId == null) {
324+
throw new RuntimeException("projectId cannot be null");
325+
}
326+
327+
if (mergeRequestId == null) {
328+
throw new RuntimeException("mergeRequestId cannot be null");
329+
}
330+
331+
Form formData = new GitLabApiForm().withParam("sha", sha);
332+
Response response = post(Response.Status.OK, formData, "projects", projectId, "merge_requests", mergeRequestId, "approve");
333+
return (response.readEntity(MergeRequest.class));
334+
}
335+
336+
/**
337+
* Unapprove a merge request.
338+
*
339+
* Note: This API endpoint is only available on 8.9 EE and above.
340+
*
341+
* POST /projects/:id/merge_requests/:merge_request_iid/unapprove
342+
*
343+
* @param projectId the project ID of the merge request
344+
* @param mergeRequestId the internal ID of the merge request
345+
* @return a MergeRequest instance with approval information included
346+
* @throws GitLabApiException if any exception occurs
347+
*/
348+
public MergeRequest unapproveMergeRequest(Integer projectId, Integer mergeRequestId) throws GitLabApiException {
349+
350+
if (projectId == null) {
351+
throw new RuntimeException("projectId cannot be null");
352+
}
353+
354+
if (mergeRequestId == null) {
355+
throw new RuntimeException("mergeRequestId cannot be null");
356+
}
357+
358+
Response response = post(Response.Status.OK, (Form)null, "projects", projectId, "merge_requests", mergeRequestId, "unapprove");
359+
return (response.readEntity(MergeRequest.class));
360+
}
281361
}

src/main/java/org/gitlab4j/api/models/MergeRequest.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
import javax.xml.bind.annotation.XmlAccessorType;
88
import javax.xml.bind.annotation.XmlRootElement;
99

10+
import org.gitlab4j.api.utils.JacksonJson;
11+
12+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
13+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
14+
1015
@XmlRootElement
1116
@XmlAccessorType(XmlAccessType.FIELD)
1217
public class MergeRequest {
@@ -43,6 +48,14 @@ public class MergeRequest {
4348
private String webUrl;
4449
private Boolean workInProgress;
4550

51+
// The approval fields will only be available when listing approvals, approving or unapproving a merge reuest.
52+
private Integer approvalsRequired;
53+
private Integer approvalsMissing;
54+
55+
@JsonSerialize(using = JacksonJson.UserListSerializer.class)
56+
@JsonDeserialize(using = JacksonJson.UserListDeserializer.class)
57+
private List<User> approvedBy;
58+
4659
public Integer getApprovalsBeforeMerge() {
4760
return approvalsBeforeMerge;
4861
}
@@ -291,6 +304,72 @@ public void setWorkInProgress(Boolean workInProgress) {
291304
this.workInProgress = workInProgress;
292305
}
293306

307+
/**
308+
* Get the number of approvals required for the merge request.
309+
*
310+
* NOTE: This property will only be used when listing, approiving, or unapproving a merge request.
311+
*
312+
* @return the number of approvals required for the merge request
313+
*/
314+
public Integer getApprovalsRequired() {
315+
return approvalsRequired;
316+
}
317+
318+
/**
319+
* Set the number of approvals required for the merge request.
320+
*
321+
* NOTE: This property will only be used when listing, approiving, or unapproving a merge request.
322+
*
323+
* @param approvalsRequired the number of approvals required for the merge request
324+
*/
325+
public void setApprovalsRequired(Integer approvalsRequired) {
326+
this.approvalsRequired = approvalsRequired;
327+
}
328+
329+
/**
330+
* Get the number of approvals missing for the merge request.
331+
*
332+
* NOTE: This property will only be used when listing, approiving, or unapproving a merge request.
333+
*
334+
* @return the number of approvals missing for the merge request
335+
*/
336+
public Integer getApprovalsMissing() {
337+
return approvalsMissing;
338+
}
339+
340+
/**
341+
* Set the number of approvals missing for the merge request.
342+
*
343+
* NOTE: This property will only be used when listing, approiving, or unapproving a merge request.
344+
*
345+
* @param approvalsMissing the number of approvals missing for the merge request
346+
*/
347+
public void setApprovalsMissing(Integer approvalsMissing) {
348+
this.approvalsMissing = approvalsMissing;
349+
}
350+
351+
/**
352+
* Get the list of users that have approved the merge request.
353+
*
354+
* NOTE: This property will only be used when listing, approiving, or unapproving a merge request.
355+
*
356+
* @return the list of users that have approved the merge request
357+
*/
358+
public List<User> getApprovedBy() {
359+
return approvedBy;
360+
}
361+
362+
/**
363+
* Set the list of users that have approved the merge request.
364+
*
365+
* NOTE: This property will only be used when listing, approiving, or unapproving a merge request.
366+
*
367+
* @param approvedBy the list of users that have approved the merge request
368+
*/
369+
public void setApprovedBy(List<User> approvedBy) {
370+
this.approvedBy = approvedBy;
371+
}
372+
294373
public static final boolean isValid(MergeRequest mergeRequest) {
295374
return (mergeRequest != null && mergeRequest.getId() != null);
296375
}

src/main/java/org/gitlab4j/api/utils/JacksonJson.java

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55
import java.io.Reader;
66
import java.text.ParseException;
77
import java.text.SimpleDateFormat;
8+
import java.util.ArrayList;
89
import java.util.Date;
10+
import java.util.List;
911
import java.util.TimeZone;
1012

1113
import javax.ws.rs.Produces;
1214
import javax.ws.rs.core.MediaType;
1315
import javax.ws.rs.ext.ContextResolver;
1416
import javax.ws.rs.ext.Provider;
1517

18+
import org.gitlab4j.api.models.User;
19+
1620
import com.fasterxml.jackson.annotation.JsonInclude.Include;
1721
import com.fasterxml.jackson.core.JsonGenerationException;
1822
import com.fasterxml.jackson.core.JsonGenerator;
@@ -23,6 +27,7 @@
2327
import com.fasterxml.jackson.databind.DeserializationFeature;
2428
import com.fasterxml.jackson.databind.JsonDeserializer;
2529
import com.fasterxml.jackson.databind.JsonMappingException;
30+
import com.fasterxml.jackson.databind.JsonNode;
2631
import com.fasterxml.jackson.databind.JsonSerializer;
2732
import com.fasterxml.jackson.databind.ObjectMapper;
2833
import com.fasterxml.jackson.databind.ObjectWriter;
@@ -168,4 +173,49 @@ public Date deserialize(JsonParser jsonparser, DeserializationContext context) t
168173
}
169174
}
170175
}
171-
}
176+
177+
/**
178+
* Serializer for the odd User instances in the "approved_by" array in the merge_request JSON.
179+
*/
180+
public static class UserListSerializer extends JsonSerializer<List<User>> {
181+
182+
@Override
183+
public void serialize(List<User> value, JsonGenerator jgen,
184+
SerializerProvider provider) throws IOException,
185+
JsonProcessingException {
186+
187+
jgen.writeStartArray();
188+
for (User user : value) {
189+
jgen.writeStartObject();
190+
jgen.writeObjectField("user", user);
191+
jgen.writeEndObject();
192+
}
193+
jgen.writeEndArray();
194+
}
195+
}
196+
197+
/**
198+
* Deserializer for the odd User instances in the "approved_by" array in the merge_request JSON.
199+
*/
200+
public static class UserListDeserializer extends JsonDeserializer<List<User>> {
201+
202+
private static final ObjectMapper mapper = new JacksonJson().getObjectMapper();
203+
204+
@Override
205+
public List<User> deserialize(JsonParser jsonParser, DeserializationContext context)
206+
throws IOException, JsonProcessingException {
207+
208+
JsonNode tree = jsonParser.readValueAsTree();
209+
int numUsers = tree.size();
210+
List<User> users = new ArrayList<>(numUsers);
211+
for (int i = 0; i < numUsers; i++) {
212+
JsonNode node = tree.get(i);
213+
JsonNode userNode = node.get("user");
214+
User user = mapper.treeToValue(userNode, User.class);
215+
users.add(user);
216+
}
217+
218+
return (users);
219+
}
220+
}
221+
}

src/test/java/org/gitlab4j/api/TestGitLabApiBeans.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,18 +202,18 @@ public void testMember() {
202202
e.printStackTrace();
203203
}
204204
}
205-
/*
205+
206206
@Test
207-
public void testMergeRequestComment() {
207+
public void testMergeRequestApprovals() {
208208

209209
try {
210-
MergeRequestComment mergeRequestComment = makeFakeApiCall(MergeRequestComment.class, "merge-request-comment");
211-
assertTrue(compareJson(mergeRequestComment, "merge-request-comment"));
210+
MergeRequest mergeRequestApprovals = makeFakeApiCall(MergeRequest.class, "approvals");
211+
assertTrue(compareJson(mergeRequestApprovals, "approvals"));
212212
} catch (Exception e) {
213213
e.printStackTrace();
214214
}
215215
}
216-
*/
216+
217217
@Test
218218
public void testMergeRequest() {
219219

0 commit comments

Comments
 (0)