Merge branch 'master' of git.curoverse.com:arvados into 11876-r-sdk
authorFuad Muhic <fmuhic@capeannenterprises.com>
Tue, 5 Dec 2017 16:14:32 +0000 (17:14 +0100)
committerFuad Muhic <fmuhic@capeannenterprises.com>
Tue, 5 Dec 2017 16:14:32 +0000 (17:14 +0100)
Arvados-DCO-1.1-Signed-off-by: Fuad Muhic <fmuhic@capeannenterprises.com>

16 files changed:
.gitignore
sdk/R/.RData [new file with mode: 0644]
sdk/R/.Rbuildignore [new file with mode: 0644]
sdk/R/ArvadosSDK.Rproj [new file with mode: 0644]
sdk/R/DESCRIPTION [new file with mode: 0644]
sdk/R/NAMESPACE [new file with mode: 0644]
sdk/R/R/Arvados.R [new file with mode: 0644]
sdk/R/R/Collection.R [new file with mode: 0644]
sdk/R/R/HttpParser.R [new file with mode: 0644]
sdk/R/R/HttpRequest.R [new file with mode: 0644]
sdk/R/R/arvados_objects.R [new file with mode: 0644]
sdk/R/R/custom_classes.R [new file with mode: 0644]
sdk/R/README [new file with mode: 0644]
sdk/R/man/Arvados-class.Rd [new file with mode: 0644]
sdk/R/man/Collection-class.Rd [new file with mode: 0644]
sdk/R/man/HttrParser-class.Rd [new file with mode: 0644]

index 0e876bb6f4d430eea2a79bbe34b0ee3f37c8a6fc..cbebe67bc03d5e4280f9a46cf72e41060ac4d7dc 100644 (file)
@@ -28,3 +28,4 @@ services/api/config/arvados-clients.yml
 *#*
 .DS_Store
 .vscode
