1 getAPIDocument <- function(){
2 url <- "https://4xphq.arvadosapi.com/discovery/v1/apis/arvados/v1/rest"
3 serverResponse <- httr::RETRY("GET", url = url)
5 httr::content(serverResponse, as = "parsed", type = "application/json")
9 generateAPI <- function()
11 #TODO: Consider passing discovery document URL as parameter.
12 #TODO: Consider passing location where to create new files.
13 JSONDocument <- getAPIDocument()
15 generateArvadosClasses(JSONDocument)
16 generateArvadosAPIClass(JSONDocument)
19 #NOTE: Arvados class generation:
21 generateArvadosAPIClass <- function(discoveryDocument)
23 classMetaData <- discoveryDocument$schemas
24 methodResources <- discoveryDocument$resources
25 resourceNames <- names(methodResources)
27 doc <- generateMethodsDocumentation(methodResources, resourceNames)
28 arvadosAPIHeader <- generateAPIClassHeader()
29 arvadosClassMethods <- generateClassContent(methodResources,
30 resourceNames, classMetaData)
31 arvadosAPIFooter <- generateAPIClassFooter()
33 arvadosClass <- c(doc,
38 #TODO: Save to a file or load in memory?
39 fileConn <- file("./R/Arvados.R", "w")
40 writeLines(unlist(arvadosClass), fileConn)
45 generateAPIClassHeader <- function()
48 "Arvados <- R6::R6Class(",
54 "\t\tinitialize = function(authToken = NULL, hostName = NULL, numRetries = 0)",
56 "\t\t\tif(!is.null(hostName))",
57 "\t\t\t\tSys.setenv(ARVADOS_API_HOST = hostName)",
59 "\t\t\tif(!is.null(authToken))",
60 "\t\t\t\tSys.setenv(ARVADOS_API_TOKEN = authToken)",
62 "\t\t\thostName <- Sys.getenv(\"ARVADOS_API_HOST\")",
63 "\t\t\ttoken <- Sys.getenv(\"ARVADOS_API_TOKEN\")",
65 "\t\t\tif(hostName == \"\" | token == \"\")",
66 "\t\t\t\tstop(paste(\"Please provide host name and authentification token\",",
67 "\t\t\t\t\t\t \"or set ARVADOS_API_HOST and ARVADOS_API_TOKEN\",",
68 "\t\t\t\t\t\t \"environment variables.\"))",
70 "\t\t\tprivate$token <- token",
71 "\t\t\tprivate$host <- paste0(\"https://\", hostName, \"/arvados/v1/\")",
72 "\t\t\tprivate$numRetries <- numRetries",
73 "\t\t\tprivate$REST <- RESTService$new(token, hostName,",
74 "\t\t\t HttpRequest$new(), HttpParser$new(),",
80 generateClassContent <- function(methodResources, resourceNames, classMetaData)
82 arvadosMethods <- Map(function(resource, resourceName)
84 methodNames <- names(resource$methods)
86 functions <- Map(function(methodMetaData, methodName)
88 methodName <- paste0(resourceName, ".", methodName)
89 createMethod(methodName, methodMetaData, classMetaData)
91 }, resource$methods, methodNames)
93 unlist(unname(functions))
95 }, methodResources, resourceNames)
100 generateAPIClassFooter <- function()
102 c("\t\tgetHostName = function() private$host,",
103 "\t\tgetToken = function() private$token,",
104 "\t\tsetRESTService = function(newREST) private$REST <- newREST",
112 "\t\tnumRetries = NULL",
115 "\tcloneable = FALSE",
119 createMethod <- function(name, methodMetaData, classMetaData)
121 args <- getMethodArguments(methodMetaData)
122 signature <- getMethodSignature(name, args)
123 body <- getMethodBody(methodMetaData, classMetaData)
131 #TODO: Make sure that arguments that are required always go first.
132 # This is not the case if request$required is false.
133 getMethodArguments <- function(methodMetaData)
135 request <- methodMetaData$request
138 if(!is.null(request))
141 requestArgs <- names(request$properties)
143 requestArgs <- paste(names(request$properties), "=", "NULL")
146 argNames <- names(methodMetaData$parameters)
148 args <- sapply(argNames, function(argName)
150 arg <- methodMetaData$parameters[[argName]]
154 if(!is.null(arg$default))
155 return(paste0(argName, " = ", "\"", arg$default, "\""))
157 return(paste(argName, "=", "NULL"))
166 getMethodSignature <- function(methodName, args)
168 collapsedArgs <- paste0(args, collapse = ", ")
169 lineLengthLimit <- 40
171 if(nchar(collapsedArgs) > lineLengthLimit)
173 return(paste0("\t\t",
174 formatArgs(paste(methodName, "= function("),
175 "\t", args, ")", lineLengthLimit)))
179 return(paste0("\t\t", methodName, " = function(", collapsedArgs, ")"))
183 getMethodBody <- function(methodMetaData, classMetaData)
185 url <- getRequestURL(methodMetaData)
186 headers <- getRequestHeaders()
187 requestQueryList <- getRequestQueryList(methodMetaData)
188 requestBody <- getRequestBody(methodMetaData)
189 request <- getRequest(methodMetaData)
190 response <- getResponse(methodMetaData)
191 errorCheck <- getErrorCheckingCode()
192 returnObject <- getReturnObject(methodMetaData, classMetaData)
193 returnStatement <- getReturnObjectValidationCode()
199 request, response, "",
204 paste0("\t\t\t", body)
207 getRequestURL <- function(methodMetaData)
209 endPoint <- methodMetaData$path
210 endPoint <- stringr::str_replace_all(endPoint, "\\{", "${")
211 url <- c(paste0("endPoint <- stringr::str_interp(\"", endPoint, "\")"),
212 paste0("url <- paste0(private$host, endPoint)"))
216 getRequestHeaders <- function()
218 c("headers <- list(Authorization = paste(\"OAuth2\", private$token), ",
219 " \"Content-Type\" = \"application/json\")")
222 getRequestQueryList <- function(methodMetaData)
224 args <- names(methodMetaData$parameters)
226 if(length(args) == 0)
227 return("queryArgs <- NULL")
229 args <- sapply(args, function(arg) paste0(arg, " = ", arg))
230 collapsedArgs <- paste0(args, collapse = ", ")
232 if(nchar(collapsedArgs) > 40)
233 return(formatArgs("queryArgs <- list(", "\t", args, ")", 40))
235 return(paste0("queryArgs <- list(", collapsedArgs, ")"))
238 getRequestBody <- function(methodMetaData)
240 request <- methodMetaData$request
242 if(is.null(request) || !request$required)
243 return("body <- NULL")
245 requestParameterName <- names(request$properties)[1]
246 paste0("body <- ", requestParameterName, "$toJSON()")
249 getRequest <- function(methodMetaData)
251 method <- methodMetaData$httpMethod
252 c(paste0("response <- private$REST$http$exec(\"", method, "\", url, headers, body,"),
253 " queryArgs, private$numRetries)")
256 getResponse <- function(methodMetaData)
258 "resource <- private$REST$httpParser$parseJSONResponse(response)"
261 getErrorCheckingCode <- function()
263 c("if(!is.null(resource$errors))",
264 "\tstop(resource$errors)")
267 getReturnObject <- function(methodMetaData, classMetaData)
269 returnClass <- methodMetaData$response[["$ref"]]
270 classArguments <- getReturnClassArguments(returnClass, classMetaData)
272 if(returnClass == "Collection")
273 return(c(formatArgs("result <- Collection$new(", "\t",
274 classArguments, ")", 40),
276 "result$setRESTService(private$REST)"))
278 formatArgs(paste0("result <- ", returnClass, "$new("),
279 "\t", classArguments, ")", 40)
282 getReturnObjectValidationCode <- function()
284 c("if(result$isEmpty())",
290 getReturnClassArguments <- function(className, classMetaData)
292 classArguments <- unique(names(classMetaData[[className]]$properties))
294 arguments <- sapply(classArguments, function(arg)
296 paste0(arg, " = resource$", arg)
303 #NOTE: Arvados class documentation:
305 generateMethodsDocumentation <- function(methodResources, resourceNames)
307 methodsDoc <- unlist(unname(Map(function(resource, resourceName)
309 methodNames <- names(resource$methods)
311 methodDoc <- Map(function(methodMetaData, methodName)
313 methodName <- paste0(resourceName, ".", methodName)
314 getMethodDocumentation(methodName, methodMetaData)
316 }, resource$methods, methodNames)
318 unlist(unname(methodDoc))
320 }, methodResources, resourceNames)))
325 getMethodDocumentation <- function(methodName, methodMetaData)
327 name <- paste("#' @name", methodName)
328 usage <- getMethodUsage(methodName, methodMetaData)
329 description <- paste("#'", methodName, "is a method defined in Arvados class.")
330 params <- getMethodDescription(methodMetaData)
331 returnValue <- paste("#' @return", methodMetaData$response[["$ref"]], "object.")
343 getMethodUsage <- function(methodName, methodMetaData)
345 lineLengthLimit <- 40
346 args <- getMethodArguments(methodMetaData)
347 c(formatArgs(paste0("#' @usage arv$", methodName,
348 "("), "#' \t", args, ")", lineLengthLimit))
351 getMethodDescription <- function(methodMetaData)
353 request <- methodMetaData$request
356 if(!is.null(request))
358 requestDoc <- unname(unlist(sapply(request$properties, function(prop)
360 className <- sapply(prop, function(ref) ref)
361 objectName <- paste0(tolower(substr(className, 1, 1)),
362 substr(className, 2, nchar(className)))
363 paste("#' @param", objectName, className, "object.")
367 argNames <- names(methodMetaData$parameters)
369 argsDoc <- unname(unlist(sapply(argNames, function(argName)
371 arg <- methodMetaData$parameters[[argName]]
372 argDescription <- arg$description
373 paste("#' @param", argName, argDescription)
376 c(requestDoc, argsDoc)
379 #NOTE: API Classes generation:
381 generateArvadosClasses <- function(resources)
383 classes <- sapply(resources$schemas, function(classSchema)
385 #NOTE: Collection is implemented manually.
386 if(classSchema$id != "Collection")
387 getArvadosClass(classSchema)
391 fileConn <- file("./R/ArvadosClasses.R", "w")
392 writeLines(unlist(classes), fileConn)
397 getArvadosClass <- function(classSchema)
399 name <- classSchema$id
400 fields <- unique(names(classSchema$properties))
401 constructorArgs <- paste(fields, "= NULL")
402 documentation <- getClassDocumentation(classSchema, constructorArgs)
404 classString <- c(documentation,
405 paste0(name, " <- R6::R6Class("),
407 paste0("\t\"", name, "\","),
410 paste0("\t\t", fields, " = NULL,"),
412 paste0("\t\t", formatArgs("initialize = function(", "\t\t",
413 constructorArgs, ")", 40)),
415 paste0("\t\t\tself$", fields, " <- ", fields),
417 paste0("\t\t\t", formatArgs("private$classFields <- c(", "\t",
418 paste0("\"", fields, "\""), ")", 40)),
421 "\t\ttoJSON = function() {",
422 "\t\t\tfields <- sapply(private$classFields, function(field)",
424 "\t\t\t\tself[[field]]",
425 "\t\t\t}, USE.NAMES = TRUE)",
427 paste0("\t\t\tjsonlite::toJSON(list(\"", tolower(name), "\" =
428 Filter(Negate(is.null), fields)), auto_unbox = TRUE)"),
431 "\t\tisEmpty = function() {",
432 "\t\t\tfields <- sapply(private$classFields,",
433 "\t\t\t function(field) self[[field]])",
435 paste0("\t\t\tif(any(sapply(fields, function(field) !is.null(field)",
436 " && field != \"\")))"),
444 "\t\tclassFields = NULL",
447 "\tcloneable = FALSE",
452 #NOTE: API Classes documentation:
454 getClassDocumentation <- function(classSchema, constructorArgs)
456 name <- classSchema$id
457 description <- classSchema$description
458 nameLowercaseFirstLetter <- paste0(tolower(substr(name, 1, 1)),
459 substr(name, 2, nchar(name)))
460 c(paste0("#' ", name),
462 paste0("#' ", description),
464 "#' @section Usage:",
465 formatArgs(paste0("#' \\preformatted{",
466 nameLowercaseFirstLetter, " -> ", name, "$new("),
467 "#' \t", constructorArgs, ")", 50),
470 paste0("#' @section Arguments:"),
472 paste0("#' ", getClassArgumentDescription(classSchema)),
475 paste0("#' @name ", name),
481 getClassArgumentDescription <- function(classSchema)
483 argDoc <- sapply(classSchema$properties, function(arg)
485 paste0("{", arg$description, "}")
488 paste0("\\item{", names(classSchema$properties), "}", argDoc)
491 #NOTE: Utility functions:
493 formatArgs <- function(prependAtStart, prependToEachSplit,
494 args, appendAtEnd, lineLength)
498 args[1:(length(args) - 1)] <- paste0(args[1:(length(args) - 1)], ",")
501 args[1] <- paste0(prependAtStart, args[1])
502 args[length(args)] <- paste0(args[length(args)], appendAtEnd)
504 argsLength <- length(args)
508 while(index <= argsLength)
513 while(nchar(line) < lineLength && index <= argsLength)
515 line <- paste(line, args[index])
519 argLines <- c(argLines, line)
522 argLines <- unlist(argLines)
523 argLinesLen <- length(argLines)
526 argLines[2:argLinesLen] <- paste0(prependToEachSplit, argLines[2:argLinesLen])