Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Style package and minor updates #2

Merged
merged 5 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
^\.\.Rcheck$
^README\.qmd$
^LICENSE\.md$
^openapitools\.json$
2 changes: 1 addition & 1 deletion .openapi-generator/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7.8.0
7.9.0
16 changes: 8 additions & 8 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
Package: laminr.api
Title: R Package Client for the Lamin API
Title: Interface to the 'LaminDB' API
Version: 0.1.0
Authors@R: c(
person("Robrecht", "Cannoodt", , "rcannood@gmail.com", role = c("aut", "cre"),
Authors@R:c(
person("Robrecht", "Cannoodt", email = "robrecht@data-intuitive.com", role = c("aut", "cre"),
comment = c(ORCID = "0000-0003-3641-729X", github = "rcannood")),
person("Luke", "Zappia", , "[email protected]", role = "aut",
comment = c(ORCID = "0000-0001-7744-8565", github = "lazappi"))
person("Luke", "Zappia", email = "[email protected]", role = "aut",
comment = c(ORCID = "0000-0001-7744-8565", github = "lazappi")),
person("Data Intuitive", email = "[email protected]", role = "aut"),
person("Lamin Labs", email = "[email protected]", role = c("aut", "cph"))
)
Description: Client package for accessing the Lamin API. Most content is
generated by the Openapi Generator
https://github.com/openapitools/openapi-generator.
Description: Client package for accessing the Lamin API.
License: Apache License (>= 2)
URL: https://github.com/data-intuitive/laminr.api
BugReports: https://github.com/data-intuitive/laminr.api/issues
Expand Down
176 changes: 104 additions & 72 deletions R/api_client.R
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,7 @@
self$max_retry_attempts <- max_retry_attempts
}
},
#' Prepare to make an API call with the retry logic.
#'

#' @description
#' Prepare to make an API call with the retry logic.
#'
Expand All @@ -141,29 +140,31 @@
#' @param body The HTTP request body.
#' @param stream_callback Callback function to process the data stream
#' @param ... Other optional arguments.
#'
#' @return HTTP response
#' @export
CallApi = function(url, method, query_params, header_params, form_params,
file_params, accepts, content_types,
body, stream_callback = NULL, ...) {

resp <- self$Execute(url, method, query_params, header_params,
form_params, file_params,
accepts, content_types,
body, stream_callback = stream_callback, ...)
form_params, file_params,
accepts, content_types,
body,
stream_callback = stream_callback, ...
)

if (is.null(self$max_retry_attempts)) {
self$max_retry_attempts <- 3
}

