Java tutorial
/* * Copyright (c) 2013, the authors. * * This file is part of 'DXFS'. * * DXFS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * DXFS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DXFS. If not, see <http://www.gnu.org/licenses/>. */ package nextflow.fs.dx.api; import static nextflow.fs.dx.DxHelper.jsonToObj; import static nextflow.fs.dx.DxHelper.objToJson; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import nextflow.fs.dx.DxHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A wrapper over the {@code DXAPI} class * * * @author Paolo Di Tommaso <paolo.ditommaso@gmail.com> * @author Beatriz San Juan <bmsanjuan@gmail.com> * */ public class DxApi { private static Logger log = LoggerFactory.getLogger(DxApi.class); static class Holder { private static final DxApi INSTANCE = new DxApi(DxHttpClient.getInstance()); } private final DxHttpClient client; private final JsonNode EMPTY_JSON = objToJson(new HashMap<>(0)); // keep track of the last method abject for testing purpose private DxMethod lastMethodObj; /* * Access to the last method object * NOT thread safe -- ONLY FOR TESTING */ DxMethod getMethodObj() { return lastMethodObj; } Map getMethodMap() { Map result = new HashMap(2); result.put("action", lastMethodObj.action); result.put("input", DxHelper.jsonToObj(lastMethodObj.input)); return result; } /** * Wrap the API method to be invoked */ public class DxMethod { final String action; final JsonNode input; public DxMethod(String m, JsonNode args) { this.action = m; this.input = args; } /** * Invoke the target method * @param <T> * @return The object as returned by the target API call */ @SuppressWarnings("unchecked") public <T> T call() throws IOException { if (log.isDebugEnabled()) { log.debug("Call API method '" + action + "' with params: " + input.toString()); } T result = (T) client.request(action, input); if (log.isDebugEnabled()) { log.debug(String.format("[result of method '%s' ==> %s]", action, String.valueOf(result))); } return result; } } /** * Used only internally, access through the singleton access method * * @param client */ protected DxApi(DxHttpClient client) { this.client = client; } /** * @return {@code DxApi} instance object */ static public DxApi getInstance() { return Holder.INSTANCE; } /** * Get a method reference to the specified api *verb* and arguments * * @param verb * @param args * @return */ DxMethod api(String verb, Object... args) { if (args.length == 0) { args = new Object[] { EMPTY_JSON }; } else if (args.length > 1) { throw new IllegalArgumentException("DxApi api accepts at most one argument"); } if (!(args[0] instanceof JsonNode)) { throw new IllegalArgumentException("DxApi api argument must be a JsonNode"); } return lastMethodObj = new DxMethod(verb, (JsonNode) args[0]); } // ------------------------ PUBLIC API START HERE ------------------------- /** * See https://wiki.dnanexus.com/API-Specification-v1.0.0/Projects#API-method:-/project/new * * @param name * @return */ public String projectNew(String name) throws IOException { Objects.requireNonNull(name); Map inputs = new HashMap(); inputs.put("name", name); JsonNode node = objToJson(inputs); JsonNode result = api("/project/new", node).call(); return result.get("id").textValue(); } /** * See https://wiki.dnanexus.com/API-Specification-v1.0.0/Projects#API-method:-/project-xxxx/describe * @param projectId * @return */ public Map projectDescribe(String projectId, String... fields) throws IOException { Objects.requireNonNull(projectId); Map inputs = new HashMap(); for (String item : fields) { inputs.put(item, true); } JsonNode result = api(String.format("/%s/describe", projectId), objToJson(inputs)).call(); return jsonToObj(result); } /** * See https://wiki.dnanexus.com/API-Specification-v1.0.0/Projects#API-method:-/project-xxxx/destroy * @param projectId * @return */ public void projectDestroy(String projectId) throws IOException { api(String.format("/%s/destroy", projectId)).call(); } /** * Invoke DnaNexus API * * See https://wiki.dnanexus.com/API-Specification-v1.0.0/Search#API-method:-/system/findDataObjects * * @param args * @return */ public List systemFindDataObjects(Map args) throws IOException { DxApi.DxMethod method = api("/system/findDataObjects", objToJson(args)); JsonNode result = method.call(); Map map = jsonToObj(result); return (List) map.get("results"); } /** * Find a file in remote DnaNexus cloud storage * * @param projectId * @param fullyQualifiedFileName * @return */ public List fileFind(String projectId, String fullyQualifiedFileName) throws IOException { int p = fullyQualifiedFileName.lastIndexOf('/'); String folder; String fileName; if (p == -1) { folder = "/"; fileName = fullyQualifiedFileName; } else { fileName = fullyQualifiedFileName.substring(p + 1); folder = fullyQualifiedFileName.substring(0, p); } Map<String, Object> scope = new HashMap<>(); scope.put("folder", folder); scope.put("project", projectId); Map<String, Object> args = new HashMap<>(); args.put("class", "file"); args.put("name", fileName); args.put("scope", scope); return systemFindDataObjects(args); } /** * https://wiki.dnanexus.com/API-Specification-v1.0.0/Search#API-method:-/system/findProjects */ @SuppressWarnings("unchecked") public List<Map<String, Object>> projectFind(String name) throws IOException { Map<String, String> request = new HashMap<>(1); if (name != null && !"".equals(name)) { request.put("name", name); } JsonNode result = api("/system/findProjects", objToJson(request)).call(); return (List<Map<String, Object>>) ((Map) jsonToObj(result)).get("results"); } /** * ListCmd the content of a folder as define by the following API * * https://wiki.dnanexus.com/API-Specification-v1.0.0/Folders-and-Deletion#API-method:-/class-xxxx/listFolder * * @param contextId * @param path The folder to be listed. Note" must start with a / (slash) character * @param describe * @return */ public Map<String, Object> folderList(String contextId, String path, boolean describe) throws IOException { Map<String, Object> input = new HashMap<>(); input.put("folder", path); input.put("describe", describe); JsonNode result = api(String.format("/%s/listFolder", contextId), objToJson(input)).call(); return jsonToObj(result); } /** * Wrap DnaNexus *newFolder* operation as define by * https://wiki.dnanexus.com/API-Specification-v1.0.0/Folders-and-Deletion#API-method:-/class-xxxx/newFolder * * @param path * @return the entity ID of the manipulated data container */ public String folderCreate(String containerId, String path, boolean create) throws IOException { /* * An example input JSON object * * '{"folder":"/alpha/beta/delta", "parents":true}' */ JsonNode obj = DxJson.getObjectBuilder().put("folder", path).put("parents", true).build(); JsonNode result = api(String.format("/%s/newFolder", containerId), obj).call(); return result.get("id").textValue(); } /** * Create the {@code JsonNode} object to delete a folder. * <p> * Example object format: * * <pre> * '{"folder":"/folderName", "recurse": true}' * </pre> * * <p> * Read more https://wiki.dnanexus.com/API-Specification-v1.0.0/Folders-and-Deletion#API-method:-/class-xxxx/removeFolder * </p> * * @param path * * @return The entity ID of the manipulated data container */ public String folderDelete(String contextId, String path, boolean recurse) throws IOException { JsonNode node = DxJson.getObjectBuilder().put("folder", path).put("recurse", recurse).build(); JsonNode result = api(String.format("/%s/removeFolder", contextId), node).call(); return result.get("id").textValue(); } /** * Creater the {@code JsonNode} input object required to delete one, or more, files * * <p> * Example JSON object format: * <pre> * '{"objects":["file-B86Q5B80j58B67zVXG3Q01K8"]}' * </pre> * </p> * * * * <p> * http://wiki.dnanexus.com/API-Specification-v1.0.0/Folders-and-Deletion#API-method:-/class-xxxx/removeObjects * </p> * * @param fileIds an array of strings representing IDs of the objects to be removed from the data * @return @return The entity ID of the manipulated data container */ public String fileDelete(String contextId, String... fileIds) throws IOException { ArrayNode container = new ArrayNode(JsonNodeFactory.instance); for (String item : fileIds) { container.add(item); } JsonNode input = DxJson.getObjectBuilder().put("objects", container).build(); JsonNode result = api(String.format("/%s/removeObjects", contextId), input).call(); return result.get("id").textValue(); } /** * Describe a file * * Read more https://wiki.dnanexus.com/API-Specification-v1.0.0/Files#API-method%3A-%2Ffile-xxxx%2Fdescribe * * * @param fileId * @return */ public Map<String, Object> fileDescribe(String fileId) throws IOException { JsonNode node = api(String.format("/%s/describe", fileId)).call(); return jsonToObj(node); } /** * https://wiki.dnanexus.com/API-Specification-v1.0.0/Name#API-method%3A-%2Fclass-xxxx%2Frename * * @param contextId * @param fileId * @param name * @return */ public String rename(String contextId, String fileId, String name) throws IOException { JsonNode input = DxJson.getObjectBuilder().put("project", contextId).put("name", name).build(); JsonNode result = api(String.format("/%s/rename", fileId), input).call(); return result.get("id").textValue(); } /** * https://wiki.dnanexus.com/API-Specification-v1.0.0/Tags#API-method%3A-%2Fclass-xxxx%2FaddTags * * @param contextId * @param fileId * @param tags * @return */ public String addFileTags(String contextId, String fileId, String[] tags) throws IOException { ArrayNode container = new ArrayNode(JsonNodeFactory.instance); for (String item : tags) { container.add(item); } JsonNode input = DxJson.getObjectBuilder().put("project", contextId).put("tags", container).build(); JsonNode result = api(String.format("/%s/addTags", fileId), input).call(); return result.get("id").textValue(); } /** * https://wiki.dnanexus.com/API-Specification-v1.0.0/Types#API-method%3A-%2Fclass-xxxx%2FaddTypes * * @param contextId * @param fileId * @param types * @return */ public String addFileTypes(String contextId, String fileId, String[] types) throws IOException { ArrayNode container = new ArrayNode(JsonNodeFactory.instance); for (String item : types) { container.add(item); } JsonNode input = DxJson.getObjectBuilder().put("project", contextId).put("types", container).build(); JsonNode result = api(String.format("/%s/addTypes", fileId), input).call(); return result.get("id").textValue(); } /** * Given a file-id return the a URL and the authorization code to download it. * * See https://wiki.dnanexus.com/API-Specification-v1.0.0/Files#API-method%3A-%2Ffile-xxxx%2Fdownload * * @param fileId * @return A map object holding the */ public Map<String, Object> fileDownload(String fileId) throws IOException { JsonNode result = api(String.format("/%s/download", fileId)).call(); return jsonToObj(result); } /** * Create a new (empty) file ready * * Read more * https://wiki.dnanexus.com/API-Specification-v1.0.0/Files#API-method%3A-%2Ffile%2Fnew * * @param contextId * @param path * @return */ public String fileNew(String contextId, String path) throws IOException { String name; String folder = null; int pos = path.lastIndexOf('/'); if (pos == -1) { name = path; } else { name = path.substring(pos + 1); folder = path.substring(0, pos); } Map<String, Object> uploadInfo = new HashMap<>(); uploadInfo.put("project", contextId); uploadInfo.put("name", name); if (folder != null && folder.length() > 0) { // set the parent folder where it must be stored uploadInfo.put("folder", folder); uploadInfo.put("parents", true); } return fileNew(uploadInfo); } /** * Create a new (empty) file ready * * Read more * https://wiki.dnanexus.com/API-Specification-v1.0.0/Files#API-method%3A-%2Ffile%2Fnew * * @param uploadInfo * @return */ public String fileNew(Map<String, Object> uploadInfo) throws IOException { JsonNode result = api("/file/new", objToJson(uploadInfo)).call(); return result.get("id").textValue(); } /** * Upload a file (part) to the remote container * * See https://wiki.dnanexus.com/API-Specification-v1.0.0/Files#API-method:-/file-xxxx/upload * * @param fileId The file-id of the file being uploaded * @param index The chunk index of the filed being uploaded (1-based) * @return */ public Map fileUpload(String fileId, int index) throws IOException { JsonNode input = DxJson.getObjectBuilder().put("index", index).build(); JsonNode result = api(String.format("/%s/upload", fileId), input).call(); return jsonToObj(result); } /** * Close the file when upload is complete * * See https://wiki.dnanexus.com/API-Specification-v1.0.0/Files#API-method:-/file-xxxx/close * * @param fileId The file-id of the file being uploaded * @return */ public Map fileClose(String fileId) throws IOException { JsonNode result = api(String.format("/%s/close", fileId)).call(); return jsonToObj(result); } /** * Clone a file to the specified path * * @param sourceContainerId * @param sourceFileId * @param targetContainerId * @param targetPath * @return An {@code Map} containing the following elements: * <li>id: the entity ID of the source container * <li>project: the entity ID of the modified destination container * <li>exists: an array of strings representing the object IDs that could not be cloned because they already exist in the destination container */ public Map<String, Object> fileClone(String sourceContainerId, String sourceFileId, String targetContainerId, String targetPath) throws IOException { List<String> wrap = new ArrayList<>(); wrap.add(sourceFileId); return fileClone(sourceContainerId, wrap, targetContainerId, targetPath); } /** * Clone a file to the specified path * * See https://wiki.dnanexus.com/API-Specification-v1.0.0/Cloning#API-method:-/class-xxxx/clone * * @param sourceContainerId * @param sourceFileId * @param targetContainerId * @param targetPath * @return An {@code Map} containing the following elements: * <li>id: the entity ID of the source container * <li>project: the entity ID of the modified destination container * <li>exists: an array of strings representing the object IDs that could not be cloned because they already exist in the destination container */ public Map<String, Object> fileClone(String sourceContainerId, List<String> sourceFileId, String targetContainerId, String targetPath) throws IOException { if (sourceContainerId == null || sourceContainerId.isEmpty()) throw new IllegalStateException("Argument `sourceContainerId` cannot be empty"); if (targetContainerId == null || targetContainerId.isEmpty()) throw new IllegalStateException("Argument `targetContainerId` cannot be empty"); Map<String, Object> request = new HashMap<>(); request.put("objects", sourceFileId); // list of files to be copied request.put("project", targetContainerId); // target container ID request.put("destination", targetPath); // folder where store the file request.put("parents", true); // create the target folder if do not exist JsonNode result = api(String.format(String.format("/%s/clone", sourceContainerId)), objToJson(request)) .call(); return jsonToObj(result); } /** * See https://wiki.dnanexus.com/API-Specification-v1.0.0/Applets%20and%20Entry%20Points#API-method:-/job/new * * @param inputs * @return */ public String jobNew(Map<String, Object> inputs) throws IOException { JsonNode result = api("/job/new", objToJson(inputs)).call(); return result.get("id").textValue(); } /** * See https://wiki.dnanexus.com/API-Specification-v1.0.0/Applets%20and%20Entry%20Points#API-method:-/job-xxxx/describe * * @param jobId */ public Map jobDescribe(String jobId, String... fields) throws IOException { Objects.requireNonNull(jobId); Map inputs = new HashMap(); for (String item : fields) { inputs.put(item, true); } JsonNode result = api(String.format("/%s/describe", jobId), objToJson(inputs)).call(); return jsonToObj(result); } /** * See https://wiki.dnanexus.com/API-Specification-v1.0.0/Applets%20and%20Entry%20Points#API-method:-/job-xxxx/terminate * * @param jobId */ public void jobTerminate(String jobId) throws IOException { Objects.requireNonNull(jobId); api(String.format("/%s/terminate", jobId)).call(); } }