R/Commands.R

Defines functions .getRequester .getDefaultSandbox doSetSandbox doInitializeSandbox findRemoteCytoscape .pathURLencode .cyFinally .cyWarnings .cyError .prepPostQueryLists .prepGetQueryArgs .command2postQueryBody .command2postQueryUrl .command2getQuery commandSleep commandRunFile commandQuit commandPause commandOpenDialog commandEcho commandsRun commandsPOST commandsHelp commandsGET commandsAPI cyrestPUT cyrestPOST cyrestGET cyrestDELETE cyrestAPI

Documented in commandEcho commandOpenDialog commandPause commandQuit commandRunFile commandsAPI commandsGET commandsHelp commandSleep commandsPOST commandsRun cyrestAPI cyrestDELETE cyrestGET cyrestPOST cyrestPUT doInitializeSandbox doSetSandbox findRemoteCytoscape .getDefaultSandbox .getRequester

# ==============================================================================
# Functions for constructing any arbitrary CyREST API or Commands API method via
# standard GET, PUT, POST and DELETE protocols. These functions handle marshalling
# and unmarshalling of urls, parameters and returns so that higher-level functions
# can work with R-friendly arguments and returns.
#
# I. CyREST API functions
# II. Commands API functions
# III. Internal functions 
# IV. Jupyter-bridge
# V. Sandbox
# 
# Note: This is where the bulk of the dependencies for other packages are used,
# e.g., utils, httr, RJSONIO, etc. Follow the use of @importFrom where prudent.
#
# ==============================================================================
# I. CyREST API functions
# ------------------------------------------------------------------------------
#' Open Swagger docs for CyREST API 
#'
#' @description Opens swagger docs in default browser for a live
#' instance of CyREST operations.
#' @param base.url (optional) Ignore unless you need to specify a custom domain,
#' port or version to connect to the CyREST API. Default is http://localhost:1234
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return Web page in browser
#' @export
#' @examples
#' \donttest{
#' cyrestAPI()
#' }
#' @importFrom utils browseURL
cyrestAPI<-function(base.url=.defaultBaseUrl){
    if(!findRemoteCytoscape(base.url)){
    browseURL(paste(base.url,'/swaggerUI/swagger-ui/index.html?url=',base.url,'/swagger.json#/',sep=""))
    } else {
        doRequestRemote("webbrowser", paste(base.url, '/swaggerUI/swagger-ui/index.html?url=',base.url,'/swagger.json#/',sep=""))
    }
}

# ------------------------------------------------------------------------------
#' @title CyREST DELETE
#'
#' @description Constructs the query, makes DELETE call and processes the result.
#' The function check whether actual call is local or remote first. If remote,
#' requests will go through Jupyter-Bridge.
#' @param operation A string to be converted to the REST query namespace
#' @param parameters A named list of values to be converted to REST query parameters 
#' @param base.url (optional) Ignore unless you need to specify a custom domain,
#' port or version to connect to the CyREST API. Default is http://localhost:1234
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return CyREST result content
#' @examples \donttest{
#' cyrestDELETE('session')
#' }
#' @importFrom RJSONIO fromJSON isValidJSON
#' @importFrom httr DELETE
#' @importFrom utils URLencode
#' @export
cyrestDELETE <- function(operation=NULL, parameters=NULL, base.url=.defaultBaseUrl){
    requester <- NULL
    doInitializeSandbox(requester, base.url=base.url)
    if(!findRemoteCytoscape(base.url)){
        q.url <- paste(base.url, operation, sep="/")
        if(!is.null(parameters)){
            q.params <- .prepGetQueryArgs(parameters)
            q.url <- paste(q.url, q.params, sep="?")
        }
        tryCatch(
            res <- DELETE(url=URLencode(q.url)), 
            error=function(c) .cyError(c, res),
            warnings=function(c) .cyWarnings(c, res),
            finally=.cyFinally(res)
        )
        if(length(res$content)>0){
            res.char <- rawToChar(res$content)
            if (RJSONIO::isValidJSON(res.char, asText = TRUE)){
                return(RJSONIO::fromJSON(res.char))
            } else {
                return(res.char)
            }
            invisible(res)
        }
    } else {
        q.url <- paste(base.url, .pathURLencode(operation), sep="/")
        if(!is.null(parameters)){
            q.params <- .prepGetQueryArgs(parameters)
            q.url <- paste(q.url, q.params, sep="?")
        }
        res <- doRequestRemote("DELETE", URLencode(q.url))
        if(length(res$content)>0){
            res.char <- rawToChar(res$content)
            if (RJSONIO::isValidJSON(res.char, asText = TRUE)){
                return(RJSONIO::fromJSON(res.char))
            } else {
                return(res.char)
            }
        } else{
            invisible(res)
        }
    }
}