if (!is.null(self$retry_status_codes)) {

for (i in 1 : self$max_retry_attempts) {
for (i in 1:self$max_retry_attempts) {
if (resp$status_code %in% self$retry_status_codes) {
Sys.sleep((2 ^ i) + stats::runif(n = 1, min = 0, max = 1))
Sys.sleep((2^i) + stats::runif(n = 1, min = 0, max = 1))
resp <- self$Execute(url, method, query_params, header_params,
form_params, file_params, accepts, content_types,
body, stream_callback = stream_callback, ...)
form_params, file_params, accepts, content_types,
body,
stream_callback = stream_callback, ...
)
} else {
break
}
Expand All @@ -172,8 +173,7 @@

resp
},
#' Make an API call
#'

#' @description
#' Make an API call
#'
Expand All @@ -188,8 +188,8 @@
#' @param body The HTTP request body.
#' @param stream_callback Callback function to process data stream
#' @param ... Other optional arguments.
#'
#' @return HTTP response
#' @export
Execute = function(url, method, query_params, header_params,
form_params, file_params,
accepts, content_types,
Expand All @@ -202,130 +202,161 @@
}

# set HTTP accept header
accept = self$select_header(accepts)
accept <- self$select_header(accepts)
if (!is.null(accept)) {
headers['Accept'] = accept
headers["Accept"] <- accept
}

# set HTTP content-type header
content_type = self$select_header(content_types)
content_type <- self$select_header(content_types)
if (!is.null(content_type)) {
headers['Content-Type'] = content_type
headers["Content-Type"] <- content_type
}

if (typeof(stream_callback) == "closure") { # stream data
if (method == "GET") {
httr::GET(url, query = query_params, headers, http_timeout,
httr::user_agent(self$`user_agent`), write_stream(stream_callback), ...)
httr::GET(url,
query = query_params, headers, http_timeout,
httr::user_agent(self$`user_agent`), write_stream(stream_callback), ...
)
} else if (method == "POST") {
httr::POST(url, query = query_params, headers, body = body,
httr::content_type("application/json"), http_timeout,
httr::user_agent(self$`user_agent`), write_stream(stream_callback), ...)
httr::POST(url,
query = query_params, headers, body = body,
httr::content_type("application/json"), http_timeout,
httr::user_agent(self$`user_agent`), write_stream(stream_callback), ...
)
} else if (method == "PUT") {
httr::PUT(url, query = query_params, headers, body = body,
httr::content_type("application/json"), http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), write_stream(stream_callback), ...)
httr::PUT(url,
query = query_params, headers, body = body,
httr::content_type("application/json"), http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), write_stream(stream_callback), ...
)
} else if (method == "PATCH") {
httr::PATCH(url, query = query_params, headers, body = body,
httr::content_type("application/json"), http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), write_stream(stream_callback), ...)
httr::PATCH(url,
query = query_params, headers, body = body,
httr::content_type("application/json"), http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), write_stream(stream_callback), ...
)
} else if (method == "HEAD") {
httr::HEAD(url, query = query_params, headers, http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), write_stream(stream_callback), ...)
httr::HEAD(url,
query = query_params, headers, http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), write_stream(stream_callback), ...
)
} else if (method == "DELETE") {
httr::DELETE(url, query = query_params, headers, http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), write_stream(stream_callback), ...)
httr::DELETE(url,
query = query_params, headers, http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), write_stream(stream_callback), ...
)
} else {
err_msg <- "Http method must be `GET`, `HEAD`, `OPTIONS`, `POST`, `PATCH`, `PUT` or `DELETE`."
rlang::abort(message = err_msg,
.subclass = "ApiException",
ApiException = ApiException$new(status = 0, reason = err_msg))
rlang::abort(
message = err_msg,
.subclass = "ApiException",
ApiException = ApiException$new(status = 0, reason = err_msg)
)
}
} else { # no streaming
if (method == "GET") {
httr_response <- httr::GET(url, query = query_params, headers, http_timeout,
httr::user_agent(self$`user_agent`), ...)
httr_response <- httr::GET(url,
query = query_params, headers, http_timeout,
httr::user_agent(self$`user_agent`), ...
)
} else if (method == "POST") {
httr_response <- httr::POST(url, query = query_params, headers, body = body,
httr::content_type("application/json"), http_timeout,
httr::user_agent(self$`user_agent`), ...)
httr_response <- httr::POST(url,
query = query_params, headers, body = body,
httr::content_type("application/json"), http_timeout,
httr::user_agent(self$`user_agent`), ...
)
} else if (method == "PUT") {
httr_response <- httr::PUT(url, query = query_params, headers, body = body,
httr::content_type("application/json"), http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), ...)
httr_response <- httr::PUT(url,
query = query_params, headers, body = body,
httr::content_type("application/json"), http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), ...
)
} else if (method == "PATCH") {
httr_response <- httr::PATCH(url, query = query_params, headers, body = body,
httr::content_type("application/json"), http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), ...)
httr_response <- httr::PATCH(url,
query = query_params, headers, body = body,
httr::content_type("application/json"), http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), ...
)
} else if (method == "HEAD") {
httr_response <- httr::HEAD(url, query = query_params, headers, http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), ...)
httr_response <- httr::HEAD(url,
query = query_params, headers, http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), ...
)
} else if (method == "DELETE") {
httr_response <- httr::DELETE(url, query = query_params, headers, http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), ...)
httr_response <- httr::DELETE(url,
query = query_params, headers, http_timeout,
http_timeout, httr::user_agent(self$`user_agent`), ...
)
} else {
err_msg <- "Http method must be `GET`, `HEAD`, `OPTIONS`, `POST`, `PATCH`, `PUT` or `DELETE`."
rlang::abort(message = err_msg,
.subclass = "ApiException",
ApiException = ApiException$new(status = 0, reason = err_msg))
rlang::abort(
message = err_msg,
.subclass = "ApiException",
ApiException = ApiException$new(status = 0, reason = err_msg)
)
}

# return ApiResponse
api_response <- ApiResponse$new()
api_response$status_code <- httr::status_code(httr_response)
api_response$status_code <- httr::status_code(httr_response)
api_response$status_code_desc <- httr::http_status(httr_response)$reason
api_response$response <- httr::content(httr_response, "raw")
api_response$headers <- httr::headers(httr_response)

api_response
}
},
#' Deserialize the content of API response to the given type.
#'

