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