# ------------------------------------------------------------------------------
#' @title CyREST GET
#'
#' @description Constructs the query, makes GET call and processes the result.
#' The function check whether actual call is local or remote first. If remote,
#' requests will go through Jupyter-Bridge.
#' @param operation A string to be converted to the REST query namespace
#' @param parameters A named list of values to be converted to REST query parameters 
#' @param base.url (optional) Ignore unless you need to specify a custom domain,
#' port or version to connect to the CyREST API. Default is http://localhost:1234
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return CyREST result content
#' @examples \donttest{
#' cyrestGET('version')
#' }
#' @importFrom RJSONIO fromJSON isValidJSON
#' @importFrom httr GET
#' @importFrom utils URLencode
#' @export
cyrestGET <- function(operation=NULL, parameters=NULL, base.url=.defaultBaseUrl){
    requester <- NULL
    doInitializeSandbox(requester, base.url=base.url)
    if(!findRemoteCytoscape(base.url)){
        q.url <- paste(base.url, operation, sep="/")
        if(!is.null(parameters)){
            q.params <- .prepGetQueryArgs(parameters)
            q.url <- paste(q.url, q.params, sep="?")
        }
        tryCatch(
            res <- GET(url=URLencode(q.url)), 
            error=function(c) .cyError(c, res),
            warnings=function(c) .cyWarnings(c, res),
            finally=.cyFinally(res)
        )
        if(length(res$content)>0){
            res.char <- rawToChar(res$content)
            if (RJSONIO::isValidJSON(res.char, asText = TRUE)){
                return(RJSONIO::fromJSON(res.char))
            } else {
                return(res.char)
            }
        } else{
            invisible(res)
        }
    } else {
        if(!is.null(operation)){
            q.url <- paste(base.url, .pathURLencode(operation), sep="/")
        } else {
            q.url <- paste(base.url)
        }
        if(!is.null(parameters)){
            q.params <- .prepGetQueryArgs(parameters)
            q.url <- paste(q.url, q.params, sep="?")
        }
        res <- doRequestRemote("GET", URLencode(q.url))
        if(length(res$content)>0){
            res.char <- rawToChar(res$content)
            if (RJSONIO::isValidJSON(res.char, asText = TRUE)){
                return(RJSONIO::fromJSON(RJSONIO::fromJSON(res.char)$text))
            } else {
                return(res.char)
            }
        } else{
            invisible(res)
        }
    }
}

# ------------------------------------------------------------------------------
#' @title CyREST POST
#'
#' @description Constructs the query and body, makes POST call and processes the result.
#' The function check whether actual call is local or remote first. If remote,
#' requests will go through Jupyter-Bridge.
#' @param operation A string to be converted to the REST query namespace
#' @param parameters A named list of values to be converted to REST query parameters 
#' @param body A named list of values to be converted to JSON
#' @param base.url (optional) Ignore unless you need to specify a custom domain,
#' port or version to connect to the CyREST API. Default is http://localhost:1234
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return CyREST result content
#' @examples \donttest{
#' cyrestPOST('networks/51/views')
#' }
#' @importFrom RJSONIO fromJSON toJSON isValidJSON
#' @importFrom httr POST content_type_json
#' @importFrom utils URLencode
#' @export
cyrestPOST <- function(operation, parameters=NULL, body=NULL, base.url=.defaultBaseUrl){
    requester <- NULL
    doInitializeSandbox(requester, base.url=base.url)
    if(!findRemoteCytoscape(base.url)){
        q.url <- paste(base.url, operation, sep="/")
        if(!is.null(parameters)){
            q.params <- .prepGetQueryArgs(parameters)
            q.url <- paste(q.url, q.params, sep="?")
        }
        q.body <- toJSON(body)
        tryCatch(
            res <- POST(url=URLencode(q.url), body=q.body, encode="json", content_type_json()), 
            error=function(c) .cyError(c, res),
            warnings=function(c) .cyWarnings(c, res),
            finally=.cyFinally(res)
        )
        if(length(res$content)>0){
            res.char <- rawToChar(res$content)
            if (RJSONIO::isValidJSON(res.char, asText = TRUE)){
                return(RJSONIO::fromJSON(res.char))
            } else {
                return(res.char)
            }
            invisible(res)
        }
    } else {
        q.url <- paste(base.url, .pathURLencode(operation), sep="/")
        if(!is.null(parameters)){
            q.params <- .prepGetQueryArgs(parameters)
            q.url <- paste(q.url, q.params, sep="?")
        }
        q.body <- body
        res <- doRequestRemote("POST", URLencode(q.url), q.body, headers=list("Content-Type" = "application/json"))
        if(length(res$content)>0){
            res.char <- rawToChar(res$content)
            if (RJSONIO::isValidJSON(res.char, asText = TRUE)){
                return(RJSONIO::fromJSON(res.char))
            } else {
                return(res.char)
            }
        } else{
            invisible(res)
        }
    }
}

