diff --git a/src/main/java/org/gitlab4j/api/GroupApi.java b/src/main/java/org/gitlab4j/api/GroupApi.java
index 7a8d433cce30fafa21f57672bb631002b847dd10..559894a8c1816b2e4f333762d94fc49630b2eb43 100644
--- a/src/main/java/org/gitlab4j/api/GroupApi.java
+++ b/src/main/java/org/gitlab4j/api/GroupApi.java
@@ -10,6 +10,7 @@ import java.util.stream.Stream;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import org.gitlab4j.api.GitLabApi.ApiVersion;
@@ -22,6 +23,8 @@ import org.gitlab4j.api.models.Group;
import org.gitlab4j.api.models.GroupFilter;
import org.gitlab4j.api.models.GroupParams;
import org.gitlab4j.api.models.GroupProjectsFilter;
+import org.gitlab4j.api.models.Iteration;
+import org.gitlab4j.api.models.IterationFilter;
import org.gitlab4j.api.models.LdapGroupLink;
import org.gitlab4j.api.models.Member;
import org.gitlab4j.api.models.Project;
@@ -1958,4 +1961,20 @@ public class GroupApi extends AbstractApi {
delete(Response.Status.OK, null, "groups", getGroupIdOrPath(groupIdOrPath), "custom_attributes", key);
}
+
+ /**
+ * Lists group iterations.
+ *
+ *
GitLab Endpoint: GET /groups/:id/iterations
+ *
+ * @param groupIdOrPath the group in the form of an Long(ID), String(path), or Group instance
+ * @param filter the iteration filter
+ * @return the list of group iterations
+ * @throws GitLabApiException if any exception occurs
+ */
+ public List listGroupIterations(Object groupIdOrPath, IterationFilter filter) throws GitLabApiException {
+ MultivaluedMap queryParams = (filter == null) ? null : filter.getQueryParams().asMap();
+ Response response = get(Response.Status.OK, queryParams, "groups", getGroupIdOrPath(groupIdOrPath), "iterations");
+ return (response.readEntity(new GenericType>() { }));
+ }
}
diff --git a/src/main/java/org/gitlab4j/api/ProjectApi.java b/src/main/java/org/gitlab4j/api/ProjectApi.java
index 8356a9fc7dd80889d57c023da5023fe3870b6d08..a1e127da313ea1f6f6a719e04f04616fbac28cd4 100644
--- a/src/main/java/org/gitlab4j/api/ProjectApi.java
+++ b/src/main/java/org/gitlab4j/api/ProjectApi.java
@@ -48,6 +48,8 @@ import org.gitlab4j.api.models.CustomAttribute;
import org.gitlab4j.api.models.Event;
import org.gitlab4j.api.models.FileUpload;
import org.gitlab4j.api.models.Issue;
+import org.gitlab4j.api.models.Iteration;
+import org.gitlab4j.api.models.IterationFilter;
import org.gitlab4j.api.models.Member;
import org.gitlab4j.api.models.Namespace;
import org.gitlab4j.api.models.Project;
@@ -4015,4 +4017,19 @@ public class ProjectApi extends AbstractApi implements Constants {
delete(Response.Status.NO_CONTENT, null, "projects", getProjectIdOrPath(projectIdOrPath), "access_tokens", tokenId);
}
+ /**
+ * Lists project iterations.
+ *
+ * GitLab Endpoint: GET /projects/:id/iterations
+ *
+ * @param projectIdOrPath the project in the form of a Long(ID), String(path), or Project instance
+ * @param filter the iteration filter
+ * @return the list of project iterations
+ * @throws GitLabApiException if any exception occurs
+ */
+ public List listProjectIterations(Object projectIdOrPath, IterationFilter filter) throws GitLabApiException {
+ MultivaluedMap queryParams = (filter == null) ? null : filter.getQueryParams().asMap();
+ Response response = get(Response.Status.OK, queryParams, "projects", getProjectIdOrPath(projectIdOrPath), "iterations");
+ return (response.readEntity(new GenericType>() { }));
+ }
}
diff --git a/src/main/java/org/gitlab4j/api/models/AbstractIssue.java b/src/main/java/org/gitlab4j/api/models/AbstractIssue.java
index c7a6e2e4559368ed8fe471f95a60170e6c401100..d618b21dac522709ad466eab48695587d8642a22 100644
--- a/src/main/java/org/gitlab4j/api/models/AbstractIssue.java
+++ b/src/main/java/org/gitlab4j/api/models/AbstractIssue.java
@@ -78,6 +78,7 @@ public abstract class AbstractIssue {
private Integer mergeRequestsCount;
private Boolean hasTasks;
private String taskStatus;
+ private Iteration iteration;
private TaskCompletionStatus taskCompletionStatus;
public Assignee getAssignee() {
@@ -325,7 +326,15 @@ public abstract class AbstractIssue {
this.taskStatus = taskStatus;
}
- public TaskCompletionStatus getTaskCompletionStatus() {
+ public Iteration getIteration() {
+ return iteration;
+ }
+
+ public void setIteration(Iteration iteration) {
+ this.iteration = iteration;
+ }
+
+ public TaskCompletionStatus getTaskCompletionStatus() {
return taskCompletionStatus;
}
diff --git a/src/main/java/org/gitlab4j/api/models/Iteration.java b/src/main/java/org/gitlab4j/api/models/Iteration.java
new file mode 100644
index 0000000000000000000000000000000000000000..b3369d64c5d7f3b7b5d15a1c207b52e8ffd571d0
--- /dev/null
+++ b/src/main/java/org/gitlab4j/api/models/Iteration.java
@@ -0,0 +1,129 @@
+package org.gitlab4j.api.models;
+
+import java.util.Date;
+
+import org.gitlab4j.api.utils.JacksonJson;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public class Iteration {
+ public enum IterationState {
+ UPCOMMING(1), CURRENT(2), CLOSED(3);
+
+ private int value;
+
+ IterationState(int value) {
+ this.value = value;
+ }
+
+ @JsonCreator
+ public static IterationState fromIntValue(int value) {
+ for (IterationState it : values()) {
+ if(it.value == value) {
+ return it;
+ }
+ }
+ throw new IllegalArgumentException("No enum found for value: " + value);
+ }
+
+ @JsonValue
+ public int toIntValue() {
+ return this.value;
+ }
+
+ @Override
+ public String toString() {
+ return name();
+ }
+ }
+
+ private Long id;
+ private Long iid;
+ private Long sequence;
+ private Long groupId;
+ private String title;
+ private String description;
+ private IterationState state;
+ private Date createdAt;
+ private Date updatedAt;
+ private Date startDate;
+ private Date dueDate;
+ private String webUrl;
+ public Long getId() {
+ return id;
+ }
+ public void setId(Long id) {
+ this.id = id;
+ }
+ public Long getIid() {
+ return iid;
+ }
+ public void setIid(Long iid) {
+ this.iid = iid;
+ }
+ public Long getSequence() {
+ return sequence;
+ }
+ public void setSequence(Long sequence) {
+ this.sequence = sequence;
+ }
+ public Long getGroupId() {
+ return groupId;
+ }
+ public void setGroupId(Long groupId) {
+ this.groupId = groupId;
+ }
+ public String getTitle() {
+ return title;
+ }
+ public void setTitle(String title) {
+ this.title = title;
+ }
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+ public IterationState getState() {
+ return state;
+ }
+ public void setState(IterationState state) {
+ this.state = state;
+ }
+ public Date getCreatedAt() {
+ return createdAt;
+ }
+ public void setCreatedAt(Date createdAt) {
+ this.createdAt = createdAt;
+ }
+ public Date getUpdatedAt() {
+ return updatedAt;
+ }
+ public void setUpdatedAt(Date updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+ public Date getStartDate() {
+ return startDate;
+ }
+ public void setStartDate(Date startDate) {
+ this.startDate = startDate;
+ }
+ public Date getDueDate() {
+ return dueDate;
+ }
+ public void setDueDate(Date dueDate) {
+ this.dueDate = dueDate;
+ }
+ public String getWebUrl() {
+ return webUrl;
+ }
+ public void setWebUrl(String webUrl) {
+ this.webUrl = webUrl;
+ }
+ @Override
+ public String toString() {
+ return (JacksonJson.toJsonString(this));
+ }
+}
diff --git a/src/main/java/org/gitlab4j/api/models/IterationFilter.java b/src/main/java/org/gitlab4j/api/models/IterationFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..58c99cd6c5f7b6306129acb8e5ef1a28fb1c3e73
--- /dev/null
+++ b/src/main/java/org/gitlab4j/api/models/IterationFilter.java
@@ -0,0 +1,183 @@
+package org.gitlab4j.api.models;
+
+import java.util.Date;
+
+import org.gitlab4j.api.Constants;
+import org.gitlab4j.api.GitLabApiForm;
+import org.gitlab4j.api.utils.ISO8601;
+import org.gitlab4j.api.utils.JacksonJsonEnumHelper;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public class IterationFilter {
+
+ public enum IterationFilterState {
+ OPENED, UPCOMING, CURRENT, CLOSED, ALL;
+
+ private static JacksonJsonEnumHelper enumHelper = new JacksonJsonEnumHelper<>(IterationFilterState.class, false, true);
+
+ @JsonCreator
+ public static IterationFilterState forValue(String value) {
+ return enumHelper.forValue(value);
+ }
+
+ @JsonValue
+ public String toValue() {
+ return (enumHelper.toString(this));
+ }
+
+ @Override
+ public String toString() {
+ return (enumHelper.toString(this));
+ }
+ }
+
+ public enum IterationFilterIn {
+ TITLE, CADENCE_TITLE;
+
+ private static JacksonJsonEnumHelper enumHelper = new JacksonJsonEnumHelper<>(IterationFilterIn.class, false, false, true);
+
+ @JsonCreator
+ public static IterationFilterIn forValue(String value) {
+ return enumHelper.forValue(value);
+ }
+
+ @JsonValue
+ public String toValue() {
+ return (enumHelper.toString(this));
+ }
+
+ @Override
+ public String toString() {
+ return (enumHelper.toString(this));
+ }
+ }
+
+ /**
+ * Return opened, upcoming, current, closed, or all iterations.
+ */
+ private IterationFilterState state;
+
+ /**
+ * Return only iterations with a title matching the provided string.
+ */
+ private String search;
+
+ /**
+ * Fields in which fuzzy search should be performed with the query given in the argument search.
+ */
+ private IterationFilterIn in;
+
+ /**
+ * Include iterations from parent group and its ancestors. Defaults to true.
+ */
+ private Boolean includeAncestors;
+
+ /**
+ * Return iterations updated after the specified date. Expected in ISO 8601 format (2019-03-15T08:00:00Z).
+ */
+ private Date updatedAfter;
+
+ /**
+ * Return iterations updated before the specified date. Expected in ISO 8601 format (2019-03-15T08:00:00Z).
+ */
+ private Date updatedBefore;
+
+ public IterationFilterState getState() {
+ return state;
+ }
+
+ public void setState(IterationFilterState state) {
+ this.state = state;
+ }
+
+ public String getSearch() {
+ return search;
+ }
+
+ public void setSearch(String search) {
+ this.search = search;
+ }
+
+ public IterationFilterIn getIn() {
+ return in;
+ }
+
+ public void setIn(IterationFilterIn in) {
+ this.in = in;
+ }
+
+ public Boolean getIncludeAncestors() {
+ return includeAncestors;
+ }
+
+ public void setIncludeAncestors(Boolean includeAncestors) {
+ this.includeAncestors = includeAncestors;
+ }
+
+ public Date getUpdatedAfter() {
+ return updatedAfter;
+ }
+
+ public void setUpdatedAfter(Date updatedAfter) {
+ this.updatedAfter = updatedAfter;
+ }
+
+ public Date getUpdatedBefore() {
+ return updatedBefore;
+ }
+
+ public void setUpdatedBefore(Date updatedBefore) {
+ this.updatedBefore = updatedBefore;
+ }
+
+ public IterationFilter withState(IterationFilterState state) {
+ this.state = state;
+ return (this);
+ }
+
+ public IterationFilter withSearch(String search) {
+ this.search = search;
+ return (this);
+ }
+
+ public IterationFilter withIn(IterationFilterIn in) {
+ this.in = in;
+ return (this);
+ }
+
+ public IterationFilter withIncludeAncestors(Boolean includeAncestors) {
+ this.includeAncestors = includeAncestors;
+ return (this);
+ }
+
+ public IterationFilter withUpdatedAfter(Date updatedAfter) {
+ this.updatedAfter = updatedAfter;
+ return (this);
+ }
+
+ public IterationFilter withUpdatedBefore(Date updatedBefore) {
+ this.updatedBefore = updatedBefore;
+ return (this);
+ }
+
+ @JsonIgnore
+ public GitLabApiForm getQueryParams(int page, int perPage) {
+ return (getQueryParams()
+ .withParam(Constants.PAGE_PARAM, page)
+ .withParam(Constants.PER_PAGE_PARAM, perPage));
+ }
+
+ @JsonIgnore
+ public GitLabApiForm getQueryParams() {
+ return new GitLabApiForm()
+ .withParam("state", state)
+ .withParam("search", search)
+ .withParam("in", in)
+ .withParam("include_ancestors", includeAncestors)
+ .withParam("updated_after", ISO8601.toString(updatedAfter, false))
+ .withParam("updated_before", ISO8601.toString(updatedBefore, false));
+ }
+}
diff --git a/src/test/java/org/gitlab4j/api/TestGitLabApiBeans.java b/src/test/java/org/gitlab4j/api/TestGitLabApiBeans.java
index 1ccb062a5305eed174bc3d5db2fcbd4357030142..fcffc9de72b065132524221a73300393ee2be7ab 100644
--- a/src/test/java/org/gitlab4j/api/TestGitLabApiBeans.java
+++ b/src/test/java/org/gitlab4j/api/TestGitLabApiBeans.java
@@ -79,6 +79,7 @@ import org.gitlab4j.api.models.ImportStatus;
import org.gitlab4j.api.models.Issue;
import org.gitlab4j.api.models.IssueLink;
import org.gitlab4j.api.models.IssuesStatistics;
+import org.gitlab4j.api.models.Iteration;
import org.gitlab4j.api.models.Job;
import org.gitlab4j.api.models.Key;
import org.gitlab4j.api.models.Label;
@@ -244,7 +245,7 @@ public class TestGitLabApiBeans {
@Test
public void testDeployment() throws Exception {
- Deployment deployment = unmarshalResource(Deployment.class, "deployment.json");
+ Deployment deployment = unmarshalResource(Deployment.class, "deployment.json");
assertTrue(compareJson(deployment, "deployment.json"));
}
@@ -346,7 +347,7 @@ public class TestGitLabApiBeans {
@Test
public void testGpgSignature() throws Exception {
- GpgSignature gpgSignature = unmarshalResource(GpgSignature.class, "gpg-signature.json");
+ GpgSignature gpgSignature = unmarshalResource(GpgSignature.class, "gpg-signature.json");
assertTrue(compareJson(gpgSignature, "gpg-signature.json"));
}
@@ -544,7 +545,7 @@ public class TestGitLabApiBeans {
@Test
public void testProjectApprovalsCofig() throws Exception {
- ProjectApprovalsConfig approvalsConfig = unmarshalResource(ProjectApprovalsConfig.class, "project-approvals-config.json");
+ ProjectApprovalsConfig approvalsConfig = unmarshalResource(ProjectApprovalsConfig.class, "project-approvals-config.json");
assertTrue(compareJson(approvalsConfig, "project-approvals-config.json"));
}
@@ -592,7 +593,7 @@ public class TestGitLabApiBeans {
@Test
public void testRemoteMirror() throws Exception {
- RemoteMirror remoteMirror = unmarshalResource(RemoteMirror.class, "remote-mirror.json");
+ RemoteMirror remoteMirror = unmarshalResource(RemoteMirror.class, "remote-mirror.json");
assertTrue(compareJson(remoteMirror, "remote-mirror.json"));
}
@@ -610,7 +611,7 @@ public class TestGitLabApiBeans {
@Test
public void testSettings() throws Exception {
- JsonNode json = readTreeFromResource("application-settings.json");
+ JsonNode json = readTreeFromResource("application-settings.json");
ApplicationSettings applicationSettings = ApplicationSettingsApi.parseApplicationSettings(json);
assertTrue(compareJson(applicationSettings.getSettings(), "application-settings.json"));
}
@@ -653,7 +654,7 @@ public class TestGitLabApiBeans {
@Test
public void testMergeRequestApprovalRule() throws Exception {
- ApprovalRule approvalRule = unmarshalResource(ApprovalRule.class, "approval-rule.json");
+ ApprovalRule approvalRule = unmarshalResource(ApprovalRule.class, "approval-rule.json");
assertTrue(compareJson(approvalRule, "approval-rule.json"));
}
@@ -783,6 +784,12 @@ public class TestGitLabApiBeans {
assertTrue(compareJson(token, "impersonation-token.json"));
}
+ @Test
+ public void testIteration() throws Exception {
+ Iteration token = unmarshalResource(Iteration.class, "iteration.json");
+ assertTrue(compareJson(token, "iteration.json"));
+ }
+
@Test
public void testOauthToken() throws Exception {
OauthTokenResponse token = unmarshalResource(OauthTokenResponse.class, "oauth-token.json");
diff --git a/src/test/resources/org/gitlab4j/api/epic-issue.json b/src/test/resources/org/gitlab4j/api/epic-issue.json
index 10561292d76c3853c79eda61855ba2cbd786036c..feb959e365eb0b98edf4be857c67e2fdb04c216d 100644
--- a/src/test/resources/org/gitlab4j/api/epic-issue.json
+++ b/src/test/resources/org/gitlab4j/api/epic-issue.json
@@ -61,5 +61,19 @@
},
"subscribed": true,
"epic_issue_id": 2,
- "relative_position": 55
+ "relative_position": 55,
+ "iteration": {
+ "id": 158,
+ "iid": 34,
+ "sequence": 9,
+ "group_id": 114,
+ "title": "title",
+ "description": "description",
+ "state": 3,
+ "created_at": "2023-08-07T00:05:06.739Z",
+ "updated_at": "2023-09-04T00:05:06.612Z",
+ "start_date": "2023-09-04T00:00:00Z",
+ "due_date": "2023-09-10T00:00:00Z",
+ "web_url": "http://example.com/example/-/iterations/158"
+ }
}
\ No newline at end of file
diff --git a/src/test/resources/org/gitlab4j/api/issue.json b/src/test/resources/org/gitlab4j/api/issue.json
index b1757ce0ba3a068e21ae1e2982b38be063a157cd..d3aaa0ca69fdbdbeb8aadaaac7062691c3c29445 100644
--- a/src/test/resources/org/gitlab4j/api/issue.json
+++ b/src/test/resources/org/gitlab4j/api/issue.json
@@ -58,5 +58,19 @@
"task_completion_status":{
"count": 0,
"completed_count": 0
- }
+ },
+ "iteration": {
+ "id": 158,
+ "iid": 34,
+ "sequence": 9,
+ "group_id": 114,
+ "title": "title",
+ "description": "description",
+ "state": 1,
+ "created_at": "2023-08-07T00:05:06.739Z",
+ "updated_at": "2023-09-04T00:05:06.612Z",
+ "start_date": "2023-09-04T00:00:00Z",
+ "due_date": "2023-09-10T00:00:00Z",
+ "web_url": "http://example.com/example/-/iterations/158"
+ }
}
\ No newline at end of file
diff --git a/src/test/resources/org/gitlab4j/api/iteration.json b/src/test/resources/org/gitlab4j/api/iteration.json
new file mode 100644
index 0000000000000000000000000000000000000000..30cd7678587ac3a128d7366cacca17bdacf5a98e
--- /dev/null
+++ b/src/test/resources/org/gitlab4j/api/iteration.json
@@ -0,0 +1,14 @@
+{
+ "id": 90,
+ "iid": 4,
+ "sequence": 2,
+ "group_id": 162,
+ "title": "title",
+ "description": "description",
+ "state": 2,
+ "created_at": "2022-03-14T05:21:11.929Z",
+ "updated_at": "2022-03-14T05:21:11.929Z",
+ "start_date": "2022-03-08T00:00:00Z",
+ "due_date": "2022-03-14T00:00:00Z",
+ "web_url": "https://gitlab.com/groups/my-group/-/iterations/90"
+}