+.Rproj.user
diff --git a/sdk/R/.RData b/sdk/R/.RData
new file mode 100644 (file)
index 0000000..0f875af
Binary files /dev/null and b/sdk/R/.RData differ
diff --git a/sdk/R/.Rbuildignore b/sdk/R/.Rbuildignore
new file mode 100644 (file)
index 0000000..91114bf
--- /dev/null
@@ -0,0 +1,2 @@
+^.*\.Rproj$
+^\.Rproj\.user$
diff --git a/sdk/R/ArvadosSDK.Rproj b/sdk/R/ArvadosSDK.Rproj
new file mode 100644 (file)
index 0000000..a648ce1
--- /dev/null
@@ -0,0 +1,20 @@
+Version: 1.0
+
+RestoreWorkspace: Default
+SaveWorkspace: Default
+AlwaysSaveHistory: Default
+
+EnableCodeIndexing: Yes
+UseSpacesForTab: Yes
+NumSpacesForTab: 4
+Encoding: UTF-8
+
+RnwWeave: Sweave
+LaTeX: pdfLaTeX
+
+AutoAppendNewline: Yes
+StripTrailingWhitespace: Yes
+
+BuildType: Package
+PackageUseDevtools: Yes
+PackageInstallArgs: --no-multiarch --with-keep.source
diff --git a/sdk/R/DESCRIPTION b/sdk/R/DESCRIPTION
new file mode 100644 (file)
index 0000000..cc46f65
--- /dev/null
@@ -0,0 +1,18 @@
+Package: ArvadosSDK
+Type: Package
+Title: What the Package Does (Title Case)
+Version: 0.1.0
+Author: Who wrote it
+Maintainer: The package maintainer <yourself@somewhere.net>
+Description: More about what it does (maybe more than one line)
+    Use four spaces when indenting paragraphs within the Description.
+License: What license is it under?
+Encoding: UTF-8
+LazyData: true
+RoxygenNote: 6.0.1.9000
+Imports:
+    httr,
+    stringr,
+    jsonlite,
+    curl,
+    XML
diff --git a/sdk/R/NAMESPACE b/sdk/R/NAMESPACE
new file mode 100644 (file)
index 0000000..f9e576a
--- /dev/null
@@ -0,0 +1,4 @@
+# Generated by roxygen2: do not edit by hand
+
+export(Arvados)
+exportClasses(Collection)
diff --git a/sdk/R/R/Arvados.R b/sdk/R/R/Arvados.R
new file mode 100644 (file)
index 0000000..abd07f9
--- /dev/null
@@ -0,0 +1,152 @@
+source("./R/HttpRequest.R")
+source("./R/HttpParser.R")
+source("./R/custom_classes.R")
+
+#' Arvados SDK Object
+#'
+#' All Arvados logic is inside this class
+#'
+#' @field token Token represents user authentification token.
+#' @field host Host represents server name we wish to connect to.
+#' @examples arv = Arvados("token", "host_name")
+#' @export Arvados
+Arvados <- setRefClass(
+
+    "Arvados",
+
+    fields = list(
+
+        getToken          = "function",
+        getHostName       = "function",
+
+        #Todo(Fudo): These are hardcoded and for debug only. Remove them later on.
+        getWebDavToken    = "function",
+        getWebDavHostName = "function",
+
+        collection_get    = "function",
+        collection_list   = "function",
+        collection_create = "function",
+        collection_update = "function",
+        collection_delete = "function"
+    ),
+
+    methods = list(
+
+        initialize = function(auth_token = NULL, host_name = NULL) 
+        {
+            # Private state
+            if(!is.null(host_name))
+               Sys.setenv(ARVADOS_API_HOST  = host_name)
+
+            if(!is.null(auth_token))
+                Sys.setenv(ARVADOS_API_TOKEN = auth_token)
+
+            host  <- Sys.getenv("ARVADOS_API_HOST");
+            token <- Sys.getenv("ARVADOS_API_TOKEN");
+
+            if(host == "" | token == "")
+                stop("Please provide host name and authentification token or set ARVADOS_API_HOST and ARVADOS_API_TOKEN environmental variables.")
+
+            version <- "v1"
+            host  <- paste0("https://", host, "/arvados/", version, "/")
+
+            # Public methods
+            getToken <<- function() { token }
+            getHostName <<- function() { host }
+
+            #Todo(Fudo): Hardcoded credentials to WebDAV server. Remove them later
+            getWebDavToken    <<- function() { "4invqy35tf70t7hmvdc83ges8ug9cklhgqq1l8gj2cjn18teuq" }
+            getWebDavHostName <<- function() { "https://collections.4xphq.arvadosapi.com/c=4xphq-4zz18-9d5b0qm4fgijeyi/_/" }
+
+            collection_get <<- function(uuid) 
+            {
+                collection_url <- paste0(host, "collections/", uuid)
+                headers <- list(Authorization = paste("OAuth2", token))
+
+                http <- HttpRequest() 
+                serverResponse <- http$GET(collection_url, headers)
+
+                httpParser <- HttpParser()
+                collection <- httpParser$parseJSONResponse(serverResponse)
+
+                if(!is.null(collection$errors))
+                    stop(collection$errors)       
+
+                collection
+            }
+
+            collection_list <<- function(filters = NULL, limit = 100, offset = 0) 
+            {
+                collection_url <- paste0(host, "collections")
+                headers <- list(Authorization = paste("OAuth2", token))
+
+                http <- HttpRequest() 
+                serverResponse <- http$GET(collection_url, headers, NULL, filters, limit, offset)
+
+                httpParser <- HttpParser()
+                collection <- httpParser$parseJSONResponse(serverResponse)
+
+                if(!is.null(collection$errors))
+                    stop(collection$errors)       
+
+                collection
+            }
+
+            collection_delete <<- function(uuid) 
+            {
+                collection_url <- paste0(host, "collections/", uuid)
+                headers <- list("Authorization" = paste("OAuth2", token),
+                                "Content-Type"  = "application/json")
+
+                http <- HttpRequest() 
+                serverResponse <- http$DELETE(collection_url, headers)
+
+                httpParser <- HttpParser()
+                collection <- httpParser$parseJSONResponse(serverResponse)
+
+                if(!is.null(collection$errors))
+                    stop(collection$errors)       
+
+                collection
+            }
+
+            collection_update <<- function(uuid, body) 
+            {
+                collection_url <- paste0(host, "collections/", uuid)
+                headers <- list("Authorization" = paste("OAuth2", token),
+                                "Content-Type"  = "application/json")
+                body <- jsonlite::toJSON(body, auto_unbox = T)
+
+                http <- HttpRequest() 
+                serverResponse <- http$PUT(collection_url, headers, body)
+
+                httpParser <- HttpParser()
+                collection <- httpParser$parseJSONResponse(serverResponse)
+
+                if(!is.null(collection$errors))
+                    stop(collection$errors)       
+
+                collection
+            }
+
+            collection_create <<- function(body) 
+            {
+                collection_url <- paste0(host, "collections")
+                headers <- list("Authorization" = paste("OAuth2", token),
+                                "Content-Type"  = "application/json")
+                body <- jsonlite::toJSON(body, auto_unbox = T)
+
+                http <- HttpRequest() 
+                serverResponse <- http$POST(collection_url, headers, body)
+
+                httpParser <- HttpParser()
+                collection <- httpParser$parseJSONResponse(serverResponse)
+
+                if(!is.null(collection$errors))
+                    stop(collection$errors)       
+
+                collection
+            }
+        }
+    )
+)
diff --git a/sdk/R/R/Collection.R b/sdk/R/R/Collection.R
new file mode 100644 (file)
index 0000000..cf2a612
--- /dev/null
@@ -0,0 +1,113 @@
+source("./R/Arvados.R")
+source("./R/HttpParser.R")
+
+#' Collection Object
+#' 
+#' @details 
+#' Todo: Update description
+#' Collection
+#' 
+#' @param uuid Object ID
+#' @param etag Object version
+#' @param owner_uuid No description
+#' @param created_at No description
+#' @param modified_by_client_uuid No description
+#' @param modified_by_user_uuid No description
+#' @param modified_at No description
+#' @param portable_data_hash No description
+#' @param replication_desired No description
+#' @param replication_confirmed_at No description
+#' @param replication_confirmed No description
+#' @param updated_at No description
+#' @param manifest_text No description
+#' @param name No description
+#' @param description No description
+#' @param properties No description
+#' @param delete_at No description
+#' @param file_names No description
+#' @param trash_at No description
+#' @param is_trashed No description
+#' 
+#' @export
+Collection <- setRefClass(
+
+    "Collection",
+
+    fields = list(uuid                     = "ANY",
+                  items                    = "ANY",
+                  etag                     = "ANY",
+                  owner_uuid               = "ANY",
+                  created_at               = "ANY",
+                  modified_by_client_uuid  = "ANY",
+                  modified_by_user_uuid    = "ANY",
+                  modified_at              = "ANY",
+                  portable_data_hash       = "ANY",
+                  replication_desired      = "ANY",
+                  replication_confirmed_at = "ANY",
+                  replication_confirmed    = "ANY",
+                  updated_at               = "ANY",
+                  manifest_text            = "ANY",
+                  name                     = "ANY",
+                  description              = "ANY",
+                  properties               = "ANY",
+                  delete_at                = "ANY",
+                  file_names               = "ANY",
+                  trash_at                 = "ANY",
+                  is_trashed               = "ANY",
+
+                  getCollectionContent = "function"
+    ),
+
+    methods = list(
+
+        initialize = function(api, uuid) 
+        {
+
+            result <- api$collection_get(uuid)
+            
+            # Private members
+            uuid                     <<- result$uuid                               
+            etag                     <<- result$etag                               
+            owner_uuid               <<- result$owner_uuid                         
+            created_at               <<- result$created_at                         
+            modified_by_client_uuid  <<- result$modified_by_client_uuid            
+            modified_by_user_uuid    <<- result$modified_by_user_uuid              
+            modified_at              <<- result$modified_at                        
+            portable_data_hash       <<- result$portable_data_hash                 
+            replication_desired      <<- result$replication_desired                
+            replication_confirmed_at <<- result$replication_confirmed_at           
+            replication_confirmed    <<- result$replication_confirmed              
+            updated_at               <<- result$updated_at                         
+            manifest_text            <<- result$manifest_text                      
+            name                     <<- result$name                               
+            description              <<- result$description                        
+            properties               <<- result$properties                         
+            delete_at                <<- result$delete_at                          
+            file_names               <<- result$file_names                         
+            trash_at                 <<- result$trash_at                           
+            is_trashed               <<- result$is_trashed                         
+
+
+            #Public methods
+
+            # Private methods
+            getCollectionContent <<- function()
+            {
+                #TODO(Fudo): Use proper URL here.
+                uri <- URLencode(api$getWebDavHostName())
+
+                # fetch directory listing via curl and parse XML response
+                h <- curl::new_handle()
+                curl::handle_setopt(h, customrequest = "PROPFIND")
+
+                #TODO(Fudo): Use proper token here.
+                curl::handle_setheaders(h, "Authorization" = paste("OAuth2", api$getWebDavToken()))
+                response <- curl::curl_fetch_memory(uri, h)
+
+                HttpParser()$parseWebDAVResponse(response, uri)
+            }
+
+            items  <<- getCollectionContent()
+        }
+    )
+)
diff --git a/sdk/R/R/HttpParser.R b/sdk/R/R/HttpParser.R
new file mode 100644 (file)
index 0000000..bbe0a60
--- /dev/null
@@ -0,0 +1,46 @@
+#' HttpParser
+#'
+HttpParser <- setRefClass(
+
+    "HttrParser",
+
+    fields = list(
+    ),
+
+    methods = list(
+        initialize = function() 
+        {
+        },
+
+        parseCollectionGet = function(server_response) 
+        {
+            parsed_response <- httr::content(server_response, as = "parsed", type = "application/json")
+
+            #Todo(Fudo): Create new Collection object and populate it
+        },
+
+        parseJSONResponse = function(server_response) 
+        {
+            parsed_response <- httr::content(server_response, as = "parsed", type = "application/json")
+
+            #Todo(Fudo): Create new Collection object and populate it
+        },
+
+        parseWebDAVResponse = function(response, uri)
+        {
+            #Todo(Fudo): Move this to HttpParser.
+            text <- rawToChar(response$content)
+            doc <- XML::xmlParse(text, asText=TRUE)
+
+            # calculate relative paths
+            base <- paste(paste("/", strsplit(uri, "/")[[1]][-1:-3], sep="", collapse=""), "/", sep="")
+            result <- unlist(
+                XML::xpathApply(doc, "//D:response/D:href", function(node) {
+                    sub(base, "", URLdecode(XML::xmlValue(node)), fixed=TRUE)
+                })
+            )
+            result[result != ""]
+        }
+
+    )
+)
diff --git a/sdk/R/R/HttpRequest.R b/sdk/R/R/HttpRequest.R
new file mode 100644 (file)
index 0000000..eecf0c1
--- /dev/null
@@ -0,0 +1,130 @@
+source("./R/custom_classes.R")
+
+HttpRequest <- setRefClass(
+
+    "HttrRequest",
+
+    fields = list(
+
+        GET    = "function",
+        PUT    = "function",
+        POST   = "function",
+        DELETE = "function"
+    ),
+
+    methods = list(
+        initialize = function() 
+        {
+            # Public methods
+            GET <<- function(url, headers = NULL, body = NULL,
+                             queryFilters = NULL, limit = 100, offset = 0)
+            {
+                print(limit)
+                headers <- httr::add_headers(unlist(headers))
+                query <- .createQuery(queryFilters, limit, offset)
+                url <- paste0(url, query)
+                print(url)
+
+                serverResponse <- httr::GET(url = url, config = headers)
+            }
+
+            PUT <<- function(url, headers = NULL, body = NULL,
+                             queryFilters = NULL, limit = 100, offset = 0)
+            {
+                headers <- httr::add_headers(unlist(headers))
+                query <- .createQuery(queryFilters, limit, offset)
+                url <- paste0(url, query)
+
+                serverResponse <- httr::PUT(url = url, config = headers, body = body)
+            }
+
+            POST <<- function(url, headers = NULL, body = NULL,
+                              queryFilters = NULL, limit = 100, offset = 0)
+            {
+                headers <- httr::add_headers(unlist(headers))
+                query <- .createQuery(queryFilters, limit, offset)
+                url <- paste0(url, query)
+
+                serverResponse <- httr::POST(url = url, config = headers, body = body)
+            }
+
+            DELETE <<- function(url, headers = NULL, body = NULL,
+                             queryFilters = NULL, limit = 100, offset = 0)
+            {
+                headers <- httr::add_headers(unlist(headers))
+                query <- .createQuery(queryFilters, limit, offset)
+                url <- paste0(url, query)
+
+                serverResponse <- httr::DELETE(url = url, config = headers)
+            }
+
+            # Private methods
+            .createQuery <- function(filters, limit, offset)
+            {
+                finalQuery <- "?alt=json"
+
+                if(!is.null(filters))
+                {
+                    filters <- sapply(filters, function(filter)
+                    {
+                        if(length(filter) != 3)
+                            stop("Filter list must have exacthey 3 elements.")
+
+                        attributeAndOperator = filter[c(1, 2)]
+                        filterList = filter[[3]]
+                        filterListIsPrimitive = TRUE
+                        if(length(filterList) > 1)
+                            filterListIsPrimitive = FALSE
+
+                        attributeAndOperator <- sapply(attributeAndOperator, function(component) {
+                            component <- paste0("\"", component, "\"")
+                        })
+
+                        filterList <- sapply(unlist(filterList), function(filter) {
+                            filter <- paste0("\"", filter, "\"")
+                        })
+
+                        filterList <- paste(filterList, collapse = ",+")
+
+                        if(!filterListIsPrimitive)
+                            filterList <- paste0("[", filterList, "]")
+
+                        filter <- c(attributeAndOperator, filterList)
+
+                        queryParameter <- paste(filter, collapse = ",+")
+                        queryParameter <- paste0("[", queryParameter, "]")
+            
+                    })
+
+                    filters <- paste(filters, collapse = ",+")
+                    filters <- paste0("[", filters, "]")
+
+                    encodedQuery <- URLencode(filters, reserved = T, repeated = T)
+
+                    finalQuery <- paste0(finalQuery, "&filters=", encodedQuery)
+
+                    #Todo(Fudo): This is a hack for now. Find a proper solution.
+                    finalQuery <- stringr::str_replace_all(finalQuery, "%2B", "+")
+                }
+
+                if(!is.null(limit))
+                {
+                    if(!is.numeric(limit))
+                        stop("Limit must be a numeric type.")
+                    
+                    finalQuery <- paste0(finalQuery, "&limit=", limit)
+                }
+
+                if(!is.null(offset))
+                {
+                    if(!is.numeric(offset))
+                        stop("Offset must be a numeric type.")
+                    
+                    finalQuery <- paste0(finalQuery, "&offset=", offset)
+                }
+
+                finalQuery
+            }
+        }
+    )
+)
diff --git a/sdk/R/R/arvados_objects.R b/sdk/R/R/arvados_objects.R
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/sdk/R/R/custom_classes.R b/sdk/R/R/custom_classes.R
new file mode 100644 (file)
index 0000000..22f1f76
--- /dev/null
@@ -0,0 +1,3 @@
+setClassUnion("characterOrNull", c("character", "NULL"))
+setClassUnion("listOrNull", c("list", "NULL"))
+setClassUnion("numericOrNull", c("numeric", "NULL"))
diff --git a/sdk/R/README b/sdk/R/README
new file mode 100644 (file)
index 0000000..0d954b6
--- /dev/null
@@ -0,0 +1 @@
+R SDK for Arvados
\ No newline at end of file
diff --git a/sdk/R/man/Arvados-class.Rd b/sdk/R/man/Arvados-class.Rd
new file mode 100644 (file)
index 0000000..315f851
--- /dev/null
@@ -0,0 +1,22 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/Arvados.R
+\docType{class}
+\name{Arvados-class}
+\alias{Arvados-class}
+\alias{Arvados}
+\title{Arvados SDK Object}
+\description{
+All Arvados logic is inside this class
+}
+\section{Fields}{
+
+\describe{
+\item{\code{token}}{Token represents user authentification token.}
+
+\item{\code{host}}{Host represents server name we wish to connect to.}
+}}
+
+
+\examples{
+arv = Arvados("token", "host_name")
+}
diff --git a/sdk/R/man/Collection-class.Rd b/sdk/R/man/Collection-class.Rd
new file mode 100644 (file)
index 0000000..79a771a
--- /dev/null
@@ -0,0 +1,56 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/Collection.R
+\docType{class}
+\name{Collection-class}
+\alias{Collection-class}
+\alias{Collection}
+\title{Collection Object}
+\arguments{
+\item{uuid}{Object ID}
+
+\item{etag}{Object version}
+
+\item{owner_uuid}{No description}
+
+\item{created_at}{No description}
+
+\item{modified_by_client_uuid}{No description}
+
+\item{modified_by_user_uuid}{No description}
+
+\item{modified_at}{No description}
+
+\item{portable_data_hash}{No description}
+
+\item{replication_desired}{No description}
+
+\item{replication_confirmed_at}{No description}
+
+\item{replication_confirmed}{No description}
+
+\item{updated_at}{No description}
+
+\item{manifest_text}{No description}
+
+\item{name}{No description}
+
+\item{description}{No description}
+
+\item{properties}{No description}
+
+\item{delete_at}{No description}
+
+\item{file_names}{No description}
+
+\item{trash_at}{No description}
+
+\item{is_trashed}{No description}
+}
+\description{
+Collection Object
+}
+\details{
+Todo: Update description
+Collection
+}
+
diff --git a/sdk/R/man/HttrParser-class.Rd b/sdk/R/man/HttrParser-class.Rd
new file mode 100644 (file)
index 0000000..5a6d1c7
--- /dev/null
@@ -0,0 +1,11 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/HttpParser.R
+\docType{class}
+\name{HttrParser-class}
+\alias{HttrParser-class}
+\alias{HttpParser}
+\title{HttpParser}
+\description{
+HttpParser
+}
+