diff --git a/NAMESPACE b/NAMESPACE index 685ad2e..debb939 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,6 +2,10 @@ S3method(.add_body,json) S3method(.add_body,multipart) +S3method(.as_nectar_auth,"NULL") +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) diff --git a/R/aaa-shared_params.R b/R/aaa-shared_params.R index 6d64f4c..6cb7e53 100644 --- a/R/aaa-shared_params.R +++ b/R/aaa-shared_params.R @@ -6,7 +6,10 @@ #' 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. @@ -33,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 @@ -42,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 diff --git a/R/auth_prepare.R b/R/auth_prepare.R index 89c42cb..2627b75 100644 --- a/R/auth_prepare.R +++ b/R/auth_prepare.R @@ -8,26 +8,53 @@ #' @param auth_fn (`function`) A function to use to authenticate a request. #' @returns A list with class `"nectar_auth"` and elements `auth_fn` and #' `auth_args`. -#' @family opinionated request functions +#' @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) - structure( + .as_nectar_auth( list(auth_fn = auth_fn, auth_args = rlang::list2(...)), - class = "nectar_auth" + call = call ) } .as_nectar_auth <- function(auth, call = rlang::caller_env()) { - if (is.null(auth)) { - return(list(auth_fn = NULL, auth_args = list())) + 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.list <- function(auth, call = rlang::caller_env()) { + if (!("auth_fn" %in% names(auth))) { + return(NextMethod()) } - if (inherits(auth, "nectar_auth")) { + if (setequal(names(auth), c("auth_fn", "auth_args"))) { + class(auth) <- "nectar_auth" return(auth) } + structure( + list( + auth_fn = auth$auth_fn, + auth_args = auth[setdiff(names(auth), "auth_fn")] + ), + 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}.", diff --git a/R/req_auth_api_key.R b/R/req_auth_api_key.R index 19644b1..b384b15 100644 --- a/R/req_auth_api_key.R +++ b/R/req_auth_api_key.R @@ -6,16 +6,9 @@ #' #' @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, the request is returned unchanged when the -#' prepared auth is applied. -#' @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, opinionated request functions #' @export #' #' @examples @@ -69,17 +62,9 @@ req_auth_api_key <- function( #' #' @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, the request is returned unchanged when the -#' prepared auth is applied. -#' @param location (`length-1 character`) Where the API key should be passed. -#' One of `"header"` (default), `"query"`, or `"cookie"`. #' @returns A list with class `"nectar_auth"` and elements `auth_fn` and #' `auth_args`. -#' @family opinionated request functions +#' @family opinionated auth functions #' @export #' #' @examples diff --git a/man/auth_api_key.Rd b/man/auth_api_key.Rd index 79441dd..f1e5377 100644 --- a/man/auth_api_key.Rd +++ b/man/auth_api_key.Rd @@ -44,12 +44,7 @@ This helper creates a reusable authentication object that can be passed to auth_api_key("X-API-Key", api_key = "my-api-key") } \seealso{ -Other opinionated request functions: -\code{\link[=auth_prepare]{auth_prepare()}}, -\code{\link[=req_init]{req_init()}}, -\code{\link[=req_modify]{req_modify()}}, -\code{\link[=req_pagination_policy]{req_pagination_policy()}}, -\code{\link[=req_prepare]{req_prepare()}}, -\code{\link[=req_tidy_policy]{req_tidy_policy()}} +Other opinionated auth functions: +\code{\link[=auth_prepare]{auth_prepare()}} } -\concept{opinionated request functions} +\concept{opinionated auth functions} diff --git a/man/auth_prepare.Rd b/man/auth_prepare.Rd index 369e103..f62f587 100644 --- a/man/auth_prepare.Rd +++ b/man/auth_prepare.Rd @@ -29,12 +29,7 @@ authentication strategy can be reused across requests. auth_prepare(req_auth_api_key, "X-API-Key", api_key = "my-api-key") } \seealso{ -Other opinionated request functions: -\code{\link[=auth_api_key]{auth_api_key()}}, -\code{\link[=req_init]{req_init()}}, -\code{\link[=req_modify]{req_modify()}}, -\code{\link[=req_pagination_policy]{req_pagination_policy()}}, -\code{\link[=req_prepare]{req_prepare()}}, -\code{\link[=req_tidy_policy]{req_tidy_policy()}} +Other opinionated auth functions: +\code{\link[=auth_api_key]{auth_api_key()}} } -\concept{opinionated request functions} +\concept{opinionated auth functions} diff --git a/man/dot-shared-params.Rd b/man/dot-shared-params.Rd index b617565..bd2458c 100644 --- a/man/dot-shared-params.Rd +++ b/man/dot-shared-params.Rd @@ -9,7 +9,10 @@ where a request is coming from. We automatically include information about your package and nectar, but use this to provide additional details. Default \code{NULL}.} -\item{api_key}{(\verb{length-1 character}) The API key to use.} +\item{api_key}{(\verb{length-1 character} or \code{NULL}) The API key to use. If this +value is \code{NULL}, the key will be removed from the request. If this value is +\code{NA} or an empty string, the request is returned unchanged when the +prepared auth is applied.} \item{arg}{(\verb{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 @@ -46,6 +49,9 @@ other than \code{GET} or \code{POST}, supply it. Case is ignored.} \item{mime_type}{(\verb{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.} +\item{location}{(\verb{length-1 character}) Where the API key should be passed. +One of \code{"header"} (default), \code{"query"}, or \code{"cookie"}.} + \item{name}{(\verb{length-1 character}) The name of a package or other thing to add to or remove from the user agent string.} @@ -57,7 +63,8 @@ using one of the iteration helpers described in request by \code{\link[=req_perform_opinionated]{req_perform_opinionated()}} and passed on as \code{next_req} to \code{\link[httr2:req_perform_iterative]{httr2::req_perform_iterative()}}.} -\item{parameter_name}{(\verb{length-1 character}) The name to use for the API key.} +\item{parameter_name}{(\verb{length-1 character}) The name of the parameter to use +in the header, query, or cookie.} \item{path}{(\code{character} or \code{list}) The route to an API endpoint. Optionally, a list or character vector with the path as one or more unnamed arguments diff --git a/man/req_auth_api_key.Rd b/man/req_auth_api_key.Rd index 43d118f..cb97fc5 100644 --- a/man/req_auth_api_key.Rd +++ b/man/req_auth_api_key.Rd @@ -55,3 +55,4 @@ req_auth_api_key(req, "api_key", api_key = "my-api-key", location = "query") # If `api_key` is NULL, the key is removed from the request req_auth_api_key(req, "X-API-Key", api_key = NULL) } +\concept{opinionated auth functions, opinionated request functions} diff --git a/man/req_init.Rd b/man/req_init.Rd index bd03e2e..f30dfbc 100644 --- a/man/req_init.Rd +++ b/man/req_init.Rd @@ -46,8 +46,6 @@ req_init( } \seealso{ Other opinionated request functions: -\code{\link[=auth_api_key]{auth_api_key()}}, -\code{\link[=auth_prepare]{auth_prepare()}}, \code{\link[=req_modify]{req_modify()}}, \code{\link[=req_pagination_policy]{req_pagination_policy()}}, \code{\link[=req_prepare]{req_prepare()}}, diff --git a/man/req_modify.Rd b/man/req_modify.Rd index be7bfc1..3b4dfd8 100644 --- a/man/req_modify.Rd +++ b/man/req_modify.Rd @@ -60,8 +60,6 @@ req_modify(req_base, query = c("param1" = "value1", "param2" = "value2")) } \seealso{ Other opinionated request functions: -\code{\link[=auth_api_key]{auth_api_key()}}, -\code{\link[=auth_prepare]{auth_prepare()}}, \code{\link[=req_init]{req_init()}}, \code{\link[=req_pagination_policy]{req_pagination_policy()}}, \code{\link[=req_prepare]{req_prepare()}}, diff --git a/man/req_pagination_policy.Rd b/man/req_pagination_policy.Rd index 14ed28c..41bfdb6 100644 --- a/man/req_pagination_policy.Rd +++ b/man/req_pagination_policy.Rd @@ -40,8 +40,6 @@ req_pagination_policy(req, httr2::iterate_with_offset("page")) } \seealso{ Other opinionated request functions: -\code{\link[=auth_api_key]{auth_api_key()}}, -\code{\link[=auth_prepare]{auth_prepare()}}, \code{\link[=req_init]{req_init()}}, \code{\link[=req_modify]{req_modify()}}, \code{\link[=req_prepare]{req_prepare()}}, diff --git a/man/req_prepare.Rd b/man/req_prepare.Rd index fb41496..01e95a5 100644 --- a/man/req_prepare.Rd +++ b/man/req_prepare.Rd @@ -93,8 +93,6 @@ req_prepare( } \seealso{ Other opinionated request functions: -\code{\link[=auth_api_key]{auth_api_key()}}, -\code{\link[=auth_prepare]{auth_prepare()}}, \code{\link[=req_init]{req_init()}}, \code{\link[=req_modify]{req_modify()}}, \code{\link[=req_pagination_policy]{req_pagination_policy()}}, diff --git a/man/req_tidy_policy.Rd b/man/req_tidy_policy.Rd index fac0dde..5bf17af 100644 --- a/man/req_tidy_policy.Rd +++ b/man/req_tidy_policy.Rd @@ -40,8 +40,6 @@ req_tidy_policy(req, httr2::resp_body_json, list(simplifyVector = TRUE)) } \seealso{ Other opinionated request functions: -\code{\link[=auth_api_key]{auth_api_key()}}, -\code{\link[=auth_prepare]{auth_prepare()}}, \code{\link[=req_init]{req_init()}}, \code{\link[=req_modify]{req_modify()}}, \code{\link[=req_pagination_policy]{req_pagination_policy()}}, diff --git a/tests/testthat/test-auth_prepare.R b/tests/testthat/test-auth_prepare.R new file mode 100644 index 0000000..ee4734c --- /dev/null +++ b/tests/testthat/test-auth_prepare.R @@ -0,0 +1,9 @@ +test_that("auth_prepare() constructs nectar_auth objects (#81)", { + test_result <- auth_prepare(req_auth_api_key, "parm", api_key = "my_key") + expect_s3_class(test_result, "nectar_auth") + expect_identical(test_result$auth_fn, req_auth_api_key) + expect_identical( + test_result$auth_args, + list("parm", api_key = "my_key") + ) +}) diff --git a/tests/testthat/test-req_auth_api_key.R b/tests/testthat/test-req_auth_api_key.R index f6e92c6..03e00aa 100644 --- a/tests/testthat/test-req_auth_api_key.R +++ b/tests/testthat/test-req_auth_api_key.R @@ -12,17 +12,7 @@ test_that("req_auth_api_key errors informatively with unused arguments", { ) }) -test_that("auth_prepare() constructs nectar_auth objects", { - test_result <- auth_prepare(req_auth_api_key, "parm", api_key = "my_key") - expect_s3_class(test_result, "nectar_auth") - expect_identical(test_result$auth_fn, req_auth_api_key) - expect_identical( - test_result$auth_args, - list("parm", api_key = "my_key") - ) -}) - -test_that("auth_api_key() prepares req_auth_api_key auth", { +test_that("auth_api_key() prepares req_auth_api_key auth (#81)", { test_result <- auth_api_key( parameter_name = "parm", api_key = "my_key", diff --git a/tests/testthat/test-req_prepare.R b/tests/testthat/test-req_prepare.R index c3746f6..ebdf931 100644 --- a/tests/testthat/test-req_prepare.R +++ b/tests/testthat/test-req_prepare.R @@ -153,7 +153,7 @@ test_that("req_prepare() applies tidying", { ) }) -test_that("req_prepare() applies prepared auth", { +test_that("req_prepare() applies prepared auth (#81)", { test_result <- req_prepare( base_url = "https://example.com", auth = auth_prepare(req_auth_api_key, "parm", api_key = "my_key") @@ -164,11 +164,11 @@ test_that("req_prepare() applies prepared auth", { ) }) -test_that("req_prepare() errors for unsupported auth objects", { +test_that("req_prepare() errors for unsupported auth objects (#81)", { expect_error( req_prepare( base_url = "https://example.com", - auth = list(auth_fn = req_auth_api_key, auth_args = list("parm")) + auth = "not_auth" ), class = "nectar-error-unsupported_auth_class" )