# ------------------------------------------------------------------------------
#' @title CyREST PUT
#'
#' @description Constructs the query and body, makes PUT call and processes the result.
#' The function check whether actual call is local or remote first. If remote,
#' requests will go through Jupyter-Bridge.
#' @param operation A string to be converted to the REST query namespace
#' @param parameters A named list of values to be converted to REST query parameters 
#' @param body A named list of values to be converted to JSON
#' @param base.url (optional) Ignore unless you need to specify a custom domain,
#' port or version to connect to the CyREST API. Default is http://localhost:1234
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return CyREST result content
#' @examples \donttest{
#' cyrestPUT()
#' }
#' @importFrom RJSONIO fromJSON isValidJSON toJSON
#' @importFrom httr PUT content_type_json
#' @importFrom utils URLencode
#' @export
cyrestPUT <- function(operation, parameters=NULL, body=NULL, base.url=.defaultBaseUrl){
    requester <- NULL
    doInitializeSandbox(requester, base.url=base.url)
    if(!findRemoteCytoscape(base.url)){
        q.url <- paste(base.url, operation, sep="/")
        if(!is.null(parameters)){
            q.params <- .prepGetQueryArgs(parameters)
            q.url <- paste(q.url, q.params, sep="?")
        }
        q.body <- toJSON(body)
        tryCatch(
            res <- PUT(url=URLencode(q.url), body=q.body, encode="json", content_type_json()), 
            error=function(c) .cyError(c, res),
            warnings=function(c) .cyWarnings(c, res),
            finally=.cyFinally(res)
        )
        if(length(res$content)>0){
            res.char <- rawToChar(res$content)
            if (RJSONIO::isValidJSON(res.char, asText = TRUE)){
                return(RJSONIO::fromJSON(res.char))
            } else {
                return(res.char)
            }
            invisible(res)
        }
    } else {
        q.url <- paste(base.url, .pathURLencode(operation), sep="/")
        if(!is.null(parameters)){
            q.params <- .prepGetQueryArgs(parameters)
            q.url <- paste(q.url, q.params, sep="?")
        }
        q.body <- body
        res <- doRequestRemote("PUT", URLencode(q.url), q.body, headers=list("Content-Type" = "application/json"))
        if(length(res$content)>0){
            res.char <- rawToChar(res$content)
            if (RJSONIO::isValidJSON(res.char, asText = TRUE)){
                return(RJSONIO::fromJSON(res.char)$text)
            } else {
                return(res.char)
            }
        } else{
            invisible(res)
        }
    }
}

# ==============================================================================
# II. Commands API functions
# ------------------------------------------------------------------------------
#' Open Swagger docs for CyREST Commands API 
#'
#' @description Opens swagger docs in default browser for a live
#' instance of Commands available via CyREST. 
#' @param base.url (optional) Ignore unless you need to specify a custom domain,
#' port or version to connect to the CyREST API. Default is http://localhost:1234
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return Web page in browser
#' @export
#' @examples
#' \donttest{
#' commandsAPI()
#' }
#' @importFrom utils browseURL
commandsAPI<-function(base.url=.defaultBaseUrl){
    browseURL(paste(base.url,'/swaggerUI/swagger-ui/index.html?url=',base.url,'/commands/swagger.json#/',sep=""))
}

# ------------------------------------------------------------------------------
#' @title Commands GET
#'
#' @description Using the same syntax as Cytoscape's Command Line Dialog,
#' this function converts a command string into a CyREST query URL, executes a GET
#' request, and parses the result content into an R list object.
#' The function check whether actual call is local or remote first. If remote,
#' requests will go through Jupyter-Bridge.
#' Extremely long commands may result in an immediate "URI Too Long" failure because
#' `commandsRun()` uses HTTP GET to call the CyREST API, and HTTP GET has known limitations.
#' You can use `commandsPost()` instead, though the return value will be different.
#' `commandsPost()` uses HTTP POST, which has no limitations.
#' @param cmd.string (char) command
#' @param base.url (optional) Ignore unless you need to specify a custom domain,
#' port or version to connect to the CyREST API. Default is http://localhost:1234
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return A \code{list}, \code{status} or None.
#' @examples
#' \donttest{
#' commandsGET('layout get preferred network=current')
#' commandsGET('network list properties network=current')
#' commandsGET('layout force-directed defaultNodeMass=1')
#' }
#' @importFrom XML htmlParse xmlValue xpathSApply
#' @importFrom httr GET
#' @export
commandsGET<-function(cmd.string, base.url = .defaultBaseUrl){
    requester <- NULL
    doInitializeSandbox(requester, base.url=base.url)
    if(!findRemoteCytoscape(base.url)){
        q.url <- .command2getQuery(cmd.string,base.url)
        tryCatch(
            res <- GET(q.url), 
            error=function(c) .cyError(c, res),
            warnings=function(c) .cyWarnings(c, res),
            finally=.cyFinally(res)
        ) 
        res.html = htmlParse(rawToChar(res$content), asText=TRUE)
        res.elem = xpathSApply(res.html, "//p", xmlValue)
        if(startsWith(res.elem[1],"[")){
            res.elem[1] = gsub("\\[|\\]|\"","",res.elem[1])
            res.elem2 = unlist(strsplit(res.elem[1],"\n"))[1]
            res.list = unlist(strsplit(res.elem2,","))
        }else {
            res.list = unlist(strsplit(res.elem[1],"\n\\s*"))
            res.list = res.list[!(res.list=="Finished")]
        }
        if(length(res.list)>0){
            res.list
        } else {
            invisible(res.list)
        }
    } else {
        q.url <- .command2getQuery(cmd.string, base.url)
        res <- doRequestRemote("GET", URLencode(q.url), headers=list("Accept" = "text/plain"))
        res.html = htmlParse(rawToChar(res$content), asText=TRUE)
        res.elem = xpathSApply(res.html, "//p", xmlValue)
        if(startsWith(res.elem[1],"[")){
            res.elem[1] = gsub("\\[|\\]|\"","",res.elem[1])
            res.elem2 = unlist(strsplit(res.elem[1],"\n"))[1]
            res.list = unlist(strsplit(res.elem2,","))
        }else {
            res.list = unlist(strsplit(res.elem[1],"\n\\s*"))
            res.list = res.list[!(res.list=="Finished")]
        }
        if(length(res.list)>0){
            unlist(strsplit(RJSONIO::fromJSON(res.list)$text,"\n"))
        } else {
            invisible(res.list)
        }
    }
}

