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