16827: Don't append '/' to requests with query params. Bump version
[arvados.git] / sdk / R / R / ArvadosFile.R
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: Apache-2.0
4
5 #' ArvadosFile
6 #'
7 #' ArvadosFile class represents a file inside Arvados collection.
8 #'
9 #' @section Usage:
10 #' \preformatted{file = ArvadosFile$new(name)}
11 #'
12 #' @section Arguments:
13 #' \describe{
14 #'   \item{name}{Name of the file.}
15 #' }
16 #'
17 #' @section Methods:
18 #' \describe{
19 #'   \item{getName()}{Returns name of the file.}
20 #'   \item{getRelativePath()}{Returns file path relative to the root.}
21 #'   \item{read(contentType = "raw", offset = 0, length = 0)}{Read file content.}
22 #'   \item{write(content, contentType = "text/html")}{Write to file (override current content of the file).}
23 #'   \item{connection(rw)}{Get connection opened in "read" or "write" mode.}
24 #'   \item{flush()}{Write connections content to a file (override current content of the file).}
25 #'   \item{remove(name)}{Removes ArvadosFile or Subcollection specified by name from the subcollection.}
26 #'   \item{getSizeInBytes()}{Returns file size in bytes.}
27 #'   \item{move(destination)}{Moves file to a new location inside collection.}
28 #'   \item{copy(destination)}{Copies file to a new location inside collection.}
29 #' }
30 #'
31 #' @name ArvadosFile
32 #' @examples
33 #' \dontrun{
34 #' myFile <- ArvadosFile$new("myFile")
35 #'
36 #' myFile$write("This is new file content")
37 #' fileContent <- myFile$read()
38 #' fileContent <- myFile$read("text")
39 #' fileContent <- myFile$read("raw", offset = 8, length = 4)
40 #'
41 #' #Write a table:
42 #' arvConnection <- myFile$connection("w")
43 #' write.table(mytable, arvConnection)
44 #' arvadosFile$flush()
45 #'
46 #' #Read a table:
47 #' arvConnection <- myFile$connection("r")
48 #' mytable <- read.table(arvConnection)
49 #'
50 #' myFile$move("newFolder/myFile")
51 #' myFile$copy("newFolder/myFile")
52 #' }
53 NULL
54
55 #' @export
56 ArvadosFile <- R6::R6Class(
57
58     "ArvadosFile",
59
60     public = list(
61
62         initialize = function(name)
63         {
64             if(name == "")
65                 stop("Invalid name.")
66
67             private$name <- name
68         },
69
70         getName = function() private$name,
71
72         getFileListing = function(fullpath = TRUE)
73         {
74             self$getName()
75         },
76
77         getSizeInBytes = function()
78         {
79             if(is.null(private$collection))
80                 return(0)
81
82             REST <- private$collection$getRESTService()
83
84             fileSize <- REST$getResourceSize(self$getRelativePath(),
85                                              private$collection$uuid)
86             fileSize
87         },
88
89         get = function(fileLikeObjectName)
90         {
91             return(NULL)
92         },
93
94         getFirst = function()
95         {
96             return(NULL)
97         },
98
99         getCollection = function() private$collection,
100
101         setCollection = function(collection, setRecursively = TRUE)
102         {
103             private$collection <- collection
104         },
105
106         getRelativePath = function()
107         {
108             relativePath <- c(private$name)
109             parent <- private$parent
110
111             while(!is.null(parent))
112             {
113                 relativePath <- c(parent$getName(), relativePath)
114                 parent <- parent$getParent()
115             }
116
117             relativePath <- relativePath[relativePath != ""]
118             paste0(relativePath, collapse = "/")
119         },
120
121         getParent = function() private$parent,
122
123         setParent = function(newParent) private$parent <- newParent,
124
125         read = function(contentType = "raw", offset = 0, length = 0)
126         {
127             if(is.null(private$collection))
128                 stop("ArvadosFile doesn't belong to any collection.")
129
130             if(offset < 0 || length < 0)
131                 stop("Offset and length must be positive values.")
132
133             REST <- private$collection$getRESTService()
134
135             fileContent <- REST$read(self$getRelativePath(),
136                                      private$collection$uuid,
137                                      contentType, offset, length)
138             fileContent
139         },
140
141         connection = function(rw)
142         {
143             if (rw == "r" || rw == "rb")
144             {
145                 REST <- private$collection$getRESTService()
146                 return(REST$getConnection(self$getRelativePath(),
147                                           private$collection$uuid,
148                                           rw))
149             }
150             else if (rw == "w")
151             {
152                 private$buffer <- textConnection(NULL, "w")
153
154                 return(private$buffer)
155             }
156         },
157
158         flush = function()
159         {
160             v <- textConnectionValue(private$buffer)
161             close(private$buffer)
162             self$write(paste(v, collapse='\n'))
163         },
164
165         write = function(content, contentType = "text/html")
166         {
167             if(is.null(private$collection))
168                 stop("ArvadosFile doesn't belong to any collection.")
169
170             REST <- private$collection$getRESTService()
171
172             writeResult <- REST$write(self$getRelativePath(),
173                                       private$collection$uuid,
174                                       content, contentType)
175             writeResult
176         },
177
178         move = function(destination)
179         {
180             if(is.null(private$collection))
181                 stop("ArvadosFile doesn't belong to any collection.")
182
183             destination <- trimFromEnd(destination, "/")
184             nameAndPath <- splitToPathAndName(destination)
185
186             newParent <- private$collection$get(nameAndPath$path)
187
188             if(is.null(newParent))
189                 stop("Unable to get destination subcollection.")
190
191             childWithSameName <- newParent$get(nameAndPath$name)
192
193             if(!is.null(childWithSameName))
194                 stop("Destination already contains content with same name.")
195
196             REST <- private$collection$getRESTService()
197             REST$move(self$getRelativePath(),
198                       paste0(newParent$getRelativePath(), "/", nameAndPath$name),
199                       private$collection$uuid)
200
201             private$dettachFromCurrentParent()
202             private$attachToNewParent(self, newParent)
203
204             private$parent <- newParent
205             private$name <- nameAndPath$name
206
207             self
208         },
209
210         copy = function(destination)
211         {
212             if(is.null(private$collection))
213                 stop("ArvadosFile doesn't belong to any collection.")
214
215             destination <- trimFromEnd(destination, "/")
216             nameAndPath <- splitToPathAndName(destination)
217
218             newParent <- private$collection$get(nameAndPath$path)
219
220             if(is.null(newParent))
221                 stop("Unable to get destination subcollection.")
222
223             childWithSameName <- newParent$get(nameAndPath$name)
224
225             if(!is.null(childWithSameName))
226                 stop("Destination already contains content with same name.")
227
228             REST <- private$collection$getRESTService()
229             REST$copy(self$getRelativePath(),
230                       paste0(newParent$getRelativePath(), "/", nameAndPath$name),
231                       private$collection$uuid)
232
233             newFile <- self$duplicate(nameAndPath$name)
234             newFile$setCollection(self$getCollection())
235             private$attachToNewParent(newFile, newParent)
236             newFile$setParent(newParent)
237
238             newFile
239         },
240
241         duplicate = function(newName = NULL)
242         {
243             name <- if(!is.null(newName)) newName else private$name
244             newFile <- ArvadosFile$new(name)
245             newFile
246         }
247     ),
248
249     private = list(
250
251         name       = NULL,
252         size       = NULL,
253         parent     = NULL,
254         collection = NULL,
255         buffer     = NULL,
256
257         attachToNewParent = function(content, newParent)
258         {
259             # We temporary set parents collection to NULL. This will ensure that
260             # add method doesn't post this file on REST.
261             # We also need to set content's collection to NULL because
262             # add method throws exception if we try to add content that already
263             # belongs to a collection.
264             parentsCollection <- newParent$getCollection()
265             content$setCollection(NULL, setRecursively = FALSE)
266             newParent$setCollection(NULL, setRecursively = FALSE)
267             newParent$add(content)
268             content$setCollection(parentsCollection, setRecursively = FALSE)
269             newParent$setCollection(parentsCollection, setRecursively = FALSE)
270         },
271
272         dettachFromCurrentParent = function()
273         {
274             # We temporary set parents collection to NULL. This will ensure that
275             # remove method doesn't remove this file from REST.
276             parent <- private$parent
277             parentsCollection <- parent$getCollection()
278             parent$setCollection(NULL, setRecursively = FALSE)
279             parent$remove(private$name)
280             parent$setCollection(parentsCollection, setRecursively = FALSE)
281         }
282     ),
283
284     cloneable = FALSE
285 )
286
287 #' print.ArvadosFile
288 #'
289 #' Custom print function for ArvadosFile class
290 #'
291 #' @param x Instance of ArvadosFile class
292 #' @param ... Optional arguments.
293 #' @export
294 print.ArvadosFile = function(x, ...)
295 {
296     collection   <- NULL
297     relativePath <- x$getRelativePath()
298
299     if(!is.null(x$getCollection()))
300     {
301         collection <- x$getCollection()$uuid
302         relativePath <- paste0("/", relativePath)
303     }
304
305     cat(paste0("Type:          ", "\"", "ArvadosFile", "\""), sep = "\n")
306     cat(paste0("Name:          ", "\"", x$getName(),   "\""), sep = "\n")
307     cat(paste0("Relative path: ", "\"", relativePath,  "\""), sep = "\n")
308     cat(paste0("Collection:    ", "\"", collection,    "\""), sep = "\n")
309 }