# ------------------------------------------------------------------------------
#' @title Commands Help
#'
#' @description Using the same syntax as Cytoscape's Command Line Dialog,
#' this function returns a list of available commands or args.
#' The function check whether actual call is local or remote first. If remote,
#' requests will go through Jupyter-Bridge.
#' @details Works with or without 'help' command prefix. Note that if you ask about a command that doesn't
#' have any arguments, this function will run the command!
#' @param cmd.string (char) command
#' @param base.url (optional) Ignore unless you need to specify a custom domain,
#' port or version to connect to the CyREST API. Default is http://localhost:1234
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return List of available commands or args
#' @examples
#' \donttest{
#' commandsHelp()
#' commandsHelp('node')
#' commandsHelp('node get attribute')
#' }
#' @importFrom XML htmlParse xmlValue xpathSApply
#' @importFrom httr GET
#' @importFrom utils head tail
#' @export
commandsHelp<-function(cmd.string='help', base.url = .defaultBaseUrl){
    requester <- NULL
    doInitializeSandbox(requester, base.url=base.url)
    s=sub('help *','',cmd.string)
    if(!findRemoteCytoscape(base.url)){
        q.url <- .command2getQuery(s,base.url)
        tryCatch(
            res <- GET(q.url), 
            error=function(c) .cyError(c, res),
            warnings=function(c) .cyWarnings(c, res),
            finally=.cyFinally(res)
        )
        res.html = htmlParse(rawToChar(res$content), asText=TRUE)
        res.elem = xpathSApply(res.html, "//p", xmlValue)
        res.list = res.elem
        if (length(res.elem)==1){
            res.list = unlist(strsplit(res.elem[1],"\n\\s*"))
        }
        print(head(res.list,1))
        vapply(tail(res.list,-1), trimws, character(1), USE.NAMES = FALSE)
    } else {
        q.url <- .command2getQuery(s, base.url)
        res <- doRequestRemote("GET", URLencode(q.url), headers=list("Accept" = "text/plain"))
        res.html = htmlParse(rawToChar(res$content), asText=TRUE)
        res.elem = xpathSApply(res.html, "//p", xmlValue)
        res.elem = (RJSONIO::fromJSON(res.elem))$text
        res.list = res.elem
        if (length(res.elem)==1){
            res.list = unlist(strsplit(res.elem[1],"\n\\s*"))
        }
        vapply(tail(res.list,-1), trimws, character(1), USE.NAMES = FALSE)
    }
}

# ------------------------------------------------------------------------------
#' @title Commands POST
#'
#' @description Using the same syntax as Cytoscape's Command Line Dialog,
#' this function converts a command string into a CyREST query URL, executes a
#' POST request, and parses the result content into an R list object.
#' The function check whether actual call is local or remote first. If remote,
#' requests will go through Jupyter-Bridge.
#' @param cmd.string (char) command
#' @param base.url (optional) Ignore unless you need to specify a custom domain,
#' port or version to connect to the CyREST API. Default is http://localhost:1234
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return A \code{list}, \code{named list}, \code{status} or None.
#' @examples
#' \donttest{
#' commandsPOST('layout get preferred')
#' commandsPOST('network list properties')
#' commandsPOST('layout force-directed defaultNodeMass=1')
#' }
#' @importFrom RJSONIO fromJSON
#' @importFrom httr POST content_type_json
#' @export
commandsPOST<-function(cmd.string, base.url = .defaultBaseUrl){
    requester <- NULL
    doInitializeSandbox(requester, base.url=base.url)
    if(!findRemoteCytoscape(base.url)){
        post.url = .command2postQueryUrl(cmd.string,base.url)
        post.body = .command2postQueryBody(cmd.string)
        tryCatch(
            res <- POST(url=post.url, body=post.body, encode="json", content_type_json()), 
            error=function(c) .cyError(c, res),
            warnings=function(c) .cyWarnings(c, res),
            finally=.cyFinally(res)
        )
        if(length(res$content)>0){
            res.data = RJSONIO::fromJSON(rawToChar(res$content))$data
            if(length(res.data)>0){
                return(res.data)
            } else{
                invisible(res.data)
            }
        }else {
            invisible(res)
        }
    } else {
        post.url = .command2postQueryUrl(cmd.string, base.url)
        post.body = .command2postQueryBody(cmd.string)
        post.body = RJSONIO::fromJSON(post.body)
        res <- doRequestRemote("POST", post.url, post.body, headers=list("Content-Type" = "application/json", "Accept" = "application/json"))
        if(length(res$content)>0){
            res.data = RJSONIO::fromJSON(RJSONIO::fromJSON(rawToChar(res$content))$text)$data
            if(length(res.data)>0){
                return(res.data)
            } else{
                invisible(res.data)
            }
        }else {
            invisible(res)
        }
    }
}

