Skip to content
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
7 changes: 7 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

S3method(.add_body,json)
S3method(.add_body,multipart)
S3method(.as_nectar_auth,"NULL")
S3method(.as_nectar_auth,"function")
S3method(.as_nectar_auth,default)
S3method(.as_nectar_auth,list)
S3method(.as_nectar_auth,nectar_auth)
S3method(.as_nectar_request,default)
S3method(.as_nectar_request,httr2_request)
S3method(.as_nectar_request,nectar_request)
Expand All @@ -12,6 +17,8 @@ S3method(resp_tidy,default)
S3method(resp_tidy,httr2_response)
S3method(resp_tidy,list)
S3method(resp_tidy,nectar_responses)
export(auth_api_key)
export(auth_prepare)
export(choose_pagination_fn)
export(compact_nested_list)
export(do_if_fn_defined)
Expand Down
16 changes: 10 additions & 6 deletions R/aaa-shared_params.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
#' where a request is coming from. We automatically include information about
#' your package and nectar, but use this to provide additional details.
#' Default `NULL`.
#' @param api_key (`length-1 character`) The API key to use.
#' @param api_key (`length-1 character` or `NULL`) The API key to use. If this
#' value is `NULL`, the key will be removed from the request. If this value is
#' `NA` or an empty string, the request is returned unchanged when the
#' prepared auth is applied.
#' @param arg (`length-1 character`) An argument name as a string. This argument
#' will be mentioned in error messages as the input that is at the origin of a
#' problem.
#' @param auth_args (`list`) An optional list of arguments to the `auth_fn`
#' function.
#' @param auth_fn (`function`) A function to use to authenticate the request. By
#' default (`NULL`), no authentication is performed.
#' @param auth (`nectar_auth` or `NULL`) Authentication prepared with
#' [auth_prepare()]. By default (`NULL`), no authentication is performed.
#' @param base_url (`length-1 character`) The part of the url that is shared by
#' all calls to the API. In some cases there may be a family of base URLs,
#' from which you will need to choose one.
Expand All @@ -35,6 +36,8 @@
#' other than `GET` or `POST`, supply it. Case is ignored.
#' @param mime_type (`length-1 character`) The mime type of any files present in
#' the body. Some APIs allow you to leave this as NULL for them to guess.
#' @param location (`length-1 character`) Where the API key should be passed.
#' One of `"header"` (default), `"query"`, or `"cookie"`.
#' @param name (`length-1 character`) The name of a package or other thing to
#' add to or remove from the user agent string.
#' @param pagination_fn (`function`) A function that takes the previous response
Expand All @@ -44,7 +47,8 @@
#' [httr2::iterate_with_offset()]. This function will be extracted from the
#' request by [req_perform_opinionated()] and passed on as `next_req` to
#' [httr2::req_perform_iterative()].
#' @param parameter_name (`length-1 character`) The name to use for the API key.
#' @param parameter_name (`length-1 character`) The name of the parameter to use
#' in the header, query, or cookie.
#' @param path (`character` or `list`) The route to an API endpoint. Optionally,
#' a list or character vector with the path as one or more unnamed arguments
#' (which will be concatenated with "/") plus named arguments to
Expand Down
76 changes: 76 additions & 0 deletions R/auth_prepare.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#' Prepare authentication independent of a request
#'
#' This constructor stores an authentication function and arguments so the same
#' authentication strategy can be reused across requests.
#'
#' @inheritParams .shared-params
#' @param auth_fn (`function`) A function to use to authenticate a request.
#' @param ... (`any`) Arguments to pass to `auth_fn`.
#' @returns A list with class `"nectar_auth"` and elements `auth_fn` and
#' `auth_args`.
#' @family opinionated auth functions
#' @export
#'
#' @examples
#' auth_prepare(req_auth_api_key, "X-API-Key", api_key = "my-api-key")
auth_prepare <- function(auth_fn, ..., call = rlang::caller_env()) {
auth_fn <- rlang::as_function(auth_fn, call = call)
.as_nectar_auth(
list(auth_fn = auth_fn, auth_args = rlang::list2(...)),
call = call
)
}

.as_nectar_auth <- function(auth, call = rlang::caller_env()) {
UseMethod(".as_nectar_auth")
}

#' @export
.as_nectar_auth.nectar_auth <- function(auth, call = rlang::caller_env()) {
return(auth)
}

#' @export
.as_nectar_auth.NULL <- function(auth, call = rlang::caller_env()) {
return(list(auth_fn = NULL, auth_args = list()))
}

