Merge branch 'master' into 13822-nm-delayed-daemon
[arvados.git] / sdk / R / R / Collection.R
index 94141d97f4437d4e5f095a193853fe10d770a283..e23da138329786cba49e3a8001479461dd30be77 100644 (file)
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+
 source("./R/Subcollection.R")
 source("./R/ArvadosFile.R")
-source("./R/HttpRequest.R")
-source("./R/HttpParser.R")
-
-#' Arvados Collection Object
+source("./R/RESTService.R")
+source("./R/util.R")
+
+#' Collection
+#' 
+#' Collection class provides interface for working with Arvados collections.
+#' 
+#' @section Usage:
+#' \preformatted{collection = Collection$new(arv, uuid)}
+#'
+#' @section Arguments:
+#' \describe{
+#'   \item{arv}{Arvados object.}
+#'   \item{uuid}{UUID of a collection.}
+#' }
+#' 
+#' @section Methods:
+#' \describe{
+#'   \item{add(content)}{Adds ArvadosFile or Subcollection specified by content to the collection.}
+#'   \item{create(fileNames, relativePath = "")}{Creates one or more ArvadosFiles and adds them to the collection at specified path.}
+#'   \item{remove(fileNames)}{Remove one or more files from the collection.}
+#'   \item{move(content, newLocation)}{Moves ArvadosFile or Subcollection to another location in the collection.}
+#'   \item{getFileListing()}{Returns collections file content as character vector.}
+#'   \item{get(relativePath)}{If relativePath is valid, returns ArvadosFile or Subcollection specified by relativePath, else returns NULL.}
+#' }
+#'
+#' @name Collection
+#' @examples
+#' \dontrun{
+#' arv <- Arvados$new("your Arvados token", "example.arvadosapi.com")
+#' collection <- Collection$new(arv, "uuid")
+#'
+#' newFile <- ArvadosFile$new("myFile")
+#' collection$add(newFile, "myFolder")
 #'
-#' Update description
+#' createdFiles <- collection$create(c("main.cpp", lib.dll), "cpp/src/")
 #'
