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