#' @export
.as_nectar_auth.function <- function(auth, call = rlang::caller_env()) {
.as_nectar_auth(list(auth_fn = auth), call = call)
}

#' @export
.as_nectar_auth.list <- function(auth, call = rlang::caller_env()) {
if (!("auth_fn" %in% names(auth))) {
return(NextMethod())
}
auth_args <- stbl::to_lst(auth$auth_args) %||% list()
if (setequal(names(auth), c("auth_fn", "auth_args"))) {
auth$auth_args <- auth_args
class(auth) <- "nectar_auth"
return(auth)
}
structure(
list(
auth_fn = auth$auth_fn,
auth_args = c(
auth_args,
auth[setdiff(names(auth), c("auth_fn", "auth_args"))]
)
),
class = "nectar_auth"
)
}

#' @export
.as_nectar_auth.default <- function(auth, call = rlang::caller_env()) {
.nectar_abort(
c(
"{.arg {auth}} must be `NULL` or a {.cls nectar_auth}.",
x = "{.arg {auth}} is {.obj_type_friendly {auth}}."
),
subclass = "unsupported_auth_class",
call = call
)
}
Comment thread
jonthegeek marked this conversation as resolved.
41 changes: 34 additions & 7 deletions R/req_auth_api_key.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,10 @@
#'
#' @inheritParams .shared-params
#' @inheritParams rlang::args_dots_empty
#' @param parameter_name (`length-1 character`) The name of the parameter to use
#' in the header, query, or cookie.
#' @param api_key (`length-1 character` or `NULL`) The API key to use. If this
#' value is `NULL`, the key will be removed from the request. If this value is
#' `NA` or an empty string, `req` is returned unchanged.
#' @param location (`length-1 character`) Where the API key should be passed.
#' One of `"header"` (default), `"query"`, or `"cookie"`.
#'
#' @inherit .shared-request return
#' @family opinionated auth functions
#' @family opinionated request functions
#' @export
#'
#' @examples
Expand Down Expand Up @@ -60,3 +55,35 @@ req_auth_api_key <- function(
req <- rlang::exec(req_api_key_set, req, !!parameter_name := api_key)
return(req)
}

#' Prepare API key authentication independent of a request
#'
#' This helper creates a reusable authentication object that can be passed to
#' [req_prepare()] via `auth`.
#'
#' @inheritParams .shared-params
#' @inheritParams rlang::args_dots_empty
#' @returns A list with class `"nectar_auth"` and elements `auth_fn` and
#' `auth_args`.
#' @family opinionated auth functions
#' @export
#'
#' @examples
#' auth_api_key("X-API-Key", api_key = "my-api-key")
auth_api_key <- function(
parameter_name,
...,
api_key = NULL,
location = c("header", "query", "cookie"),
call = rlang::caller_env()
) {
rlang::check_dots_empty(call = call)
location <- rlang::arg_match(location, error_call = call)
auth_prepare(
req_auth_api_key,
parameter_name = parameter_name,
api_key = api_key,
location = location,
call = call
)
}
6 changes: 3 additions & 3 deletions R/req_prepare.R
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ req_prepare <- function(
mime_type = NULL,
method = NULL,
additional_user_agent = NULL,
auth_fn = NULL,
auth_args = list(),
auth = NULL,
tidy_fn = NULL,
tidy_args = list(),
pagination_fn = NULL,
Expand All @@ -48,7 +47,8 @@ req_prepare <- function(
method = method,
call = call
)
req <- do_if_fn_defined(req, auth_fn, !!!auth_args, call = call)
auth <- .as_nectar_auth(auth, call = call)
req <- do_if_fn_defined(req, auth$auth_fn, !!!auth$auth_args, call = call)
if (length(pagination_fn)) {
req <- req_pagination_policy(req, pagination_fn, call = call)
}
Expand Down
51 changes: 51 additions & 0 deletions man/auth_api_key.Rd

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

36 changes: 36 additions & 0 deletions man/auth_prepare.Rd

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

18 changes: 11 additions & 7 deletions man/dot-shared-params.Rd

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

17 changes: 16 additions & 1 deletion man/req_auth_api_key.Rd

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

1 change: 1 addition & 0 deletions man/req_init.Rd

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

1 change: 1 addition & 0 deletions man/req_modify.Rd

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

1 change: 1 addition & 0 deletions man/req_pagination_policy.Rd

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

Loading
Loading