#' @description
#' Deserialize the content of API response to the given type.
#'
#' @param raw_response Raw response.
#' @param return_type R return type.
#' @param pkg_env Package environment.
#'
#' @return Deserialized object.
#' @export
deserialize = function(raw_response, return_type, pkg_env) {
resp_obj <- jsonlite::fromJSON(raw_response)
self$deserializeObj(resp_obj, return_type, pkg_env)
},
#' Deserialize the response from jsonlite object based on the given type
#'

#' @description
#' Deserialize the response from jsonlite object based on the given type
#' by handling complex and nested types by iterating recursively
#' Example return_types will be like "array[integer]", "map(Pet)", "array[map(Tag)]", etc.,
#' Example return_types will be like "array[integer]", "map(Pet)",
#' "array[map(Tag)]", etc.,
#'
#' @param obj Response object.
#' @param return_type R return type.
#' @param pkg_env Package environment.
#'
#' @return Deserialized object.
#' @export
deserializeObj = function(obj, return_type, pkg_env) {
return_obj <- NULL
primitive_types <- c("character", "numeric", "integer", "logical", "complex")

# To handle the "map" type
if (startsWith(return_type, "map(")) {
inner_return_type <- regmatches(return_type,
regexec(pattern = "map\\((.*)\\)", return_type))[[1]][2]
inner_return_type <- regmatches(
return_type,
regexec(pattern = "map\\((.*)\\)", return_type)
)[[1]][2]
return_obj <- lapply(names(obj), function(name) {
self$deserializeObj(obj[[name]], inner_return_type, pkg_env)
})
names(return_obj) <- names(obj)
} else if (startsWith(return_type, "array[")) {
# To handle the "array" type
inner_return_type <- regmatches(return_type,
regexec(pattern = "array\\[(.*)\\]", return_type))[[1]][2]
inner_return_type <- regmatches(
return_type,
regexec(pattern = "array\\[(.*)\\]", return_type)
)[[1]][2]
if (c(inner_return_type) %in% primitive_types) {
return_obj <- vector("list", length = length(obj))
if (length(obj) > 0) {
for (row in 1:length(obj)) {

Check warning on line 359 in R/api_client.R

View workflow job for this annotation

GitHub Actions / R-CMD-check

file=R/api_client.R,line=359,col=25,[seq_linter] 1:length(...) is likely to be wrong in the empty edge case. Use seq_along(...) instead.
return_obj[[row]] <- self$deserializeObj(obj[row], inner_return_type, pkg_env)
}
}
Expand All @@ -333,9 +364,11 @@
if (!is.null(nrow(obj))) {
return_obj <- vector("list", length = nrow(obj))
if (nrow(obj) > 0) {
for (row in 1:nrow(obj)) {

Check warning on line 367 in R/api_client.R

View workflow job for this annotation

GitHub Actions / R-CMD-check

file=R/api_client.R,line=367,col=27,[seq_linter] 1:nrow(...) is likely to be wrong in the empty edge case. Use seq_len(nrow(...)) instead.
return_obj[[row]] <- self$deserializeObj(obj[row, , drop = FALSE],
inner_return_type, pkg_env)
return_obj[[row]] <- self$deserializeObj(
obj[row, , drop = FALSE],
inner_return_type, pkg_env
)
}
}
}
Expand Down Expand Up @@ -367,15 +400,14 @@
}
return_obj
},
#' Return a property header (for accept or content-type).
#'

#' @description
#' Return a property header (for accept or content-type). If JSON-related MIME is found,
#' return it. Otherwise, return the first one, if any.
#'
#' @param headers A list of headers
#'
#' @return A header (e.g. 'application/json')
#' @export
select_header = function(headers) {
if (length(headers) == 0) {
return(invisible(NULL))
Expand Down
10 changes: 3 additions & 7 deletions R/api_exception.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,13 @@ ApiException <- R6::R6Class(
reason = NULL,
body = NULL,
headers = NULL,
#' Initialize a new ApiException class.
#'

#' @description
#' Initialize a new ApiExceptino class.
#' Initialize a new ApiException class.
#'
#' @param status HTTP status.
#' @param reason Reason of the ApiException.
#' @param http_response HTTP response object.
#' @export
initialize = function(status = NULL, reason = NULL, http_response = NULL) {
if (!is.null(http_response)) {
self$status <- http_response$status_code
Expand All @@ -47,13 +45,11 @@ ApiException <- R6::R6Class(
self$headers <- NULL
}
},
#' Returns the string format of ApiException.
#'

#' @description
#' Returns the string format of ApiException.
#'
#' @return the string format of ApiException.
#' @export
toString = function() {
errorMsg <- ""
errorMsg <- paste("status : ", self$status, "\n", sep = "")
Expand Down
Loading