nextflow.fs.dx.api.DxApi.java Source code

Java tutorial

Introduction

Here is the source code for nextflow.fs.dx.api.DxApi.java

Source

/*
 * 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();
    }

}