# ------------------------------------------------------------------------------
#' @title Run a Command
#'
#' @description Using the same syntax as Cytoscape's Command Line Dialog,
#' this function converts a command string into a CyREST query URL, executes a GET
#' request, and parses the result content into an R list object. Same as commandsGET.
#' Extremely long commands may result in an immediate "URI Too Long" failure because
#' `commandsRun()` uses HTTP GET to call the CyREST API, and HTTP GET has known limitations.
#' You can use `commandsPost()` instead, though the return value will be different.
#' `commandsPost()` uses HTTP POST, which has no limitations.
#' @param cmd.string (char) command
#' @param base.url (optional) Ignore unless you need to specify a custom domain,
#' port or version to connect to the CyREST API. Default is http://localhost:1234
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return A \code{list}, \code{status} or None.
#' @examples
#' \donttest{
#' commandsRun('layout get preferred')
#' commandsRun('network list properties')
#' commandsRun('layout force-directed defaultNodeMass=1')
#' }
#' @export
commandsRun<-function(cmd.string, base.url = .defaultBaseUrl){
    commandsGET(cmd.string,base.url)
}

# ------------------------------------------------------------------------------
#' @title Command Echo
#'
#' @description The echo command will display the value of the variable specified 
#' by the variableName argument, or all variables if variableName is not provided.
#' @param variable.name (optional) The name of the variable to display. Default is 
#' to display all variable values using "*".
#' @param base.url (optional) Ignore unless you need to specify a custom domain, 
#' port or version to connect to the CyREST API. Default is http://localhost:1234 
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return Value of variable
#' @examples \donttest{
#' commandEcho()
#' }
#' @export
commandEcho <- function(variable.name="*", base.url = .defaultBaseUrl){
    commandsPOST(paste0('command echo variableName="',variable.name,'"'),
                 base.url = base.url)
}

# ------------------------------------------------------------------------------
#' @title Command Open Dialog
#'
#' @description The command line dialog provides a field to enter commands and 
#' view results. It also provides the help command to display namespaces, 
#' commands, and arguments
#' @param base.url (optional) Ignore unless you need to specify a custom domain, 
#' port or version to connect to the CyREST API. Default is http://localhost:1234 
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return None
#' @examples \donttest{
#' commandOpenDialog()
#' }
#' @export
commandOpenDialog <- function(base.url = .defaultBaseUrl){
    commandsPOST('command open dialog',
                 base.url = base.url)
}

# ------------------------------------------------------------------------------
#' @title Command Pause
#'
#' @description The pause command displays a dialog with the text provided in the 
#' message argument and waits for the user to click OK.
#' @param message (optional) Text to display in pause dialog
#' @param base.url (optional) Ignore unless you need to specify a custom domain, 
#' port or version to connect to the CyREST API. Default is http://localhost:1234 
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return None
#' @examples \donttest{
#' commandPause('Please click OK to continue.')
#' }
#' @export
commandPause <- function(message="", base.url = .defaultBaseUrl){
    commandsPOST(paste0('command pause message="',message,'"'),
                 base.url = base.url)
}

# ------------------------------------------------------------------------------
#' @title Command Quit
#'
#' @description This command causes Cytoscape to exit. It is typically used at 
#' the end of a script file
#' @param base.url (optional) Ignore unless you need to specify a custom domain, 
#' port or version to connect to the CyREST API. Default is http://localhost:1234 
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return None
#' @examples \donttest{
#' commandQuit()
#' }
#' @export
commandQuit <- function(base.url = .defaultBaseUrl){
    commandsPOST('command quit',
                 base.url = base.url)
}

# ------------------------------------------------------------------------------
#' @title Command Run File
#'
#' @description The run command will execute a command script from the file pointed 
#' to by the file argument, which should contain Cytoscape commands, one per line. 
#' Arguments to the script are provided by the args argument
#' @param file Path to command script file
#' @param args The script arguments as key:value pairs separated by commas
#' @param base.url (optional) Ignore unless you need to specify a custom domain, 
#' port or version to connect to the CyREST API. Default is http://localhost:1234 
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return None
#' @examples \donttest{
#' commandRunFile('/path/to/my/file.txt')
#' }
#' @export
commandRunFile <- function(file, args=NULL, base.url = .defaultBaseUrl){
    args.str <- ""
    if(!is.null(args))
        args.str <- paste0(' args="',args,'"')
    
    commandsPOST(paste0('command run',args.str,' file="',getAbsSandboxPath(file),'"'),
                 base.url = base.url)
}

