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