-#' @examples arv = Collection$new(api, uuid)
-#' @export CTest
-CTest <- R6::R6Class(
-    "CTest",
+#' collection$remove("location/to/my/file.cpp")
+#'
+#' collection$move("folder/file.cpp", "file.cpp")
+#'
+#' arvadosFile <- collection$get("location/to/my/file.cpp")
+#' arvadosSubcollection <- collection$get("location/to/my/directory/")
+#' }
+NULL
+
+#' @export
+Collection <- R6::R6Class(
+
+    "Collection",
+
     public = list(
 
-        api  = NULL,
-        uuid = NULL,
+               uuid = NULL,
+        # api  = NULL,
 
-        initialize = function(api, uuid)
+               initialize = function(api, uuid) 
         {
-            self$api <- api
-            private$http <- HttpRequest$new()
-            private$httpParser <- HttpParser$new()
+            # self$api <- api
+            private$REST <- api$getRESTService()
 
             self$uuid <- uuid
-            collection <- self$api$getCollection(uuid)
 
-            private$fileContent <- private$getCollectionContent()
+            private$fileContent <- private$REST$getCollectionContent(uuid)
             private$tree <- CollectionTree$new(private$fileContent, self)
         },
 
         add = function(content, relativePath = "")
         {
-            if(relativePath == "" ||
+            if(is.null(private$tree))
+                private$genereateCollectionTreeStructure()
+
+            if(relativePath == ""  ||
                relativePath == "." ||
                relativePath == "./")
             {
-                subcollection <- private$tree$.__enclos_env__$private$tree
+                subcollection <- private$tree$getTree()
             }
             else
             {
-                if(endsWith(relativePath, "/") && nchar(relativePath) > 0)
-                    relativePath <- substr(relativePath, 1, nchar(relativePath) - 1)
+                relativePath <- trimFromEnd(relativePath, "/")
+                subcollection <- self$get(relativePath)
+            }
+
+            if(is.null(subcollection))
+                stop(paste("Subcollection", relativePath, "doesn't exist."))
+
+            if("ArvadosFile"   %in% class(content) ||
+               "Subcollection" %in% class(content))
+            {
+                if(content$getName() == "")
+                    stop("Content has invalid name.")
 
+                subcollection$add(content)
+                content
+            }
+            else
+            {
+                stop(paste0("Expected AravodsFile or Subcollection object, got ",
+                            paste0("(", paste0(class(content), collapse = ", "), ")"),
+                            "."))
+            }
+        },
+
+        create = function(fileNames, relativePath = "")
+        {
+            if(is.null(private$tree))
+                private$genereateCollectionTreeStructure()
+
+            if(relativePath == ""  ||
+               relativePath == "." ||
+               relativePath == "./")
+            {
+                subcollection <- private$tree$getTree()
+            }
+            else
+            {
+                relativePath  <- trimFromEnd(relativePath, "/") 
                 subcollection <- self$get(relativePath)
             }
 
             if(is.null(subcollection))
                 stop(paste("Subcollection", relativePath, "doesn't exist."))
 
-            if(is.character(content))
+            if(is.character(fileNames))
             {
-                sapply(content, function(fileName)
+                arvadosFiles <- NULL
+                sapply(fileNames, function(fileName)
                 {
-                    subcollection$add(ArvadosFile$new(fileName))
+                    childWithSameName <- subcollection$get(fileName)
+                    if(!is.null(childWithSameName))
+                        stop("Destination already contains file with same name.")
+
+                    newFile <- ArvadosFile$new(fileName)
+                    subcollection$add(newFile)
+
+                    arvadosFiles <<- c(arvadosFiles, newFile)
                 })
+
+                if(length(arvadosFiles) == 1)
+                    return(arvadosFiles[[1]])
+                else
+                    return(arvadosFiles)
             }
-            else if("ArvadosFile"   %in% class(content) ||
-                    "Subcollection" %in% class(content))
+            else 
             {
-                subcollection$add(content)
+                stop(paste0("Expected character vector, got ",
+                            paste0("(", paste0(class(fileNames), collapse = ", "), ")"),
+                            "."))
             }
         },
 
-        remove = function(content)
+        remove = function(paths)
         {
-            if(is.character(content))
+            if(is.null(private$tree))
+                private$genereateCollectionTreeStructure()
+
+            if(is.character(paths))
             {
-                sapply(content, function(filePath)
+                sapply(paths, function(filePath)
                 {
-                    if(endsWith(filePath, "/") && nchar(filePath) > 0)
-                        filePath <- substr(filePath, 1, nchar(filePath) - 1)
-
+                    filePath <- trimFromEnd(filePath, "/")
                     file <- self$get(filePath)
 
                     if(is.null(file))
                         stop(paste("File", filePath, "doesn't exist."))
 
-                    file$removeFromCollection()
+                    parent <- file$getParent()
+
+                    if(is.null(parent))
+                        stop("You can't delete root folder.")
+
+                    parent$remove(file$getName())
                 })
+
+                "Content removed"
             }
-            else if("ArvadosFile"   %in% class(content) ||
-                    "Subcollection" %in% class(content))
+            else 
             {
-                if(is.null(content$.__enclos_env__$private$collection) || 
-                   content$.__enclos_env__$private$collection$uuid != self$uuid)
-                    stop("Subcollection doesn't belong to this collection.")
-
-                content$removeFromCollection()
+                stop(paste0("Expected character vector, got ",
+                            paste0("(", paste0(class(paths), collapse = ", "), ")"),
+                            "."))
             }
         },
 
-        getTree = function() private$tree,
-
-        getFileContent = function() private$getCollectionContent(),
-
-        get = function(relativePath)
+        move = function(content, newLocation)
         {
-            private$tree$getElement(relativePath)
-        }
-    ),
+            if(is.null(private$tree))
+                private$genereateCollectionTreeStructure()
 
-    private = list(
-
-        http       = NULL,
-        httpParser = NULL,
-        tree       = NULL,
+            content <- trimFromEnd(content, "/")
 
-        fileContent = NULL,
+            elementToMove <- self$get(content)
 
-        getCollectionContent = function()
-        {
-            collectionURL <- URLencode(paste0(self$api$getWebDavHostName(), "c=", self$uuid))
+            if(is.null(elementToMove))
+                stop("Content you want to move doesn't exist in the collection.")
 
-            headers = list("Authorization" = paste("OAuth2", self$api$getToken()))
-
-            response <- private$http$PROPFIND(collectionURL, headers)
-
-            parsedResponse <- private$httpParser$parseWebDAVResponse(response, collectionURL)
-            parsedResponse[-1]
+            elementToMove$move(newLocation)
         },
 
-        createFilesOnREST = function(files)
+        getFileListing = function()
         {
-            sapply(files, function(filePath)
-            {
-                private$createNewFile(filePath, NULL, "text/html")
-            })
+            if(is.null(private$tree))
+                private$genereateCollectionTreeStructure()
+
+            content <- private$REST$getCollectionContent(self$uuid)
+            content[order(tolower(content))]
         },
-        
-        generateTree = function(content)
+
+        get = function(relativePath)
         {
-            treeBranches <- sapply(collectionContent, function(filePath)
-            {
-                splitPath <- unlist(strsplit(filePath$name, "/", fixed = TRUE))
+            if(is.null(private$tree))
+                private$genereateCollectionTreeStructure()
 
-                branch = private$createBranch(splitPath, filePath$fileSize)      
-            })
+            private$tree$getElement(relativePath)
         },
 
-        createNewFile = function(relativePath, content, contentType)
+               toJSON = function() 
         {
-            fileURL <- paste0(self$api$getWebDavHostName(), "c=", self$uuid, "/", relativePath);
-            headers <- list(Authorization = paste("OAuth2", self$api$getToken()), 
-                            "Content-Type" = contentType)
-            body <- content
+                       fields <- sapply(private$classFields, function(field)
+                       {
+                               self[[field]]
+                       }, USE.NAMES = TRUE)
+                       
+                       jsonlite::toJSON(list("collection" = 
+                     Filter(Negate(is.null), fields)), auto_unbox = TRUE)
+               },
+
+               isEmpty = function() {
+                       fields <- sapply(private$classFields,
+                                        function(field) self[[field]])
+
+                       if(any(sapply(fields, function(field) !is.null(field) && field != "")))
+                               FALSE
+                       else
+                               TRUE
+               },
+
+        getRESTService = function() private$REST,
+        setRESTService = function(newRESTService) private$REST <- newRESTService
+    ),
 
-            serverResponse <- private$http$PUT(fileURL, headers, body)
+    private = list(
 
-            if(serverResponse$status_code != 201)
-                stop(paste("Server code:", serverResponse$status_code))
+        REST        = NULL,
+        tree        = NULL,
+        fileContent = NULL,
+        classFields = NULL,
 
-            print(paste("File created:", relativePath))
-        },
-        
-        deleteFromREST = function(relativePath)
+        genereateCollectionTreeStructure = function()
         {
-            fileURL <- paste0(self$api$getWebDavHostName(), "c=", self$uuid, "/", relativePath);
-            headers <- list(Authorization = paste("OAuth2", self$api$getToken())) 
-
-            serverResponse <- private$http$DELETE(fileURL, headers)
+            if(is.null(self$uuid))
+                stop("Collection uuid is not defined.")
 
-            if(serverResponse$status_code != 204)
-                stop(paste("Server code:", serverResponse$status_code))
+            if(is.null(private$REST))
+                stop("REST service is not defined.")
 
-            print(paste("File deleted", relativePath))
+            private$fileContent <- private$REST$getCollectionContent(self$uuid)
+            private$tree <- CollectionTree$new(private$fileContent, self)
         }
     ),
 
     cloneable = FALSE
 )
+
+#' print.Collection
+#'
+#' Custom print function for Collection class
+#'
+#' @param x Instance of Collection class
+#' @param ... Optional arguments.
+#' @export 
+print.Collection = function(x, ...)
+{
+    cat(paste0("Type: ", "\"", "Arvados Collection", "\""), sep = "\n")
+    cat(paste0("uuid: ", "\"", x$uuid,               "\""), sep = "\n")
+}