# ------------------------------------------------------------------------------
#' @title Command Sleep
#'
#' @description The sleep command will pause processing for a period of time as 
#' specified by duration seconds. It is typically used as part of a command script.
#' @param duration (optional) The time in seconds to sleep
#' @param base.url (optional) Ignore unless you need to specify a custom domain, #' port or version to connect to the CyREST API. Default is http://localhost:1234 #' and the latest version of the CyREST API supported by this version of RCy3.
#' @return None
#' @examples \donttest{
#' commandSleep(5)
#' }
#' @export
commandSleep <- function(duration=NULL, base.url = .defaultBaseUrl){
    dur.str <- ""
    if(!is.null(duration))
        dur.str <- paste0(' duration="',duration,'"')
    
    commandsPOST(paste0('command sleep',dur.str),
                 base.url = base.url)
}

# ==============================================================================
# III. Internal functions
# 
# Dev Notes: Prefix internal functions with a '.'. Do not @export and in general
# skip royxgen docs for these functions, with the exception of @importFrom lines.
# ------------------------------------------------------------------------------
# Command string to CyREST GET query URL
#
# @description Converts a command string to a CyREST GET query URL.
# @param cmd.string (char) command
# @param base.url cyrest base url for communicating with cytoscape
# @return CyREST GET URL
# @examples
# \donttest{
# .command2getQuery('layout get preferred')
# }
#' @importFrom utils URLencode
.command2getQuery<-function(cmd.string, base.url = .defaultBaseUrl){
    cmd.string = sub(" ([A-Za-z0-9_-]*=)","XXXXXX\\1",cmd.string)
    cmdargs = unlist(strsplit(cmd.string,"XXXXXX"))
    cmd = cmdargs[1]
    if(is.na(cmd)){cmd=""}
    q.cmd = URLencode(paste(base.url, "commands", sub(" ","/",cmd), sep="/"))
    args = cmdargs[2]
    if (is.na(args)){
        q.cmd
    }else{
        args = gsub("\"","",args)
        p = "[A-Za-z0-9_-]+="
        m = gregexpr(p,args)
        args1 = unlist(regmatches(args,m))
        args1 = gsub('=','',args1)
        #args1 = unlist(str_extract_all(args,"[A-Za-z0-9_-]+(?==)")) # requires stringr lib
        args2 = unlist(strsplit(args," *[A-Za-z0-9_-]+="))
        args2 = args2[-1]
        names(args2) <- args1
        q.args = .prepGetQueryArgs(args2)
        paste(q.cmd,q.args,sep="?")
    }
}

# ------------------------------------------------------------------------------
# Command string to CyREST POST query URL
#
# @description Converts a command string to a CyREST POST query URL.
# @param cmd.string (char) command
# @param base.url cyrest base url for communicating with cytoscape
# @return CyREST POST URL
# @examples
# \donttest{
# .command2postQueryUrl('network clone network="current"')
# # http://localhost:1234/v1/commands/network/clone
# }
#' @importFrom utils URLencode
.command2postQueryUrl<-function(cmd.string, base.url = .defaultBaseUrl){
    cmd.string = sub(" ([A-Za-z0-9_-]*=)","XXXXXX\\1",cmd.string)
    cmdargs = unlist(strsplit(cmd.string,"XXXXXX"))
    cmd = cmdargs[1]
    if(is.na(cmd)){cmd=""}
    URLencode(paste(base.url, "commands", sub(" ","/",cmd), sep="/"))
}

# ------------------------------------------------------------------------------
# Command string to CyREST POST query JSON body.
#
# @description Converts a command string to a CyREST POST query JSON body.
# @details POST requests require at leaset one arg, so a "filler" arg is provided if NULL
# @param cmd.string (char) command
# @param base.url cyrest base url for communicating with cytoscape
# @return CyREST POST JSON body
# @examples
# \donttest{
# .command2postQueryBody('network clone network="current"')
# # {
# # "network": "current"
# # }
# }
#' @importFrom RJSONIO toJSON
.command2postQueryBody<-function(cmd.string){
    cmd.string = sub(" ([A-Za-z0-9_-]*=)","XXXXXX\\1",cmd.string)
    cmdargs = unlist(strsplit(cmd.string,"XXXXXX"))
    args = cmdargs[2]
    if (is.na(args))
        args = 'atLeastOneArg=required' #supply a benign "filler" if NULL
    args = gsub("\"","",args)
    p = "[A-Za-z0-9_-]+="
    m = gregexpr(p,args)
    args1 = unlist(regmatches(args,m))
    args1 = gsub('=','',args1)
    #args1 = unlist(str_extract_all(args,"[A-Za-z0-9_-]+(?==)")) # requires stringr lib
    args2 = unlist(strsplit(args," *[A-Za-z0-9_-]+="))
    args2 = args2[-1]
    names(args2) <- args1
    return(toJSON(args2))
}
# Takes a named list and makes a string for GET query urls
#' @importFrom utils URLencode
.prepGetQueryArgs <- function(named.args){
    args1 <- names(named.args)
    args2 <- unlist(unname(named.args))
    q.args = paste(args1[1],URLencode(as.character(args2[1])),sep="=")
    for (i in seq(args1)[-1]){
        arg = paste(args1[i],URLencode(as.character(args2[i])),sep="=")
        q.args = paste(q.args,arg,sep="&")
    }
    return(q.args)
}

