001package com.box.sdk;
002
003import static com.box.sdk.PagingParameters.DEFAULT_LIMIT;
004import static com.box.sdk.PagingParameters.marker;
005import static com.box.sdk.SortParameters.none;
006
007import com.eclipsesource.json.Json;
008import com.eclipsesource.json.JsonObject;
009import java.net.URL;
010import java.util.Iterator;
011
012/**
013 * Provides methods for deleting, recovering, and viewing a user's trashed files and folders.
014 *
015 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link
016 * BoxAPIException} (unchecked meaning that the compiler won't force you to handle it) if an error
017 * occurs. If you wish to implement custom error handling for errors related to the Box REST API,
018 * you should capture this exception explicitly.
019 */
020public class BoxTrash implements Iterable<BoxItem.Info> {
021
022  /** Get Item URL Template. */
023  public static final URLTemplate GET_ITEMS_URL = new URLTemplate("folders/trash/items/");
024  /** Folder Info URL Template. */
025  public static final URLTemplate FOLDER_INFO_URL_TEMPLATE = new URLTemplate("folders/%s/trash");
026  /** File Info URL Template. */
027  public static final URLTemplate FILE_INFO_URL_TEMPLATE = new URLTemplate("files/%s/trash");
028  /** Restore File URL Template. */
029  public static final URLTemplate RESTORE_FILE_URL_TEMPLATE = new URLTemplate("files/%s");
030  /** Restore Folder URL Template. */
031  public static final URLTemplate RESTORE_FOLDER_URL_TEMPLATE = new URLTemplate("folders/%s");
032
033  private final BoxAPIConnection api;
034
035  /**
036   * Constructs a BoxTrash using a given API connection.
037   *
038   * @param api the API connection to be used by the trash.
039   */
040  public BoxTrash(BoxAPIConnection api) {
041    this.api = api;
042  }
043
044  /**
045   * Permanently deletes a trashed folder.
046   *
047   * @param folderID the ID of the trashed folder to permanently delete.
048   */
049  public void deleteFolder(String folderID) {
050    URL url = FOLDER_INFO_URL_TEMPLATE.build(this.api.getBaseURL(), folderID);
051    BoxAPIRequest request = new BoxAPIRequest(this.api, url, "DELETE");
052    request.send().close();
053  }
054
055  /**
056   * Gets information about a trashed folder.
057   *
058   * @param folderID the ID of the trashed folder.
059   * @return info about the trashed folder.
060   */
061  public BoxFolder.Info getFolderInfo(String folderID) {
062    URL url = FOLDER_INFO_URL_TEMPLATE.build(this.api.getBaseURL(), folderID);
063    BoxJSONRequest request = new BoxJSONRequest(this.api, url, "GET");
064    try (BoxJSONResponse response = request.send()) {
065      JsonObject jsonObject = Json.parse(response.getJSON()).asObject();
066
067      BoxFolder folder = new BoxFolder(this.api, jsonObject.get("id").asString());
068      return folder.new Info(response.getJSON());
069    }
070  }
071
072  /**
073   * Gets information about a trashed folder that's limited to a list of specified fields.
074   *
075   * @param folderID the ID of the trashed folder.
076   * @param fields the fields to retrieve.
077   * @return info about the trashed folder containing only the specified fields.
078   */
079  public BoxFolder.Info getFolderInfo(String folderID, String... fields) {
080    String queryString = new QueryStringBuilder().appendParam("fields", fields).toString();
081    URL url = FOLDER_INFO_URL_TEMPLATE.buildWithQuery(this.api.getBaseURL(), queryString, folderID);
082    BoxJSONRequest request = new BoxJSONRequest(this.api, url, "GET");
083    try (BoxJSONResponse response = request.send()) {
084      JsonObject jsonObject = Json.parse(response.getJSON()).asObject();
085
086      BoxFolder folder = new BoxFolder(this.api, jsonObject.get("id").asString());
087      return folder.new Info(response.getJSON());
088    }
089  }
090
091  /**
092   * Restores a trashed folder back to its original location.
093   *
094   * @param folderID the ID of the trashed folder.
095   * @return info about the restored folder.
096   */
097  public BoxFolder.Info restoreFolder(String folderID) {
098    URL url = RESTORE_FOLDER_URL_TEMPLATE.build(this.api.getBaseURL(), folderID);
099    BoxJSONRequest request = new BoxJSONRequest(this.api, url, "POST");
100    JsonObject requestJSON = new JsonObject().add("", "");
101    request.setBody(requestJSON.toString());
102    try (BoxJSONResponse response = request.send()) {
103      JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
104
105      BoxFolder restoredFolder = new BoxFolder(this.api, responseJSON.get("id").asString());
106      return restoredFolder.new Info(responseJSON);
107    }
108  }
109
110  /**
111   * Restores a trashed folder to a new location with a new name.
112   *
113   * @param folderID the ID of the trashed folder.
114   * @param newName an optional new name to give the folder. This can be null to use the folder's
115   *     original name.
116   * @param newParentID an optional new parent ID for the folder. This can be null to use the
117   *     folder's original parent.
118   * @return info about the restored folder.
119   */
120  public BoxFolder.Info restoreFolder(String folderID, String newName, String newParentID) {
121    JsonObject requestJSON = new JsonObject();
122
123    if (newName != null) {
124      requestJSON.add("name", newName);
125    }
126
127    if (newParentID != null) {
128      JsonObject parent = new JsonObject();
129      parent.add("id", newParentID);
130      requestJSON.add("parent", parent);
131    }
132
133    URL url = RESTORE_FOLDER_URL_TEMPLATE.build(this.api.getBaseURL(), folderID);
134    BoxJSONRequest request = new BoxJSONRequest(this.api, url, "POST");
135    request.setBody(requestJSON.toString());
136    try (BoxJSONResponse response = request.send()) {
137      JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
138
139      BoxFolder restoredFolder = new BoxFolder(this.api, responseJSON.get("id").asString());
140      return restoredFolder.new Info(responseJSON);
141    }
142  }
143
144  /**
145   * Permanently deletes a trashed file.
146   *
147   * @param fileID the ID of the trashed folder to permanently delete.
148   */
149  public void deleteFile(String fileID) {
150    URL url = FILE_INFO_URL_TEMPLATE.build(this.api.getBaseURL(), fileID);
151    BoxAPIRequest request = new BoxAPIRequest(this.api, url, "DELETE");
152    request.send().close();
153  }
154
155  /**
156   * Gets information about a trashed file.
157   *
158   * @param fileID the ID of the trashed file.
159   * @return info about the trashed file.
160   */
161  public BoxFile.Info getFileInfo(String fileID) {
162    URL url = FILE_INFO_URL_TEMPLATE.build(this.api.getBaseURL(), fileID);
163    BoxJSONRequest request = new BoxJSONRequest(this.api, url, "GET");
164    try (BoxJSONResponse response = request.send()) {
165      JsonObject jsonObject = Json.parse(response.getJSON()).asObject();
166
167      BoxFile file = new BoxFile(this.api, jsonObject.get("id").asString());
168      return file.new Info(response.getJSON());
169    }
170  }
171
172  /**
173   * Gets information about a trashed file that's limited to a list of specified fields.
174   *
175   * @param fileID the ID of the trashed file.
176   * @param fields the fields to retrieve.
177   * @return info about the trashed file containing only the specified fields.
178   */
179  public BoxFile.Info getFileInfo(String fileID, String... fields) {
180    String queryString = new QueryStringBuilder().appendParam("fields", fields).toString();
181    URL url = FILE_INFO_URL_TEMPLATE.buildWithQuery(this.api.getBaseURL(), queryString, fileID);
182    BoxJSONRequest request = new BoxJSONRequest(this.api, url, "GET");
183    try (BoxJSONResponse response = request.send()) {
184      JsonObject jsonObject = Json.parse(response.getJSON()).asObject();
185
186      BoxFile file = new BoxFile(this.api, jsonObject.get("id").asString());
187      return file.new Info(response.getJSON());
188    }
189  }
190
191  /**
192   * Restores a trashed file back to its original location.
193   *
194   * @param fileID the ID of the trashed file.
195   * @return info about the restored file.
196   */
197  public BoxFile.Info restoreFile(String fileID) {
198    URL url = RESTORE_FILE_URL_TEMPLATE.build(this.api.getBaseURL(), fileID);
199    BoxJSONRequest request = new BoxJSONRequest(this.api, url, "POST");
200    JsonObject requestJSON = new JsonObject().add("", "");
201    return getInfo(requestJSON, request);
202  }
203
204  /**
205   * Restores a trashed file to a new location with a new name.
206   *
207   * @param fileID the ID of the trashed file.
208   * @param newName an optional new name to give the file. This can be null to use the file's
209   *     original name.
210   * @param newParentID an optional new parent ID for the file. This can be null to use the file's
211   *     original parent.
212   * @return info about the restored file.
213   */
214  public BoxFile.Info restoreFile(String fileID, String newName, String newParentID) {
215    JsonObject requestJSON = new JsonObject();
216
217    if (newName != null) {
218      requestJSON.add("name", newName);
219    }
220
221    if (newParentID != null) {
222      JsonObject parent = new JsonObject();
223      parent.add("id", newParentID);
224      requestJSON.add("parent", parent);
225    }
226
227    URL url = RESTORE_FILE_URL_TEMPLATE.build(this.api.getBaseURL(), fileID);
228    BoxJSONRequest request = new BoxJSONRequest(this.api, url, "POST");
229    return getInfo(requestJSON, request);
230  }
231
232  /**
233   * Returns an iterator over the items in the trash.
234   *
235   * @return an iterator over the items in the trash.
236   */
237  public Iterator<BoxItem.Info> iterator() {
238    return items(none(), marker(DEFAULT_LIMIT)).iterator();
239  }
240
241  /**
242   * Returns an iterable containing the items in trash. You can specify sort order, limit of files
243   * requested, ofset or use marker based pagination.
244   *
245   * @param sortParameters describes sorting parameters. Sort parameters are supported only with
246   *     offset based pagination. Use {@link SortParameters#none()} to ignore sorting.
247   * @param pagingParameters describes paging parameters
248   * @param fields the fields to retrieve.
249   * @return an iterable containing the items in the trash.
250   */
251  public Iterable<BoxItem.Info> items(
252      SortParameters sortParameters, PagingParameters pagingParameters, String... fields) {
253    QueryStringBuilder builder = sortParameters.asQueryStringBuilder();
254    validateSortIsSelectedWithOffsetPaginationOnly(pagingParameters, builder);
255
256    if (fields.length > 0) {
257      builder.appendParam("fields", fields);
258    }
259    final String query = builder.toString();
260    return () -> {
261      URL url = GET_ITEMS_URL.buildWithQuery(this.api.getBaseURL(), query);
262      if (pagingParameters == null) {
263        return new BoxItemIterator(this.api, url, marker(DEFAULT_LIMIT));
264      } else {
265        return new BoxItemIterator(this.api, url, pagingParameters);
266      }
267    };
268  }
269
270  private BoxFile.Info getInfo(JsonObject requestJSON, BoxJSONRequest request) {
271    request.setBody(requestJSON.toString());
272    try (BoxJSONResponse response = request.send()) {
273      JsonObject responseJSON = Json.parse(response.getJSON()).asObject();
274
275      BoxFile restoredFile = new BoxFile(this.api, responseJSON.get("id").asString());
276      return restoredFile.new Info(responseJSON);
277    }
278  }
279
280  /**
281   * Throws IllegalArgumentException exception when sorting and marker pagination is selected.
282   *
283   * @param pagingParameters paging definition to check
284   * @param sortQuery builder containing sort query
285   */
286  private void validateSortIsSelectedWithOffsetPaginationOnly(
287      PagingParameters pagingParameters, QueryStringBuilder sortQuery) {
288    if (pagingParameters != null
289        && pagingParameters.isMarkerBasedPaging()
290        && sortQuery.toString().length() > 0) {
291      throw new IllegalArgumentException(
292          "Sorting is not supported when using marker based pagination.");
293    }
294  }
295}