Skip to content

Commit faefb3d

Browse files
authored
feat(tasks): support for multivalued status in listTasks (crowdin#334)
1 parent 027181e commit faefb3d

6 files changed

Lines changed: 250 additions & 34 deletions

File tree

src/main/java/com/crowdin/client/tasks/TasksApi.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
import com.crowdin.client.core.model.ResponseObject;
1515
import com.crowdin.client.tasks.model.*;
1616

17+
import java.util.EnumSet;
1718
import java.util.List;
1819
import java.util.Map;
1920
import java.util.Optional;
21+
import java.util.stream.Collectors;
2022

2123
public class TasksApi extends CrowdinApi {
2224
public TasksApi(Credentials credentials) {
@@ -41,7 +43,7 @@ public TasksApi(Credentials credentials, ClientConfig clientConfig) {
4143
*/
4244
public ResponseList<Task> listTasks(Long projectId, Integer limit, Integer offset, Status status, Integer assigneeId) throws HttpException, HttpBadRequestException {
4345
Map<String, Optional<Object>> queryParams = HttpRequestConfig.buildUrlParams(
44-
"status", Optional.ofNullable(status),
46+
"status", Optional.ofNullable(status != null ? status.to(status) : null),
4547
"assigneeId", Optional.ofNullable(assigneeId),
4648
"limit", Optional.ofNullable(limit),
4749
"offset", Optional.ofNullable(offset)
@@ -50,6 +52,42 @@ public ResponseList<Task> listTasks(Long projectId, Integer limit, Integer offse
5052
return TaskResponseList.to(taskResponseList);
5153
}
5254

55+
/**
56+
* Lists tasks for a given project, filtered by multiple statuses.
57+
*
58+
* @param projectId project identifier
59+
* @param params Query params
60+
* @return List of tasks
61+
* @see <ul>
62+
* <li><a href="https://developer.crowdin.com/api/v2/#operation/api.projects.tasks.getMany" target="_blank"><b>API Documentation</b></a></li>
63+
* <li><a href="https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.tasks.getMany" target="_blank"><b>Enterprise API Documentation</b></a></li>
64+
* </ul>
65+
*/
66+
public ResponseList<Task> listTasks(Long projectId, ListTasksParams params) throws HttpException, HttpBadRequestException {
67+
ListTasksParams query = Optional.ofNullable(params).orElse(new ListTasksParams());
68+
69+
EnumSet<Status> statuses = query.getStatuses();
70+
71+
Map<String, Optional<Object>> queryParams = HttpRequestConfig.buildUrlParams(
72+
"status", Optional.ofNullable(
73+
statuses == null ? null : statuses.stream()
74+
.map(status -> status.to(status))
75+
.collect(Collectors.joining(","))
76+
),
77+
"assigneeId", Optional.ofNullable(query.getAssigneeId()),
78+
"limit", Optional.ofNullable(query.getLimit()),
79+
"offset", Optional.ofNullable(query.getOffset())
80+
);
81+
82+
TaskResponseList taskResponseList = this.httpClient.get(
83+
this.url + "/projects/" + projectId + "/tasks",
84+
new HttpRequestConfig(queryParams),
85+
TaskResponseList.class
86+
);
87+
88+
return TaskResponseList.to(taskResponseList);
89+
}
90+
5391
/**
5492
* @param projectId project identifier
5593
* @param request request object
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.crowdin.client.tasks.model;
2+
3+
import com.crowdin.client.core.model.Pagination;
4+
import lombok.Data;
5+
import lombok.EqualsAndHashCode;
6+
7+
import java.util.EnumSet;
8+
9+
@EqualsAndHashCode(callSuper = true)
10+
@Data
11+
public class ListTasksParams extends Pagination {
12+
private EnumSet<Status> statuses;
13+
private Long assigneeId;
14+
}

src/test/java/com/crowdin/client/tasks/TasksApiTest.java

Lines changed: 86 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,28 @@
77
import com.crowdin.client.core.model.ResponseObject;
88
import com.crowdin.client.framework.RequestMock;
99
import com.crowdin.client.framework.TestClient;
10-
import com.crowdin.client.tasks.model.AssignedTeam;
11-
import com.crowdin.client.tasks.model.Assignee;
12-
import com.crowdin.client.tasks.model.AssigneeRequest;
13-
import com.crowdin.client.tasks.model.CreateTaskEnterpriseStringsBasedRequest;
14-
import com.crowdin.client.tasks.model.CreateTaskRequest;
15-
import com.crowdin.client.tasks.model.CreateTaskEnterpriseVendorRequest;
16-
import com.crowdin.client.tasks.model.CreateTaskStringsBasedRequest;
17-
import com.crowdin.client.tasks.model.Progress;
18-
import com.crowdin.client.tasks.model.Status;
19-
import com.crowdin.client.tasks.model.Task;
20-
import com.crowdin.client.tasks.model.Type;
10+
import com.crowdin.client.tasks.model.*;
2111
import com.crowdin.client.tasks.model.pending.CreateEnterprisePendingTaskRequest;
2212
import com.crowdin.client.tasks.model.pending.CreatePendingTaskRequest;
13+
import lombok.SneakyThrows;
2314
import org.apache.http.client.methods.HttpDelete;
2415
import org.apache.http.client.methods.HttpGet;
2516
import org.apache.http.client.methods.HttpPatch;
2617
import org.apache.http.client.methods.HttpPost;
2718
import org.junit.jupiter.api.Test;
2819

29-
import java.util.Arrays;
30-
import java.util.Calendar;
31-
import java.util.Date;
32-
import java.util.HashMap;
33-
import java.util.List;
34-
import java.util.Map;
35-
import java.util.TimeZone;
20+
import java.util.*;
3621

3722
import static java.util.Collections.singletonList;
38-
import static org.junit.jupiter.api.Assertions.assertEquals;
23+
import static org.junit.jupiter.api.Assertions.*;
24+
import static org.junit.jupiter.api.Assertions.assertNotNull;
3925

4026
public class TasksApiTest extends TestClient {
4127

4228
private final Long projectId = 12L;
4329
private final Long enterpriseProjectId = 13L;
30+
private final Long multiStatusProjectId = 14L;
31+
private final Long singleStatusProjectId = 15L;
4432
private final Long taskId = 2L;
4533
private final Long prevTaskId = 1L;
4634
private final Status status = Status.TODO;
@@ -61,26 +49,75 @@ public List<RequestMock> getMocks() {
6149
RequestMock.build(this.url + "/projects/" + projectId + "/tasks/" + taskId, HttpDelete.METHOD_NAME),
6250
RequestMock.build(this.url + "/projects/" + projectId + "/tasks/" + taskId, HttpPatch.METHOD_NAME, "api/tasks/editTask.json", "api/tasks/task.json"),
6351
RequestMock.build(this.url + "/user/tasks", HttpGet.METHOD_NAME, "api/tasks/listTasks.json"),
64-
RequestMock.build(this.url + "/user/tasks/" + taskId, HttpPatch.METHOD_NAME, "api/tasks/editTask.json", "api/tasks/task.json")
52+
RequestMock.build(this.url + "/user/tasks/" + taskId, HttpPatch.METHOD_NAME, "api/tasks/editTask.json", "api/tasks/task.json"),
53+
RequestMock.build(this.url + "/projects/" + multiStatusProjectId + "/tasks", HttpGet.METHOD_NAME, "api/tasks/multiStatusListTasks.json", new HashMap<String, String>() {{
54+
put("status", "todo,done");
55+
}}),
56+
RequestMock.build(this.url + "/projects/" + singleStatusProjectId + "/tasks", HttpGet.METHOD_NAME, "api/tasks/singleStatusListTasks.json", new HashMap<String, String>() {{
57+
put("status", "in_progress");
58+
}})
6559
);
6660
}
6761

6862
@Test
6963
public void listTasksTest() {
70-
Assignee assignee = new Assignee();
71-
assignee.setId(1L);
72-
assignee.setUsername("john_smith");
73-
assignee.setFullName("john_smith");
74-
assignee.setAvatarUrl("");
75-
assignee.setWordsCount(5);
76-
assignee.setWordsLeft(3);
7764
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
7865
ResponseList<Task> taskResponseList = this.getTasksApi().listTasks(projectId, null, null, null, null);
79-
assertEquals(taskResponseList.getData().size(), 1);
80-
assertEquals(taskResponseList.getData().get(0).getData().getId(), taskId);
81-
assertEquals(taskResponseList.getData().get(0).getData().getStatus(), status);
82-
assertEquals(new Date(119, Calendar.SEPTEMBER,27,7,0,14), taskResponseList.getData().get(0).getData().getDeadline());
83-
assertEquals(taskResponseList.getData().get(0).getData().getAssignees().get(0), assignee);
66+
67+
assertNotNull(taskResponseList.getData().get(0).getData());
68+
assertEquals(1, taskResponseList.getData().size());
69+
70+
assertEquals(projectId, taskResponseList.getData().get(0).getData().getProjectId());
71+
72+
assertListTasks(taskResponseList);
73+
}
74+
75+
@Test
76+
public void listTasksTest_testSingleStatus() {
77+
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
78+
ResponseList<Task> taskResponseList = this.getTasksApi().listTasks(singleStatusProjectId, null, null, Status.IN_PROGRESS, null);
79+
80+
assertNotNull(taskResponseList.getData().get(0).getData());
81+
assertEquals(1, taskResponseList.getData().size());
82+
assertEquals(singleStatusProjectId, taskResponseList.getData().get(0).getData().getProjectId());
83+
assertEquals(Status.IN_PROGRESS, taskResponseList.getData().get(0).getData().getStatus());
84+
85+
assertListTasks(taskResponseList);
86+
}
87+
88+
@Test
89+
void listTasksTest_withNullStatuses() {
90+
ListTasksParams listTasksParams = new ListTasksParams();
91+
listTasksParams.setStatuses(null);
92+
93+
ResponseList<Task> taskResponseList = this.getTasksApi().listTasks(projectId, listTasksParams);
94+
95+
assertNotNull(taskResponseList);
96+
assertEquals(1, taskResponseList.getData().size());
97+
assertEquals(projectId, taskResponseList.getData().get(0).getData().getProjectId());
98+
99+
assertListTasks(taskResponseList);
100+
}
101+
102+
@Test
103+
public void listTasksTest_multipleStatuses() {
104+
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
105+
106+
EnumSet<Status> statuses = EnumSet.of(Status.TODO, Status.DONE);
107+
108+
ListTasksParams listTasksParams = new ListTasksParams();
109+
listTasksParams.setStatuses(statuses);
110+
111+
ResponseList<Task> taskResponseList = this.getTasksApi().listTasks(multiStatusProjectId, listTasksParams);
112+
113+
assertNotNull(taskResponseList.getData().get(0).getData());
114+
assertEquals(1, taskResponseList.getData().size());
115+
assertEquals(multiStatusProjectId, taskResponseList.getData().get(0).getData().getProjectId());
116+
117+
Status responseProjectStatus = taskResponseList.getData().get(0).getData().getStatus();
118+
assertTrue(statuses.contains(responseProjectStatus));
119+
120+
assertListTasks(taskResponseList);
84121
}
85122

86123
@Test
@@ -282,4 +319,21 @@ public void editUserTaskTest() {
282319
assertEquals(taskResponseObject.getData().getId(), taskId);
283320
assertEquals(taskResponseObject.getData().getStatus(), status);
284321
}
322+
323+
//<editor-fold desc="Common method for listTasks assertion">
324+
@SneakyThrows
325+
private void assertListTasks(ResponseList<Task> taskResponseList) {
326+
Assignee assignee = new Assignee();
327+
assignee.setId(1L);
328+
assignee.setUsername("john_smith");
329+
assignee.setFullName("john_smith");
330+
assignee.setAvatarUrl("");
331+
assignee.setWordsCount(5);
332+
assignee.setWordsLeft(3);
333+
334+
assertEquals(taskId, taskResponseList.getData().get(0).getData().getId());
335+
assertEquals(new Date(119, Calendar.SEPTEMBER,27,7,0,14), taskResponseList.getData().get(0).getData().getDeadline());
336+
assertEquals(assignee, taskResponseList.getData().get(0).getData().getAssignees().get(0));
337+
}
338+
//</editor-fold>
285339
}

src/test/resources/api/tasks/listTasks.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"data": {
55
"id": 2,
6-
"projectId": 2,
6+
"projectId": 12,
77
"creatorId": 6,
88
"type": 1,
99
"status": "todo",
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"data": [
3+
{
4+
"data": {
5+
"id": 2,
6+
"projectId": 14,
7+
"creatorId": 6,
8+
"type": 1,
9+
"status": "todo",
10+
"title": "French",
11+
"assignees": [
12+
{
13+
"id": 1,
14+
"username": "john_smith",
15+
"fullName": "john_smith",
16+
"avatarUrl": "",
17+
"wordsCount": 5,
18+
"wordsLeft": 3
19+
}
20+
],
21+
"assignedTeams": [
22+
{
23+
"id": 1,
24+
"wordsCount": 5
25+
}
26+
],
27+
"fileIds": [
28+
1
29+
],
30+
"progress": {
31+
"total": 24,
32+
"done": 15,
33+
"percent": 62
34+
},
35+
"sourceLanguageId": "en",
36+
"targetLanguageId": "fr",
37+
"description": "Proofread all French strings",
38+
"hash": "dac37aff364d83899128e68afe0de4994",
39+
"translationUrl": "/proofread/9092638ac9f2a2d1b5571d08edc53763/all/en-fr/10?task=dac37aff364d83899128e68afe0de4994",
40+
"wordsCount": 24,
41+
"filesCount": 2,
42+
"commentsCount": 0,
43+
"deadline": "2019-09-27T07:00:14+00:00",
44+
"timeRange": "string",
45+
"workflowStepId": 10,
46+
"createdAt": "2019-09-23T09:04:29+00:00",
47+
"updatedAt": "2019-09-23T09:04:29+00:00"
48+
}
49+
}
50+
],
51+
"pagination": {
52+
"offset": 0,
53+
"limit": 25
54+
}
55+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"data": [
3+
{
4+
"data": {
5+
"id": 2,
6+
"projectId": 15,
7+
"creatorId": 6,
8+
"type": 1,
9+
"status": "in_progress",
10+
"title": "French",
11+
"assignees": [
12+
{
13+
"id": 1,
14+
"username": "john_smith",
15+
"fullName": "john_smith",
16+
"avatarUrl": "",
17+
"wordsCount": 5,
18+
"wordsLeft": 3
19+
}
20+
],
21+
"assignedTeams": [
22+
{
23+
"id": 1,
24+
"wordsCount": 5
25+
}
26+
],
27+
"fileIds": [
28+
1
29+
],
30+
"progress": {
31+
"total": 24,
32+
"done": 15,
33+
"percent": 62
34+
},
35+
"sourceLanguageId": "en",
36+
"targetLanguageId": "fr",
37+
"description": "Proofread all French strings",
38+
"hash": "dac37aff364d83899128e68afe0de4994",
39+
"translationUrl": "/proofread/9092638ac9f2a2d1b5571d08edc53763/all/en-fr/10?task=dac37aff364d83899128e68afe0de4994",
40+
"wordsCount": 24,
41+
"filesCount": 2,
42+
"commentsCount": 0,
43+
"deadline": "2019-09-27T07:00:14+00:00",
44+
"timeRange": "string",
45+
"workflowStepId": 10,
46+
"createdAt": "2019-09-23T09:04:29+00:00",
47+
"updatedAt": "2019-09-23T09:04:29+00:00"
48+
}
49+
}
50+
],
51+
"pagination": {
52+
"offset": 0,
53+
"limit": 25
54+
}
55+
}

0 commit comments

Comments
 (0)