Skip to content

Commit

Permalink
Export the sub-functions. (#18)
Browse files Browse the repository at this point in the history
Closes #10.
  • Loading branch information
jonthegeek authored Mar 12, 2024
1 parent 789beb2 commit f9f8b01
Show file tree
Hide file tree
Showing 14 changed files with 239 additions and 179 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ Remotes:
Config/testthat/edition: 3
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.3
RoxygenNote: 7.3.1
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ S3method(.add_body,json)
S3method(.add_body,multipart)
export(call_api)
export(compact_nested_list)
export(req_perform)
export(req_prepare)
export(security_api_key)
export(stabilize_string)
importFrom(fs,path)
importFrom(httr2,req_perform)
importFrom(rlang,":=")
8 changes: 6 additions & 2 deletions R/aaa_shared.R
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,20 @@
#' @param case The case standard to apply. The possible values are
#' self-descriptive. Defaults to "snake_case".
#' @param depth The current recursion depth.
#' @param path The route to an API endpoint. Optionally, a list with the path
#' plus variables to [glue::glue()] into the path.
#' @param method If the method is something other than GET or POST, supply it.
#' Case is ignored.
#' @param mime_type A character scalar indicating the mime type of any files
#' present in the body. Some APIs allow you to leave this as NULL for them to
#' guess.
#' @param path The route to an API endpoint. Optionally, a list with the path
#' plus variables to [glue::glue()] into the path.
#' @param query An optional list of parameters to pass in the query portion of
#' the request.
#' @param req An [httr2::request()] object.
#' @param security_fn A function to use to authenticate the request. By default
#' (`NULL`), no authentication is performed.
#' @param security_args An optional list of arguments to the `security_fn`
#' function.
#' @param user_agent A string to identify where this request is coming from.
#' It's polite to set the user agent to identify your package, such as
#' "MyPackage (https://mypackage.com)".
Expand Down
72 changes: 11 additions & 61 deletions R/call.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@
#'
#' This function implements an opinionated framework for making API calls. It is
#' intended to be used inside an API client package. It serves as a wrapper
#' around the `req_` family of functions, such as [httr2::request()].
#' around the `req_` family of functions, such as [httr2::request()], as well as
#' [httr2::req_perform()] and, by default, [httr2::resp_body_json()].
#'
#' @inheritParams .shared-parameters
#' @param security_fn A function to use to authenticate the request. By default
#' (`NULL`), no authentication is performed.
#' @param security_args An optional list of arguments to the `security_fn`
#' function.
#' @param response_parser A function to use to parse the server response.
#' Defaults to [httr2::resp_body_json()], since JSON responses are common. Set
#' this to `NULL` to return the raw response from [httr2::req_perform()].
Expand All @@ -28,72 +25,21 @@ call_api <- function(base_url,
response_parser = httr2::resp_body_json,
response_parser_args = list(),
user_agent = "nectar (https://nectar.api2r.org)") {
req <- .req_prep(
req <- req_prepare(
base_url = base_url,
path = path,
query = query,
body = body,
method = method,
mime_type = mime_type,
user_agent = user_agent,
security_fn = security_fn,
security_args = security_args
method = method,
user_agent = user_agent
)
resp <- .resp_get(req)
req <- .req_security_apply(req, security_fn, security_args)
resp <- req_perform(req)
resp <- .resp_parse(resp, response_parser, response_parser_args)
return(resp)
}

.resp_get <- function(req) {
return(httr2::req_perform(req)) # nocov
}

.req_prep <- function(base_url,
path,
query,
body,
method,
mime_type,
user_agent,
security_fn,
security_args) {
req <- httr2::request(base_url)
req <- .req_path_append(req, path)
req <- .req_query_flatten(req, query)
req <- .req_body_auto(req, body, mime_type)
req <- .req_method_apply(req, method)
req <- .req_user_agent_apply(req, user_agent)
req <- .req_security_apply(req, security_fn, security_args)
return(req)
}

.req_path_append <- function(req, path) {
if (length(path)) {
path <- rlang::inject(glue::glue(!!!path))
}
return(httr2::req_url_path_append(req, path))
}

.req_method_apply <- function(req, method) {
if (!length(method)) {
# I'm pretty sure this is a current httr2 or httptest2 bug. NULL methods
# fail during testing.
if (length(req$body)) {
method <- "POST"
} else {
method <- "GET"
}
}
return(httr2::req_method(req, method))
}

.req_user_agent_apply <- function(req, user_agent) {
if (length(user_agent)) {
req <- httr2::req_user_agent(req, user_agent)
}
return(req)
}

.req_security_apply <- function(req, security_fn, security_args) {
if (length(security_fn)) {
req <- rlang::inject(
Expand All @@ -103,6 +49,10 @@ call_api <- function(base_url,
return(req)
}

#' @export
#' @importFrom httr2 req_perform
httr2::req_perform

.resp_parse <- function(resp, response_parser, response_parser_args) {
if (length(response_parser)) {
resp <- .resp_parse_apply(
Expand Down
55 changes: 55 additions & 0 deletions R/req.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#' Prepare a request for an API
#'
#' This function implements an opinionated framework for preparing an API
#' request. It is intended to be used inside an API client package. It serves as
#' a wrapper around the `req_` family of functions, such as [httr2::request()].
#'
#' @inheritParams .shared-parameters
#' @inheritParams rlang::args_dots_empty
#'
#' @return A [httr2::request()] object.
#' @export
req_prepare <- function(base_url,
...,
path = NULL,
query = NULL,
body = NULL,
mime_type = NULL,
method = NULL,
user_agent = "nectar (https://nectar.api2r.org)") {
rlang::check_dots_empty()
req <- httr2::request(base_url)
req <- .req_path_append(req, path)
req <- .req_query_flatten(req, query)
req <- .req_body_auto(req, body, mime_type)
req <- .req_method_apply(req, method)
req <- .req_user_agent_apply(req, user_agent)
return(req)
}

.req_path_append <- function(req, path) {
if (length(path)) {
path <- rlang::inject(glue::glue(!!!path))
}
return(httr2::req_url_path_append(req, path))
}

.req_method_apply <- function(req, method) {
if (!length(method)) {
# I'm pretty sure this is a current httr2 or httptest2 bug. NULL methods
# fail during testing.
if (length(req$body)) {
method <- "POST"
} else {
method <- "GET"
}
}
return(httr2::req_method(req, method))
}

.req_user_agent_apply <- function(req, user_agent) {
if (length(user_agent)) {
req <- httr2::req_user_agent(req, user_agent)
}
return(req)
}
3 changes: 2 additions & 1 deletion man/call_api.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions man/dot-shared-parameters.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions man/reexports.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 53 additions & 0 deletions man/req_prepare.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions tests/testthat/_snaps/req_body.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# bodies with paths are handled properly

Code
test_result <- call_api(base_url = "https://example.com", body = list(foo = "bar",
baz = fs::path(test_path("img-test.png"))), response_parser = NULL,
user_agent = NULL)
test_result <- req_prepare(base_url = "https://example.com", body = list(foo = "bar",
baz = fs::path(test_path("img-test.png"))), user_agent = NULL)
test_result$body
Output
$data
Expand Down
Loading

0 comments on commit f9f8b01

Please sign in to comment.