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