# Parses all the possible list types and keywords accepted by Commands API.
# If column designation is supported, simply provide a column name; otherwise
# it is assumed to not be supported and returns a simple list. 
.prepPostQueryLists <- function(cmd.list=NULL, cmd.by.col=NULL){
    if (is.null(cmd.list)) {
        cmd.list.ready = "selected" #need something here for edge selections to work
    } else if (!is.null(cmd.by.col)) {
        cmd.list.col = NULL
        for (i in seq_len(length(cmd.list))) {
            cmd.list.col[i] = paste(cmd.by.col, cmd.list[i], sep = ":")
        }
        cmd.list.ready = paste(cmd.list.col, collapse = ",")
    } else {
        cmd.list.ready = paste(cmd.list, collapse=",")
    }
    return(cmd.list.ready)
}

# ------------------------------------------------------------------------------
# CyRest Message Handler
#
# @description Provides helpful messaages for CyREST issues.
# @c Error condition
# @res Result with error
# @return A stop message
# @examples
# \donttest{
# tryCatch(
#     res <- cyRestGET("version"),
#     error=function(c) {.cyError(c, res)}
# )
#
.cyError<-function(c, res){
    err_conn = 'Connection refused' # Connection Error
    if (length(grep(err_conn,c$message)) == 0){ # Certain 404 Errors
        stop(simpleError("Not Found"))
    } else {
        message("Oh no! I can't find Cytoscape. RCy3 can not continue!
Please check that Cytoscape is running, CyREST is installed and your base.url parameter is correct.")
        write(sprintf("Failed to execute: %s",res[[1]]), stderr())
        stop(simpleError(conditionMessage(c)))
    } 
}

.cyWarnings<-function(c, res){
    #Pass along any warnings and carry on
    message(c$message)
}

#' @importFrom RJSONIO fromJSON
#' @importFrom XML htmlParse xmlValue xpathSApply
.cyFinally<-function(res){
    if(!is.null(res)){
        # Check HTTP Errors
        if(res$status_code == 414) { 
            print("URI Too Long: The command you attempted to execute is too large to be handled via GET request. Consider switching to commandsPost() for larger queries.")
        }
        if(res$status_code > 299){
            write(sprintf("Failed to execute: %s",res[[1]]), stderr())
            if(res[[3]]$`content-type` == "text/plain" ||
               res[[3]]$`content-type` == "text/html;charset=iso-8859-1"){
                errmsg <- paste(xpathSApply(htmlParse(rawToChar(res$content), asText=TRUE), "//p", xmlValue),
                                xpathSApply(htmlParse(rawToChar(res$content), asText=TRUE), "//pre", xmlValue))
                stop(simpleError(errmsg))
            } else if (res[[3]]$`content-type` == "application/json"){
                stop(simpleError(RJSONIO::fromJSON(rawToChar(res$content))$errors[[1]]$message))
            } else {
                stop()
            }
        }
    }
}

# ------------------------------------------------------------------------------
# Encode Operation
#
# Applies URLencode to each step along the URL path without clobbering the path.
.pathURLencode <- function(operation){
    steps <- strsplit(operation,"\\/")[[1]]
    paste(lapply(steps, URLencode, reserved = TRUE), collapse = "/")
}

# ==============================================================================
# IV. Jupyter-bridge 
# ------------------------------------------------------------------------------
#' @title findRemoteCytoscape
#' @description Figure out whether CyREST is local or remote. If remote, we'll 
#' want to go through Jupyter-Bridge.
#' @param base.url (optional) Ignore unless you need to specify a custom domain,
#' port or version to connect to the CyREST API. Default is http://localhost:1234
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return None
#' @examples
#' \donttest{
#' findRemoteCytoscape()
#' }
#' @export
findRemoteCytoscape<-function(base.url=.defaultBaseUrl){
    checkNotebookIsRunning()
    checkRunningRemote(base.url)
    #doInitializeSandbox(requester, base.url=.defaultBaseUrl) 
    # make sure there's a sandbox before executing a command
    if(is.null(checkRunningRemote(base.url))){
        stop('Cannot find local or remote Cytoscape. Start Cytoscape and then proceed.')
    }
    return(runningRemoteCheck(base.url))
}

# ==============================================================================
# V. Sandbox
# ------------------------------------------------------------------------------
#' @title doInitializeSandbox
#' @description doInitializeSandbox
#' @param requester requester 
#' @param base.url Ignore unless you need to specify a custom domain, port or 
#' version to connect to the CyREST API. Default is http://127.0.0.1:1234 and 
#' the latest version of the CyREST API supported by this version of RCy3.
#' @return None
#' @examples
#' \donttest{
#' doInitializeSandbox()
#' }
#' @export
doInitializeSandbox <- function(requester=NULL, base.url = .defaultBaseUrl){
    if(getSandboxReinitialize()){
        return(doSetSandbox(.getDefaultSandbox(base.url), requester, base.url = base.url))
    } else {
        return(getCurrentSandbox())
    }
}

# ------------------------------------------------------------------------------
#' @title doSetSandbox
#' @description doSetSandbox
#' @param sandboxToSet sandbox to set
#' @param requester requester
#' @param base.url Ignore unless you need to specify a custom domain, port or 
#' version to connect to the CyREST API. Default is http://127.0.0.1:1234 and 
#' the latest version of the CyREST API supported by this version of RCy3.
#' @return None
#' @examples
#' \donttest{
#' doSetSandbox()
#' }
#' @importFrom glue glue
#' @export
doSetSandbox <- function(sandboxToSet, requester=NULL, base.url = .defaultBaseUrl){
    requester <- .getRequester(base.url)
    if(is.null(sandboxToSet[['sandboxName']])){
        sandboxToSet[['sandboxName']] <- getDefaultSandbox()[['sandboxName']]
    }
    sandboxName <- sandboxToSet[['sandboxName']]
    if(!is.null(sandboxName)){
        tryCatch(
            expr = {
                if(!requester){
                    r <- POST(url=glue('{base.url}/commands/filetransfer/setSandbox'), body=sandboxToSet, encode="json", content_type_json())
                    newSandbox <- setCurrentSandbox(sandboxName, RJSONIO::fromJSON(rawToChar(r$content))$data[['sandboxPath']])
                } else {
                    r <- doRequestRemote("POST", glue('{base.url}/commands/filetransfer/setSandbox'), qbody=sandboxToSet, headers=list("Content-Type" = "application/json"))
                    newSandbox <- setCurrentSandbox(sandboxName, RJSONIO::fromJSON(RJSONIO::fromJSON(rawToChar(r$content))$text)$data[['sandboxPath']])
                }
            },
            error = function(e){
                caller <- deparse(sys.call())
                message <- RJSONIO::fromJSON(rawToChar(r$content))[['errors']]
                stop(message, caller)
            }
        )
    } else {
        defaultSandboxPath <- getDefaultSandboxPath()
        if(is.null(defaultSandboxPath)){
            if(getNotebookIsRunning()){
                tryCatch(
                    expr = {
                        if(!requester){
                        r <- POST(url=glue('{base.url}/commands/filetransfer/getFileInfo'), body=list('sandboxName' = NULL,'fileName' = '.'), encode="json", content_type_json())
                        defaultSandboxPath <- setDefaultSandboxPath(RJSONIO::fromJSON(rawToChar(r$content))$data[['filePath']])
                        } else {
                            r <- doRequestRemote("POST", glue('{base.url}/commands/filetransfer/getFileInfo'), qbody=list('sandboxName' = NULL,'fileName' = '.'), headers=list("Content-Type" = "application/json"))
                            defaultSandboxPath <- setDefaultSandboxPath(RJSONIO::fromJSON(RJSONIO::fromJSON(rawToChar(r$content))$text)$data[['filePath']])
                        }
                    },
                    error = function(e){
                        defaultSandboxPath <- NULL
                        print('Warning: FileTransfer app is not available, so sandbox operations will fail')
                    }
                )
            } else {
                defaultSandboxPath <- getwd()
            }
        }
        newSandbox <- setCurrentSandbox(NULL, defaultSandboxPath)
    }
    setSandboxReinitialize(FALSE)
    return(newSandbox)
}

# ------------------------------------------------------------------------------
#' @title .getDefaultSandbox
#' @description .getDefaultSandbox
#' @param base.url (optional) Ignore unless you need to specify a custom domain,
#' port or version to connect to the CyREST API. Default is http://localhost:1234
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return None
#' @examples
#' \donttest{
#' .getDefaultSandbox()
#' }
.getDefaultSandbox <- function(base.url=.defaultBaseUrl){
    default <- getDefaultSandbox()
    if(length(default) == 0){
        if(getNotebookIsRunning() || runningRemoteCheck(base.url)){
            default <- sandboxInitializer(list(sandboxName=RCy3env$.predefinedSandboxName))
        } else {
            default <- sandboxInitializer(list(sandboxName=NULL))
        }
        setDefaultSandbox(default)
    }
    return(default)
}

# ------------------------------------------------------------------------------
#' @title .getRequester
#' @description .getRequester
#' @param base.url (optional) Ignore unless you need to specify a custom domain,
#' port or version to connect to the CyREST API. Default is http://localhost:1234
#' and the latest version of the CyREST API supported by this version of RCy3.
#' @return None
#' @examples
#' \donttest{
#' .getRequester()
#' }
.getRequester <- function(base.url=.defaultBaseUrl){
    if(findRemoteCytoscape(base.url)){
        return(TRUE)
    } else {
        return(FALSE)
    }
}
cytoscape/RCy3 documentation built on Nov. 1, 2024, 2:49 a.m.