X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/7f2de2ec1d52c8e5984b610c5930f2d9629bd357..3aaefcb3c76ff470b475d950398d01255e87712a:/sdk/R/R/ArvadosFile.R diff --git a/sdk/R/R/ArvadosFile.R b/sdk/R/R/ArvadosFile.R index 2da3db3b39..fb1d3b335c 100644 --- a/sdk/R/R/ArvadosFile.R +++ b/sdk/R/R/ArvadosFile.R @@ -1,8 +1,58 @@ -#' ArvadosFile Object +# Copyright (C) The Arvados Authors. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +#' ArvadosFile +#' +#' ArvadosFile class represents a file inside Arvados collection. +#' +#' @section Usage: +#' \preformatted{file = ArvadosFile$new(name)} +#' +#' @section Arguments: +#' \describe{ +#' \item{name}{Name of the file.} +#' } +#' +#' @section Methods: +#' \describe{ +#' \item{getName()}{Returns name of the file.} +#' \item{getRelativePath()}{Returns file path relative to the root.} +#' \item{read(contentType = "raw", offset = 0, length = 0)}{Read file content.} +#' \item{write(content, contentType = "text/html")}{Write to file (override current content of the file).} +#' \item{connection(rw)}{Get connection opened in "read" or "write" mode.} +#' \item{flush()}{Write connections content to a file (override current content of the file).} +#' \item{remove(name)}{Removes ArvadosFile or Subcollection specified by name from the subcollection.} +#' \item{getSizeInBytes()}{Returns file size in bytes.} +#' \item{move(destination)}{Moves file to a new location inside collection.} +#' \item{copy(destination)}{Copies file to a new location inside collection.} +#' } #' -#' Update description +#' @name ArvadosFile +#' @examples +#' \dontrun{ +#' myFile <- ArvadosFile$new("myFile") #' -#' @export ArvadosFile +#' myFile$write("This is new file content") +#' fileContent <- myFile$read() +#' fileContent <- myFile$read("text") +#' fileContent <- myFile$read("raw", offset = 8, length = 4) +#' +#' #Write a table: +#' arvConnection <- myFile$connection("w") +#' write.table(mytable, arvConnection) +#' arvadosFile$flush() +#' +#' #Read a table: +#' arvConnection <- myFile$connection("r") +#' mytable <- read.table(arvConnection) +#' +#' myFile$move("newFolder/myFile") +#' myFile$copy("newFolder/myFile") +#' } +NULL + +#' @export ArvadosFile <- R6::R6Class( "ArvadosFile", @@ -11,9 +61,10 @@ ArvadosFile <- R6::R6Class( initialize = function(name) { - private$name <- name - private$http <- HttpRequest$new() - private$httpParser <- HttpParser$new() + if(name == "") + stop("Invalid name.") + + private$name <- name }, getName = function() private$name, @@ -25,16 +76,14 @@ ArvadosFile <- R6::R6Class( getSizeInBytes = function() { - collectionURL <- URLencode(paste0(private$collection$api$getWebDavHostName(), - "c=", private$collection$uuid)) - fileURL <- paste0(collectionURL, "/", self$getRelativePath()); - - headers = list("Authorization" = paste("OAuth2", private$collection$api$getToken())) + if(is.null(private$collection)) + return(0) - propfindResponse <- private$http$PROPFIND(fileURL, headers) + REST <- private$collection$getRESTService() - sizes <- private$httpParser$extractFileSizeFromWebDAVResponse(propfindResponse, collectionURL) - as.numeric(sizes) + fileSize <- REST$getResourceSize(self$getRelativePath(), + private$collection$uuid) + fileSize }, get = function(fileLikeObjectName) @@ -49,7 +98,7 @@ ArvadosFile <- R6::R6Class( getCollection = function() private$collection, - setCollection = function(collection) + setCollection = function(collection, setRecursively = TRUE) { private$collection <- collection }, @@ -81,124 +130,119 @@ ArvadosFile <- R6::R6Class( if(offset < 0 || length < 0) stop("Offset and length must be positive values.") - if(!(contentType %in% private$http$validContentTypes)) - stop("Invalid contentType. Please use text or raw.") - - range = paste0("bytes=", offset, "-") + REST <- private$collection$getRESTService() - if(length > 0) - range = paste0(range, offset + length - 1) - - fileURL = paste0(private$collection$api$getWebDavHostName(), - "c=", private$collection$uuid, "/", self$getRelativePath()); + fileContent <- REST$read(self$getRelativePath(), + private$collection$uuid, + contentType, offset, length) + fileContent + }, - if(offset == 0 && length == 0) + connection = function(rw) + { + if (rw == "r" || rw == "rb") { - headers <- list(Authorization = paste("OAuth2", - private$collection$api$getToken())) + REST <- private$collection$getRESTService() + return(REST$getConnection(self$getRelativePath(), + private$collection$uuid, + rw)) } - else + else if (rw == "w") { - headers <- list(Authorization = paste("OAuth2", private$collection$api$getToken()), - Range = range) + private$buffer <- textConnection(NULL, "w") + + return(private$buffer) } + }, - serverResponse <- private$http$GET(fileURL, headers) + flush = function() + { + v <- textConnectionValue(private$buffer) + close(private$buffer) + self$write(paste(v, collapse='\n')) + }, - if(serverResponse$status_code < 200 || serverResponse$status_code >= 300) - stop(paste("Server code:", serverResponse$status_code)) + write = function(content, contentType = "text/html") + { + if(is.null(private$collection)) + stop("ArvadosFile doesn't belong to any collection.") - parsedServerResponse <- httr::content(serverResponse, contentType) - parsedServerResponse - }, + REST <- private$collection$getRESTService() - connection = function(rw) - { - if (rw == "r") { - return(textConnection(self$read("text"))) - } else if (rw == "w") { - private$buffer <- textConnection(NULL, "w") - return(private$buffer) - } - }, - - flush = function() { - v <- textConnectionValue(private$buffer) - close(private$buffer) - self$write(paste(v, collapse='\n')) - }, + writeResult <- REST$write(self$getRelativePath(), + private$collection$uuid, + content, contentType) + writeResult + }, - write = function(content, contentType = "text/html") + move = function(destination) { if(is.null(private$collection)) stop("ArvadosFile doesn't belong to any collection.") - fileURL = paste0(private$collection$api$getWebDavHostName(), - "c=", private$collection$uuid, "/", self$getRelativePath()); - headers <- list(Authorization = paste("OAuth2", private$collection$api$getToken()), - "Content-Type" = contentType) - body <- content + destination <- trimFromEnd(destination, "/") + nameAndPath <- splitToPathAndName(destination) + + newParent <- private$collection$get(nameAndPath$path) + + if(is.null(newParent)) + stop("Unable to get destination subcollection.") + + childWithSameName <- newParent$get(nameAndPath$name) + + if(!is.null(childWithSameName)) + stop("Destination already contains content with same name.") + + REST <- private$collection$getRESTService() + REST$move(self$getRelativePath(), + paste0(newParent$getRelativePath(), "/", nameAndPath$name), + private$collection$uuid) - serverResponse <- private$http$PUT(fileURL, headers, body) + private$dettachFromCurrentParent() + private$attachToNewParent(self, newParent) - if(serverResponse$status_code < 200 || serverResponse$status_code >= 300) - stop(paste("Server code:", serverResponse$status_code)) + private$parent <- newParent + private$name <- nameAndPath$name - parsedServerResponse <- httr::content(serverResponse, "text") - parsedServerResponse + self }, - move = function(newLocation) + copy = function(destination) { - #todo test if file can be moved - if(is.null(private$collection)) stop("ArvadosFile doesn't belong to any collection.") - if(endsWith(newLocation, paste0(private$name, "/"))) - { - newLocation <- substr(newLocation, 0, - nchar(newLocation) - - nchar(paste0(private$name, "/"))) - } - else if(endsWith(newLocation, private$name)) - { - newLocation <- substr(newLocation, 0, - nchar(newLocation) - nchar(private$name)) - } - else - { - stop("Destination path is not valid.") - } + destination <- trimFromEnd(destination, "/") + nameAndPath <- splitToPathAndName(destination) - newParent <- private$collection$get(newLocation) + newParent <- private$collection$get(nameAndPath$path) if(is.null(newParent)) - { stop("Unable to get destination subcollection.") - } - childWithSameName <- newParent$get(private$name) + childWithSameName <- newParent$get(nameAndPath$name) if(!is.null(childWithSameName)) - stop("Destination already contains file with same name.") - - status <- private$collection$moveOnREST(self$getRelativePath(), - paste0(newParent$getRelativePath(), - "/", self$getName())) + stop("Destination already contains content with same name.") - #Note: We temporary set parents collection to NULL. This will ensure that - # add method doesn't post file on REST server. - parentsCollection <- newParent$getCollection() - newParent$setCollection(NULL, setRecursively = FALSE) + REST <- private$collection$getRESTService() + REST$copy(self$getRelativePath(), + paste0(newParent$getRelativePath(), "/", nameAndPath$name), + private$collection$uuid) - newParent$add(self) - - newParent$setCollection(parentsCollection, setRecursively = FALSE) + newFile <- self$duplicate(nameAndPath$name) + newFile$setCollection(self$getCollection()) + private$attachToNewParent(newFile, newParent) + newFile$setParent(newParent) - private$parent <- newParent + newFile + }, - "Content moved successfully." + duplicate = function(newName = NULL) + { + name <- if(!is.null(newName)) newName else private$name + newFile <- ArvadosFile$new(name) + newFile } ), @@ -208,10 +252,58 @@ ArvadosFile <- R6::R6Class( size = NULL, parent = NULL, collection = NULL, - http = NULL, - httpParser = NULL, - buffer = NULL + buffer = NULL, + + attachToNewParent = function(content, newParent) + { + # We temporary set parents collection to NULL. This will ensure that + # add method doesn't post this file on REST. + # We also need to set content's collection to NULL because + # add method throws exception if we try to add content that already + # belongs to a collection. + parentsCollection <- newParent$getCollection() + content$setCollection(NULL, setRecursively = FALSE) + newParent$setCollection(NULL, setRecursively = FALSE) + newParent$add(content) + content$setCollection(parentsCollection, setRecursively = FALSE) + newParent$setCollection(parentsCollection, setRecursively = FALSE) + }, + + dettachFromCurrentParent = function() + { + # We temporary set parents collection to NULL. This will ensure that + # remove method doesn't remove this file from REST. + parent <- private$parent + parentsCollection <- parent$getCollection() + parent$setCollection(NULL, setRecursively = FALSE) + parent$remove(private$name) + parent$setCollection(parentsCollection, setRecursively = FALSE) + } ), cloneable = FALSE ) + +#' print.ArvadosFile +#' +#' Custom print function for ArvadosFile class +#' +#' @param x Instance of ArvadosFile class +#' @param ... Optional arguments. +#' @export +print.ArvadosFile = function(x, ...) +{ + collection <- NULL + relativePath <- x$getRelativePath() + + if(!is.null(x$getCollection())) + { + collection <- x$getCollection()$uuid + relativePath <- paste0("/", relativePath) + } + + cat(paste0("Type: ", "\"", "ArvadosFile", "\""), sep = "\n") + cat(paste0("Name: ", "\"", x$getName(), "\""), sep = "\n") + cat(paste0("Relative path: ", "\"", relativePath, "\""), sep = "\n") + cat(paste0("Collection: ", "\"", collection, "\""), sep = "\n") +}