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