001package com.box.sdk;
002
003import com.eclipsesource.json.Json;
004import com.eclipsesource.json.JsonArray;
005import com.eclipsesource.json.JsonObject;
006import com.eclipsesource.json.JsonValue;
007import java.net.URL;
008import java.util.ArrayList;
009import java.util.Date;
010import java.util.List;
011
012/**
013 * Represents a task on Box. Tasks can have a due date and can be assigned to a specific user.
014 */
015@BoxResourceType("task")
016public class BoxTask extends BoxResource {
017
018    /**
019     * Task URL Template.
020     */
021    public static final URLTemplate TASK_URL_TEMPLATE = new URLTemplate("tasks/%s");
022    /**
023     * Get Assignments URL Template.
024     */
025    public static final URLTemplate GET_ASSIGNMENTS_URL_TEMPLATE = new URLTemplate("tasks/%s/assignments");
026    /**
027     * Add Task Assignment URL Template.
028     */
029    public static final URLTemplate ADD_TASK_ASSIGNMENT_URL_TEMPLATE = new URLTemplate("task_assignments");
030
031    /**
032     * Constructs a BoxTask for a task with a given ID.
033     *
034     * @param api the API connection to be used by the task.
035     * @param id  the ID of the task.
036     */
037    public BoxTask(BoxAPIConnection api, String id) {
038        super(api, id);
039    }
040
041    /**
042     * Deletes this task.
043     */
044    public void delete() {
045        URL url = TASK_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
046        BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE");
047        request.send().close();
048    }
049
050    /**
051     * Adds a new assignment to this task.
052     *
053     * @param assignTo the user to assign the assignment to.
054     * @return information about the newly added task assignment.
055     */
056    public BoxTaskAssignment.Info addAssignment(BoxUser assignTo) {
057        JsonObject taskJSON = new JsonObject();
058        taskJSON.add("type", "task");
059        taskJSON.add("id", this.getID());
060
061        JsonObject assignToJSON = new JsonObject();
062        assignToJSON.add("id", assignTo.getID());
063
064        JsonObject requestJSON = new JsonObject();
065        requestJSON.add("task", taskJSON);
066        requestJSON.add("assign_to", assignToJSON);
067
068        URL url = ADD_TASK_ASSIGNMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL());
069        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
070        request.setBody(requestJSON.toString());
071        try (BoxJSONResponse response = request.send()) {
072            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
073
074            BoxTaskAssignment addedAssignment = new BoxTaskAssignment(this.getAPI(), responseJSON.get("id").asString());
075            return addedAssignment.new Info(responseJSON);
076        }
077    }
078
079    /**
080     * Adds a new assignment to this task using user's login as identifier.
081     *
082     * @param assignToLogin the login of user to assign the task to.
083     * @return information about the newly added task assignment.
084     */
085    public BoxTaskAssignment.Info addAssignmentByLogin(String assignToLogin) {
086        JsonObject taskJSON = new JsonObject();
087        taskJSON.add("type", "task");
088        taskJSON.add("id", this.getID());
089
090        JsonObject assignToJSON = new JsonObject();
091        assignToJSON.add("login", assignToLogin);
092
093        JsonObject requestJSON = new JsonObject();
094        requestJSON.add("task", taskJSON);
095        requestJSON.add("assign_to", assignToJSON);
096
097        URL url = ADD_TASK_ASSIGNMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL());
098        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST");
099        request.setBody(requestJSON.toString());
100        try (BoxJSONResponse response = request.send()) {
101            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
102
103            BoxTaskAssignment addedAssignment = new BoxTaskAssignment(this.getAPI(), responseJSON.get("id").asString());
104            return addedAssignment.new Info(responseJSON);
105        }
106    }
107
108    /**
109     * Gets any assignments for this task.
110     *
111     * @return a list of assignments for this task.
112     */
113    public List<BoxTaskAssignment.Info> getAssignments() {
114        URL url = GET_ASSIGNMENTS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
115        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET");
116        try (BoxJSONResponse response = request.send()) {
117            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
118
119            int totalCount = responseJSON.get("total_count").asInt();
120            List<BoxTaskAssignment.Info> assignments = new ArrayList<>(totalCount);
121            JsonArray entries = responseJSON.get("entries").asArray();
122            for (JsonValue value : entries) {
123                JsonObject assignmentJSON = value.asObject();
124                BoxTaskAssignment assignment =
125                    new BoxTaskAssignment(this.getAPI(), assignmentJSON.get("id").asString());
126                BoxTaskAssignment.Info info = assignment.new Info(assignmentJSON);
127                assignments.add(info);
128            }
129
130            return assignments;
131        }
132    }
133
134    /**
135     * Gets an iterable of all the assignments of this task.
136     *
137     * @param fields the fields to retrieve.
138     * @return an iterable containing info about all the assignments.
139     */
140    public Iterable<BoxTaskAssignment.Info> getAllAssignments(String... fields) {
141        final QueryStringBuilder builder = new QueryStringBuilder();
142        if (fields.length > 0) {
143            builder.appendParam("fields", fields);
144        }
145        return () -> {
146            URL url = GET_ASSIGNMENTS_URL_TEMPLATE.buildWithQuery(
147                BoxTask.this.getAPI().getBaseURL(), builder.toString(), BoxTask.this.getID());
148            return new BoxTaskAssignmentIterator(BoxTask.this.getAPI(), url);
149        };
150    }
151
152
153    /**
154     * Gets information about this task.
155     *
156     * @param fields the fields to retrieve.
157     * @return info about this task.
158     */
159    public Info getInfo(String... fields) {
160        URL url = TASK_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
161        if (fields.length > 0) {
162            QueryStringBuilder builder = new QueryStringBuilder().appendParam("fields", fields);
163            url = TASK_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), builder.toString(), this.getID());
164        }
165        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "GET");
166        try (BoxJSONResponse response = request.send()) {
167            JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
168            return new Info(responseJSON);
169        }
170    }
171
172    /**
173     * Updates the information about this task with any info fields that have been modified locally.
174     *
175     * <p>The only fields that will be updated are the ones that have been modified locally. For example, the following
176     * code won't update any information (or even send a network request) since none of the info's fields were
177     * changed:</p>
178     *
179     * <pre>BoxTask task = new BoxTask(api, id);
180     * BoxTask.Info info = task.getInfo();
181     * task.updateInfo(info);</pre>
182     *
183     * @param info the updated info.
184     */
185    public void updateInfo(BoxTask.Info info) {
186        URL url = TASK_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID());
187        BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT");
188        request.setBody(info.getPendingChanges());
189        try (BoxJSONResponse response = request.send()) {
190            JsonObject jsonObject = Json.parse(response.getJSON()).asObject();
191            info.update(jsonObject);
192        }
193    }
194
195    /**
196     * Enumerates the possible actions that a task can have.
197     */
198    public enum Action {
199        /**
200         * The task must be reviewed.
201         */
202        REVIEW("review"),
203
204        /**
205         * The task must be completed.
206         */
207        COMPLETE("complete");
208
209        private final String jsonValue;
210
211        Action(String jsonValue) {
212            this.jsonValue = jsonValue;
213        }
214
215        static Action fromJSONString(String jsonValue) {
216            if (jsonValue.equals("review")) {
217                return REVIEW;
218            } else if (jsonValue.equals("complete")) {
219                return COMPLETE;
220            } else {
221                throw new IllegalArgumentException("The provided JSON value isn't a valid Action.");
222            }
223        }
224
225        String toJSONString() {
226            return this.jsonValue;
227        }
228    }
229
230    /**
231     * Enumerates the possible completion rules for a task.
232     */
233    public enum CompletionRule {
234
235        /**
236         * The task must be completed by all assignees.
237         */
238        ALL_ASSIGNEES("all_assignees"),
239
240        /**
241         * The task must be completed by at least one assignee.
242         */
243        ANY_ASSIGNEE("any_assignee");
244
245        private final String jsonValue;
246
247        CompletionRule(String jsonValue) {
248            this.jsonValue = jsonValue;
249        }
250
251        String toJSONString() {
252            return this.jsonValue;
253        }
254    }
255
256    /**
257     * Contains information about a BoxTask.
258     */
259    public class Info extends BoxResource.Info {
260        private BoxFile.Info item;
261        private Date dueAt;
262        private String action;
263        private String completionRule;
264        private String message;
265        private List<BoxTaskAssignment.Info> taskAssignments;
266        private boolean completed;
267        private BoxUser.Info createdBy;
268        private Date createdAt;
269
270        /**
271         * Constructs an empty Info object.
272         */
273        public Info() {
274            super();
275        }
276
277        /**
278         * Constructs an Info object by parsing information from a JSON string.
279         *
280         * @param json the JSON string to parse.
281         */
282        public Info(String json) {
283            super(json);
284        }
285
286        /**
287         * Constructs an Info object using an already parsed JSON object.
288         *
289         * @param jsonObject the parsed JSON object.
290         */
291        Info(JsonObject jsonObject) {
292            super(jsonObject);
293        }
294
295        @Override
296        public BoxTask getResource() {
297            return BoxTask.this;
298        }
299
300        /**
301         * Gets the file associated with this task.
302         *
303         * @return the file associated with this task.
304         */
305        public BoxFile.Info getItem() {
306            return this.item;
307        }
308
309        /**
310         * Gets the date at which this task is due.
311         *
312         * @return the date at which this task is due.
313         */
314        public Date getDueAt() {
315            return this.dueAt;
316        }
317
318        /**
319         * Sets the task's due date.
320         *
321         * @param dueAt the task's due date.
322         */
323        public void setDueAt(Date dueAt) {
324            this.dueAt = dueAt;
325            this.addPendingChange("due_at", BoxDateFormat.format(dueAt));
326        }
327
328        /**
329         * Gets the action the task assignee will be prompted to do.
330         *
331         * @return the action the task assignee will be prompted to do.
332         */
333        public String getTaskType() {
334            return this.action;
335        }
336
337        /**
338         * Returns the completion rule for the task.
339         *
340         * @return the task completion rule.
341         */
342        public String getCompletionRule() {
343            return this.completionRule;
344        }
345
346        /**
347         * Sets the task's completion rule.
348         *
349         * @param completionRule the new completion rule.
350         */
351        public void setCompletionRule(CompletionRule completionRule) {
352            this.completionRule = completionRule.toJSONString();
353            this.addPendingChange("completion_rule", completionRule.toJSONString());
354        }
355
356        /**
357         * Gets the message that will be included with this task.
358         *
359         * @return the message that will be included with this task.
360         */
361        public String getMessage() {
362            return this.message;
363        }
364
365        /**
366         * Sets the task's message.
367         *
368         * @param message the task's new message.
369         */
370        public void setMessage(String message) {
371            this.message = message;
372            this.addPendingChange("message", message);
373        }
374
375        /**
376         * Gets the collection of task assignments associated with this task.
377         *
378         * @return the collection of task assignments associated with this task.
379         */
380        public List<BoxTaskAssignment.Info> getTaskAssignments() {
381            return this.taskAssignments;
382        }
383
384        /**
385         * Gets whether or not this task has been completed.
386         *
387         * @return whether or not this task has been completed.
388         */
389        public boolean isCompleted() {
390            return this.completed;
391        }
392
393        /**
394         * Gets the user who created this task.
395         *
396         * @return the user who created this task.
397         */
398        public BoxUser.Info getCreatedBy() {
399            return this.createdBy;
400        }
401
402        /**
403         * Gets when this task was created.
404         *
405         * @return when this task was created.
406         */
407        public Date getCreatedAt() {
408            return this.createdAt;
409        }
410
411        @Override
412        void parseJSONMember(JsonObject.Member member) {
413            super.parseJSONMember(member);
414
415            String memberName = member.getName();
416            JsonValue value = member.getValue();
417            try {
418                switch (memberName) {
419                    case "item":
420                        JsonObject itemJSON = value.asObject();
421                        String itemID = itemJSON.get("id").asString();
422                        BoxFile file = new BoxFile(getAPI(), itemID);
423                        this.item = file.new Info(itemJSON);
424                        break;
425                    case "due_at":
426                        this.dueAt = BoxDateFormat.parse(value.asString());
427                        break;
428                    case "action":
429                        this.action = value.asString();
430                        break;
431                    case "completion_rule":
432                        this.completionRule = value.asString();
433                        break;
434                    case "message":
435                        this.message = value.asString();
436                        break;
437                    case "task_assignment_collection":
438                        this.taskAssignments = this.parseTaskAssignmentCollection(value.asObject());
439                        break;
440                    case "is_completed":
441                        this.completed = value.asBoolean();
442                        break;
443                    case "created_by":
444                        JsonObject userJSON = value.asObject();
445                        String userID = userJSON.get("id").asString();
446                        BoxUser user = new BoxUser(getAPI(), userID);
447                        this.createdBy = user.new Info(userJSON);
448                        break;
449                    case "created_at":
450                        this.createdAt = BoxDateFormat.parse(value.asString());
451                        break;
452                    default:
453                        break;
454                }
455
456            } catch (Exception e) {
457                throw new BoxDeserializationException(memberName, value.toString(), e);
458            }
459        }
460
461        private List<BoxTaskAssignment.Info> parseTaskAssignmentCollection(JsonObject jsonObject) {
462            int count = jsonObject.get("total_count").asInt();
463            List<BoxTaskAssignment.Info> taskAssignmentCollection = new ArrayList<>(count);
464            JsonArray entries = jsonObject.get("entries").asArray();
465            for (JsonValue value : entries) {
466                JsonObject entry = value.asObject();
467                String id = entry.get("id").asString();
468                BoxTaskAssignment assignment = new BoxTaskAssignment(getAPI(), id);
469                taskAssignmentCollection.add(assignment.new Info(entry));
470            }
471
472            return taskAssignmentCollection;
473        }
474    }
475}